测量运动自行车速度,上传给上位机软件,处理VR视频播放。正好公司有现成的stm32f1系列单片机开发板,所以我就想到了使用它来实现这个小功能。
$ k$ q1 v# w l! M
& X& i8 b/ a* B5 l$ L1. 硬件配置:
/ A3 ~" ~% M: y- q. U
/ @" g% a% j% e \1.1. 运动自行车;
# q1 x! m! h' D. I
* r5 }1 w& Z0 K5 s; s: M9 d+ z( L6 A, B, q
( J, E3 y) ?9 ~% u1 Y* j+ s
1.2. 磁感应开关与专用磁铁;
0 m; R7 r3 j/ n+ c( S/ X: P+ m; n( X T- Q$ ?) m' q6 Y
" S, J( d9 A% ]' f g: D
" O; ]5 y; H' g! W% N; P! J1.3. 基于Stm32f103zet6芯片的开发板(七星虫),如下图;
& C1 i+ ~" N; r+ A1 c
. E* b5 |) O) e7 @! y6 ?5 o
) [. [" {' t! g# @2 M3 u' O+ [1 t7 z7 P" w! ^
1.4. 连接线若干;
# M& o2 h3 I( b5 n. o! j) ~7 u
1.5. miniusb线缆,用于给开发板供电及串口通信。
! y6 y* a. o& G, E8 _
5 z1 \! @. F, T# M6 ~ _6 r; R( X5 u0 {' Z- b5 C# B# n
7 C: ]6 P, ]2 r' r, [
2. 系统描述与框图:
7 R1 N+ [' E6 K; O- L+ }
% U. c4 k8 A/ f' X. v运动自行车车轮上安装5只磁铁,通过磁感应开关检测磁铁产生信号,接入stm32开发板PE0引脚。测量出的速度值通过串口发送给PC上位机软件(mini usb线缆连接)。硬件框图如下:4 l: P( N9 x3 ^# `1 f N( ^) D
$ N9 e. D( b5 ^, ?5 ]' l/ r6 }+ D( R* D
; t$ A0 j/ I: v1 a1 c* j1 N' ^4 [
& N8 Y- m& R4 c1 y
3. 软件实现
( y& G% N7 l8 Y* D4 M# [/ j! V
6 ~7 @4 |; G; O' Y' _3.1.设定开发板PE0引脚下降沿中断,在引脚中断服务函数里累计中断次数(即磁感应开关感应到磁铁的次数),同时每累计10次LED2交换一次状态。外部初始化代码及中断服务函数如下:0 S& P8 X) y' y9 ?# N: ?0 ^ e5 G
, ]$ j3 q4 i: h
- void EXTIX_Init(void)
7 v+ C2 D: E! H ?& [$ v - {
$ w u1 V/ b! A - EXTI_InitTypeDef EXTI_InitStructure;
% m! ^. m4 c" ~( y( C/ S2 ~ - NVIC_InitTypeDef NVIC_InitStructure; k$ u2 M$ S2 n' V4 x( ]! X
- GPIO_InitTypeDef GPIO_InitStructure;
5 I- t9 e3 k3 m# G9 p - // 引脚端口初始化 PE0 b+ q4 _, J1 k
- GPIO_InitStructure.GPIO_Pin = DEF_BIT_00;! X- y7 [. c7 ] V
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;2 V4 }2 ?5 Y. T& R r+ g, S
- GPIO_Init(GPIOE, &GPIO_InitStructure);
, p4 B* A+ u( Q; N7 I1 H, S0 v - // 启 AFIO 时钟
8 c' e; Q6 K7 l+ w# P7 d% Q/ l# | - RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);3 ?; o6 {% t0 X4 z
- //GPIOE.0 中断线以及中断初始化配置,下降沿触发
% m+ F, y! K& F5 C* I$ X - GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource0);// 配置中断线为0+ G7 K1 S2 j1 T) b! l9 E
- EXTI_InitStructure.EXTI_Line=EXTI_Line0;
: T- O- J m2 ?7 O - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
9 B) Z. g; \2 q5 U1 b, z/ Z - EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
, H9 _$ l; o) U9 \$ U* {0 ^: C - EXTI_InitStructure.EXTI_LineCmd = ENABLE;
( ~' z# I5 }; r+ A - EXTI_Init(&EXTI_InitStructure); // 初始化中断线参数
2 \9 A1 R. D1 a; G0 m& H5 O6 q - 4 \1 W" ]0 L$ O( e
- NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键外部中断通道* t0 B i7 _/ K
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,
1 x4 F$ }; ~- `0 Q& X - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2" T9 Q0 `& g" q6 k7 b. [
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道 u5 M* B) R' a; x
- NVIC_Init(&NVIC_InitStructure); // 初始化 NVIC2 u5 N1 S* |2 e/ n
- }3 [; Z: j( I7 s; e0 S; ^
- // 外部中断0服务程序
/ X+ O- _ A4 T6 k# d2 B, b - long long lSpeedCnt = 0;
8 v* F- r- {3 t2 c% O - void EXTI0_IRQHandler(void)# U j3 |1 X# a' o' e! l
- {
' `+ D8 g2 q8 L - OSIntEnter(); // 告诉ucosii系统进入中断
1 A! A/ n: k( T2 J, X$ `* n - ; w# X# ]* ?% C7 @, b
- if(GPIO_ReadInputDataBit(GPIOE, DEF_BIT_00)==0) // PE0检测到下降沿
1 z. E5 h; t. g! i! ? - {- Q* G, S' X; W+ M
- // 累计中断次数,每隔10次改变led1状态4 q# X6 L9 K& }$ [" J! L6 l
- if(!((lSpeedCnt++)%10))
0 l# W! a! V4 x2 n5 e7 G$ w! ^ - {# `* W! C, d2 u! S5 |
- BSP_LED_Toggle(2);' m3 r' S5 h8 J, r A2 P
- }
- l/ u+ l' I$ R* V/ D7 t* { P$ \. i - }
. i* Y) I3 h* l9 J - EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE0上的中断标志位
; U9 ^" A# O+ x+ h9 n+ y! w
: ?( v( g; P( u" _5 M- OSIntExit // 告诉ucosii系统退出中断
" Y& C9 L1 [" I7 A% F9 l. \4 l - }
复制代码
+ j& o( j- G0 H. X% }# v9 _9 W" O7 W8 U, J& \' U% _8 Z/ i
3.2.在启用一个定时器中断,周期为1s,在定时器中断服务函数里计算自行车的速度。计算方式如下:v = p / μ * C,其中:v是速度:m/s,p是磁感应开关感应频率, μ为车轮上安装磁铁个数:5,C为自行车车轮周长:1.38m。定时器中断初始化代码及中断服务函数如下:
' k; P, f y! [9 e4 v; T
/ w2 X# k# _" ^( X: q- /*******************************************************************************2 g$ P- B! I& m+ C+ R
- * Function Name : BSP_TIM2_Init
+ Z$ e6 f4 D9 Z' v - * Description : Compute return latest speed measurement
: b6 h: v/ R7 r% l5 M - * Input : None& r& b/ L( a( P3 }. B5 ^' S
- * Output : s16
6 j) z4 D2 g1 }" |4 H0 }" m/ t - * Return : Return the speed in 0.1 Hz resolution.
# U7 `, s6 C: X3 ?; T. h - *******************************************************************************/
6 x5 x- B6 B; B - static void BSP_TIM2_Init(u16 arr, u16 psc)5 r3 n+ R1 Q9 P; T& J6 _0 N
- {
3 W6 p+ f9 w; d, P - TIM_TimeBaseInitTypeDef bsp_tim2_init; , K; |1 P3 h" e' {# O( I
- ; m* j& z" t6 O
- //使能TIM2时钟3 P% S" F k+ ~5 `3 I
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);9 E9 t0 [/ }* n( @& r2 }) t
- ' [5 O% F& W# `. Y4 a
7 b% y, }+ e5 B @. c1 Z' F- TIM_DeInit(TIM2);4 O; G9 a, |1 U2 @4 ]# A
- TIM_TimeBaseStructInit(&bsp_tim2_init);, D. i! A, O3 m1 T7 w1 S3 \4 n: s
- //TIM2初始化8 m4 u0 T" C0 Z6 c- l
- bsp_tim2_init.TIM_Prescaler = psc; //时钟预分频 定时器每隔 (psc+1)/72 us计数一次
: Y; E! O& M) S2 _' n( F) | - bsp_tim2_init.TIM_CounterMode = TIM_CounterMode_Up; //向上计数5 P: O. y/ v3 T
- bsp_tim2_init.TIM_Period = arr; //计数满(arr+1)次更新重装载寄存器数据
6 q I, c5 N: C - bsp_tim2_init.TIM_ClockDivision = TIM_CKD_DIV1; //时钟不分频! {# e2 K& X6 s6 L9 b
- // bsp_tim2_init.TIM_RepetitionCounter = ; //高级定时器用,这里不需设置
! d. ?) _4 h, M" r0 O - TIM_TimeBaseInit(TIM2, &bsp_tim2_init); //初始化定时器
( y+ Z& S' Q1 ~; b4 C4 ^ - : f, D. Q: t: t9 @; [# f
- //设置定时器TIM2中断) }( ^9 [7 U) H0 m3 g) N' O
- TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //设置定时器更新中断3 D) l0 |! g' I$ C
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志,防止刚上电时进一次中断
4 _4 W* {5 ^, B - : I3 e8 N& x# y. R
- //初始化中断1 I6 H7 ]/ t( D7 T; M
- BSP_NVIC_Init(TIM2_IRQn, 3, 3); " J% {: C1 s, u- y- n
- ( m9 i @# e* H# t n+ o9 }/ t" C5 p, b
- //使能定时器TIM2- [' j3 @' w% w1 f% g
- TIM_Cmd(TIM2, ENABLE);# X# G4 f, A4 Q" W& z% x
- }
复制代码- long long lvalCur;
; {" w' c ]2 o1 L - long long lValPrev;5 `. q/ m0 r1 V1 w
- float fSpeedVal;
7 A( e# b% x! g9 A8 L - void TIM2_IRQHandler(void)
9 ]6 U- {5 T% h" @2 L: h1 b - {
2 f8 R9 }: j- Z) }( O9 U/ c1 p5 t - OSIntEnter(); // 告诉ucosii系统进入中断
, O* X* [) k( f7 E" ?) \ - + R" d/ n, N7 T' k3 C+ D$ P b
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
* M$ \& M5 V- M, H( \" \ - { ! m* t: t! r. V. ~2 i! P. v4 i
- extern long long lSpeedCnt;1 @" R- {7 a6 Y5 ]' F8 G# w1 V1 Q
- . w1 e& G' b( |) k+ f* ]; C+ B# [
- lvalCur = lSpeedCnt;* T1 z4 E9 h2 x6 u
- fSpeedVal = (float)lvalCur - (float)lValPrev;- \, u' ^- W8 A2 Q4 c5 P9 P& P
- fSpeedVal /= 5.0;# N9 D+ W8 _( g$ q
- fSpeedVal *= 1.38; 4 L' ]6 O K$ C2 O+ [3 q4 K$ O
- lValPrev = lvalCur;- `$ [7 S3 ? \! D& R+ e: }, r
- TIM_ClearFlag(TIM2, TIM_IT_Update);
8 L; v2 B( J. g$ d$ a" f - }
, B& K3 m7 U* N- Y - OSIntExit(); // 告诉ucosii系统退出中断 f" ^4 l2 P7 P3 G$ l9 e* O- T
$ H4 d( l6 z+ K5 I8 E- }
复制代码
, |+ Q5 b# ~" o: _3.3.系统共有两个任务,其中一个任务每50ms发送一次速度值给PC机,另一个控制LED1闪烁,周期100ms,用于指示系统正常运行。+ D T/ a8 v9 ?' X4 A
5 L' a, V0 s+ C8 w8 S L. u
第一个任务中运行代码如下:/ v. p% U3 P3 a+ T
! F1 R" i1 J3 q; P0 Q6 ^. e
- while(DEF_TRUE)
+ @% S' p% z' @; u- z/ r - {
2 i" I* _, c+ }+ i - extern float fSpeedVal;( e9 U3 @! v4 b' m6 ]9 \
- if((int)(fSpeedVal*100) > 9999). u9 S9 J0 y. ]* ?' J4 o# g( O
- {
5 C" A* B/ {3 |; B6 n - printf("9999");
7 C+ A. C5 p# W+ w9 k$ r8 t$ t - }( O: }" ^" j; ~$ f2 _% |/ N2 `
- else if((int)(fSpeedVal*100) > 999)5 i$ s, J0 D c& e5 j1 X* Z5 a
- {: v6 Y$ x- ~) Y: r! N
- printf("%d",(int)(fSpeedVal*100)); , Y3 h) G# L9 ~7 T4 B+ U9 d
- } ^( S. `7 h* X
- else if((int)(fSpeedVal*100) > 99)
- u6 H. q& F: a: h - {6 X5 p: y& w. N/ J. f% O* p7 X+ T
- printf("0%d",(int)(fSpeedVal*100));
' c. q9 }/ q9 \! g' a - }
9 @& n$ b3 B9 b' B+ _ - else if((int)(fSpeedVal*100) > 9)
9 w& m* y# |6 j# u - {
! \" Y& ]9 ~- {1 j - printf("00%d",(int)(fSpeedVal*100));
! Z1 ^& F' V9 |* [ - }: r+ V( [- X% a$ z$ e( {+ a! U
- else$ x0 F% ^! Z, l3 k6 g, M
- {
: A3 o/ a( |8 k8 O9 }* C7 U - printf("000%d",(int)(fSpeedVal*100));
( g& W& m! @7 S; Z- R - }
9 R/ |- x; z' ?# R2 w! P0 `6 y
4 `- L2 b+ N0 B9 l" I' G- OSTimeDlyHMSM(0, 0, 0, 80);( k2 M1 U+ @, L9 ^
- }
复制代码 2 |4 B2 N! n' T$ ]+ B
第二个任务中代码如下:
$ n# w. S; s8 h# r$ _# t: i; m1 P0 w& C" o7 J
- while (DEF_TRUE)
, B4 v% p! q& P1 \ - { 6 i4 T' c/ T4 `( d
- BSP_LED_Toggle(1); H* L, B$ a9 h# G; h, q0 n+ y& p
- OSTimeDlyHMSM(0, 0, 0, 100);
" B" ^ ]5 `; j( n- {: }& Z; } - }
复制代码
, X6 s s) b# U# Q, [: ]整体运行稳定,满足项目需求。
5 n# K1 ]$ v" H- r1 T
3 b2 }+ G" F/ x0 n9 c( F, ~) s/ N) d# n* K. j5 {) Y# I( o
|