测量运动自行车速度,上传给上位机软件,处理VR视频播放。正好公司有现成的stm32f1系列单片机开发板,所以我就想到了使用它来实现这个小功能。
: W+ T. y% b% M, J; m/ @& R& b/ F- A9 u) y+ r; i
1. 硬件配置:! J# W0 ]8 g& ]& r S+ ^
' E9 t1 W2 N' ]1.1. 运动自行车;
3 j0 n$ \2 y8 L6 r( }$ Z1 ]- w8 D; h l ]) j, ]6 A- d. Q
1 b: Q; W: a& V8 }" x+ u- Z% L
7 _7 f3 r7 a# H1 c
1.2. 磁感应开关与专用磁铁; & Y4 r6 r' f" ^5 r! S/ a: M
; m# d1 a; o' O3 T$ c) l4 ^
( n- i5 }# ^ u5 B# [* o$ V) L1 O, {. }3 i$ ~2 x
1.3. 基于Stm32f103zet6芯片的开发板(七星虫),如下图;
% A! l/ U' a& v0 v P3 X& t
! S1 h: j8 ^7 d" t t. T! a j/ L5 W( h$ ~
0 r; ` v' P4 [' k; R' R3 N; C9 a1.4. 连接线若干;
. u/ C# J- n# n. k$ t4 {! }
' v# N% c" R% |/ `8 R1.5. miniusb线缆,用于给开发板供电及串口通信。
& S: C; K# a( Y G7 k
7 @9 `, }& S8 {1 K* e
) y/ p" L, l/ F1 b# h8 B- h; X$ s; p# }, i2 V) s( K; j
2. 系统描述与框图: S7 m/ H4 ~9 n+ t7 W5 H$ A
$ c2 `6 l% p3 ]4 O& d
运动自行车车轮上安装5只磁铁,通过磁感应开关检测磁铁产生信号,接入stm32开发板PE0引脚。测量出的速度值通过串口发送给PC上位机软件(mini usb线缆连接)。硬件框图如下:+ h% M3 }: _; d( o; _
$ A& ]2 u- O7 q9 z5 {" Z9 \
2 W4 G( q) }; r8 \ Z. t# @) c# k' t/ o
7 ]! W: t9 o5 V7 B y9 H3. 软件实现. K( d+ x6 ^) ?0 d: k
% R" U. d7 `& z) i( S
3.1.设定开发板PE0引脚下降沿中断,在引脚中断服务函数里累计中断次数(即磁感应开关感应到磁铁的次数),同时每累计10次LED2交换一次状态。外部初始化代码及中断服务函数如下:7 A; Q, k; m! y8 \- ~) p
* n( X( h+ f' v( ]0 t- a2 h- void EXTIX_Init(void)
, U0 a( x+ A O, l1 _) ]* ` - {
7 G( n/ E2 q! }) d% [1 |: U - EXTI_InitTypeDef EXTI_InitStructure;
; F. Y, Q3 ~7 T. y' r" u8 t - NVIC_InitTypeDef NVIC_InitStructure;) O' ^" u2 ^/ J$ }9 m
- GPIO_InitTypeDef GPIO_InitStructure;
5 k. i0 g8 }8 I! Y5 K - // 引脚端口初始化 PE0
`/ s: a4 j! E: Y! P2 H - GPIO_InitStructure.GPIO_Pin = DEF_BIT_00;' _$ L- s. }. ]# P0 {/ [3 U" e
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;" ]$ j" Z6 x* ~9 h" B
- GPIO_Init(GPIOE, &GPIO_InitStructure); ! W, ?! D* c0 M' h/ f) A: B
- // 启 AFIO 时钟( ^6 b5 g. K+ n+ w" v: ^) @. x: `
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
; z5 Y. ~% d" ?/ _! f W" u9 Q - //GPIOE.0 中断线以及中断初始化配置,下降沿触发8 B$ z) h5 x. \: m' Q8 m" }
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource0);// 配置中断线为0- s$ M ^+ b+ E& ~$ F' |
- EXTI_InitStructure.EXTI_Line=EXTI_Line0;. y8 G I2 y+ x- ^5 Z3 e
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
" Y u4 v7 Q' V/ D! k" g2 P, x" A* R - EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发. ^1 {; `6 p8 ^
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;
% ^( N, {# I1 S e" |! a0 B5 ]& f - EXTI_Init(&EXTI_InitStructure); // 初始化中断线参数
' Q: v+ @; `) s/ G! Z
6 X8 k: J a5 ]( f+ v2 O4 N- NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键外部中断通道" F2 r& w" b) a5 W9 Y
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,
5 C4 Z/ k$ M4 C5 X5 p, Z. a - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 24 s; Y4 o, a! r1 K/ f3 B e
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
; _+ ?! M, [# Q( Q* U - NVIC_Init(&NVIC_InitStructure); // 初始化 NVIC* w s, P, w+ L9 T2 J" `
- }" l8 M- _' ~ A9 R" I
- // 外部中断0服务程序
3 `6 E8 N9 a3 M - long long lSpeedCnt = 0;( |7 o# b4 Y7 P# v! y
- void EXTI0_IRQHandler(void)9 h. j7 \6 B/ h8 d$ C+ N% Q9 J
- {
# |$ z4 O; v$ z; E - OSIntEnter(); // 告诉ucosii系统进入中断; y1 {2 S. G% U0 { L
; [1 ?0 H, C1 A2 |5 X; {5 x; m- if(GPIO_ReadInputDataBit(GPIOE, DEF_BIT_00)==0) // PE0检测到下降沿
- ]% K' ?/ {% y, t7 q2 K - {
5 L. f" }$ G, o" G& D4 L - // 累计中断次数,每隔10次改变led1状态
) A0 S' x% U( K+ t$ g - if(!((lSpeedCnt++)%10))
+ v5 ~" g' F3 K$ i; K. b - {
3 [+ ]+ S: S6 \; b- F - BSP_LED_Toggle(2);
$ Z: |" L9 `- Z: J8 \3 h - }( H: f& O1 L+ a& W4 W" p
- }6 V8 J* B' e' q, F, c, K- Q
- EXTI_ClearITPendingBit(EXTI_Line0); // 清除LINE0上的中断标志位 s) s3 {" j" c
- 7 j2 H& N9 E/ V6 N% g) z5 j
- OSIntExit // 告诉ucosii系统退出中断: M/ L) e* e5 S. o) d$ D- G- B9 i
- }
复制代码 - H2 X+ T) a$ U. S
( C* [* N$ B) j5 J: O3.2.在启用一个定时器中断,周期为1s,在定时器中断服务函数里计算自行车的速度。计算方式如下:v = p / μ * C,其中:v是速度:m/s,p是磁感应开关感应频率, μ为车轮上安装磁铁个数:5,C为自行车车轮周长:1.38m。定时器中断初始化代码及中断服务函数如下:
3 @$ i9 |3 }1 T! b5 h, E4 h2 t) d+ h
- /*******************************************************************************; T! U: m; e2 b( \
- * Function Name : BSP_TIM2_Init
% @! ^# w4 B2 r' E3 Q& O) c) l( z" q - * Description : Compute return latest speed measurement
4 Z; w! x& R6 k. ]/ }6 i# H - * Input : None
* L) Q* E- E) I - * Output : s16
# w$ K. @ B8 n' t+ c - * Return : Return the speed in 0.1 Hz resolution. 2 f; ]1 M8 R6 E
- *******************************************************************************/, H! G7 j" V; j1 ^& A
- static void BSP_TIM2_Init(u16 arr, u16 psc)& Z8 r7 c9 y- R
- {
. r3 W& ?" e5 \# J% M" y - TIM_TimeBaseInitTypeDef bsp_tim2_init; / |5 l/ Z+ Z1 d% j
6 d2 W s: c' H* H( @9 U# S: P9 f- //使能TIM2时钟
2 _2 R, s V& ]" g7 L - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
: D E6 H! z1 ^9 V2 {* A1 \8 E - 6 I, e( v8 B9 M' w- ^3 }9 N3 R6 @3 F
2 I8 A$ _9 U# k; _- TIM_DeInit(TIM2);8 B$ r/ D5 Q9 m( j
- TIM_TimeBaseStructInit(&bsp_tim2_init);
4 i6 |3 x1 W/ i0 f - //TIM2初始化
) P- s( O) b' l* Y7 ~/ y/ k' b - bsp_tim2_init.TIM_Prescaler = psc; //时钟预分频 定时器每隔 (psc+1)/72 us计数一次
1 K/ H$ B0 J1 x9 X; b' P' L' U - bsp_tim2_init.TIM_CounterMode = TIM_CounterMode_Up; //向上计数1 i% V; ]# s9 k
- bsp_tim2_init.TIM_Period = arr; //计数满(arr+1)次更新重装载寄存器数据
# f' l: F' C# x4 [7 {& D3 x$ ` - bsp_tim2_init.TIM_ClockDivision = TIM_CKD_DIV1; //时钟不分频
1 e; X. R; y1 M- j+ K' h - // bsp_tim2_init.TIM_RepetitionCounter = ; //高级定时器用,这里不需设置 ~; r+ q. S6 A& L" o3 ^
- TIM_TimeBaseInit(TIM2, &bsp_tim2_init); //初始化定时器
+ [0 f9 G$ ^9 ]: m6 D2 P
) i; Q# N+ G6 D. o1 B- //设置定时器TIM2中断
" q7 S: F5 h: W N( H9 R( j - TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //设置定时器更新中断
! `1 u( G0 ]$ ^! B/ k& g+ D - TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志,防止刚上电时进一次中断
! T l* T8 d( p2 ?" H9 F# D* K. r
5 ]4 r ^, T4 _* Z4 q7 R+ }; f, V" E- //初始化中断1 \7 `7 e3 @4 }$ Z8 t
- BSP_NVIC_Init(TIM2_IRQn, 3, 3); 7 X8 u9 o. V! U; d/ T4 D+ Z
' e$ x9 X: c6 u, O5 Q! S4 R, m5 _- //使能定时器TIM2
4 T1 l, g" }) v) |' } - TIM_Cmd(TIM2, ENABLE);# R( A/ C& C( D6 A5 \ g
- }
复制代码- long long lvalCur;
; s& T0 e# y% E% A - long long lValPrev;1 v) ^; j( L9 s: a, i
- float fSpeedVal;( O0 K# Y6 q' t- \1 a3 @
- void TIM2_IRQHandler(void)5 M, r: B" l9 v0 X4 }, U9 d
- {, u9 p) S- d% P
- OSIntEnter(); // 告诉ucosii系统进入中断
j$ c/ W- k" C3 B. @4 w - }0 e' V& A/ h4 A1 v
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
$ _5 f$ y: ?5 ~ - { 0 D& X1 P5 a( b* k
- extern long long lSpeedCnt;. p, v& i" J, L0 S% O4 D
; C1 y# y" p, q# L# i- lvalCur = lSpeedCnt;- ^' X! W) [. @/ o+ O
- fSpeedVal = (float)lvalCur - (float)lValPrev;9 y, h9 W5 m$ \7 f7 ]
- fSpeedVal /= 5.0;
% p9 R1 f- u6 |; v, M! w+ b' `0 R" ? - fSpeedVal *= 1.38; 7 U! f+ Q: _: R) }1 {; p
- lValPrev = lvalCur;0 |+ C( d5 Z$ F6 V
- TIM_ClearFlag(TIM2, TIM_IT_Update);1 `8 R# i. u4 r4 T3 S
- }
. Z+ v/ T2 k" T; V9 K - OSIntExit(); // 告诉ucosii系统退出中断
) }. b" `9 V2 S! \1 @, e( ~0 i5 S. \
5 N- Q; L) ~$ p6 T9 X# u- }
复制代码
& J$ A D c) K- }, X2 a3.3.系统共有两个任务,其中一个任务每50ms发送一次速度值给PC机,另一个控制LED1闪烁,周期100ms,用于指示系统正常运行。' N9 ^ ~9 f+ \- O b5 S/ K9 @9 M
- _3 } z) ` P" g6 h; x" c
第一个任务中运行代码如下:
! W# W5 Z. I( v: i: o7 r! j! G9 Y! L: M6 h# |* G+ C. H
- while(DEF_TRUE)
7 J I, a V4 m' o, R& x - { : P& @2 |& U& ?& C' J
- extern float fSpeedVal;0 X5 f0 U) d; m+ U
- if((int)(fSpeedVal*100) > 9999)! j& ^8 V1 M0 J
- {
: z+ q$ g* [) w; N9 s - printf("9999"); % c" Z& O1 p: |! P) N8 W9 g& V
- }6 ?" q# T( u6 Z9 a# B6 @3 p
- else if((int)(fSpeedVal*100) > 999)
/ V& ^" X- y4 o5 r2 t) ~ `" r - {( ?& R) |/ r2 u" V0 _
- printf("%d",(int)(fSpeedVal*100));
% }5 |) }2 \" a5 k' i; o I - }
. b- {/ x( z: i; d - else if((int)(fSpeedVal*100) > 99)
) z3 i' q9 \9 W+ |3 N& q. Z - {
& C5 u9 B/ [# }2 ?' g+ u4 i - printf("0%d",(int)(fSpeedVal*100)); * l9 x% ^; f. t1 R6 Z/ H4 n
- }
5 g, k b* \% g1 m( E/ z - else if((int)(fSpeedVal*100) > 9); F% A4 x& o6 N! J
- {. `, Z" h! p p9 v' W
- printf("00%d",(int)(fSpeedVal*100)); 9 f8 S& g2 G! v, J+ j9 {- u
- }. B0 Q3 m8 c! o; x; f
- else
3 r3 x: q2 _, q7 C0 I, } n! r - {
( W1 e, p+ |% d- [9 h+ `, A - printf("000%d",(int)(fSpeedVal*100));
}4 G, S. R" O. S) m1 U - } " N5 D2 E3 r3 d5 m! |% D
- - ^2 v/ A9 Z4 }6 u+ B( u; s
- OSTimeDlyHMSM(0, 0, 0, 80);3 C% [# l. a/ ], T! _
- }
复制代码 & i% B# E5 n3 U4 H2 _
第二个任务中代码如下:: q) N, p2 ~, i% Z! C5 P, \; o
3 Z* V' ]% R* s. x- while (DEF_TRUE)" h3 o6 B& n" B' ~ c8 ~
- {
$ B* I1 D6 `- u) F4 z7 `) I8 R+ s - BSP_LED_Toggle(1); 5 K8 a- @5 f) X g7 d5 b+ t3 u
- OSTimeDlyHMSM(0, 0, 0, 100);# ~7 l2 s- Q. A' d
- }
复制代码 3 Y4 l" G S5 X9 i6 G7 m
整体运行稳定,满足项目需求。% x9 `* l* Q: _# H
3 {( [" h) n% q. B) v4 _4 M
; b$ l6 X3 n, d7 w! ?9 h+ \ |