二、应广单片机点亮一个灯

        上一节介绍基础,由于时间仓促,写得十分模糊。这一节开始从一个个实际例子,向大家展示应广单片机的各项功能。由于看本文的读者层次不相同,这里全部按照初学者视之。

废话少说,上例程:

//========================================================
#include "extern.h"

KeyBounce  equ 30*1000    //30ms

void FPPA0(void)
{
.ADJUST_IC SYSCLK=IHRC/2;
pac = 0b_1111_1110;
pa = 0b_0000_0000;
paph = 0b_0000_0001;
$ padier 0b_1111_1001;

while (1)
{
    if( pa.0 == 0 )
    {
        if(--KeyBounce  == 0)
        {
            if( pa.0 == 0 )
            {
                A = pa.3;
                A ~= 0b00001000;
                pa = A; 
            }
        }
    }
}
}
//========================================================

以上例程非常简单

#include "extern.h"

是程序的头文件。

void FPPA0(void)

相当于程序的 main() 函数。

.ADJUST_IC SYSCLK=IHRC/2;

是对芯片时钟的设定,具体参考使用的单片机芯片来设定。

pac = 0b_1111_1110;
pa = 0b_0000_0000;
paph = 0b_0000_0001;
$ padier 0b_1111_1001;

是对芯片引脚设定。这里设定 pa.0 为输入,启用上拉电阻。具体设置可参考芯片手册。

while (1)

无限循环函数,与大多数函数相似。

if( pa.0 == 0 )
判断 pa.0 是否被人按下了。如果不是,直接跳过。如果被按下。

if(--KeyBounce  == 0)

程序连续判断了30 * 1000 次后还是 被按下。执行:

A = pa.3;
A ~= 0b00001000;
pa = A; 
取反 pa.3 的电平。程序的外围器件将 pa.3 的引脚连接 LED 灯,取反 pa.3 的电平时,实现点亮或者熄灭一颗灯的效果。












