测量运动自行车速度,上传给上位机软件,处理VR视频播放。正好公司有现成的stm32f1系列单片机开发板,所以我就想到了使用它来实现这个小功能。2 E" ~; V" h, s, l" t$ [& s4 m5 M
$ @& ?/ w/ c+ _ ?/ j2 B( W
1. 硬件配置:
2 w0 I. ~- D& W$ I
1 R$ l7 T: Z' C, h: y1 T" q4 \1.1. 运动自行车;1 E v9 F. J( X! A
P/ L* \* n) M: J. `0 L$ x# k4 z
; s: P- [- e) Q& W! E8 y8 b
0 ~4 K: }0 E* ~' ?1.2. 磁感应开关与专用磁铁;
5 R1 X0 c: h$ W- c- [# v0 z2 Y
h0 Y) a4 @+ h' ^* e/ x
6 i/ G/ |0 h. P/ ]3 W" N; h5 ^6 i$ B1 p4 n, _
1.3. 基于Stm32f103zet6芯片的开发板(七星虫),如下图;
2 l* R5 V; a$ ?4 B
* Z/ K6 g w% O) ^( o: f+ b
; h3 ^: U" r" a- S0 _ f1 |, g- A2 B n
1.4. 连接线若干;
+ @0 J e" q" R3 U
" @1 p- m3 R6 q" ~1.5. miniusb线缆,用于给开发板供电及串口通信。$ p" o. v* ^0 O9 a6 u3 \& ^, P
7 u8 g' C+ s/ \8 b
9 u( L" h. d+ @
1 g: y, d' V$ J& @+ E2. 系统描述与框图:- y3 g; O% S' i, @5 N3 E1 ?
! Y, t* C8 i1 Z p# I2 h5 h; y运动自行车车轮上安装5只磁铁,通过磁感应开关检测磁铁产生信号,接入stm32开发板PE0引脚。测量出的速度值通过串口发送给PC上位机软件(mini usb线缆连接)。硬件框图如下:
( D T5 Y! T& ]1 @- V$ C8 Z1 |( p p1 n2 @0 ?! T2 u8 C4 ]5 f
; e0 Z9 d. h% V4 i
4 A+ |+ F2 B, }) v: d+ X: P2 l% T" I7 A7 r5 j, y) J+ d6 a2 p6 H
3. 软件实现( R- D+ Q8 A2 D) U# ~# H5 Y
' U$ v' t+ q9 c6 U) y5 ~8 D3.1.设定开发板PE0引脚下降沿中断,在引脚中断服务函数里累计中断次数(即磁感应开关感应到磁铁的次数),同时每累计10次LED2交换一次状态。外部初始化代码及中断服务函数如下:
7 q9 X! ?" I7 l2 x3 j% f
2 Q D% }& Z3 X! c; J- void EXTIX_Init(void)
( Q- w! S6 K0 ^: v - {
# Q* H% X) q F1 G* u2 ~* X8 \# E - EXTI_InitTypeDef EXTI_InitStructure;
. I* g* P( y/ G T; _- e - NVIC_InitTypeDef NVIC_InitStructure;
' c% r6 u8 R. K* u( ? - GPIO_InitTypeDef GPIO_InitStructure;
! w$ U( |! q1 J0 \ - // 引脚端口初始化 PE0
* P U! u6 z9 a* u - GPIO_InitStructure.GPIO_Pin = DEF_BIT_00;7 Z3 Q6 l6 p: \* k( l2 z) o
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;8 @, ^7 L2 w$ p9 O& t! ]
- GPIO_Init(GPIOE, &GPIO_InitStructure); 8 a) |0 `0 ^5 V; r6 ?
- // 启 AFIO 时钟4 `4 q3 k7 k6 \2 z
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/ p4 V7 i% g0 g - //GPIOE.0 中断线以及中断初始化配置,下降沿触发
& Z* T5 F7 Y% p1 n9 S% b: X - GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource0);// 配置中断线为09 v' O# b) d2 A* S7 E g! n B
- EXTI_InitStructure.EXTI_Line=EXTI_Line0;
+ G& X( k+ U3 s: o! S$ s - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;. Z+ T# X z" U- Q" Q5 w8 l; I
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发; p- N' v6 N/ V, w. @3 d
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;
5 ^) [ n$ t: @# [3 p; |, C3 c - EXTI_Init(&EXTI_InitStructure); // 初始化中断线参数* E$ G) Y" s- O3 Y' }
- 8 H) g6 X6 g" V* |- ^
- NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键外部中断通道
" |, j0 h% I- {) _. D - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,8 T+ G1 H4 ^* k+ j& C* x0 p
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2
. r. ]' H" U* b+ f2 q* w3 v; r: ` - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道- J9 ^, A1 g! M0 ^) O# D
- NVIC_Init(&NVIC_InitStructure); // 初始化 NVIC
; Q8 _! e% K2 S4 X - }- U7 z) s& y- k& o0 @8 p9 Z
- // 外部中断0服务程序
- r" l |& w, H: D: x/ g - long long lSpeedCnt = 0;* P# y$ _4 T9 y! h
- void EXTI0_IRQHandler(void)
( _! n$ c4 B% R" M3 h g8 m - {
" _7 C0 u5 _3 S; h- G. e - OSIntEnter(); // 告诉ucosii系统进入中断
1 H" G1 }& I6 n4 @
3 D) b. ?1 W" z: \/ I b/ f7 q8 {$ V- if(GPIO_ReadInputDataBit(GPIOE, DEF_BIT_00)==0) // PE0检测到下降沿% O T8 }* p" P$ c3 g& Z
- {% F$ W& ~) `1 [
- // 累计中断次数,每隔10次改变led1状态* z7 p% }4 A$ d% _$ B
- if(!((lSpeedCnt++)%10))
; Z% \2 n) d9 p, u8 `! o; Z - {- F$ v4 g0 y4 U+ S/ {
- BSP_LED_Toggle(2);. n: `4 u( m0 C- S4 H" d/ m
- }- C+ N% y/ L k0 V" f% X; O' X
- }/ }" @% V V; p1 `! v& {
- EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE0上的中断标志位6 K% L& e2 e* Q6 ^2 g8 j0 s0 W6 W5 a
- ' B7 l. [0 g% r: i2 C" N
- OSIntExit // 告诉ucosii系统退出中断6 y8 C# c' e) y" o
- }
复制代码
( Z! I! k9 O A: |: o' p0 G1 p, R# L- N2 A" n% T5 w& l
3.2.在启用一个定时器中断,周期为1s,在定时器中断服务函数里计算自行车的速度。计算方式如下:v = p / μ * C,其中:v是速度:m/s,p是磁感应开关感应频率, μ为车轮上安装磁铁个数:5,C为自行车车轮周长:1.38m。定时器中断初始化代码及中断服务函数如下:9 T1 I. O# H6 g
) \% ^' ~1 a' _! l/ i' X0 ]) F
- /*******************************************************************************3 g$ Q! j( F4 z' K' E% u/ @
- * Function Name : BSP_TIM2_Init
; ]5 s7 I, \! e6 J1 [9 H - * Description : Compute return latest speed measurement
0 c( J6 ~0 H- \. v% D$ m+ S - * Input : None8 `- B# h8 Z ?- x
- * Output : s16; t. T6 B* \ S3 X5 u, j
- * Return : Return the speed in 0.1 Hz resolution.
/ p& ]1 O. Z7 ?8 ^1 _! r" _ - *******************************************************************************/2 |% P6 n0 t2 n" l% l' m- H
- static void BSP_TIM2_Init(u16 arr, u16 psc)3 V- d% G' j' ?9 C0 o! m$ R+ ^
- {
" u. Q8 `! W, _$ X+ j" C/ H2 j - TIM_TimeBaseInitTypeDef bsp_tim2_init;
5 P7 }$ X; `. O- |+ x: L9 c
3 ?9 R7 T! u! [5 W! [1 l7 z- //使能TIM2时钟
( r6 `% h; K2 z; q% { - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);5 r% k/ |( O% g9 e& S6 R
% b$ n+ |$ d2 C: ?/ l* q) f- 3 w# X: B. {/ l3 f; n3 x0 M
- TIM_DeInit(TIM2);& j" @+ B( @ E. V- M6 a5 s
- TIM_TimeBaseStructInit(&bsp_tim2_init);
% o- Z, P5 R x: P2 T! ` - //TIM2初始化
$ F! {6 O; _: G) } - bsp_tim2_init.TIM_Prescaler = psc; //时钟预分频 定时器每隔 (psc+1)/72 us计数一次" B/ y U& N, I, m( K5 m
- bsp_tim2_init.TIM_CounterMode = TIM_CounterMode_Up; //向上计数& r5 q2 Y7 r1 J
- bsp_tim2_init.TIM_Period = arr; //计数满(arr+1)次更新重装载寄存器数据
; `: `6 G; O1 k; H. z+ G - bsp_tim2_init.TIM_ClockDivision = TIM_CKD_DIV1; //时钟不分频
/ C8 _& B) X; Q; V - // bsp_tim2_init.TIM_RepetitionCounter = ; //高级定时器用,这里不需设置* K: r. L: h! M7 e! ]- ]1 M
- TIM_TimeBaseInit(TIM2, &bsp_tim2_init); //初始化定时器+ ?$ q8 F2 H& \
( m2 ^3 \( x6 @- //设置定时器TIM2中断
7 l% L2 f3 }8 X- k9 E$ J - TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //设置定时器更新中断* q0 I- N% j( g
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志,防止刚上电时进一次中断
& i. a2 P4 U/ Y9 O0 i - 0 t# g: `& `, b$ [2 Y
- //初始化中断
4 W9 B1 g* f! I; | - BSP_NVIC_Init(TIM2_IRQn, 3, 3); & n% @, @8 ^8 x" h- v; C
' B- V7 m0 o% }6 n- //使能定时器TIM2
% F+ X s6 A6 |! d/ [8 U - TIM_Cmd(TIM2, ENABLE);
2 l0 o8 j; n) ?. C - }
复制代码- long long lvalCur;
' Z4 s, m4 T0 C1 I, s' @ - long long lValPrev;
9 v& M; L, q2 j# V6 n - float fSpeedVal;. o; A: L% K. M7 S4 c# P
- void TIM2_IRQHandler(void)( g+ W4 t1 m8 t/ n4 ]
- {8 ]( _+ s- D/ _: N* G( `6 [6 g
- OSIntEnter(); // 告诉ucosii系统进入中断* ~7 Y3 m: L8 Z, ~! |7 h# M: x
{' H8 V* L3 G8 Q- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
3 i7 q/ q# @# Z4 N) V$ H - { 1 @7 w$ X$ _; p; |2 ?4 z
- extern long long lSpeedCnt;9 M% n+ G3 B4 B
- $ ?% O. z: _% l I1 |3 e# C
- lvalCur = lSpeedCnt;) U% I2 V9 O4 ]
- fSpeedVal = (float)lvalCur - (float)lValPrev;
v7 y3 f3 W; v. k0 L/ \4 j/ ? - fSpeedVal /= 5.0;
7 |( G2 q% u; y. H - fSpeedVal *= 1.38;
/ {, o7 W; o4 m8 s+ _( R" g; } - lValPrev = lvalCur;
' n5 b2 ^5 D! v/ W - TIM_ClearFlag(TIM2, TIM_IT_Update);
; B4 O- \% u: Z3 k# n - }# a% b" C X$ I$ k v0 H
- OSIntExit(); // 告诉ucosii系统退出中断
$ F; M" [, `6 f) j - ! c, p. B0 z' k8 s* P
- }
复制代码
0 y5 W9 {4 L! p7 M$ ~0 z( Z8 a% y9 Q3.3.系统共有两个任务,其中一个任务每50ms发送一次速度值给PC机,另一个控制LED1闪烁,周期100ms,用于指示系统正常运行。
( b( P2 k$ n' H
: D" N* s- x* x% w第一个任务中运行代码如下:4 o/ `6 ~8 m: J' E; I- T
* | ]0 n% R% X
- while(DEF_TRUE)
. r0 b# Z) k6 ^ [- W! Z - { # e* [2 M# z4 l
- extern float fSpeedVal;( B0 ]3 O% G8 _; i# ~% X% p, O
- if((int)(fSpeedVal*100) > 9999)
+ l. u6 u0 M* U2 H - {0 g- r4 J- V" j% \8 @/ ?* p9 _4 `
- printf("9999"); 1 R3 I* _8 V7 D, j# J
- }0 `% L/ E2 H; e3 Z
- else if((int)(fSpeedVal*100) > 999)
H1 q! b0 e& j9 C - {
( z0 ?& Z3 j* y( y8 {, `9 l - printf("%d",(int)(fSpeedVal*100));
- |: v- q6 m; C/ R! j5 {! F3 t - }. y. R8 P3 R, |) B, t
- else if((int)(fSpeedVal*100) > 99)# w2 U$ L/ v" a' b! W
- {# K/ Y3 Z( d9 Z+ i( ~3 N( B" }6 U( F+ o
- printf("0%d",(int)(fSpeedVal*100));
# i. [5 ^& v# k - }1 }6 ?) P7 ^2 }6 K/ {6 { `
- else if((int)(fSpeedVal*100) > 9). M6 w& b" B: @& T) b7 V* r
- {4 ?6 Q: `4 T# U% V0 _( e
- printf("00%d",(int)(fSpeedVal*100));
- X. j7 p$ F+ B$ O$ U - }
( }* e b: u6 y7 S1 U - else3 M- O2 I5 h9 M7 {7 G& @9 S
- {
9 n$ ^ S Q- U* D* ]* G - printf("000%d",(int)(fSpeedVal*100));
& p; r4 N1 `/ F- g, I - } 1 @) V4 G& y( n* K; [& q# K
- 4 a. R5 Z. T* X8 H' q' O6 C
- OSTimeDlyHMSM(0, 0, 0, 80);4 f8 R$ i9 D) J4 g& X: G
- }
复制代码
7 G: Q! v* O- [$ S第二个任务中代码如下:
. `2 q4 c/ P2 l$ _; b( @
0 \& ^' @' n0 Z3 M- while (DEF_TRUE)6 u5 D j: \! \% O
- {
) `% Y& i- w" v" E; P: f; M - BSP_LED_Toggle(1);
' e- O5 P2 y0 S3 }. @, q - OSTimeDlyHMSM(0, 0, 0, 100);7 _( Y+ o5 L, K" T4 R( |$ Q+ G
- }
复制代码 ! r4 D* v, T$ g# \9 u9 C/ Z
整体运行稳定,满足项目需求。. |5 f. `5 t$ B3 o% _1 w! J
) j8 z! y6 Z4 O4 l
; [- L8 d- O8 {. g9 {) A |