【项目分享】基于STM32超声波避障小车
( q2 J$ K6 t! M8 F X' B
3 L. c" O. v/ {* ~9 C
$ n& d$ R* A7 {( L+ k% F* I不管是对于初学者还是对于一个玩过单片机的电子爱好者来说,或多或少都接触到过小车项目,今天给大家介绍的的一个项目基于STM32超声波避障小车。这也是我曾经的一个课设,在此开源分享给大家,全文5000多字,干货满满,加油读完,保证你收货多多
4 o2 ^. L* c$ p) R) C v7 N* d7 P
7 I1 o2 w$ [2 W3 P- j1 I
) ]/ @% v* U) ^5 [. M# V$ c" e处理器电路设计9 j2 g. T0 r$ g# J: e# k' D! k+ q: X
单片机是系统的CPU,是系统稳定、正常运行的重要前提,以下为单片机选型的两种方案:
+ N2 @" B+ i; ]9 r0 i(1)传统的8位单片机,是通过超大规模集成电路对其进行集成为一个独立芯片的控制器。内部组件包括CPU、随机存储器、只读存储器、I/O接口、中断系统、计时器、串口通讯、数模转换等。STC89C52单片机是最常见的51单片机,但是资源较少,精确度低,处理速度相比STM32单片机差很多。
8 r7 u% l" _0 a2 o2 C C8 w(2)使用目前市面上最常见的STM32单片机,STM32系列单片机可分为ARMCortex-M3内核体系结构的不同应用领域。它可分为STM32F1系列和STM32F4系列,STM32F1系列单片机时钟频率最高可达72米,在同一产品中性能最好。单片机的基本处理速度为36米,16位单片机的性能也很好。微晶片的内建快闪记忆体相当大,范围从32kb到512kb,可视需要选择。单个设备的功耗非常低,仅360mA,32位单片机产品的功耗最低,每兆赫只有0.5安培。特别值得一提的是,内接单晶片汇流排是一种Harvard架构,可执行速度高达1.25 DMIPS/MHz的指令。此芯片越来越多地被用作主要控制器。
! p2 j- p4 Y4 L# U% | 通过对单片机的资源和处理时间的速度我们采用选择STM32103C8T6为本系统主控芯片,程序下载是只需要一个JLINK就可以轻松完成。控制器模块电路如下所示:
; q. f+ I$ S) \" ~
/ q. V3 q- a) a: L. q& m! X1 ]' q电源模块设计% M1 f2 Z- ?, m \' l$ V; y
本设计采用锂电池供电, 模块的供电电压一般都为5V,同时超声波模块需要较大的电流才能正常工作,所以在降压的基础上也要保证足够大的输出电流。本设计采用可调输出版本,模块的输入电压范围广,输出电压在1.25V-35V内可调,电压转换效率高,输出纹波小。降压电路如下所示:
9 U( }+ e* }5 R B
# t6 s- F; i# J4 M
电机驱动模块设计
/ ?! a, M( Y. K9 `5 ?7 m要完成转向是能够利用单片机实现的,然而单片机I0的带负载能力弱,因此我们选择了大功率放大器件TB6612FNG。TB6612FNG是采用MOSFET-H桥结构的双通道大电流电路输出,可以控制2个电机的驱动。相比普通的电机驱动,外围电路非常简单,只需要一个芯片和一个钽电容进行PWM输出滤波,系统尺寸小。PWM信号输入频率范围广,轻松满足本设计的需求。
9 ~ e: A' h4 g$ r i0 g$ l
' B9 f* G- I/ b+ j2 O' x$ Z电机驱动引脚表+ a- d9 l3 {# Q) w! X; k4 B0 _
- 1控制芯片:TB66129 v# ^" e4 d# T: l
- 2控制芯片数量:2) M R* t* ?- s( m" Z
- 3 1号TB6612引脚分配:6 d. Y; r) e& Z! ]6 W- ^
- 4 VM PWMA--------->TIM1_CH1(PA8)/ }- u$ b3 M8 a, x0 z
- 5 VCC AIN2--------->GPIOB_12
/ A" a* _- c7 ?7 T0 X$ X - 6 GND AIN1--------->GPIOB_13' o" A! |2 W a5 T I( W
- 7 AO1 STBY--------->GPIOB_14% c: _+ Z0 {! Y1 j
- 8 AO2 BIN1--------->GPIOB_15. b' g8 I! X- {+ F
- 9 BO2 BIN2--------->GPIOA_127 v, R$ K0 d; H8 l, M# a3 l
- 10 BO1 PWMB--------->TIM1_CH2(PA9)
: X5 x5 l$ F# l/ `) \9 w0 t - 11 GND GND; j' n5 B4 z. }' m. P- \/ [0 E* V! f
- 12 2号TB6612引脚分配:
1 O: Q. I4 [$ U3 t1 x - 13 VM PWMA--------->TIM1_CH3(PA10)
0 \9 w8 L; S; k5 V z: H+ x - 14 VCC AIN2--------->GPIOB_5
V2 Q& z" { q - 15 GND AIN1--------->GPIOB_6' F" c' k; N9 G9 E4 E: o- @) d
- 16 AO1 STBY--------->GPIOB_75 J( G: s1 @( k; l% c
- 17 AO2 BIN1--------->GPIOB_8: S- x7 \" U: s2 V0 o# Q2 d* V7 M
- 18 BO2 BIN2--------->GPIOA_97 u" @ Z+ o9 y2 |
- 19 BO1 PWMB--------->TIM1_CH4(PA11)
g2 v* S$ c( e1 O& s# _ - 20 GND GND
: L: G: J9 V, r% S - 21真值表% F+ L8 ?$ D3 D7 Y$ m$ x
- 22 AIN1 0 1 0 13 U. _; w* f5 @, y
- 23 AIN2 0 0 1 1
& _$ L- p* I' X% V' l4 Y, Y - 24 BIN1 0 1 0 1: J" J2 _ j+ Y& u& z* g
- 25 BIN2 0 0 1 1
' U" F" C `. w: T - 26 停止 正转 反转 刹车
复制代码 0 \0 S0 x" }. R6 }! H4 N- V
! e6 c M- E- }* e1 E电机所用到的定时器配置0 W& x8 z& ]; h) ~! z3 j
- <font size="3">1//初始化TIMX,设置TIMx的ARR,PSC
+ h8 d+ ^: l* E - 2//arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期8 r. d7 g# J$ f# B/ ?! w9 a
- 3//定时器选择TIM1
7 Q% M& H1 A9 {* a - 4static void TB6612_ADVANCE_TIM1_Mode_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)- B9 Q' n2 i9 Q+ O
- 5{* B/ J; b) ^, }3 E, X- u
- 6 //-----------------时基结构体初始化-------------------------/
( d T$ D4 J& @3 Q; h5 y- e - 7 TIM_TimeBaseInitTypeDef TIM_TimeStructure;0 p; _! Z0 z3 w' @& T& D# p
- 8 /*开启定时器1时钟,即内部时钟CK_INT=72M*/
V. X$ \5 K4 F+ n2 n' _/ E* u# c - 9 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
: E0 n/ J1 E }1 R9 @3 c# ^ - 10 TIM_DeInit(TIMx);' A" T0 a p% r! _6 q8 M
- 11 /*内部时钟作为计数器时钟,72MHZ*/+ D# ?1 _# P f
- 12 TIM_InternalClockConfig(TIMx);
# j b8 c# w* L- Q) n+ r - 13 /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/- \- k2 q% G7 f* f1 G; k; U
- 14 TIM_TimeStructure.TIM_Period=arr;1 v, \; F* z2 H3 ~. T
- 15 /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(71+1)=1MHZ*/
" {$ F" {7 E1 D - 16 TIM_TimeStructure.TIM_Prescaler=psc-1;. P L. c W! _( c/ A" P7 C
- 17 /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/: i9 W9 c9 j# _& z( Q8 G4 e
- 18 TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;8 t% {" ]/ M% k6 |7 V" f
- 19 /*向上计数模式*/# B+ _: q" z7 H/ O! x! U
- 20 TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
1 A7 ^2 ~, G! R k/ W$ ?9 P - 21 /*重复计数器*/; c, U: S: R/ I! m- n
- 22 TIM_TimeStructure.TIM_RepetitionCounter=0;
+ J2 U$ a* y0 J' X - 23 /*初始化定时器*/
' r; |$ r9 c8 Z5 p9 Z - 24 TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);3 x5 S' P; n/ Z; ?0 k! s7 Y
- 25 /*使能ARR预装载寄存器(影子寄存器)*/
! ^7 o G7 r$ k {% @ - 26 TIM_ARRPreloadConfig(TIMx,ENABLE);2 M" F/ a( J/ z2 n! Z* ~4 s
- 27 //-----------------输出比较结构体初始化-----------------------/
- N4 I3 ?* o- F' f$ W) C: x1 r - 28 TIM_OCInitTypeDef TIM_OCInitStructure;
6 z3 @* J& X5 B" O - 29 /*PWM模式设置,设置为PWM模式1*/
7 k* k2 _5 b$ s6 W, W% z7 t, d- f1 { - 30 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;3 O5 _3 u+ g+ o7 \2 k4 O
- 31 /*PWM输出使能相应的IO口输出信号*/
* J: a8 f }3 |% W2 H- l O, ?2 B - 32 TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;/ ^, Y* O' e$ s, o3 H
- 33 /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/' y" f/ e* _! w: x
- 34 TIM_OCInitStructure.TIM_Pulse=duty;7 r! \ b) v6 X/ O. ]; j! a) z8 q. m
- 35 /*输出通道电平极性设置*/8 _& ?* [' @% n, n- O) h1 i( Y
- 36 TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;2 z N/ R6 Q8 D. g
- 37 /*初始化输出比较参数*/0 d) L, j! B# z% `
- 38 TIM_OC1Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道1+ v& q, I, o/ Z
- 39 TIM_OC2Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道2
1 \( B. t! }" [& Q - 40 TIM_OC3Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道3' q" s; e; m4 C% w- \( R9 G" J
- 41 TIM_OC4Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道4; w& k8 J1 T% P
- 42 /*自动重装载*/7 k: {9 \5 c2 L9 z
- 43 TIM_OC1PreloadConfig(TIMx,TIM_OCPreload_Enable);0 [/ @; o7 ~) T% F }$ x
- 44 TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
% |# ]% ] x/ X - 45 TIM_OC3PreloadConfig(TIMx,TIM_OCPreload_Enable);0 ^( @0 k. j* k Q' a( M+ i
- 46 TIM_OC4PreloadConfig(TIMx,TIM_OCPreload_Enable);2 I9 O2 m: ~8 n# E$ ^7 {$ C& N
- 47 /*使能计数器*/
3 s$ c3 E8 r/ ^0 ^ - 48 TIM_Cmd(TIMx,ENABLE);( h) i! q& i/ C- }/ P
- 49 /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/% f/ E b/ F3 Q! S1 F/ o
- 50 TIM_CtrlPWMOutputs(TIMx,ENABLE); / I6 o8 v) R E
- 51}
9 \2 z4 r/ Y L/ I+ D - 52//高级定时器输出通道初始化函数# _ X& Z& g, s( E
- 53static void TB6612_ADVANCE_TIM_Gpio_Config()
6 Q( x8 e, v& i5 B - 54{
8 m% C7 Z7 L/ k$ C2 Q8 I - 55 GPIO_InitTypeDef GPIO_InitStruct;
% K4 x/ I9 D, V! Z+ A - 56 /*----------通道1配置--------------*/
* ?8 } W0 J# ~9 J" T - 57 /*定时器1输出比较通道*/$ z* Z, p! w7 n; g6 y- }
- 58 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);' I0 u+ I/ m4 I
- 59 /*配置为复用推挽输出*/
- E" D& e5 d: f* Y: a - 60 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
1 m3 Z7 T, a% r* Z - 61 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
( }7 h8 p1 m9 x5 o8 N. @ - 62 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;4 ]0 h s' V7 w+ |! l2 |
- 63 GPIO_Init(GPIOA,&GPIO_InitStruct);
* R! c2 g3 h# B% O - 64 /*-----------通道二配置-------------*/$ ^2 H& [* ?- m$ e3 Q
- 65 /*定时器1输出比较通道*/- Y& J& m/ K% ~2 B& y
- 66 /*配置为复用推挽输出*/. l% E' f- K( h
- 67 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
( p( n7 z1 j9 \, j4 X - 68 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;$ ]- O5 a4 N9 y" C, q
- 69 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
% k) \, |5 v$ S; M3 |. U% T' g5 O - 70 GPIO_Init(GPIOA,&GPIO_InitStruct);+ ], u- T: N6 z- E4 t k
- 71 /*-----------通道三配置-------------*/: Z' h/ Y7 L, ~$ }
- 72 /*定时器1输出比较通道*/
9 j8 B k }/ K2 \2 W - 73 /*配置为复用推挽输出*/! T" n# t. m5 Q7 C4 d
- 74 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;5 u; E! m. {! B
- 75 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
* [$ B! ~; u6 g' _ - 76 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
/ o) M q& ^* a - 77 GPIO_Init(GPIOA,&GPIO_InitStruct);
: ]% U, w& O6 a& e+ {# Z ~, P- j1 B - 78 /*-----------通道四配置-------------*/
. I4 s0 W5 d$ r/ {. z# K - 79 /*定时器1输出比较通道*/6 U# R& v8 E* @/ I0 [1 p
- 80 /*配置为复用推挽输出*/
! _* N5 x* y' m4 I- V - 81 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;$ l8 N" Q6 \. v" H
- 82 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;! ]2 u4 R* A4 I$ ], W5 ?+ i' S
- 83 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;( m1 z; [. U1 ~& e! F
- 84 GPIO_Init(GPIOA,&GPIO_InitStruct);
6 a* l& R' C4 d - 85}</font>
复制代码 2 Y+ K) C8 l M m" N8 Z f; N5 E
5 r& ^! E' ~* P

8 d4 {* K# o/ h/ b; _+ u 超声波模块5 V5 K6 Y$ d- I; u, N
/ ` c: k# m: J3 X9 s! u0 ^! {5 Q
, e& }/ b6 A: Z, Z- H9 K; j 采用HC-SR04超声波模块,该芯片具有较高的集成度以及良好的稳定性,测度距离十分精确,十分稳定。供电电压为DC5V供电电流小于10mA,探测距离为0.010m-3.5m一共有四个引脚VCC(DC5V)、Triger(发射端)、Echo(接收端)、GND(地)。HC-SR04实物图如下:
2 ~! n/ A# C( g
7 C }' U5 ]- H6 F: X X
该模块是利用单片机的IO触发电平测距,单片机内部利用普通定时器产生一个高电平信号之后,超声波就可以自主发送频率为40khz的方波,然后等待信号的返回;若有信号返回,单片机IO口就立刻输出一高电平,利用高电平产生的时间可以计算小车与障碍物的距离。最终距离就是高电平持续时间乘以声音在空气中传播的速度再除以2,可以反复测量距离。
7 \/ Q$ Q0 o2 _ 在程序开始首先初始化超声波,利用定时器并设置时基的自动重装载初值1000,psc为预分频值72,这样的话我们产生一次中断的时间是1ms,并设置抢占优先级0,子优先级3。HC_SR04_Echo引脚接收到高电平,打开定时器,且每1ms进入一次中断。在测量时首先让Trig发送一个大于10us的高电平,然后拉高HC_SR04_Trig,当Echo为0时打开定时器计时,当Echo为1时关闭定时器,通过公式计算距离。
/ ]' j3 w# n1 i Y模块工作原理:3 m# B6 t' O9 u% }3 Y
(1)单片机触发引脚,输出高电平信号;! t; z9 C: C: E( F, [6 _' A
(2)模块发送端自动发送特定频率的方波; B! A' ~; Z8 v- }* [
(3)如果有信号返回,通过IO输出一高电平,高电平持续的时间就是超声波的发射时长;5 R4 f, W, V$ u% ^6 h! B
(4)测试距离=(高电平时间*声速(340M/S))/2。' o; a6 j( C$ F8 i
注意:在硬件操作上需要首先让模块地端先连接,否则会影响模块工作。测距时,被测物体的摆放不能太过于杂乱,否则会影响测试结果。- V) L5 @; l% F# G7 ?3 m
, W( t5 ^ q! u- I
超声波重要代码(可参考)
6 d. i/ H* I) g0 ~- 1/* 获取接收到的高电平的时间(us*/9 G$ @0 B2 g7 k" T% |! y
- 2uint32_t Get_HC_SR04_Time(void)
4 }% Z8 w- s9 X - 3{ g; M$ q# H& _% }" G6 ^
- 4 uint32_t t=0;* @) E: l7 d7 ?( c+ D# s/ Y5 P( O& v
- 5 t=Acoustic_Distance_Count*1000;//us
4 Y' @) s/ _- {5 A1 l( m+ e3 B N, o - 6 t+=TIM_GetCounter(TIM2);//获取us
$ f7 ~) O4 X0 \5 x - 7 TIM2->CNT =0;4 X% ~9 n- P1 [, g4 s2 V: L) b" U9 w
- 8 Acoustic_Distance_Count=0;3 r. ?9 v+ ^! A% z0 N$ v
- 9 Systic_Delay_us(100);" ?' V; ^, u5 l' [' \- J9 x4 Y' B
- 10 return t;1 k9 F3 G; K: M) |: b8 t7 L& H
- 11}1 B% c1 H* S. D! U" B$ L4 p
- 12/*获取距离*/9 a l% @1 L& @4 ^' X& D& Y
- 13void Get_HC_SR04_Distance(void)
1 q: J( D: f6 H/ o& X# {% o - 14{
! I; s% m$ w5 c/ _9 K$ v& W - 15 static uint16_t count=0;
" l9 ?! @7 u6 c - 16 switch(count)3 U, |- ^. h3 [8 n$ Y
- 17 {+ J: \* N8 t. `3 [6 h
- 18 case 1: {+ ?! o6 N0 W0 _. l
- 19 {
- w3 m N/ D7 d. O" @9 E - 20 GPIO_SetBits(Acoustic_Port,HC_SR04_Trig);//Trig发送一个大于10us的高电平: K: V) z, \$ X, ?2 `8 x' q
- 21 }break;' \9 G+ Z% w1 r! ^
- 22
8 S; F7 a B% ~. d L7 @9 d - 23 case 15:0 I7 y9 B; d% ~) }. B
- 24 {. n( |5 u9 K5 w3 f" T: o2 N
- 25 count=0;
2 @( i4 c5 W. N+ L# x! y - 26 GPIO_ResetBits(Acoustic_Port,HC_SR04_Trig);
& d2 q& z j" a$ B3 O - 27 while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==0);//当Echo为0时打开定时器 计时- \/ z# ?7 n9 F' I. n5 ~' j+ c% O
- 28 Open_Tim2();
" o6 T% F, i) h* v! F - 29 while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==1);//当Echo为0时打开定时器 计时
S6 |* }. o/ b' L! o! K. L9 C& P - 30 Close_Tim2();
+ P) c& P2 N( f) |- K5 J - 31 HC_SR04_Distance=(float)(Get_HC_SR04_Time()/5.78);
d+ B7 J( ~4 Z# i - 32
' R' ]' [- Z; c; N4 e* C6 m - 33 }break;; {9 C. P+ C! Y2 Q' U; b: b/ ~
- 34 default:break;
* R: a- V$ W$ J: A; r& [) \ x - 35 }
}% M" q5 s3 u8 h2 a8 S2 [- E9 b - 36 count++;8 K1 E6 q8 U! W* J! Z2 \
- 37}
复制代码
7 Y1 C. l s- E* T1 C% ]/ n6 C8 H# Q7 ?7 ~- Z# E+ z' w! Y1 P
; |. a) k& u0 a
. {0 e l$ ]' p
) G0 |1 E* A5 ?+ U舵机模块1 h0 L& o' E/ X( \% c! P- ]1 d
. H4 ^5 y' y8 E3 X' D# O
本系统使用的是SG90型号的舵机,舵机是一种常见的角度驱动器,本系统需要判断不同位置的障碍物可以且对转向的力度小。舵机可以理解为方向盘称,方向盘是一个常见的名字。它实际上是一个伺服马达。舵机实物图如下:5 U% ]/ Q; `- S E3 ]0 I
0 {3 Y7 b) S6 ?8 T4 F
舵机模块接口简单,舵机模块只有三个引脚。分别引引出了三根线左右两边是电源正负接口线,中间一根是PWM信号线直接连接单片机的控制引脚。通过控制单片机的引脚输出的脉冲宽度进而控制舵机旋转的角度。舵机每增加0.1ms 舵机对应增加9度。3 Z$ T" w( h/ ]8 c8 c- u: w0 _
0.5ms---------0! s6 H8 F6 @/ t' d! J
1.0ms---------45
& m& g; O- v. z1.5ms---------90
6 y% X5 b4 _3 v. W" Q/ {# A2.0ms---------135" @( r' ^& \2 S5 }4 b
2.5ms-----------180% f& p: {9 M* Q8 J! A
20ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平持续时间为1.5ms,周期为20ms的方波,duty=1.5/20=7.5%。在这里设置定时器自动重装载寄存器arr的值为1000,所以当占空比为百分之75是,在程序中就要设置占空比为75/1000=7.5%, 这就是具体的算法。0 h& U" E% {2 \1 f! ~
舵机重要代码(可参考): d$ k0 x7 ?5 v6 s
- 1/**PWM引脚初始化*/
0 I( \% ~" n5 h% L& Q- _ - 2static void SERVO_Gpio_Init(void)! i$ C1 j2 S2 R; }
- 3{, E) ]1 I5 L3 k/ i
- 4 GPIO_InitTypeDef GPIO_InitStruct;
; s9 Y& |/ c! b. R8 C* C5 ?6 ] - 5 /*----------通道2配置--------------*/& ^- u% _7 ~8 d) F
- 6 /*定时器3输出比较通道*/4 j2 R3 ~+ T$ L7 o
- 7 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);( d! U0 R) J5 f
- 8 /*配置为复用推挽输出*/9 u% O7 P( D' R6 P+ i
- 9 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;$ j ^4 u' x1 x4 S3 Q# w( A
- 10 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7;
! O$ y% o8 M; o5 `' l# s - 11 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;. g4 S* }; J- ]0 f! u. ~/ ]
- 12 GPIO_Init(GPIOA,&GPIO_InitStruct); . a4 y. g( }7 ]3 i+ k. s
- 13}3 t6 x9 j. W" E, P5 }- M
- 14//定时器3初始化,设置TIMx的ARR,PSC2 H5 m# Z" H5 i- ?! m& B
- 15//arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期3 e$ `* O) G( g$ w
- 16static void SERVO_TIM_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)
0 ?- H6 {' ^+ O& c/ b) i6 Q - 17{4 b$ a( s! d% z9 l, L
- 18 //-----------------时基结构体初始化-------------------------/
0 Z5 \3 V2 } p) F' m - 19 TIM_TimeBaseInitTypeDef TIM_TimeStructure;
- h5 ^& ^, ~; i& r+ k9 k - 20 /*开启定时器3时钟,即内部时钟CK_INT=72M*/6 V; X# \' ?' g
- 21 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);( f/ G$ f5 u8 M% V
- 22 TIM_DeInit(TIMx);2 e' c% d9 u1 ^9 j4 w+ Q
- 23 /*内部时钟作为计数器时钟,72MHZ*/
! l8 @) C. E; [, c - 24 TIM_InternalClockConfig(TIMx);+ A) e$ C5 P+ M
- 25 /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/8 q0 m& U5 q0 T7 R% [
- 26 TIM_TimeStructure.TIM_Period=arr;//1000 当定时器从0计数到999,即1000次,为一个定时周期
% ~$ s1 o9 X! g7 t! i- d7 e& Y3 ]' F - 27 /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(1440-1+1)=0.05MHZ*/
6 y4 a I$ e. @: L0 F8 F( {1 O0 O# `1 ` - 28 TIM_TimeStructure.TIM_Prescaler=psc-1;;//1400 //即定时器的频率为5KHZ ' _8 O2 Y+ s. f; Y. H
- 29 /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/ \7 L; E& q- j! a8 L% J3 ]. F2 l
- 30 TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
: g# P& u9 r* s8 ]3 o. K - 31 /*向上计数模式*/) z4 ]1 R7 m. j Y+ d. N# v4 B" h
- 32 TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;4 U7 A% V* Y( D6 |: N: X5 i0 }7 T+ N
- 33 /*重复计数器*/
% I j5 Y4 l5 i1 I- i - 34 TIM_TimeStructure.TIM_RepetitionCounter=0;
* U# }$ C3 T& Y# Z, o* Z9 A9 O - 35 /*初始化定时器*/! ]0 v, ~1 v3 d9 r/ w4 e1 u9 d9 i
- 36 TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);( }, P! J2 P! X
- 37 /*使能ARR预装载寄存器(影子寄存器)*/
/ e" E6 @3 }/ q4 Y$ c$ O5 _0 y - 38 TIM_ARRPreloadConfig(TIMx,ENABLE);
( R+ I% t6 z" C) U2 k. Y - 39 //-----------------输出比较结构体初始化 开始-----------------------/& J f+ q' J0 T
- 40 TIM_OCInitTypeDef TIM_OCInitStructure;
/ G! f: R' }, ]1 t( ^2 i - 41 /*PWM模式设置,设置为PWM模式1*/
$ n X" P5 o( N. ~" G. ?4 x - 42 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
; b( S0 s5 x8 v8 k& [9 K - 43 /*PWM输出使能相应的IO口输出信号*/
4 Y4 H( J! j& O - 44 TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;. d& Q* N$ x5 {& ~7 w* E1 l* V
- 45 /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/
* J' s" V2 E2 O; c3 D* G - 46 TIM_OCInitStructure.TIM_Pulse=duty; //占空比大小0 m# R5 R7 a, b5 j! o+ ~
- 47 /*输出通道电平极性设置*/
0 \! X/ j1 O) |; W( U& }5 p* B |1 y - 48 TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
" d' r$ M+ X( O9 Z - 49 /*初始化输出比较参数*/$ C, s! _3 w9 q7 G/ t b/ m* I/ O
- 50 TIM_OC2Init(TIMx,&TIM_OCInitStructure);2 G, \+ l( A, Z Q0 I
- 51 //-----------------输出比较结构体初始化 结束-----------------------/ 0 v* P& ]. k& w6 w6 h
- 52 /*自动重装载*/
( P) A/ P4 T4 ^8 ^; |% s+ M" k; y - 53 TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);2 L" a* I# ]2 t g7 [# S0 T' W( r
- 54 /*使能计数器*/
# \6 w( h8 r) n9 {* ~1 L - 55 TIM_Cmd(TIMx,ENABLE);5 `+ b% o! G: r! P3 u' g
- 56 /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/+ e" i( }: a3 [
- 57 TIM_CtrlPWMOutputs(TIMx,ENABLE);
6 r; o5 ?3 w$ d/ N/ {/ t0 | - 58}
H4 M1 F5 G# c - 59/*舵机PWM初始化- F- Z3 \! B( A, h+ e9 Z
- 60 每增加0.1ms 舵机对应增加9度( M$ A4 N6 y1 r
- 610.5ms---------02 G( R t9 @ f" V: H+ a- z
- 621.0ms---------45 ) D" e+ \6 F+ s
- 631.5ms---------90
" i9 Q1 z( H5 B, } w9 [7 t) c - 642.0ms---------135& o. w3 ]2 q" a9 I
- 652.5ms-----------180
2 p% q8 g% a6 l4 s) @ - 662.1ms turn_left=1508 k; d8 M7 {/ W6 Q4 d5 A* ?. f
- 670.8ms turn_right=25
: j2 A# r$ r- q( e" \1 z - 681.3ms turn_front=75
1 t0 I( x8 A5 _' ?& R - 6920ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平为1.5ms,周期为20ms的方波,duty=1.5/20=7.5% ,而定时器自动重装载寄存器arr的值为 1000 ,所以duty=75,时占空比为75/1000=7.5%.
0 V' q, Z9 u' ^8 p2 P) k9 S - 70*/
. Y) m$ J5 K1 L - 71void SERVO_Init(void)( T: ?0 h8 Y' `# o* H
- 72{
( |' n& ?. W! g$ T }. @7 z - 73 SERVO_Gpio_Init();; Q* A5 K0 b8 }- @0 ~" A+ b
- 74 SERVO_TIM_Config(TIM3,1000,1440,turn_front);
! P9 i c' t% R( s. ] - 75/** 我们把定时器设置自动重装载寄存器 arr 的值为 1000,设置时钟预分频器为 1440,则2 }4 L/ F- H) Y2 j! E4 E
- 76驱动计数器的时钟:CK_CNT = CK_INT / (1440-1+1)=0.05M,则计数器计数一次的时间等于:
! d( @; Q1 p. t* b- d' H - 771/CK_CNT=20us,当计数器计数到 ARR 的值 1000 时,产生一次中断,则中断一次的时间
% q" E/ v' q! |/ q - 78为:1/CK_CNT*ARR=20ms。
) R/ @" F* Y, {; p6 T - 79PWM 信号的频率 f = TIM_CLK/{(ARR+1)*(PSC+1)} TIM_CLK=72MHZ m$ C1 T' D% h3 n3 U
- 80 = 72 000 000/(1000*1440)=5KHZ ' l7 U S% V% Z3 q/ M. O6 \
- 81*/ 3 z5 `1 G) A& d/ M1 s) [2 }+ `
- 82}6 }& j) S |) Q- @- v2 J
- 83/*舵机角度控制*/% n- \5 n3 h5 m( v4 a) a! O! K* X/ u
- 84void SERVO_Angle_Control(uint16_t Compare2) L! z8 K3 `, l* F1 @
- 85{' R/ i) T, H3 | s ]+ ^' w% n
- 86 TIM_SetCompare2(TIM3,Compare2);: H/ ?0 |8 |4 {% }# A& n
- 87}
复制代码
, z+ T0 U( o' H) K 5 t- Y7 L" ^1 }2 @
编码器模块
8 |1 b+ C, C. Y3 S" c : Q3 ~2 V1 A; |! z( k$ j. ]
调节小车前进的速度和避障快慢我们采用EC11旋转式编码器,可以用于光度、湿度、音量调节等参数的调节。EC11编码器的形状类似于电位器,中心有一个旋钮可以调节PWM信号,光电码盘利用光电转换原理输出三组方波脉冲。EC11编码器的实物图如下:+ ^( o, n( z) t4 S" d# K& o/ Y
& F! G4 Q' m5 Y6 s3 J$ \
% o- ]# z2 z9 o# j- |
OLED显示模块
/ u1 o8 M. I' _; }/ p 3 _1 [: C/ \0 p
用来显示小车转速,以及左右编码器数值和电池电压等参数所用的是OLED显示模块,分辨率较高,而且功耗低,正常显示时仅0.06W,供电电压范围在3.3V-5V,有IIC和SPI两种通信协议可供选择。显示模块的亮度和对比度可以通过程序设置。由于它使用寿命长以及其他的优点,OLED更加适合小系统,本系统由于单片机引脚有限,不适合利用简单的LCD1602或者12864来显示,在多方对比之下OLED效果更好。OLED显示部分相对比较简单,大家参考中景园的例程就可以实现。
$ x/ x6 S% w& N8 T, y! f8 \9 ~9 j% y/ q5 o
4 u4 h7 Z8 J( l l
( R5 w+ C& b+ p. K" v
: U3 R% @' u, G文章出处:程序员小哈
/ Q3 n6 r# G8 O6 M* g+ { |