测量运动自行车速度,上传给上位机软件,处理VR视频播放。正好公司有现成的stm32f1系列单片机开发板,所以我就想到了使用它来实现这个小功能。8 k) K; Z' C% ?6 q+ _# ~3 T( [
+ M! X" x3 e6 r
1. 硬件配置:
; B. J; Q3 v) Q! t* `7 k
}+ B( I/ C" S4 x! r# }5 _2 Q" N1.1. 运动自行车;0 w! S$ }9 g) b) R. t- s) W
H7 w. j# b6 ~' n# O6 C* |
' s" A2 G: l0 b1 s, z5 a1 f, I% A! ?
8 A: b9 f9 o6 u
1.2. 磁感应开关与专用磁铁; 1 `( V" b. i. w) o; X
5 ]% {, W; T W6 @( O9 Z
( o6 g1 U8 m- ] P3 x* ?3 ~6 H; p p; k6 q% w; }
1.3. 基于Stm32f103zet6芯片的开发板(七星虫),如下图;" c2 c% h! j7 M
% Q5 w4 `; y8 t# J* x, P; g) i& P- W
% [, {" c8 I1 }6 Q E7 W
1.4. 连接线若干;
' y$ R! M+ X, o: n4 L. p6 y- G, L5 c
1.5. miniusb线缆,用于给开发板供电及串口通信。
2 S1 K7 M4 v" c& v8 D- v& O* a; r( g. E
+ {" L# W6 E g8 i% U3 {; L8 {
( C- @0 i! { \' M, ~. S
2. 系统描述与框图:4 g7 g+ z# X+ e! |
' Z( I0 G, I" Z$ X& v( I
运动自行车车轮上安装5只磁铁,通过磁感应开关检测磁铁产生信号,接入stm32开发板PE0引脚。测量出的速度值通过串口发送给PC上位机软件(mini usb线缆连接)。硬件框图如下:
' W5 U; u( s- G1 `! h# p
! ?5 P) A3 Q' b5 v3 t, j3 d: d) r" f- x, c6 P
# o1 B8 C" x7 n9 J. Q2 T
6 P, c: k& n$ F% U# N9 M5 I' M3. 软件实现$ }# M& c6 s0 T+ I! H( b" J
6 ]; ~" _% U5 K. n) {3.1.设定开发板PE0引脚下降沿中断,在引脚中断服务函数里累计中断次数(即磁感应开关感应到磁铁的次数),同时每累计10次LED2交换一次状态。外部初始化代码及中断服务函数如下:5 y- l7 e5 R% g( X% k
- L5 l/ d# M# q$ P4 p- D' l- void EXTIX_Init(void)
" y1 |7 j+ Y ]6 Q7 c1 N: F - {; _- y/ t4 y$ p5 v# ~* B& E& `
- EXTI_InitTypeDef EXTI_InitStructure;7 Z8 F3 E0 z# T
- NVIC_InitTypeDef NVIC_InitStructure;0 H0 M+ Z8 Y2 |" \- F' {
- GPIO_InitTypeDef GPIO_InitStructure;
* D% O7 z# J# [0 @7 Z - // 引脚端口初始化 PE0% G* X7 o2 w p$ {2 C6 C. D
- GPIO_InitStructure.GPIO_Pin = DEF_BIT_00;2 q9 s, q- r% ?; Q- O
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;+ v d: y6 Z: Z
- GPIO_Init(GPIOE, &GPIO_InitStructure);
* Y- g' T, a: R/ b/ k7 ]2 ]- X - // 启 AFIO 时钟
9 E. [4 i- L2 C( |/ L2 E$ } - RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
+ D8 ~9 X( H7 U5 Z - //GPIOE.0 中断线以及中断初始化配置,下降沿触发# o% [ I' D) n( ]0 q& I2 o
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource0);// 配置中断线为0
% m4 w4 A8 g% t6 L, d& H - EXTI_InitStructure.EXTI_Line=EXTI_Line0;
: Q1 S7 I( a* a) d - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;" ^7 c7 P: J* @8 J! p- u
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发# ] R/ Z9 G9 B" p9 b- ]/ x
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;$ Q& Z4 x% I/ m8 n" I) P X
- EXTI_Init(&EXTI_InitStructure); // 初始化中断线参数5 q9 X" [0 f$ A; l0 [# u, w; j
) T; M7 e4 ~2 q8 k- NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键外部中断通道
8 L2 G9 C% k0 C4 ^! N - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,( a" g4 i4 i O: G2 W2 f/ v
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2% H9 A# Y; \( k: u
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
9 q* R$ V( I1 v - NVIC_Init(&NVIC_InitStructure); // 初始化 NVIC. n% K4 k( ]: p# L0 m. J# A7 D
- }
6 Q% C5 T: n; v& A; J; O: z# a - // 外部中断0服务程序& t( F1 q) D! n$ f& l! E
- long long lSpeedCnt = 0;
7 t8 N" `& k$ T - void EXTI0_IRQHandler(void)5 ^0 K& S! F% L9 ]* P
- {
( q5 \0 U' L8 \ p( q4 L8 ] - OSIntEnter(); // 告诉ucosii系统进入中断6 S& u' G6 z4 L. n
- * \0 t2 ^ i- o
- if(GPIO_ReadInputDataBit(GPIOE, DEF_BIT_00)==0) // PE0检测到下降沿
. ?4 M3 g+ m3 g8 e( F - {( R. ^, O; T: i) Y5 i
- // 累计中断次数,每隔10次改变led1状态1 o, D4 w G/ g" w& L. D" k
- if(!((lSpeedCnt++)%10))
2 c3 E: O7 f9 z - {- E' @3 [& T% u" k
- BSP_LED_Toggle(2);, ^, H8 S% x2 H* S) B- r! o
- }: ^4 R% Z# h3 W. I
- }3 m5 U# u3 e% {1 F
- EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE0上的中断标志位
" ]7 w6 [5 ?; a/ j. [$ W3 m" B
" r) [" I3 C# E- OSIntExit // 告诉ucosii系统退出中断; T' e w( g0 I2 L
- }
复制代码
/ C/ H% b: g% }- e
7 \: U ~% Q3 B7 x3.2.在启用一个定时器中断,周期为1s,在定时器中断服务函数里计算自行车的速度。计算方式如下:v = p / μ * C,其中:v是速度:m/s,p是磁感应开关感应频率, μ为车轮上安装磁铁个数:5,C为自行车车轮周长:1.38m。定时器中断初始化代码及中断服务函数如下:
( d3 B- g" O0 Y, I2 o& d' O$ {$ `. ` s8 B: r* Y8 Y: a O I5 i) G
- /*******************************************************************************% z. V) I+ b K S* m' q
- * Function Name : BSP_TIM2_Init2 o3 @2 R' S" c9 P3 E* J
- * Description : Compute return latest speed measurement
1 C+ s6 w" x3 `. n! M& c9 R; Z/ | - * Input : None7 X- @' I( T( }# W. j, u
- * Output : s16
( k5 g: s8 K9 N) A* ~ a: C - * Return : Return the speed in 0.1 Hz resolution. 5 L. d4 L9 i1 F. M A3 L
- *******************************************************************************/
" Y3 ` O \* y - static void BSP_TIM2_Init(u16 arr, u16 psc)
; S; t. Q) k; I$ ^+ l - {
$ O' {4 {$ g' s3 _5 f9 z( w ?' {7 E6 p! U - TIM_TimeBaseInitTypeDef bsp_tim2_init;
% e- [' a7 z5 i7 t, M - 8 x: Y8 d# \8 R% L, B$ V- H
- //使能TIM2时钟
2 v' c. {) ?, J( }/ R - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);1 N5 s/ o' w) k8 h
- & Y# j1 B4 T4 p0 g8 T$ `
, W/ p7 H& b9 x! s2 E h- TIM_DeInit(TIM2);- b# }, F) _6 D2 c' @- t9 b
- TIM_TimeBaseStructInit(&bsp_tim2_init);
9 b o& K$ r% Y7 h3 W' p& G - //TIM2初始化1 X8 c% i1 n- m/ B) z' n
- bsp_tim2_init.TIM_Prescaler = psc; //时钟预分频 定时器每隔 (psc+1)/72 us计数一次
8 t( i/ A4 o' M* \9 B - bsp_tim2_init.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
; X& G" G: ^/ J, x - bsp_tim2_init.TIM_Period = arr; //计数满(arr+1)次更新重装载寄存器数据# x/ a0 ~5 o' j5 E+ ]3 d
- bsp_tim2_init.TIM_ClockDivision = TIM_CKD_DIV1; //时钟不分频
1 M0 u% ?( B7 W2 ~; q' g - // bsp_tim2_init.TIM_RepetitionCounter = ; //高级定时器用,这里不需设置 z% t2 d# Z' e7 K+ B4 K5 _
- TIM_TimeBaseInit(TIM2, &bsp_tim2_init); //初始化定时器
, j% {2 a) W/ F4 a) U+ c - 9 n- _2 Y. X* F0 K
- //设置定时器TIM2中断! n" {& t3 Y+ U. B
- TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //设置定时器更新中断
6 p6 x/ d0 ?* Y4 ] - TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志,防止刚上电时进一次中断
( t# B9 H' g" B7 E, b
) A; h' y6 q( t/ G" f. @- //初始化中断6 U$ z& E3 I% ]: F
- BSP_NVIC_Init(TIM2_IRQn, 3, 3); 7 m1 ?. N3 }4 G h' a
- / S4 b" ]- |5 I) S7 a9 ^
- //使能定时器TIM2' J7 x/ C/ n; f8 o" o; U8 u! p
- TIM_Cmd(TIM2, ENABLE); o/ g j% d, j9 e5 p# |; a
- }
复制代码- long long lvalCur;7 Y" C0 m7 F# Z2 v, k& ~3 r
- long long lValPrev;
( o, `( Y1 _ ? - float fSpeedVal;
( ]1 i( Z- f6 [( C' \+ S A% T - void TIM2_IRQHandler(void)
0 m; H' Z8 f8 @0 ` - {* y. c" C' M q
- OSIntEnter(); // 告诉ucosii系统进入中断7 P' ] F' ?: q7 c- v+ j
- ( k, Z! J/ d2 U( _1 u
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)3 D, }8 x4 J2 b) P( @ F6 q! r
- { ' k* U+ P. H5 p' }+ v6 M4 \0 e8 {
- extern long long lSpeedCnt;
5 |+ [! y: b0 H* u! ~9 }; A
; q' _& R- s1 x5 a3 M- lvalCur = lSpeedCnt;
6 x2 @2 V; n$ i! M - fSpeedVal = (float)lvalCur - (float)lValPrev;
' N& u6 Q5 P8 p; ?9 d5 H- I* l2 n - fSpeedVal /= 5.0;
$ I# {; Q- z h) b - fSpeedVal *= 1.38;
/ g4 L- P* X( Z - lValPrev = lvalCur;6 D, g& I6 N5 K L1 g6 N) H
- TIM_ClearFlag(TIM2, TIM_IT_Update);
: H1 s) R" q) i! x+ G% L6 ?, ? - }
( T& N0 L2 e' A# ^6 P: G - OSIntExit(); // 告诉ucosii系统退出中断) U" y( g% w+ s$ w
/ E& T! {& P! W9 M. g+ v/ V9 N- }
复制代码
5 w/ z0 G+ W9 h6 t$ g3 J3.3.系统共有两个任务,其中一个任务每50ms发送一次速度值给PC机,另一个控制LED1闪烁,周期100ms,用于指示系统正常运行。7 C1 w7 V4 @* @
k8 u6 V! H8 e1 P( i: |. z
第一个任务中运行代码如下:$ K; W! t" z+ \3 J: K
" L- @ V; E4 _( ?- c% ~7 X
- while(DEF_TRUE)9 F; r9 J% ~$ t' v9 {/ k, G
- {
* X8 M, y% j4 t# C% \ - extern float fSpeedVal;
# X7 e) d3 d ^) X - if((int)(fSpeedVal*100) > 9999)
& t% d& J( X9 R! d5 n' q. G2 q( O - {9 v3 n0 M& Z+ W, D0 X a
- printf("9999");
4 H8 T7 N! Z/ m - }
5 i. Q/ l: J9 H, Q3 j4 P - else if((int)(fSpeedVal*100) > 999)
t v8 f: d( L& u( n: G8 G( G - {# b; Z. k4 y, Y
- printf("%d",(int)(fSpeedVal*100));
) @/ a: V: J* d - }
# C3 G- J b/ f- q2 c - else if((int)(fSpeedVal*100) > 99)# |! r4 q/ I1 ]# x7 o: W
- {
1 K0 Y, `7 X& f; R% Z- L7 z - printf("0%d",(int)(fSpeedVal*100));
: q4 O4 a7 y5 {: b8 N - }2 Q! _ X) r& e+ U1 r3 r
- else if((int)(fSpeedVal*100) > 9)4 P/ E# V# C! t; v+ H p0 r+ x
- {
5 A$ Q% E2 W0 c' }9 a9 n: Q$ N - printf("00%d",(int)(fSpeedVal*100)); & b2 z2 X% \$ E
- }
2 C* y8 T7 w) [$ Q$ t/ K& [& I - else7 k8 i1 }1 i! C; K- W& ^; D
- { I5 G% Q) m4 d7 i9 Y; e
- printf("000%d",(int)(fSpeedVal*100)); % d& Q3 a+ P6 E/ u# N9 z( m
- }
0 \+ r0 r/ Y2 A. E - 1 e9 Q8 I2 O* w; G$ D
- OSTimeDlyHMSM(0, 0, 0, 80);
. z e4 g; ]3 x Q - }
复制代码 8 ?$ a1 f1 j4 c1 e, U$ V- y
第二个任务中代码如下:) L4 V) _9 A! @" _$ f7 W& s% C9 e7 {
% ^1 D( v5 _ c( D( T. V- while (DEF_TRUE)
6 y% j7 U! x. U6 c2 P# B7 r - { 3 p8 O. n$ c! a' {) P! f7 A1 l" q4 v
- BSP_LED_Toggle(1);
/ x$ Y; A/ m: C: m - OSTimeDlyHMSM(0, 0, 0, 100);3 h3 S( j/ U- }$ `% b
- }
复制代码 1 H* ]- C. p" x+ s2 M1 ~* B! @
整体运行稳定,满足项目需求。
) r8 Z7 x+ o' Q7 t! x8 j2 K( H6 x9 t
* h( V3 Q- x" u7 C2 H; t. Y% \ |