|
STM32 点灯丨V2.0.0 接着昨天完成的功能,今天来让咱们的程序进化,怎么用LED灯来提示故障码? 闪烁,咱们已经做了,比较简单。那么提示故障码又该如何做呢? 这个也不算难,就是在闪烁的基础上,加上了次数。 我这里,把LED的功能写成了一个函数,方便以后使用。 写程序,我一般都是直接在原来的工程上修改的。这次我是在正点原子的定时器例程上修改的,因为上面有些东西可以直接用。 我们还是用昨天的硬件,就是挂载在PB5的LED,初始化还是和昨天一样,还加了定时器的初始化。 - void TIM3_Int_Init(u16 arr,u16 psc)
! S. U. j+ n7 H3 A. [3 w5 u - {
; o) a) ]) _& ~! N3 v) N - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
G7 e+ O. x+ B1 u& O- Q - NVIC_InitTypeDef NVIC_InitStructure;3 U' V8 S7 [! L& i/ _
- 0 Y! A0 q5 K% O0 b, c
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
$ l( B; ?0 p% a' A% q/ I" T/ F6 T - $ ~8 \: F# S! j+ E" S) }+ Z
- //定时器TIM3初始化. \+ U4 }3 ^+ g' h( M
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
/ p, r+ Y1 }+ q7 t+ s) l: i - TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
" C0 b) J, V1 v" w$ b - TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
" T% w; A7 W1 l0 y3 H - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式3 `( R" Y" h4 `7 H0 ~/ x# q
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位. Z, `, ~1 A$ w {( W% c
- 1 o& y1 k. ~6 L9 P9 w5 b; ~
- TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断8 |7 }8 y5 ?* E, a( r
- + ~ r: S. C7 c+ G, F0 E5 m! h
- //中断优先级NVIC设置3 g9 u( j# \# C; s8 F' D, x
- NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断6 e' ?! n0 H. g2 W. D, Q
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级) u+ c! x" @1 m/ y2 G$ H
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级$ Z. r5 P- p5 [$ I0 R
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
9 Z( p, ]! y7 C) l1 u - NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
1 }7 s3 b$ D8 I' Z) |
: T, Y) M0 a2 `, u7 R
; z2 A$ g& N5 \) \, w" j- TIM_Cmd(TIM3, ENABLE); //使能TIMx
9 X1 ^ Y1 q" f. J4 W; n1 b( ^4 P - }0 ^ W; M" ^; [$ M/ r$ F
4 s4 U7 @0 m. h" P- void TIM3_IRQHandler(void) //TIM3中断
- G/ h3 b' R6 A: b& U- Y - {5 ?- f! h' L+ y f8 e1 @ f6 F& k/ }
- if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否- T) J$ ^3 E4 q$ m
- {
2 j6 B7 p' w) [6 i, c, r5 B - SysTimerFlag = 1;7 B) ~2 \9 t* W! L* j
- TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIMx更新中断标志
3 h* U# e1 T# C p! ? - }$ O J5 c' k# q9 c$ y. m
- }
复制代码 ) p$ p( Z# s G7 [
我还做了一个函数来获取这个时间标志,这个标志的功能,是让我的程序跑在10ms的循环里 - u8 GetTimerFlag(void)2 F% G5 i P7 ^! c& R
- {4 w, c) w; O5 g+ Z. l) |8 `
- if(SysTimerFlag)
r$ S8 S2 I0 b% F" G - {, G2 {7 ~1 G: u! ~2 p8 r( z
- SysTimerFlag = 0;
4 t6 o9 I+ t5 U, A. Z$ P6 I - return 1;
\4 K# ~! n: C& y8 |# S - }3 ^; Q! F$ h2 J- `! z$ N8 e7 g
- return 0;0 t' T- O! k5 F+ I' ^
- }
复制代码
( h$ r2 b: T2 r4 N8 Y& d! W每获取一次,就将标志清零。 最后,就是今天的主角了,为了方便,我用了宏定义将LED的引脚定义出来 - #define LED PBout(5) // PB5
复制代码 . q" h' k5 u- a4 F5 ~
这样就比原来省事多了。 然后就是LED的功能了,我这里用结构体将LED的一些参数进行了封装,方便管理,结构体是真的好用,大家可以多用用。 - typedef struct/ T% n5 B$ \7 |! E' @8 Y
- {8 d* h& M& \! |/ t; P. ^4 Q2 {; I
- u16 Err_num; //故障代码( L l2 J) c3 z2 E6 t
- u8 Type; //LED的类型,0:常亮,1:闪烁,2:故障提示
& w4 R: g% H+ ?7 z3 z7 L - u8 Enable; //使能led2 M2 ?, m' X/ T& v2 F
- }LED_t;2 F" ?; J5 @$ S( c; f
& l% k+ y, B) b9 p- LED_t s_LED;
复制代码 & k" R; i( v1 U+ D: E& a) U0 b- I
因为要让LED闪烁,这就要关心一下它闪烁的频率,我这里为了以后维护方便,也用宏将它定义出来 - #define FLASH_FREQ 30 //闪烁频率,单位10ms
复制代码
1 u8 S* U1 {4 H) K- T. |, c: m最后,主要功能的实现,我也做成了函数,只要将这个函数放到一个每10ms执行一次的循环里就可以了 - //10ms
3 L# j* r% O6 t8 o s+ Q. m - void LED_Lib(void)
8 j) D0 |2 `3 z* g7 x4 @ q" ? - {
) B2 t* a3 t9 l - static u32 s_Cnt;
; Q3 k2 c4 D+ I7 l, {- J- g% X - static bool Flash;
/ e7 a" y0 D/ c- R7 m u9 Z - static u32 ErrCnt;# c" z- Q1 W4 j
-
. o1 I! [2 A P7 |2 s - s_Cnt = (s_Cnt +1)%(FLASH_FREQ * 1000);
3 Z! b/ Z/ Z; h h {( I - switch(s_LED.Type): u: Y3 l: h9 y9 c
- {
; `$ \. S) m$ H8 | - case 0 :
3 k$ L/ `0 E4 V6 Y/ ? - LED = !s_LED.Enable; //赋值给IO口1 o x( t% |# v; o
- break; * ~4 G0 B! X# W9 ]( u5 I) e8 G8 X$ |
- case 1:+ L: b1 X t) s* q+ u) w
- if(s_Cnt % FLASH_FREQ == 0) //500ms,闪烁一次
3 R; \+ _8 ^6 m - {
: n7 |. `3 U/ Q9 }; U" J - Flash = !Flash; //0灭,1:亮 o$ K: a$ n8 Q! ?5 z* p7 G" J
- ) U7 v L9 v3 N
- LED = !(s_LED.Enable & Flash) ; //赋值给IO口
8 A1 T) S) V* V - }
5 v! f4 S7 z. |4 x& k- h- d& H8 O - break;$ O9 r' i0 U9 D7 V* P
- case 2:: d$ F- j7 Z2 \# v2 m! s }
- if(s_Cnt % FLASH_FREQ == 0) //间隔500ms
, G2 z& i) L( }7 S' d; t7 v - { E8 E$ S" K8 \
- Flash = !Flash; //翻转
! M0 t! l$ C [# { - if(Flash == 1)
6 n! p' L+ B* A3 z- @ - ErrCnt++;2 f! K$ c9 V+ C2 F* Y; s: P
- if(ErrCnt > s_LED.Err_num)+ n$ |& M( ?' s! { [0 D* K; _( [6 p! j
- {
; r0 k! o" i( m, r* R0 ]5 C# E - ErrCnt++;
4 n; V `" [# N. z. q5 m2 v - Flash = 0;
$ P3 z6 ~9 h3 j6 m. c x - if(ErrCnt > s_LED.Err_num + 2)//灭两个周期0 K1 k- \6 R2 H- u
- ErrCnt = 0;
- I) P4 p3 h2 u5 _* h - }+ z9 o7 |8 v$ x6 c9 B) o: F7 p
- LED = !(s_LED.Enable & Flash) ; //赋值给IO口 8 u% T7 Y$ p! N; Q3 X+ ^
- }1 { `- i3 G, n
- break;
) c6 _. l# W! o% e' K S - default:4 @$ b1 }8 v2 g' `9 f1 }& I
- break;
) G& v6 B1 n" ^' ? - }
* Z' }, n6 {. H4 C' E - }
复制代码 1 m. S2 `% J2 F
我这里用到了布尔定义,大家要用这个关键词时,要在文件里包含它的头文件进来 4 r6 E. e7 Y4 o( k1 E
然后就是主函数了,由于我这里只有这个功能,主函数比较简单 - int main(void)0 ]' ~) _% D- ^. r: V
- {
* S) i) j9 o. `# v3 ?% h! t - delay_init(); //延时函数初始化 ' d7 r- p0 Y( C
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
1 |+ k6 j" J$ e! D8 V, ?9 ^ D - LED_Init(); //LED端口初始化3 w4 b; q1 t& R& n( j+ c$ T$ i ^& ~+ |
- TIM3_Int_Init(99,7199); //10Khz的计数频率,计数到100为10ms
6 `% T9 I/ W% J5 B6 h - while(1)9 U* K- J; F% w$ |2 T1 l
- {2 ~* s" K% u) T7 i, S
- if(GetTimerFlag())3 ]2 K2 M0 M; o D$ A
- {
1 b8 Y; O0 I; W% |1 A+ I - LED_Lib();
! }5 w+ L1 s6 w& t7 a9 ?( _ - }" A9 u, g% B% [$ H" x
- }
' { b1 X' G. w; G' z1 D( x) f% \; Z - }
复制代码
# C& B, A+ U5 x: a& r; ` 到这里,程序就写完了,我们可以验证了。大家要主要,刷了程序,直接上电是不亮的,因为当前,s_LED的参数全都是0,所以不亮。我们需要进入Debug,然后手动给s_LED的参数赋值。 实际应用的时候,只要在需要将这个结构体放到头文件里,然后包含到需要的文件里,在需要的时候给这几个参数赋值就好,程序只要走到这段函数里,都会是用最新的参数来执行。 下面是测试,拍得不好,请见谅。 文章出处: Yue無缺 0 h; ]9 c7 U5 ]& X; J
|