测量运动自行车速度,上传给上位机软件,处理VR视频播放。正好公司有现成的stm32f1系列单片机开发板,所以我就想到了使用它来实现这个小功能。& z& z- F6 o9 z/ r# q
' V+ q& s6 H" o3 B% V! W1. 硬件配置:5 m7 y; F- x' {; E& [
+ K- p% L5 d0 e/ E7 b1.1. 运动自行车;
$ y8 {, U- L, X r* _
$ o) t6 g7 q0 a$ `7 K! O6 O/ K+ ?; d# Z
8 D2 I6 S/ f; c5 R3 M& \1.2. 磁感应开关与专用磁铁;
/ y5 M' ?' M# C) b
" y: E& \* }; V
8 \- ~% L( Y5 _( |
\7 q- ?9 }1 |* L" [1.3. 基于Stm32f103zet6芯片的开发板(七星虫),如下图;
& Z: t5 P* C* J6 s2 {8 a6 i9 K$ d% X- l. q. m) O! C
& H1 M. U% l0 X& s- B0 z
" D! D1 ^6 V: J5 i" N% e4 i
1.4. 连接线若干;
, d- t# ^+ V+ U' x
3 X. h+ R( S5 y! F+ _1.5. miniusb线缆,用于给开发板供电及串口通信。
7 @; a8 J" s ]- ]' ^9 S/ i6 Z) @4 B$ p, J2 ]' K
( j4 k( T) S3 \' P/ i+ \6 k3 Y) G9 v" u( ?" s2 O. P
2. 系统描述与框图:) Z( x! g& T! E& x& F: p% K
; R# h: m5 t/ P. C$ t
运动自行车车轮上安装5只磁铁,通过磁感应开关检测磁铁产生信号,接入stm32开发板PE0引脚。测量出的速度值通过串口发送给PC上位机软件(mini usb线缆连接)。硬件框图如下:" }% V. i0 ?8 D& L% e1 B: d! n3 y/ X/ V
+ F9 @% |6 P" `1 g
( D4 T: Z6 \$ C) g0 _- ]% \+ Z/ E4 y. V+ G4 }# R) X S! f
; w0 O. ~, }9 K8 O( [6 O
3. 软件实现4 v( _" _1 K- y
% T8 Z: L/ ~0 G5 w- q
3.1.设定开发板PE0引脚下降沿中断,在引脚中断服务函数里累计中断次数(即磁感应开关感应到磁铁的次数),同时每累计10次LED2交换一次状态。外部初始化代码及中断服务函数如下:: Z1 V# m- [# ]- O/ f/ X
& l6 P( T* p+ u; \4 {6 {5 J
- void EXTIX_Init(void)" e7 I1 l6 p% R2 f; j+ ?1 y
- {
' f$ D Y }4 e7 @4 H - EXTI_InitTypeDef EXTI_InitStructure;
4 ^ Q1 M: C5 c$ C - NVIC_InitTypeDef NVIC_InitStructure;
5 M; c4 a1 L- r) m) ?$ w' ^4 @! m5 F - GPIO_InitTypeDef GPIO_InitStructure;6 n3 `* B( C. y! O+ T
- // 引脚端口初始化 PE06 W5 @3 k' u9 g; Y
- GPIO_InitStructure.GPIO_Pin = DEF_BIT_00;/ G) h+ A& l# U- B/ v& o
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;& l$ v9 T* q; a& p7 H
- GPIO_Init(GPIOE, &GPIO_InitStructure); ; \% I0 M# G5 M7 y' T( |
- // 启 AFIO 时钟
) n; \7 b5 U4 r, u' E3 [) Q$ S - RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
* h6 ?. R/ n; L; E2 ~ - //GPIOE.0 中断线以及中断初始化配置,下降沿触发3 }4 F, f: L! T5 `
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource0);// 配置中断线为0
' f( U7 M* O2 S2 {( J; B - EXTI_InitStructure.EXTI_Line=EXTI_Line0;' m3 m6 E6 I1 M
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;/ A$ X- E# z4 F2 q
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发2 u8 S: I! r; a2 ~9 ~2 T
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;
; d# W) ^0 K) w! ^, N - EXTI_Init(&EXTI_InitStructure); // 初始化中断线参数
9 `; G1 n9 h1 v- d+ A6 I U
- y) Z7 V. [8 d" c/ C) E* [- NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键外部中断通道
" T. ~- Q+ T: F; k3 O H) W - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,
, h3 [; d- q( w% U- P - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2& _% a% C' u1 h1 C! n: _
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
$ n6 }9 x8 F! Q - NVIC_Init(&NVIC_InitStructure); // 初始化 NVIC; r: t" N" ~) Q
- }) c) [- M$ {; U! z! U
- // 外部中断0服务程序9 `- f( N' }- j! _
- long long lSpeedCnt = 0;$ Q. k& `( q& o& V' t. a1 @! P9 z
- void EXTI0_IRQHandler(void)" D% g0 V+ W6 L) j) \& u
- {% B6 q, S$ r1 G( {% I6 M3 Q, F
- OSIntEnter(); // 告诉ucosii系统进入中断
. O+ R" ?1 X% F9 l* _ - 3 C& E4 Q+ n, S5 I& e
- if(GPIO_ReadInputDataBit(GPIOE, DEF_BIT_00)==0) // PE0检测到下降沿* Y9 L3 a$ c, U" a: R
- {' a$ o" |1 S0 }% F3 k2 |* v' t, D
- // 累计中断次数,每隔10次改变led1状态4 _4 l( l4 N6 C( y; F1 P7 F
- if(!((lSpeedCnt++)%10))' d5 W: Z8 O& D; x* Z
- {
9 v; e" M2 ^, T) ^/ _ - BSP_LED_Toggle(2);* S6 p4 u+ O4 D1 ]) T3 d
- }
/ q/ V) ]* h1 V( A! q - }$ {& r b# N( `4 }% j+ R$ c
- EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE0上的中断标志位
, z. L2 k* U* {& p8 I0 I) w
5 l1 V3 ~8 |) R: g- OSIntExit // 告诉ucosii系统退出中断
! f7 O q) ~9 R/ {3 n" G* X - }
复制代码
' n2 v" f5 {7 ]8 w: L4 i' H
+ k$ _! v b4 v) }3.2.在启用一个定时器中断,周期为1s,在定时器中断服务函数里计算自行车的速度。计算方式如下:v = p / μ * C,其中:v是速度:m/s,p是磁感应开关感应频率, μ为车轮上安装磁铁个数:5,C为自行车车轮周长:1.38m。定时器中断初始化代码及中断服务函数如下:
% O& j1 F' _' r' x! I6 M1 }9 k P M: w* x- _: W1 f
- /*******************************************************************************" \& i7 ]+ g0 V* n) t
- * Function Name : BSP_TIM2_Init7 }9 m z H5 R9 C0 g
- * Description : Compute return latest speed measurement
# N) C$ P! T, i5 O {: {" d: I - * Input : None
; j6 K) E- h& `/ `$ E - * Output : s16" y% ~! m6 f& ?7 a, _$ W G
- * Return : Return the speed in 0.1 Hz resolution. & K3 @3 y, @0 b: _2 ^3 Z2 Y# G
- *******************************************************************************/7 r% B" q. C5 K
- static void BSP_TIM2_Init(u16 arr, u16 psc)
( w5 F) ^# |! F% V2 ]2 l/ H - {) U4 q; M y0 ^9 F7 G3 P
- TIM_TimeBaseInitTypeDef bsp_tim2_init;
. I- N: x* H1 H+ B9 j5 X2 O - ! z0 T$ L' l0 g4 ~" {+ Z2 z/ |
- //使能TIM2时钟
/ d' G0 ~6 Y% N - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);4 t, j, G9 x1 ]# |7 I$ _
- " Q$ ?" {0 T9 O% [
- & \6 y" s U$ p/ U/ N0 s' c$ e
- TIM_DeInit(TIM2);
8 M6 T/ u2 ^( L - TIM_TimeBaseStructInit(&bsp_tim2_init);3 k8 w, _) z6 E
- //TIM2初始化
* C: e7 H& P+ b2 e" k1 k - bsp_tim2_init.TIM_Prescaler = psc; //时钟预分频 定时器每隔 (psc+1)/72 us计数一次
7 @. N) Q6 Z- ]1 J, k5 U% @ - bsp_tim2_init.TIM_CounterMode = TIM_CounterMode_Up; //向上计数: [& y0 b5 P! N6 l
- bsp_tim2_init.TIM_Period = arr; //计数满(arr+1)次更新重装载寄存器数据
/ f6 p! v o' A7 t - bsp_tim2_init.TIM_ClockDivision = TIM_CKD_DIV1; //时钟不分频
. S4 a" o" [$ J7 o - // bsp_tim2_init.TIM_RepetitionCounter = ; //高级定时器用,这里不需设置
6 X2 Q' _" O; ]: Z: [3 a - TIM_TimeBaseInit(TIM2, &bsp_tim2_init); //初始化定时器9 v+ T5 D) |2 f% o. D/ Q' I. j j
- 0 ^ }( D( @( [- {- v e" a* k
- //设置定时器TIM2中断# T5 q* {& E! U8 t
- TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //设置定时器更新中断
# [- c& M/ @/ S* `+ G - TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志,防止刚上电时进一次中断
* D! Z2 n, P$ i2 U: B6 g9 C - " ?* o. g0 Y& k$ z7 ~+ d
- //初始化中断
$ [% V) f, s% Y& b8 w - BSP_NVIC_Init(TIM2_IRQn, 3, 3);
; b, G. Q1 Q; p+ k8 U% S - & ^. @. V8 ]& s) [. F k) a
- //使能定时器TIM2& J9 h. U" r6 N
- TIM_Cmd(TIM2, ENABLE);
) t# W4 \9 c+ K! T/ f0 {3 Y" M$ K5 S( F+ T - }
复制代码- long long lvalCur;3 Y* O% b+ H1 m" S" p' s9 D
- long long lValPrev;
3 I$ j' j* o6 J2 o$ V1 b% C - float fSpeedVal;2 @( D0 G2 A9 h+ o; n
- void TIM2_IRQHandler(void)& H' f; W" N; v% o
- {
" o* n K9 _* n; D. `- q! g" `9 @ - OSIntEnter(); // 告诉ucosii系统进入中断. v, G& V7 e, T9 l
- ! L" L* C( h" ]" \+ P; J
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)/ a, U! d" y5 P, T) S2 ]! a( k
- { , {/ v" r% K6 @
- extern long long lSpeedCnt;" B& p6 B8 g6 I( A5 ?- ^
- 6 O3 `& f/ Q$ o5 V
- lvalCur = lSpeedCnt;4 g7 n1 j# A2 n
- fSpeedVal = (float)lvalCur - (float)lValPrev;
7 Z6 i. g* S4 Q7 p - fSpeedVal /= 5.0;0 L u7 o7 [. C) |. R
- fSpeedVal *= 1.38;
. i( N; `& x, ] - lValPrev = lvalCur;
3 A3 S0 U1 ?( ~# q1 S - TIM_ClearFlag(TIM2, TIM_IT_Update);
7 r& r) @6 S! Z! P2 Q- g - }1 L" M A9 o8 s- T" p+ t1 M3 v, s
- OSIntExit(); // 告诉ucosii系统退出中断8 z# |4 ~0 e8 ~' l6 H% s- ?
- - t, i- |: o# `, g, l1 @
- }
复制代码
. F, L+ Z% o+ F, @' }, \9 y3.3.系统共有两个任务,其中一个任务每50ms发送一次速度值给PC机,另一个控制LED1闪烁,周期100ms,用于指示系统正常运行。9 V v* q! a" X. K, O4 |
; ], ^9 V% R& V; Y% X' s( k
第一个任务中运行代码如下:- N( i! C: t0 y/ a' B/ x( G1 M# R
, x+ B9 a5 C1 s2 t: A# V+ P! D/ F* z% R- while(DEF_TRUE)
. ]. a2 y; y" r6 Y - {
' y$ M6 z) H) ?8 D/ E) m$ S - extern float fSpeedVal;7 I+ ~# d! a4 ~9 s8 E2 h1 B8 o2 O
- if((int)(fSpeedVal*100) > 9999)4 j% I! ? `. Y2 p/ o
- {1 x: v, c5 B3 [! X4 l
- printf("9999");
. h6 m- N! R9 S, u6 U - }0 B: _. f$ r8 D- n8 z4 a
- else if((int)(fSpeedVal*100) > 999)! G' j [* ^/ M' h
- {. B* C4 J4 K W7 @- B7 V: Z) [
- printf("%d",(int)(fSpeedVal*100)); % I; i+ V2 y, r
- }: R! Z% T7 W: `
- else if((int)(fSpeedVal*100) > 99)
' X' A5 |& p$ v- l$ |# }' W - {# S4 `5 r4 L+ Q! U) f
- printf("0%d",(int)(fSpeedVal*100)); 4 i- M M' T9 p, c Y1 C; [
- }$ p$ q, L9 Q, ~& [/ L ?6 M
- else if((int)(fSpeedVal*100) > 9)
3 d4 ]' l% @ j. M, @9 V" i+ o - {
0 y2 i& Z: _1 w4 w% s0 \+ e - printf("00%d",(int)(fSpeedVal*100));
8 [0 f# y+ N: y5 Y2 f - }" W, E3 s* A4 f: j+ H, ]1 q9 z
- else
) j% D7 A' i2 l5 _7 T - {
( J9 x3 P. j- k- r" c# R - printf("000%d",(int)(fSpeedVal*100));
0 h, q y& M5 | } - }
) ]3 V; E4 V7 \$ V/ k
7 r# \3 ~7 Q$ g, V) G- OSTimeDlyHMSM(0, 0, 0, 80); k2 `* i, ~/ O% a7 p
- }
复制代码 7 R A& O/ [; L! l+ n: Z# y& ~
第二个任务中代码如下:
5 a' `- b' d! k! E8 [
9 A y6 w: b' [0 g3 r- while (DEF_TRUE)- F9 |# Z' h& i( P: X% `% k4 h+ Y4 o$ d
- { . ^( _0 {3 @8 f/ K
- BSP_LED_Toggle(1);
5 Q# P; K# e* p+ p% |) c8 r4 A - OSTimeDlyHMSM(0, 0, 0, 100);
+ t; o; ?9 N0 Y - }
复制代码 & j& l& ^0 x9 \$ e
整体运行稳定,满足项目需求。
# s# a F0 g; N% J& J
' O& l' Y1 S2 P D4 ~1 w4 u! R
1 j) _' i$ ]$ [ |