|
STM32 点灯丨V2.0.0 接着昨天完成的功能,今天来让咱们的程序进化,怎么用LED灯来提示故障码? 闪烁,咱们已经做了,比较简单。那么提示故障码又该如何做呢? 这个也不算难,就是在闪烁的基础上,加上了次数。 我这里,把LED的功能写成了一个函数,方便以后使用。 写程序,我一般都是直接在原来的工程上修改的。这次我是在正点原子的定时器例程上修改的,因为上面有些东西可以直接用。 我们还是用昨天的硬件,就是挂载在PB5的LED,初始化还是和昨天一样,还加了定时器的初始化。 - void TIM3_Int_Init(u16 arr,u16 psc)
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
-
- //定时器TIM3初始化
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
-
- TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
- //中断优先级NVIC设置
- NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
- NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
- TIM_Cmd(TIM3, ENABLE); //使能TIMx
- }
- void TIM3_IRQHandler(void) //TIM3中断
- {
- if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
- {
- SysTimerFlag = 1;
- TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIMx更新中断标志
- }
- }
复制代码
我还做了一个函数来获取这个时间标志,这个标志的功能,是让我的程序跑在10ms的循环里 - u8 GetTimerFlag(void)
- {
- if(SysTimerFlag)
- {
- SysTimerFlag = 0;
- return 1;
- }
- return 0;
- }
复制代码
每获取一次,就将标志清零。 最后,就是今天的主角了,为了方便,我用了宏定义将LED的引脚定义出来 - #define LED PBout(5) // PB5
复制代码
这样就比原来省事多了。 然后就是LED的功能了,我这里用结构体将LED的一些参数进行了封装,方便管理,结构体是真的好用,大家可以多用用。 - typedef struct
- {
- u16 Err_num; //故障代码
- u8 Type; //LED的类型,0:常亮,1:闪烁,2:故障提示
- u8 Enable; //使能led
- }LED_t;
- LED_t s_LED;
复制代码
因为要让LED闪烁,这就要关心一下它闪烁的频率,我这里为了以后维护方便,也用宏将它定义出来 - #define FLASH_FREQ 30 //闪烁频率,单位10ms
复制代码
最后,主要功能的实现,我也做成了函数,只要将这个函数放到一个每10ms执行一次的循环里就可以了 - //10ms
- void LED_Lib(void)
- {
- static u32 s_Cnt;
- static bool Flash;
- static u32 ErrCnt;
-
- s_Cnt = (s_Cnt +1)%(FLASH_FREQ * 1000);
- switch(s_LED.Type)
- {
- case 0 :
- LED = !s_LED.Enable; //赋值给IO口
- break;
- case 1:
- if(s_Cnt % FLASH_FREQ == 0) //500ms,闪烁一次
- {
- Flash = !Flash; //0灭,1:亮
-
- LED = !(s_LED.Enable & Flash) ; //赋值给IO口
- }
- break;
- case 2:
- if(s_Cnt % FLASH_FREQ == 0) //间隔500ms
- {
- Flash = !Flash; //翻转
- if(Flash == 1)
- ErrCnt++;
- if(ErrCnt > s_LED.Err_num)
- {
- ErrCnt++;
- Flash = 0;
- if(ErrCnt > s_LED.Err_num + 2)//灭两个周期
- ErrCnt = 0;
- }
- LED = !(s_LED.Enable & Flash) ; //赋值给IO口
- }
- break;
- default:
- break;
- }
- }
复制代码
我这里用到了布尔定义,大家要用这个关键词时,要在文件里包含它的头文件进来
然后就是主函数了,由于我这里只有这个功能,主函数比较简单 - int main(void)
- {
- delay_init(); //延时函数初始化
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
- LED_Init(); //LED端口初始化
- TIM3_Int_Init(99,7199); //10Khz的计数频率,计数到100为10ms
- while(1)
- {
- if(GetTimerFlag())
- {
- LED_Lib();
- }
- }
- }
复制代码
到这里,程序就写完了,我们可以验证了。大家要主要,刷了程序,直接上电是不亮的,因为当前,s_LED的参数全都是0,所以不亮。我们需要进入Debug,然后手动给s_LED的参数赋值。 实际应用的时候,只要在需要将这个结构体放到头文件里,然后包含到需要的文件里,在需要的时候给这几个参数赋值就好,程序只要走到这段函数里,都会是用最新的参数来执行。 下面是测试,拍得不好,请见谅。 文章出处: Yue無缺
|