#include "extern.h" #define PA_IO 0b0110_0111 //数据寄存器 #define PAC_IO 0b0000_0000 //PAC控制寄存器,定义端口输入或输出模式,1/0——输出/输入 #define PAPH_IO 0b0111_0111 //PAPH:上拉控制寄存器,1/0——1启用,0停用 #define PAPH_LED 0b0000_0110 #define Padier_IO 0b1111_1001 //Padier:数字输入启用寄存器,1/0——启用/停用,停用无法唤醒系统 //0位为1,启用PA0数字输入、系统唤醒和中断请求。 //=============================固定脚位设定=========================================================== #define Key PA.5 #define LEDCOM PA.6 #define LED12 PA.0 #define LED34 PA.4 #define LEDOUT1 PA.3 //===================================================================================================== //暂时没看懂的变量 #define PAC_LEDCOM 0x40 #define PAC_LED34 0x10 #define PAC_LED12 0x01 //===================================================================================================== void TM2_PWM(void); //===================================================================================================== word Point; //equ 0x00-0x01 word reload_T16; //equ 0x02-0x03 //===================================================================================================== //-----------变量1------------- WORD R_Flag; //---------定时延迟类---------- bit b_1ms_Flag : R_Flag.0 //1ms定时延迟 bit b_1s_Flag : R_Flag.1 //定时1s标志位 bit b_2FS_Flag : R_Flag.3 //2min定时延迟 bit b_sleep : R_Flag.5 //休眠标志位 bit b_Once : R_Flag.6 //进行无极调试时,第一次设置占空比 //---------按键标志类---------- bit b_Key_DownUp : R_Flag.7 //按键按下标志位 bit b_Key_SignLong : R_Flag.8 //按键长时间按下标志位 bit b_LED_Flag : R_Flag.9 //第三档LED闪烁标志位 bit R_Pwm_DownUp : R_Flag.10 //PWM无极调数标志位 bit b_12s_Flag : R_Flag.11 //12s定时标志位 //---------电源标志类---------- bit b_CHRG_Start : R_Flag.12 //10ms中,充电检测启动标志位 bit b_VBAT_Time2 : R_Flag.15 //电池充到4.1v之后开启倒计时 //===================================================================================================== //4个led控制 byte R_Flag1; //8位,通过他的每一位来操作四个LED bit b_LedOne : R_Flag1.0 //显示LED1 bit b_LedTwo : R_Flag1.1 //显示LED1+LED2 bit b_LedThree : R_Flag1.2 //显示LED1+LED2+LED3 bit b_LedFour : R_Flag1.3 //显示LED1+LED2+LED3+LED4 bit b_OneLedFlicker : R_Flag1.4 //LED1闪烁 bit b_TwoLedFlicker : R_Flag1.5 //LED2闪烁 bit b_ThreeLedFlicker : R_Flag1.6 //LED3闪烁 bit b_FourLedFlicker : R_Flag1.7 //LED4闪烁 //===================================================================================================== //休眠函数缓存参数 byte R_temp1; byte R_LedStatus; //LED指示10ms循环 byte R_PacBuffer; //IO_PAC缓存器 //==================================充电控制模块参数=================================================== //充电标志参数 bit b_charge : R_Flag.4 //===================================================================================================== //野生延迟函数 WORD R_500msCnt; WORD R_Key_Icnt; WORD R_1sCnt; //T16定时器使用的1s延迟寄存器 //野生定时寄存器 byte R_16ms; byte R_12s; byte R_duty; //占空比缓存寄存器 //充电LED开启标志位 byte R_Flag2; bit b_Chage1 : R_Flag2.0 //LED1开启标志 bit b_Chage2 : R_Flag2.1 //LED1+LED2开启标志 bit b_Chage3 : R_Flag2.2 //LED1+LED2+LED3开启标志 bit b_Chage4 : R_Flag2.3 //LED1+LED2+LED3+LED4开启标志 //bit b_LedDisable1 : R_Flag2.4 //LED1闪烁标志 //bit b_LedDisable2 : R_Flag2.5 //LED2闪烁标志 //bit b_LedDisable3 : R_Flag2.6 //LED3闪烁标志 //bit b_LedDisable4 : R_Flag2.7 //LED4闪烁标志 //充电LED控制参数 byte R_GpccBuffer1; //比较器比较缓存计数器1 byte R_GpccBuffer2; //比较器比较缓存计数器2 byte R_GpccBuffer3; //比较器比较缓存计数器3 //===================================================================================================== byte R_KeyTempCnt; //按键功能计数器 byte R_timeStop; //开机之后5s关机 byte R_timeMode; //等待时间 byte R_timeTemp; //等待时间 byte R_threeModeTemp; //第三档计数标志位 //Led初始化函数 void LedPlayInit(void) { if(b_LedOne) { R_LedStatus++; //LED缓冲标志 if(R_LedStatus == 2) { PAC = PAC_IO; //0b0000_0000,端口控制寄存器,控制引脚的输入模式或输出模式,0/1:输入/输出 PAPH = PAPH_LED; //0b0000_0110,上拉控制寄存器,0/1:停用/启用 b_CHRG_Start = 0; //10ms,充电检测启动标志位 } else if(R_LedStatus == 8) { if(b_OneLedFlicker) //led1闪烁关闭 { PAC = PAC_IO; //0b0000_0000,端口控制寄存器,控制引脚的输入模式或输出模式,0/1:输入/输出 } else { R_PacBuffer = PAC_IO; //IO_PAC缓存器 PAC = R_PacBuffer | PAC_LEDCOM | PAC_LED12; //PAC:端口控制寄存器,控制引脚的输入模式或输出模式,0/1:输入/输出 //PAC_LEDCOM---0x40--0b0100_0000 //PAC_LED12---0x01--0b0000_0001 LED12 = 0;//PA.0脚 LEDCOM = 1;//PA.6脚 } } else if(R_LedStatus == 10)//LED缓冲标志 { PAC = PAC_IO; //0b0000_0000,端口控制寄存器,控制引脚的输入模式或输出模式,0/1:输入/输出 PAPH = PAPH_IO; //0b0000_0110,上拉控制寄存器,0/1:停用/启用 R_LedStatus = 0; b_CHRG_Start= 1; //10ms,充电检测启动标志位 } } //----------------------------------------------------------------------------------------------------------- if(b_LedTwo) { R_LedStatus++; //LED缓冲标志 if(R_LedStatus == 2) { PAC = PAC_IO; //PAC:端口控制寄存器,控制引脚的输入模式或输出模式 PAPH = PAPH_LED; //PAPH:上拉控制寄存器 b_CHRG_Start= 0; //10ms,充电检测启动标志位 } else if(R_LedStatus == 6) { if(b_TwoLedFlicker) { PAC = PAC_IO; } else { R_PacBuffer = PAC_IO; PAC = R_PacBuffer | PAC_LEDCOM | PAC_LED12; LED12 = 1;//PA.0脚 LEDCOM = 0;//PA.6脚 } } else if(R_LedStatus == 8) { R_PacBuffer = PAC_IO; PAC = R_PacBuffer | PAC_LEDCOM | PAC_LED12; LED12 = 1;//PA.0脚 LEDCOM = 0;//PA.6脚 } else if(R_LedStatus == 10) { PAC = PAC_IO; PAPH = PAPH_IO; //0b0111_0111 //PAPH:上拉控制寄存器,1/0——1启用,0停用 R_LedStatus = 0; //LED缓冲标志 b_CHRG_Start= 1; //10ms,充电检测启动标志位 } } if(b_LedThree) //显示LED1+LED2+LED3 { R_LedStatus++; //LED缓冲标志 if(R_LedStatus == 2) { PAC =PAC_IO; //APC端口控制寄存器,控制引脚的输入或输出模式 PAPH =PAPH_LED; //上拉控制寄存器 b_CHRG_Start=0; //10ms,充电检测启动标志位 } else if(R_LedStatus == 4) { if(b_ThreeLedFlicker) { PAC = PAC_IO; //0b0000_0000,PAC端口控制寄存器,控制引脚的输入或输出模式,0/1——输入/输出 } else { R_PacBuffer = PAC;//缓冲APC的值 PAC = R_PacBuffer | PAC_LEDCOM | PAC_LED34; LED34 = 0; //PA.4 LEDCOM = 1; //PA.6 } } else if(R_LedStatus == 6) { R_PacBuffer = PAC_IO; PAC = R_PacBuffer | PAC_LEDCOM | PAC_LED12; LED12 = 1; //PA.0 LEDCOM = 0; //PA.6 } else if(R_LedStatus == 8) { LED12 = 0; LEDCOM = 1; } else if(R_LedStatus == 10) { PAC = PAC_IO; PAPH = PAPH_IO; R_LedStatus = 0; //复位 b_CHRG_Start = 1; } } //----------------------------------------------------------------------------------------------------------- if(b_LedFour) { R_LedStatus++; if(R_LedStatus == 2) { b_CHRG_Start = 0; //10ms,充电检测启动标志位 PAPH = PAPH_LED; //PAPH端口上拉控制寄存器 if(b_FourLedFlicker) { PAC = PAC_IO; //端口控制寄存器 } else { R_PacBuffer = PAC; PAC = R_PacBuffer | PAC_LEDCOM | PAC_LED34; LED34 = 1; LEDCOM = 0; } } else if(R_LedStatus == 4) { R_PacBuffer = PAC; PAC = R_PacBuffer | PAC_LEDCOM | PAC_LED34; LED34 = 0; LEDCOM = 1; } else if(R_LedStatus == 6) { R_PacBuffer = PAC_IO; PAC = R_PacBuffer | PAC_LEDCOM | PAC_LED12; LED12 = 1; LEDCOM = 0; } else if(R_LedStatus == 8) { LED12 = 0; LEDCOM = 1; } else if(R_LedStatus == 10) { PAC = PAC_IO; PAPH = PAPH_IO; R_LedStatus = 0; b_CHRG_Start = 1; } } } //四个LED控制函数 void LedPlay(void) { GPCS = 0b0010_0011; //比较器选择寄存器 GPCC = 0b1_0_0_0_010_0; //比较器控制寄存器 //启用比较器——正输入 < 负输入——比较器的结果没有TM2_CLK采样输出——比较器输出的结果没有反极性 //010:内部1.2vband-gap参考电压——Vr作为比较器正输入来源 .delay 100; if(GPCC.6) //GPCC的比较结果,正输入 > 负输入(1) { R_GpccBuffer1++; //比较器比较缓存计数器 R_GpccBuffer2 = 0; R_GpccBuffer3 = 0; if(R_GpccBuffer1 >= 50) { R_GpccBuffer1 = 0; if(b_charge) //充电进行标志 { b_Chage1 = 1; //显示LED1 b_Chage2 = 1; //显示LED+LED2 b_Chage3 = 1; //显示LED1+LED2+LED3 if(b_Chage4 == 0)//显示LED1+LED2+LED3+LED4 { goto LedPlayIO4; } } else { goto LedPlayIO4; } if(b_2FS_Flag == 0) //2min定时延时标志 { if(b_charge) //充电进行标志 { b_VBAT_Time2 = 1; //电池充电到4.1v之后开启倒计时 } } } goto LedPlay_ret; LedPlayIO4: b_LedOne = 0; //显示LED1 b_LedTwo = 0; //显示LED1+LED2 b_LedThree = 0; //显示LED1+LED2+LED3 b_LedFour = 1; //显示LED1+LED2+LED3+LED4 goto LedPlay_ret; } //--------------------------------------------------------------------------- GPCS = 0b0010_0100; //停用比较器输出,停用比较器唤醒——0100:比较器参考电压 GPCC = 0b1_0_0_0_010_0; //比较器控制寄存器 //启用比较器——(正输入 < 负输入):0——比较器的结果没有TM2_CLK采样输出——比较器输出结果没有反极性 //负输入来源010:内部1.2vband-gap参考电压——正输入来源Vr .delay 100; if(GPCC.6) { R_GpccBuffer2++; //比较器比较缓存计数器 R_GpccBuffer1 = 0; R_GpccBuffer3 = 0; if(R_GpccBuffer2 >= 50) { R_GpccBuffer2 = 0; if(b_charge) //充电进行标志 { b_Chage1 = 1; b_Chage2 = 1; if(b_Chage3 == 0) { goto LedPlayIO3; } } else { goto LedPlayIO3; } } goto LedPlay_ret; LedPlayIO3: b_LedOne = 0; b_LedTwo = 0; b_LedThree = 1; b_LedFour = 0; goto LedPlay_ret; } //--------------------------------------------------------------------------- GPCS = 0b0010_0111;//比较器输出停用——比较器唤醒停用———0111:比较器参考电压 GPCC = 0b1_0_0_0_010_0; //停用比较器——(正输入 < 负输入)0——比较器的结果没有TM2_CLK采样输出——比较器输出的结果没有反极性 //010:比较器负输入来源Vr——正输入来源Vr .delay 100; if(GPCC.6) //比较器的结果。正输入 > 负输入(1) { R_GpccBuffer3++; //比较器比较缓存计数器 R_GpccBuffer1 = 0; R_GpccBuffer2 = 0; if(R_GpccBuffer3 >= 50) { R_GpccBuffer3 = 0; if(b_charge) { b_Chage1 = 1; //显示LED1 if(b_Chage2 == 0) //显示LED1+LED2 { goto LedPlayIO2; } } else { goto LedPlayIO2; } } goto LedPlay_ret; LedPlayIO2: b_LedOne = 0; b_LedTwo = 1; //显示LED1+LED2 b_LedThree = 0; b_LedFour = 0; goto LedPlay_ret; } //--------------------------------------------------------------------------- GPCS = 0b0010_1011; GPCC = 0b1_0_0_0_010_0; .delay 100; if(GPCC.6) //如果正输入 > 负输入(1) { R_GpccBuffer1++; R_GpccBuffer2 = 0; R_GpccBuffer3 = 0; if(R_GpccBuffer1 >= 50) { R_GpccBuffer1 = 0; if(b_charge)//充电进行标志 { if(b_Chage1 == 0) { goto LedPlayIO; } } else { goto LedPlayIO; } } goto LedPlay_ret; LedPlayIO: b_LedOne = 1; //显示LED1 b_LedTwo = 0; b_LedThree = 0; b_LedFour = 0; goto LedPlay_ret; } LedPlay_ret: if(b_charge) //充电进行标志 { R_500msCnt++; //500ms延迟标志 if(R_500msCnt >= 500) { R_500msCnt = 0; if(b_LedOne == 1) { if(b_OneLedFlicker)//LED1闪烁标志位 b_OneLedFlicker = 0; else b_OneLedFlicker = 1; } if(b_LedTwo == 1) //显示Led1+Led2 { if(b_TwoLedFlicker) //led2闪烁 b_TwoLedFlicker = 0; else b_ThreeLedFlicker = 1; } if(b_LedThree == 1) { if(b_ThreeLedFlicker) b_ThreeLedFlicker = 0; else b_ThreeLedFlicker = 1; } if(b_LedFour == 1) { if(b_FourLedFlicker) b_FourLedFlicker = 0; else b_FourLedFlicker = 1; } } } else//没有充电 { b_OneLedFlicker = 0; b_TwoLedFlicker = 0; b_ThreeLedFlicker = 0; b_FourLedFlicker = 0; } LedPlayInit(); //LED初始化函数 } //=============还没有看明白的函数================= void LedDownUp(void) { R_16ms++; //16ms定时寄存器 if(R_16ms >= 16) { R_16ms = 0; if(R_Pwm_DownUp == 0)//PWM无极调数标志位,其他地方只有在休眠时置0 { if(R_duty > 76)//R_duty:占空比缓冲器 { R_duty--; } else { R_duty = 76; R_Pwm_DownUp = 1; } TM2B = R_duty;//TM2B:timer2上限寄存器 } else { if(R_duty < 255) { R_duty++; } else { R_duty = 255; R_Pwm_DownUp = 0; } TM2B = R_duty; } } } //==================功能函数======================// //控制电筒Led功能 void FunctionKey(void) { if(b_Key_DownUp == 1)//按钮按下标志位,该值主要是ScanKey函数控制 { b_Key_DownUp = 0;//复位标志位 R_KeyTempCnt++;//按键功能位 if(R_KeyTempCnt > 3) R_KeyTempCnt = 0;//大于3就复位,因为只有3个功能 //没看懂,先注释掉 if(R_timeStop >= 10) { R_KeyTempCnt = 0; R_timeStop = 0; } } switch(R_KeyTempCnt) { case 0: LED12 = 0; //PA.0 LEDCOM = 0; //PA.6 b_sleep = 1; //休眠标志位,进入休眠模式 //R_sleepCnt = 0; //休眠倒计时计数器 //b_Once = 0; //第一次无极调试时,设置第一次占空比 //R_Pwm_DownUp = 0; LEDOUT1 = 0; //PA.3 //R_timeStop = 0; $ TM2C STOP; //停止Timer2计数器 break; case 1: //===================唤醒前的准备=========================== if(b_Once == 0) //设置第一次占空比标志 { b_Once = 1; $ TM2C IHRC,PA3,PWM; R_duty = 255; //R_duty占空比缓存 TM2B = R_duty; //Timer2上限寄存器,TM2B=255 //计算公式:time = 1/[时钟源/(分辨率*预分频*分频)] //time=1/[16/(2^8*1*3)]=0.000048s=0.048ms //0.000048*255=0.012s=12ms,12ms后TM2B产生第一次中断 R_timeMode = 0; } //=========================================================== //----------------唤醒后----------------------- b_sleep = 0;//退出休眠模式 if(b_Key_SignLong == 1)//Scankey函数控制值,为1说明按下按钮超过10ms { LedDownUp(); } if(b_12s_Flag == 1)//在T16中断函数中12s后会置1 { b_12s_Flag = 0; if(R_timeMode == 0) { R_timeTemp++; if(R_timeTemp >= 25) { R_timeTemp = 30; if(R_duty > 128) R_duty--; else R_timeMode = 1; } } else if(R_timeMode == 1) { R_timeTemp++; if(R_timeTemp >= 3) { R_timeTemp = 0; if(R_duty > 76) R_duty--; else R_timeMode =2; } } } TM2B = R_duty; break; case 2: TM2B = 127;//降低占空比来降低亮度 //---------初始化3档功能位所需参数---------- R_threeModeTemp = 0; b_LED_Flag = 0; break; case 3: TM2B = 255;//提高占空比,恢复亮度 R_16ms++;//16ms定时计数器 if(R_16ms >= 39)//39ms后 { R_16ms = 0;// R_threeModeTemp++;//在case2中被初始化为0 if(b_Led_Flag == 0)//在case2中被初始化为0 { if(R_threeModeTemp.0 == 1) { $ TM2C Stop; } else { $ TM2C IHRC,PA3,PWM; } } if(R_threeModeTemp == 25) { b_Led_Flag = 1; $ TM2C Stop; } else if(R_threeModeTemp >= 27) { b_Led_Flag = 0; R_threeModeTemp = 0; } } break; default: break; } } //==============按钮检测函数=======================// void Scankey(void) { if(!Key)//按钮按下为低电平 { R_Key_Icnt++;//按键消抖计数器 if(R_Key_Icnt >= 1000)//判断按钮是否真的按下 { R_Key_Icnt = 1001; b_Key_SignLong = 1;//按钮长时间按下标志位 } } else { if(b_Key_SignLong != 1) { if(R_Key_Icnt > 35) { b_Key_DownUp = 1;//按键按下标志位置1,按键按下 } } //复位标志位 R_Key_Icnt = 0; b_Key_SignLong = 0; } } //初始化T16时钟 void T16_Init() { reload_T16 = 16383 - 1000; stt16 reload_T16; //给T16装载初始值,设定计数器初始值reload_T16,当计数器累加超过设定中断源时产生中断; //计算公式为[1/(时钟源/分频器)]*(中断源-reload_T16)=[1/(1M/1)]*(2^14-(16383-1000))=1ms $ T16M SYSCLK,/1,bit14; //选择时钟源,分频器,中断源。(当选择位由低到高或者由高到低时,发生中断事件); //时钟源选择可以选择STOP, SYSCLK, PA4_F, IHRC, EOSC, ILRC, PA0_F;分频器可选择/1, /4, /16, /64 //中断源可选择BIT8, BIT9, BIT10, BIT11, BIT12, BIT13, BIT14, BIT15 INTRQ = 0; //中断请求寄存器 INTEN.T16 = 1; //INTEN中断允许寄存器,开T16中断 ENGINT; //开全局中断 } //初始化TM2计数器 void TM2_PWM(void) { TM2CT = 0; //tm2ct计数寄存器,通过每次进中断修改该值来获得精准的时间 TM2B = 178; //上限寄存器,计数值超出上限寄存器的设定值时会产生中断; $ TM2C IHRC,PA3,PWM; //选择时钟源,输出口,模式 //时钟源可选择STOP, SYSCLK, IHRC, EOSC, ILRC, GPCRS, PA0_R, PA0_F, PB0_R, PB0_F, PA4_R, PA4_F //注:产生中断时不需要输出脚和反极性; $ TM2S 8bit,/1,/3; //分辨率选择,可以选择8bit或6bit;时钟预分频选择,有/1, /4, /16, /64;时钟分频选择/1 ~ /32 //计算公式为 1/{时钟源*2/[2*(TM2B+1)*预分频*分频]} // = 1/(1M*2/(2*TM2B+1)*1*3) = 1/(1000000*2/(2*(178+1)*1*3)) = 0.000537s //若要定时更精确的时间,可以更改tm2ct的值,一般建议tm2ct值为0 //计算公式为 1/{时钟源*2/[2*(TM2B+1-tm2ct)*预分频*分频]} $ TM2C STOP; //停止Timer2计数器 } //初始化各个端口 void io_Initialization(void) { PA = PA_IO; //0b0110_0111 PAC = PAC_IO; //0b0000_0000 PAPH= PAPH_IO; //0b0111_0111 $ PADIER Padier_IO; //0b1111_1001 } void FPPA0 (void) { Initialization: .ADJUST_IC SYSCLK=IHRC/16,IHRC=16MHz,VDD=3.655V; // SYSCLK=IHRC/4 // Insert Initial Code CLKMD.En_WatchDog = 0; //关闭看门狗功能 wdreset; //看门狗清零 DISGINT; //关全局中断 io_Initialization(); //初始化各个端口函数 //清零系统RAM L_ClearRAM: Point=_SYS(RAM_SIZE) - 1; //获取系统RAM大小 a = 0; do { *Point = a; //清零RAM }while(--Point$0); //移动指针 //$针对寄存器操作的一种特殊语法,主要功能是对特定寄存器存储位置的值进行自减操作 L_RFInitialization: //初始化T16,Timer2 T16_Init(); TM2_PWM(); //====初始化四个LED控制 b_LedOne = 0; b_LedTwo = 0; b_LedThree = 0; b_LedFour = 0; R_temp1 = 255; //--------------------------------------------------------------------------------// do { if(b_1ms_Flag == 1) { b_1ms_Flag = 0; LedPlay(); } }while(--R_temp1); while (1) { // ... // wdreset; if(b_1ms_Flag == 1) { b_1ms_Flag = 0; Scankey(); FunctionKey(); } } } void Interrupt (void) { pushaf; if (Intrq.T16) { // T16 Trig // User can add code Intrq.T16 = 0; //... stt16 reload_T16;//16383-1000 b_1ms_Flag = 1; R_1sCnt++; if(R_1sCnt >= 1000)//定时1s { b_1s_Flag = 1; //定时1s的标志 R_1sCnt = 0; //定时1s计数器 R_12s++; //定时12s累加器 if(R_12s >= 12) { R_12s = 0; b_12s_Flag = 1; //12定时标记 } } } popaf; } 给我讲解一下这个程序
最新发布
06-05
### 嵌入式C程序功能与逻辑解析 以下是对包含端口初始化、LED控制、按键扫描和定时器中断等功能的嵌入式C程序的工作原理进行的专业性与完整性解析。 #### 端口初始化 端口初始化是嵌入式系统运行的基础,主要涉及配置微控制器的GPIO(通用输入/输出)引脚模式。这包括设置引脚方向(输入或输出)、速度、上下拉电阻等参数[^1]。 ```c void GPIO_Init(void) { // 配置LED引脚为输出模式 GPIOA->MODER |= (1 << 0); // PA0 设置为输出模式 GPIOA->OTYPER &= ~(1 << 0); // 推挽输出 GPIOA->OSPEEDR |= (3 << 0); // 最高输出速度 // 配置按键引脚为输入模式 GPIOB->MODER &= ~(3 << 2); // PB1 设置为输入模式 GPIOB->PUPDR |= (1 << 2); // 上拉电阻启用 } ``` #### LED控制 LED控制通过操作GPIO引脚实现,通常使用`SET`或`RESET`操作来点亮或熄灭LED。这种操作直接修改寄存器值以改变引脚状态[^2]。 ```c void LED_Toggle(void) { GPIOA->ODR ^= (1 << 0); // 切换PA0引脚状态 } ``` #### 按键扫描 按键扫描用于检测用户输入,通常通过读取GPIO引脚的状态实现。为了提高可靠性,需要加入去抖动处理,防止误触发[^3]。 ```c uint8_t Button_Read(void) { uint8_t button_state = 0; if ((GPIOB->IDR & (1 << 1)) == 0) { // 检测PB1是否按下 delay_ms(20); // 去抖动延迟 if ((GPIOB->IDR & (1 << 1)) == 0) { button_state = 1; // 按键按下 } } return button_state; } ``` #### 定时器中断 定时器中断用于实现周期性任务调度,例如LED闪烁或数据采集。通过配置定时器寄存器并启用中断服务函数,可以在指定时间间隔内执行特定码[^4]。 ```c void TIM2_IRQHandler(void) { if (TIM2->SR & TIM_SR_UIF) { // 检查更新中断标志 TIM2->SR &= ~TIM_SR_UIF; // 清除中断标志 LED_Toggle(); // 切换LED状态 } } void Timer_Init(void) { RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // 启用TIM2时钟 TIM2->PSC = 7999; // 预分频器设置为8000-1 TIM2->ARR = 999; // 自动重装载值设置为1000-1 TIM2->DIER |= TIM_DIER_UIE; // 启用更新中断 NVIC_EnableIRQ(TIM2_IRQn); // 启用TIM2中断 TIM2->CR1 |= TIM_CR1_CEN; // 启动定时器 } ``` #### 系统主循环 主循环负责协调各个模块的功能,确保程序按照预期顺序执行。在主循环中,可以调用按键扫描函数,并根据按键状态执行相应操作[^5]。 ```c int main(void) { GPIO_Init(); Timer_Init(); while (1) { if (Button_Read()) { // 执行按键按下后的操作 } } } ``` ### 工作原理总结 上述程序通过端口初始化配置GPIO引脚,利用LED控制实现基本的输出功能,借助按键扫描实现用户交互,并通过定时器中断实现周期性任务调度。这些模块共同构成了一个完整的嵌入式系统框架[^6]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值