测量运动自行车速度,上传给上位机软件,处理VR视频播放。正好公司有现成的stm32f1系列单片机开发板,所以我就想到了使用它来实现这个小功能。4 B G% _* a) K. b q- r
4 s: K2 ?$ l; D- ]; s
1. 硬件配置:2 |' @& S* u! s0 e* \
9 x- K. u; J. w# K
1.1. 运动自行车;
! ] s; }* S$ O$ E6 ?% g5 v* ?# g% @5 e( Y, a0 g
- u. S# w4 ?1 s& h1 P) N7 k
" F' N* v. X8 J- ~1 ~. d1.2. 磁感应开关与专用磁铁;
* \% [. k% i7 X/ u! ?% }& g s0 H/ H8 i8 s, r4 \& p+ b
% x# J, H6 i4 z) q" O
" z7 d( O( E$ k0 t7 R' @
1.3. 基于Stm32f103zet6芯片的开发板(七星虫),如下图;
% y/ T) t1 L$ a" F1 H) [
/ z+ l x( C* c1 m; ?* t& A
6 W+ H# \+ ]0 c3 Q" Z) o
3 M/ @' A/ @. D1.4. 连接线若干;
' g; Y; N5 Q* r4 F4 [0 D/ t0 f# n" {' A' Q( f
1.5. miniusb线缆,用于给开发板供电及串口通信。. q' |) q; Y- y, \0 @
! r, c( u+ g% R6 x2 \- X+ d! L; m* b4 \* d+ h
) ^, @ [- l8 F# n) I! m: V( Y0 o2. 系统描述与框图:3 ~- t+ ~/ r2 |4 W+ A! D4 l1 S
) y) @, t* q" f! z" q
运动自行车车轮上安装5只磁铁,通过磁感应开关检测磁铁产生信号,接入stm32开发板PE0引脚。测量出的速度值通过串口发送给PC上位机软件(mini usb线缆连接)。硬件框图如下:
7 b, B" h, A( R4 f! s7 t0 V7 i& D6 }& j# I0 v
# D. p v; _, W+ ?! Y' J4 E% r
8 ~* Z: I: {0 q, r
5 W' b: T0 R0 c O; S1 v2 L1 `3. 软件实现
v" a! Y& Q2 c e8 Z- ~, Z; b# l& @. |' K$ m% f
3.1.设定开发板PE0引脚下降沿中断,在引脚中断服务函数里累计中断次数(即磁感应开关感应到磁铁的次数),同时每累计10次LED2交换一次状态。外部初始化代码及中断服务函数如下:/ K0 m+ ]( k/ U6 w" r5 O
7 [; C: ^1 t& a6 q9 e
- void EXTIX_Init(void)
1 }- j, m5 G* p7 D( e5 a( g - {( j: F/ |5 X3 V0 ?; E+ D
- EXTI_InitTypeDef EXTI_InitStructure;
@5 Y4 z$ W: v7 ?4 E( X5 H - NVIC_InitTypeDef NVIC_InitStructure;
( b% o! f H' k$ q - GPIO_InitTypeDef GPIO_InitStructure;% v ~3 u7 z5 i0 t8 k& l
- // 引脚端口初始化 PE0
) ~6 M. [0 v/ {" K% E: o* K - GPIO_InitStructure.GPIO_Pin = DEF_BIT_00;$ \$ F* P' ?% }! w( a- R5 P) v
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
4 m- y; q9 z- p3 }# W - GPIO_Init(GPIOE, &GPIO_InitStructure);
) T( q% v* ~1 B0 P# H! z: s - // 启 AFIO 时钟
$ R- n3 [6 `/ a! _ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
6 R9 [. U# b9 \: A# z - //GPIOE.0 中断线以及中断初始化配置,下降沿触发
5 U! H$ G3 q4 J+ P. t' {( M - GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource0);// 配置中断线为0( l* E: o4 N, n) S/ R b, Y" {
- EXTI_InitStructure.EXTI_Line=EXTI_Line0;" l- E( c+ v* w8 |2 |
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;: e; ]. w3 w3 u, @
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
9 v" ]* a1 n4 V - EXTI_InitStructure.EXTI_LineCmd = ENABLE;% X2 D0 x: K2 \7 Y" V3 N2 H* M
- EXTI_Init(&EXTI_InitStructure); // 初始化中断线参数0 l0 o* m% s. y* k2 N; }
- 0 c; u# \+ T' R, m& \
- NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键外部中断通道
, ~" `, G; `% y& P7 u - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,5 ?. t9 }- Z3 ?, ?3 X/ J
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2- a7 ~/ @! I( C# c& d1 ?: }
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
& n9 }' t" p6 Q6 Z/ g9 G. T8 u - NVIC_Init(&NVIC_InitStructure); // 初始化 NVIC
# b1 j; x6 x$ b! }: s z - }& _) t( J5 r, K! p
- // 外部中断0服务程序7 b3 ?: W* f! ^: V/ i1 \, h
- long long lSpeedCnt = 0;
: X j8 D% }% z - void EXTI0_IRQHandler(void)
0 K) ~! t2 E) }! l - {( O2 c7 j- A! ?" @. n0 i( ~
- OSIntEnter(); // 告诉ucosii系统进入中断6 l& I. K" }9 O+ g5 N# p& |( c: i
- ( d; @& p: z1 l# W2 t- t! ^
- if(GPIO_ReadInputDataBit(GPIOE, DEF_BIT_00)==0) // PE0检测到下降沿
- \* s- Y3 V- O e) M - {
' V2 v# |' |3 W - // 累计中断次数,每隔10次改变led1状态
0 Y. f. N6 E" z2 y4 f+ v - if(!((lSpeedCnt++)%10))
: Z" d/ L% Y4 o" ^ b! O# S - {/ u2 C* N. M" d* a) D) z
- BSP_LED_Toggle(2);
& O- S9 i5 M$ k: |% } - }
9 ?1 {! x/ Q3 C- K7 ]$ z' k( A - }
; d: K. v% n8 x7 W6 R - EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE0上的中断标志位
X& m) z. v7 S. m4 Q
! S3 v9 {; W; g$ ~; |6 |- OSIntExit // 告诉ucosii系统退出中断) U. Z- E# o7 P: [% W5 r
- }
复制代码
3 o3 b$ f+ n8 Z( B X% W
" u' d+ ]) J4 g$ q; z) }6 ]3.2.在启用一个定时器中断,周期为1s,在定时器中断服务函数里计算自行车的速度。计算方式如下:v = p / μ * C,其中:v是速度:m/s,p是磁感应开关感应频率, μ为车轮上安装磁铁个数:5,C为自行车车轮周长:1.38m。定时器中断初始化代码及中断服务函数如下:
9 e+ p9 ?# Y% t' t
4 A+ E& _ b" |0 u& T4 v2 W$ g& \- /*******************************************************************************
0 g. ~; ]9 ?, q( H - * Function Name : BSP_TIM2_Init% H$ _/ H+ p: \" U! r |5 p( x9 n
- * Description : Compute return latest speed measurement I9 n' S3 B3 Z$ P L; ]
- * Input : None/ b) g) P' ^1 `" U# ~. `' c/ I
- * Output : s16
) }) E4 t5 b1 U* w- o2 w$ Y+ \ - * Return : Return the speed in 0.1 Hz resolution.
, h- x8 s/ U$ h5 J7 `) N$ S - *******************************************************************************/
* m4 M4 c$ t1 }6 ^ - static void BSP_TIM2_Init(u16 arr, u16 psc)
4 @0 I: o* ]: L1 R - {
) e3 N, H# M" C2 j. C% b - TIM_TimeBaseInitTypeDef bsp_tim2_init; % X8 H; E- I" x6 l5 Z% [* p( ]
- ; v5 J* U" E: a7 z
- //使能TIM2时钟# G8 p0 @. v4 f' }4 P6 @* b5 N
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
c2 T5 a2 w& {1 N/ \- W/ r
8 F6 R# z* \- K7 O+ |6 m' w% l# d: f
/ Y: A2 ^& k! A3 T3 ^, Z- TIM_DeInit(TIM2);
7 j9 j" Z/ }6 Q |1 M - TIM_TimeBaseStructInit(&bsp_tim2_init);& _+ k: t& [/ N% Y
- //TIM2初始化, }5 ~3 [9 \! }1 F& ^8 L' c
- bsp_tim2_init.TIM_Prescaler = psc; //时钟预分频 定时器每隔 (psc+1)/72 us计数一次
" X" H7 u0 ]+ I( g( m - bsp_tim2_init.TIM_CounterMode = TIM_CounterMode_Up; //向上计数1 d3 w A$ @( T1 {+ W% w7 {/ G
- bsp_tim2_init.TIM_Period = arr; //计数满(arr+1)次更新重装载寄存器数据
+ \' K8 ?/ y# V0 x - bsp_tim2_init.TIM_ClockDivision = TIM_CKD_DIV1; //时钟不分频- s; ]' D) i5 y2 b3 } @4 V
- // bsp_tim2_init.TIM_RepetitionCounter = ; //高级定时器用,这里不需设置. C" q" Y- Y) e* B& ^' F' l* m0 i
- TIM_TimeBaseInit(TIM2, &bsp_tim2_init); //初始化定时器
6 B& z: q2 w$ F4 L0 t
# D- l' n) s I. \' ^- //设置定时器TIM2中断
; Q6 x) G/ J' f4 o T# Y4 ~% C& H - TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //设置定时器更新中断
' V9 V* `4 |6 f, F. i P - TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志,防止刚上电时进一次中断
1 k0 J0 |! r! d1 {. F1 C
0 q! V; t1 F! e! k6 r: G- //初始化中断. d5 F1 r8 E( r& W0 ^6 R
- BSP_NVIC_Init(TIM2_IRQn, 3, 3); * i+ o4 P4 Q0 b& ?
- - \% T9 [+ r# Y/ M c
- //使能定时器TIM2
9 B8 U& _ A* H/ V+ e) m( w% G - TIM_Cmd(TIM2, ENABLE);
! S/ \3 { O, v* z, m6 Z - }
复制代码- long long lvalCur;3 d7 A$ w( q! `/ ]$ ]! J
- long long lValPrev;( w1 t- R1 X+ J' A+ Z
- float fSpeedVal;
! a6 q" X P. H& z - void TIM2_IRQHandler(void)
, d) W0 [# n# W, Q" r z" V" y4 p - {# E W8 p: |) S: z
- OSIntEnter(); // 告诉ucosii系统进入中断
" g ~0 x ]* N& o& E - 6 l, q" K% g: N7 l0 R, w2 c1 X
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
, i; ? a# S! S. x, o* c - { 0 f% C# |8 c( f- ^( u
- extern long long lSpeedCnt;% s( |7 J6 {& U/ [
; K/ A% P) ^- C0 `$ G- lvalCur = lSpeedCnt;
9 w( D$ J3 I, G+ E: Q+ L, k - fSpeedVal = (float)lvalCur - (float)lValPrev;
/ G0 S+ x7 E7 S, G3 R+ \1 p - fSpeedVal /= 5.0;$ O7 _5 E% C) m8 }: v
- fSpeedVal *= 1.38;
( \$ M8 D p; v* R7 O) S9 Z6 Y - lValPrev = lvalCur;
$ w( i6 `8 X9 j2 T, Q* y! G3 e - TIM_ClearFlag(TIM2, TIM_IT_Update);
! D! d9 G+ b: \' ^7 I/ Y, b1 H3 | - }
8 [% a; _ e. T3 Q& e7 J C( K - OSIntExit(); // 告诉ucosii系统退出中断
! Y/ f9 T' w/ n- x$ }
0 [" U# {/ S$ Z m/ u& e5 }- }
复制代码 " M8 I0 g; N2 Y" v7 ] `, B
3.3.系统共有两个任务,其中一个任务每50ms发送一次速度值给PC机,另一个控制LED1闪烁,周期100ms,用于指示系统正常运行。& U5 f# W# e& U1 H3 p
# o; P& R) z: V8 [7 P2 e4 [第一个任务中运行代码如下:6 x* {) G$ l. K/ e9 ^
$ I8 {$ I6 H6 r: n6 k' V. o+ b$ i
- while(DEF_TRUE)
# c# n7 s! A* \, c, z- o4 o6 e4 I- F8 z/ b - {
& `! ]$ |, X/ P w- Z( j; m - extern float fSpeedVal;
: M! _% H. F7 X - if((int)(fSpeedVal*100) > 9999)
# r2 n' Z( }. n# D1 Y - {* p0 p1 M7 d/ W
- printf("9999");
6 R0 ~/ H/ p/ i - }
9 B; }* D9 R9 M - else if((int)(fSpeedVal*100) > 999)& ]+ s9 s# C4 N& D& H
- {
! q2 t) b+ }: l5 r - printf("%d",(int)(fSpeedVal*100)); # L; D) d1 D9 X% w5 r, G# i
- }+ C, o. N T4 o# `$ T) ]
- else if((int)(fSpeedVal*100) > 99)
z6 r2 B" `7 N& O1 e - {5 V* u, w: I; W9 E8 W n+ q
- printf("0%d",(int)(fSpeedVal*100));
) ^+ ^# o* \, D |8 D7 c - }! W$ x& o( T9 f! Y. `- K: i
- else if((int)(fSpeedVal*100) > 9)
+ q8 S; M& ]$ N- ]: P3 Q: y( j - {9 ~4 ?* A/ G' y" o" c- x V
- printf("00%d",(int)(fSpeedVal*100));
j0 d2 J; Y# ^- }# y, b - }
! @; d6 T6 h8 L1 e( R - else
. |4 s1 A6 `" @" H. n - {
, o# ^) S1 B" K+ h7 A+ C - printf("000%d",(int)(fSpeedVal*100)); . p; Z0 r) w* X1 e
- } ) a }9 d. k8 ]8 Z$ }; @9 Q
- . V4 u; P S! H' Z
- OSTimeDlyHMSM(0, 0, 0, 80);
& Z: S8 b! G7 m4 k0 x$ Q$ ]; ^3 F K - }
复制代码
6 W k. @# I# y. t第二个任务中代码如下:
t& Z) ?! t$ }& H) C$ _) n$ E' k9 X! e- y2 N" G K1 n' `5 h% e
- while (DEF_TRUE)
& I9 k5 c% Q. ?! c - {
7 V& I9 `) x+ Q/ J- z5 ] - BSP_LED_Toggle(1); + _$ ?: |- F9 P3 P2 P
- OSTimeDlyHMSM(0, 0, 0, 100);
" J3 y+ G0 A! Q. r, O - }
复制代码
2 q' M% n" O7 O& ^5 N# \整体运行稳定,满足项目需求。
$ o' @7 Y: p2 g' B5 T( j h8 t$ {- }( P; Q! J( z1 j
; q' j! h! F" f5 s/ w
|