今天给大家介绍的的一个项目基于STM32超声波避障小车。. V8 b/ n& l% s4 l% R$ T8 A
# A- f) f2 ]* b$ }# t9 m5 m" w
处理器电路设计
单片机是系统的CPU,是系统稳定、正常运行的重要前提,以下为单片机选型的两种方案:; k0 {- e& E( ?9 M; ]
(1)传统的8位单片机,是通过超大规模集成电路对其进行集成为一个独立芯片的控制器。内部组件包括CPU、随机存储器、只读存储器、I/O接口、中断系统、计时器、串口通讯、数模转换等。STC89C52单片机是最常见的51单片机,但是资源较少,精确度低,处理速度相比STM32单片机差很多。 (2)使用目前市面上最常见的STM32单片机,STM32系列单片机可分为ARMCortex-M3内核体系结构的不同应用领域。它可分为STM32F1系列和STM32F4系列,STM32F1系列单片机时钟频率最高可达72米,在同一产品中性能最好。单片机的基本处理速度为36米,16位单片机的性能也很好。微晶片的内建快闪记忆体相当大,范围从32kb到512kb,可视需要选择。单个设备的功耗非常低,仅360mA,32位单片机产品的功耗最低,每兆赫只有0.5安培。特别值得一提的是,内接单晶片汇流排是一种Harvard架构,可执行速度高达1.25 DMIPS/MHz的指令。此芯片越来越多地被用作主要控制器。 通过对单片机的资源和处理时间的速度我们采用选择STM32103C8T6为本系统主控芯片,程序下载是只需要一个JLINK就可以轻松完成。控制器模块电路如下所示:
- ~$ X: U' W+ D8 P, k5 q7 v- I* L# |' N
7 C$ T2 Y! ?, T5 O
电源模块设计
本设计采用锂电池供电, 模块的供电电压一般都为5V,同时超声波模块需要较大的电流才能正常工作,所以在降压的基础上也要保证足够大的输出电流。本设计采用可调输出版本,模块的输入电压范围广,输出电压在1.25V-35V内可调,电压转换效率高,输出纹波小。降压电路如下所示:
8 G' [' w6 z5 p% R* h' g3 n: I
! S- M3 \ ^# F2 i
电机驱动模块设计
要完成转向是能够利用单片机实现的,然而单片机I0的带负载能力弱,因此我们选择了大功率放大器件TB6612FNG。TB6612FNG是采用MOSFET-H桥结构的双通道大电流电路输出,可以控制2个电机的驱动。相比普通的电机驱动,外围电路非常简单,只需要一个芯片和一个钽电容进行PWM输出滤波,系统尺寸小。PWM信号输入频率范围广,轻松满足本设计的需求。 4 ]8 V% |1 |% C1 y
电机驱动引脚表- 1控制芯片:TB6612 |8 r& H! J- }, D; s+ ?8 w [* Y
- 2控制芯片数量:28 S) U. {6 o; `5 P3 ^
- 3 1号TB6612引脚分配:) _( D4 J0 `( I( M d
- 4 VM PWMA--------->TIM1_CH1(PA8)5 r0 Z: r+ {( `! X, r5 w$ Z b" y
- 5 VCC AIN2--------->GPIOB_124 Q: q% Y- V' y; L% A9 _
- 6 GND AIN1--------->GPIOB_13
" b9 ~0 S( r+ @# M; f - 7 AO1 STBY--------->GPIOB_14
8 _) o* P% B b. v3 x! U1 f3 V. a8 v - 8 AO2 BIN1--------->GPIOB_15
. ^1 |( x* s' M" c# K, z( G% c - 9 BO2 BIN2--------->GPIOA_12
/ h% `% S. X- q. E' @- a! {- m - 10 BO1 PWMB--------->TIM1_CH2(PA9)0 y, A! r$ x, j* M- N0 B$ G
- 11 GND GND7 e& o, ]% i r6 w) V8 s
- 12 2号TB6612引脚分配:/ a, H$ i( l8 Y& E6 e$ N
- 13 VM PWMA--------->TIM1_CH3(PA10)
2 N8 K0 F2 w" v7 w6 H( H9 ~5 q - 14 VCC AIN2--------->GPIOB_5
& c5 M# o4 t$ E( ]4 p/ v - 15 GND AIN1--------->GPIOB_6
9 M. |0 O" Z# K, h% u - 16 AO1 STBY--------->GPIOB_71 b1 K& Z+ |% l8 n3 q, m: f8 e( k
- 17 AO2 BIN1--------->GPIOB_8
: X7 ^% a3 |" D$ {3 j - 18 BO2 BIN2--------->GPIOA_9* L/ O& n) C; r. i. J
- 19 BO1 PWMB--------->TIM1_CH4(PA11)
& C" S& m; L: A/ D( d/ J; _ - 20 GND GND7 t( w& y9 k: A" ~$ V2 R+ ?
- 21真值表8 R6 p1 w a `# q! Q/ V3 v4 _& w
- 22 AIN1 0 1 0 1/ ^3 N6 Q( V" ]: i6 w: ^
- 23 AIN2 0 0 1 1) d! Y) p0 a$ }2 ]. \
- 24 BIN1 0 1 0 1
: N6 c |$ `$ l4 ?' N - 25 BIN2 0 0 1 1
: z9 n& h" E/ C" g- r - 26 停止 正转 反转 刹车
复制代码
q2 h6 ~5 u: @: w. t8 e. V 电机所用到的定时器配置- 1//初始化TIMX,设置TIMx的ARR,PSC5 C1 X/ i) `6 R
- 2//arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期6 p3 L8 n! j+ g5 K' W; P
- 3//定时器选择TIM1! v8 j: x8 j5 f2 b! y2 i
- 4static void TB6612_ADVANCE_TIM1_Mode_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty)$ D7 P6 H8 [+ i* W
- 5{; W$ H" r- `; P" I) H, @& J" n
- 6 //-----------------时基结构体初始化-------------------------/
- u# g% ]' f1 `+ P - 7 TIM_TimeBaseInitTypeDef TIM_TimeStructure;
; w K( M* D; I* J6 U - 8 /*开启定时器1时钟,即内部时钟CK_INT=72M*/# l$ m. H7 `" Z( `1 g
- 9 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
$ T# c$ D0 l; F$ R7 a9 N! E - 10 TIM_DeInit(TIMx);
* R% R( V! f1 p - 11 /*内部时钟作为计数器时钟,72MHZ*/
; ^. r! o: ~# i1 D% L - 12 TIM_InternalClockConfig(TIMx);
/ m h8 `# ^# _2 w7 e7 J$ r/ J - 13 /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/
' e. u+ F4 S: A/ W( F, D( i - 14 TIM_TimeStructure.TIM_Period=arr;0 g1 s; u0 l( M/ T7 B+ d
- 15 /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(71+1)=1MHZ*/# k( v" `8 C u- ^( `+ O5 c5 J
- 16 TIM_TimeStructure.TIM_Prescaler=psc-1;3 g4 G5 o! a7 ~. i- s8 T
- 17 /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/
8 Z c) q B# y - 18 TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;7 ]2 s6 ~1 `/ W, V: E8 I5 S! ~" a* X
- 19 /*向上计数模式*/
1 V6 D$ d. \' n1 `! C( V( d2 ~ - 20 TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
6 A% b# P: S+ k, k" E - 21 /*重复计数器*/
& \3 u' W: O& _, P- v5 P - 22 TIM_TimeStructure.TIM_RepetitionCounter=0;# D6 N0 r' h( M8 k
- 23 /*初始化定时器*/8 E' p' K, |; B4 _; W: p' L# o
- 24 TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);% N" B2 X" y; y& |3 E. e2 c
- 25 /*使能ARR预装载寄存器(影子寄存器)*/7 m6 J9 Q" \5 S/ i
- 26 TIM_ARRPreloadConfig(TIMx,ENABLE);2 `4 r: O' ]0 k# N5 `- O
- 27 //-----------------输出比较结构体初始化-----------------------/7 C) U, X' d/ ^$ W: r9 ?
- 28 TIM_OCInitTypeDef TIM_OCInitStructure;' h+ m4 C2 m, d. ~9 l- R
- 29 /*PWM模式设置,设置为PWM模式1*/
2 [& B! W; }( U! l: w5 F - 30 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
+ b$ Z( @- l: I, \4 Q9 @ G4 O - 31 /*PWM输出使能相应的IO口输出信号*/1 P5 G. ^% R3 r. H
- 32 TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
2 Y% {% W8 J( Q% n8 \, E* I/ @; T% P! q - 33 /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/
) K) n* S2 _( p5 p6 i$ K* @ - 34 TIM_OCInitStructure.TIM_Pulse=duty;% `7 [* x- N Z- N6 e
- 35 /*输出通道电平极性设置*/" y8 C3 C* k9 Z! R8 [. u
- 36 TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;
8 I v6 G( R0 x. r& ]. O - 37 /*初始化输出比较参数*/% y9 L1 Q) ]- k
- 38 TIM_OC1Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道1
4 r2 e) J1 n/ r$ s+ Q& d7 g - 39 TIM_OC2Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道2/ m. g. v7 L" Z: D* Y8 Y5 ]! z% ?
- 40 TIM_OC3Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道3; L/ m. L" d" e" v( N7 s9 {
- 41 TIM_OC4Init(TIMx,&TIM_OCInitStructure);//初始化TIM1 通道49 \6 s( }, M/ {, P
- 42 /*自动重装载*/
9 E/ B0 @6 m5 E6 x" s, d - 43 TIM_OC1PreloadConfig(TIMx,TIM_OCPreload_Enable);) x/ n5 F$ t8 H N! k8 A0 ?
- 44 TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);
4 Y( h* c+ Q: v/ }/ m$ s - 45 TIM_OC3PreloadConfig(TIMx,TIM_OCPreload_Enable);. O, |$ t+ |8 \
- 46 TIM_OC4PreloadConfig(TIMx,TIM_OCPreload_Enable);
2 j$ E, y+ X8 A3 ]4 G' D8 q% @) h - 47 /*使能计数器*/, D C6 X4 Q: u
- 48 TIM_Cmd(TIMx,ENABLE);
@: E }; I- f - 49 /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/
( @ j9 p) A' e$ d - 50 TIM_CtrlPWMOutputs(TIMx,ENABLE);
, Z# _, E1 f' I5 C( G/ `! E - 51}5 X2 g# z* k0 |$ N
- 52//高级定时器输出通道初始化函数
3 T6 |7 |: o4 ?' i7 q$ L - 53static void TB6612_ADVANCE_TIM_Gpio_Config()
( M5 w3 `$ D7 I2 N0 b. \) W4 V - 54{
! b: p3 b( l/ r, \/ g- }& F - 55 GPIO_InitTypeDef GPIO_InitStruct;
) r( |: D9 q+ \! ~% i - 56 /*----------通道1配置--------------*/" p. J" m) C" w# E6 a$ _
- 57 /*定时器1输出比较通道*/! M) @. g) y0 M5 g Y
- 58 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);0 h& }! K0 [/ s+ J! B& R" S: E
- 59 /*配置为复用推挽输出*/4 T' ~+ ^; k, X/ k
- 60 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;7 C& R0 E& V) C9 A& {* V# t
- 61 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
5 {' ^ u2 Q' }, G) t/ ? - 62 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
5 s2 x% Z, P& s - 63 GPIO_Init(GPIOA,&GPIO_InitStruct);
0 ?" u5 B( E$ v! p G6 T - 64 /*-----------通道二配置-------------*/9 t9 q2 i# ^, Q8 u
- 65 /*定时器1输出比较通道*/* g: j0 v6 E. ]8 ~
- 66 /*配置为复用推挽输出*/
9 e# V5 e# X4 ~% [6 O - 67 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;# a# E# f0 u3 G
- 68 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;9 H: ^' C: N5 k5 m
- 69 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
y- d7 }( R3 ^2 l - 70 GPIO_Init(GPIOA,&GPIO_InitStruct); V" y' v0 U1 V% r: V
- 71 /*-----------通道三配置-------------*/
2 ^4 H% A5 l x2 L: p - 72 /*定时器1输出比较通道*/! L% v5 k% J. f. d' X5 D
- 73 /*配置为复用推挽输出*/
+ G- I- a3 }" I# k0 c+ _ - 74 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;% c3 T6 u* `" I I+ B
- 75 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
+ L+ e5 S' Q. z4 t - 76 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;+ a' [! z% L6 {8 N+ C/ F
- 77 GPIO_Init(GPIOA,&GPIO_InitStruct);- O4 `3 Q9 I+ A, H. L) [
- 78 /*-----------通道四配置-------------*/
2 q! k4 d* X/ l% v& ~. s) a - 79 /*定时器1输出比较通道*/# O4 N. m0 A* i" q
- 80 /*配置为复用推挽输出*/
4 A4 K+ E1 }" w) n9 Z$ o - 81 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
8 S2 \' s: @/ O p" D6 k+ E5 G - 82 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
! C) K; Z! ?: K1 ] C* ^ - 83 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
8 l" n' d! D3 {* f* c; m! t [ - 84 GPIO_Init(GPIOA,&GPIO_InitStruct);
! A1 V9 A% Y' W0 R" y - 85}
复制代码
4 X: h7 d' ?: l % P @: Z* U# e8 m$ R0 m4 T" O
超声波模块8 ]8 q" G' g+ e+ @2 X& ^
采用HC-SR04超声波模块,该芯片具有较高的集成度以及良好的稳定性,测度距离十分精确,十分稳定。供电电压为DC5V供电电流小于10mA,探测距离为0.010m-3.5m一共有四个引脚VCC(DC5V)、Triger(发射端)、Echo(接收端)、GND(地)。HC-SR04实物图如下:
4 `1 i% B2 b% V K4 S' d+ m + i& M+ m) j" v& t" l6 }
该模块是利用单片机的IO触发电平测距,单片机内部利用普通定时器产生一个高电平信号之后,超声波就可以自主发送频率为40khz的方波,然后等待信号的返回;若有信号返回,单片机IO口就立刻输出一高电平,利用高电平产生的时间可以计算小车与障碍物的距离。最终距离就是高电平持续时间乘以声音在空气中传播的速度再除以2,可以反复测量距离。 在程序开始首先初始化超声波,利用定时器并设置时基的自动重装载初值1000,psc为预分频值72,这样的话我们产生一次中断的时间是1ms,并设置抢占优先级0,子优先级3。HC_SR04_Echo引脚接收到高电平,打开定时器,且每1ms进入一次中断。在测量时首先让Trig发送一个大于10us的高电平,然后拉高HC_SR04_Trig,当Echo为0时打开定时器计时,当Echo为1时关闭定时器,通过公式计算距离。 模块工作原理:, Z. |* H1 M- v0 j
(1)单片机触发引脚,输出高电平信号;
6 e d1 p: h: n2 z(2)模块发送端自动发送特定频率的方波;4 s( n# Y" N. S3 ]
(3)如果有信号返回,通过IO输出一高电平,高电平持续的时间就是超声波的发射时长;
+ g0 {; H3 m" M( f0 z m2 k(4)测试距离=(高电平时间*声速(340M/S))/2。 注意:在硬件操作上需要首先让模块地端先连接,否则会影响模块工作。测距时,被测物体的摆放不能太过于杂乱,否则会影响测试结果。 - F3 U% S" R/ w
}9 ?9 k$ `! Q3 {1 v( \超声波重要代码(可参考)- 1/* 获取接收到的高电平的时间(us*/
6 w5 j9 d8 v- Q. W! g. A7 L ~" q - 2uint32_t Get_HC_SR04_Time(void)
% y2 ]( U. e5 K k! ^3 G - 3{
/ H4 e' ]6 O9 {* ^1 n2 [' m4 p - 4 uint32_t t=0;0 @) l" ~5 T* W+ [5 x( j/ {6 v
- 5 t=Acoustic_Distance_Count*1000;//us
# @3 V2 k& o! X: |6 i1 ?9 c - 6 t+=TIM_GetCounter(TIM2);//获取us
8 I" f9 G) }9 z8 }1 v* m - 7 TIM2->CNT =0;
* M' G- e) N* }3 g) d h - 8 Acoustic_Distance_Count=0;
( S0 i' V8 c+ A$ @, R+ a, N - 9 Systic_Delay_us(100);; F! d4 ^3 \8 w" P8 B& a* W
- 10 return t;( r2 _$ E6 f7 Y) o! j
- 11}% G& ]& p+ q. q1 [
- 12/*获取距离*/
( T- |3 M2 j* z n8 K( `& t5 Z0 C - 13void Get_HC_SR04_Distance(void) r' I+ `5 x8 E
- 14{
' A2 ?) u; k4 O ` e1 h! O& [+ ] - 15 static uint16_t count=0;
& b" f1 n+ H: M8 u% p8 p1 d6 Y - 16 switch(count)4 o2 j7 T) s. k- Q
- 17 {& q! M: e+ `! M! F I D
- 18 case 1:+ P1 V3 F6 A6 `/ I; d
- 19 {+ U/ s' z; r, M. ]0 O I
- 20 GPIO_SetBits(Acoustic_Port,HC_SR04_Trig);//Trig发送一个大于10us的高电平
+ b i5 m6 s6 Q. S0 k* j - 21 }break;
9 p2 E. r H" @4 H% ^! N - 22
2 I6 S8 ~; A2 ?8 a6 S; Q - 23 case 15:
4 h) x* U+ h1 F3 u9 V6 A# d - 24 {
. k, U( b. q# R9 p) Y: y! f, G - 25 count=0;
9 W. ?! p# }& K9 |% o5 G - 26 GPIO_ResetBits(Acoustic_Port,HC_SR04_Trig);
* W! N( _4 ^+ E* F - 27 while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==0);//当Echo为0时打开定时器 计时% R; t! D% [* M* j4 D8 g* \2 S
- 28 Open_Tim2();8 N) P4 |6 }. X7 T, n6 i* G" ?& ]
- 29 while(GPIO_ReadInputDataBit(Acoustic_Port,HC_SR04_Echo)==1);//当Echo为0时打开定时器 计时
- D" Q0 x* ]4 g0 N c( | - 30 Close_Tim2();
# G) T8 J; R. K1 d, v - 31 HC_SR04_Distance=(float)(Get_HC_SR04_Time()/5.78);
6 l3 j& E4 l- d6 O, l1 v' y9 j, g - 32
0 C: }9 O* K5 I2 `9 Z- q5 ` - 33 }break;
, r; i! k% Z7 K$ q3 I+ a$ M H - 34 default:break;; ?* ?+ e' u& l7 D8 ~
- 35 }
; ?, i5 [* M! |' M5 U/ G2 S - 36 count++;! |+ T, u; P% s3 v, W
- 37}
复制代码 ' j0 t0 D* K/ L. e, ]
舵机模块
本系统使用的是SG90型号的舵机,舵机是一种常见的角度驱动器,本系统需要判断不同位置的障碍物可以且对转向的力度小。舵机可以理解为方向盘称,方向盘是一个常见的名字。它实际上是一个伺服马达。舵机实物图如下:/ W/ T x8 @0 u( q0 u+ G' T
" G3 N* O7 i4 V {% N
# h3 q8 {0 u' N) N: |/ D3 `
舵机模块接口简单,舵机模块只有三个引脚。分别引引出了三根线左右两边是电源正负接口线,中间一根是PWM信号线直接连接单片机的控制引脚。通过控制单片机的引脚输出的脉冲宽度进而控制舵机旋转的角度。舵机每增加0.1ms 舵机对应增加9度。 0.5ms---------0 1.0ms---------45 1.5ms---------90 2.0ms---------135 2.5ms-----------180 20ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平持续时间为1.5ms,周期为20ms的方波,duty=1.5/20=7.5%。在这里设置定时器自动重装载寄存器arr的值为1000,所以当占空比为百分之75是,在程序中就要设置占空比为75/1000=7.5%, 这就是具体的算法。 / H% G. @ O" n( O, ]" v
舵机重要代码(可参考)- 1/**PWM引脚初始化*/* g5 T7 l1 V. e/ H( P1 a
- 2static void SERVO_Gpio_Init(void); ?' J A' \9 H9 c3 C
- 3{
% g7 Y0 H, }& m6 L. t - 4 GPIO_InitTypeDef GPIO_InitStruct;
. x/ I3 P. Q9 ~% x y* P+ }0 z - 5 /*----------通道2配置--------------*/+ I5 c' s0 N+ E) i1 Z& p$ T
- 6 /*定时器3输出比较通道*/
0 ?& }7 [7 \9 e3 \, d - 7 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
8 l/ V7 p5 Q" `2 [ - 8 /*配置为复用推挽输出*/0 L$ d [- z: o1 W) k4 E) g, _
- 9 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;9 [* ?5 |! Q& f6 V4 d" |) K
- 10 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7;
( {* C5 x; L7 X - 11 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;/ \* _( m: h- N9 f( x' U
- 12 GPIO_Init(GPIOA,&GPIO_InitStruct);
* C: r& k w4 M6 P& I - 13}& e7 @4 c+ \* k/ r
- 14//定时器3初始化,设置TIMx的ARR,PSC& t; y0 J; G) l6 d& `/ E" W
- 15//arr:自动重装载初值,psc为预分频值,两者配合控制定时器时钟的周期 R `" j' ~* d; B' H
- 16static void SERVO_TIM_Config(TIM_TypeDef* TIMx,uint16_t arr,uint16_t psc,uint16_t duty), G* N/ ]6 x/ B s) z+ c; f0 \
- 17{0 {) H' p+ s3 O& p2 w# P
- 18 //-----------------时基结构体初始化-------------------------/
7 s8 O+ V7 x+ Q/ x6 O4 a& S - 19 TIM_TimeBaseInitTypeDef TIM_TimeStructure;
/ K q$ d7 Z: _! u" Y - 20 /*开启定时器3时钟,即内部时钟CK_INT=72M*/* O& K! q* C! e8 r) c w2 L
- 21 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);% p4 q" P0 E% Y1 ?+ h
- 22 TIM_DeInit(TIMx);% L+ j/ B5 p3 S! _8 ?
- 23 /*内部时钟作为计数器时钟,72MHZ*/
8 B0 q9 X# Q8 b: a - 24 TIM_InternalClockConfig(TIMx);
7 s, T& a, _4 K! ]5 A( G9 `6 s7 _ - 25 /*自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断*/' ^6 @$ E' P3 y/ b) q
- 26 TIM_TimeStructure.TIM_Period=arr;//1000 当定时器从0计数到999,即1000次,为一个定时周期/ [, n" y4 G4 D8 ]" |* d
- 27 /*时钟预分频系数为71,则驱动计数器的时钟CK_CNT=CK_INT/(1440-1+1)=0.05MHZ*/
. {/ z: M+ Y- y% n. g - 28 TIM_TimeStructure.TIM_Prescaler=psc-1;;//1400 //即定时器的频率为5KHZ
# c, A0 Y' w' o& F& ?. l7 g - 29 /*设置时钟分割,TIM_CKD_DIV1=0,PWM波不延时*/
! L" N* S1 f8 R; j - 30 TIM_TimeStructure.TIM_ClockDivision=TIM_CKD_DIV1;
; H3 f5 A/ [' k - 31 /*向上计数模式*/
& C3 {( k# X6 g) V' x - 32 TIM_TimeStructure.TIM_CounterMode=TIM_CounterMode_Up;
. U& ]% T- T$ v5 h" b - 33 /*重复计数器*/
1 j- U" B' s B# Y- z- y - 34 TIM_TimeStructure.TIM_RepetitionCounter=0;
1 T( }9 P0 F, A' ?: U+ P - 35 /*初始化定时器*/
9 q. r: V7 X' a5 h - 36 TIM_TimeBaseInit(TIMx,&TIM_TimeStructure);
/ x3 {8 B$ v$ A0 Z& F- |4 X# f - 37 /*使能ARR预装载寄存器(影子寄存器)*/
* k {( O$ ^( H- a9 i - 38 TIM_ARRPreloadConfig(TIMx,ENABLE);3 A0 ? `" A& S
- 39 //-----------------输出比较结构体初始化 开始-----------------------/
9 t7 O: k# O% i( J V - 40 TIM_OCInitTypeDef TIM_OCInitStructure;! {5 X' X' k0 F7 B n8 ^
- 41 /*PWM模式设置,设置为PWM模式1*/( \7 {1 ?, y6 E1 z1 q5 J% L
- 42 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
/ C: ?$ @+ N; ^* \( b - 43 /*PWM输出使能相应的IO口输出信号*/
2 a4 T3 r7 s( z' Q+ H) ] - 44 TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
$ n5 v1 k. K9 [- V - 45 /*设置占空比大小,CCR1[15:0]: 捕获/比较通道1的值,若CC1通道配置为输出:CCR1包含了装入当前捕获/比较1寄存器的值(预装载值)。*/ C+ Z$ [" `0 n& {: n8 i4 ?& U0 ?
- 46 TIM_OCInitStructure.TIM_Pulse=duty; //占空比大小: p& u9 h' M+ t& Y& L0 f: n, D0 V
- 47 /*输出通道电平极性设置*/
$ \' e5 F0 w2 k$ C' U$ I - 48 TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;& q7 M3 Z) U7 O2 I
- 49 /*初始化输出比较参数*/
" T- m' W0 {$ ] n6 d# R" e - 50 TIM_OC2Init(TIMx,&TIM_OCInitStructure);* _ h' v3 Z9 m
- 51 //-----------------输出比较结构体初始化 结束-----------------------/ ; L0 L- y& r; |5 m$ R
- 52 /*自动重装载*/' a- a+ m- h# P6 J; X: ^
- 53 TIM_OC2PreloadConfig(TIMx,TIM_OCPreload_Enable);: g9 i- ~9 e. u) w: d$ p7 p4 y4 K
- 54 /*使能计数器*/
5 A* K. f3 n) G7 g7 D2 S - 55 TIM_Cmd(TIMx,ENABLE);8 |0 Y6 A3 l1 f5 i$ o
- 56 /*主输出使能,如果设置了相应的使能位(TIMx_CCER寄存器的CCxE、CCxNE位),则开启OC和OCN输出。*/
+ u9 s" C D$ P* Q - 57 TIM_CtrlPWMOutputs(TIMx,ENABLE);
1 h' D3 t; I" e# n! J; s& v - 58}
3 C K$ W+ {0 N( F4 a0 F) F - 59/*舵机PWM初始化# S8 L) f! X% H, A
- 60 每增加0.1ms 舵机对应增加9度
: j b7 P6 A/ A$ {1 ~' x - 610.5ms---------0
, X Q9 N* M/ J - 621.0ms---------45
3 g. {+ A; f+ E4 o0 M - 631.5ms---------90+ E& ? Q& d% A0 |% K- y& C0 C; F
- 642.0ms---------1358 I* b1 ~6 e7 R9 J1 i
- 652.5ms-----------180% q- D7 K X+ D5 h8 \) \
- 662.1ms turn_left=150
+ G- j; C7 u( Z4 r0 G) ?; I - 670.8ms turn_right=25
5 Y- x/ M/ O+ j; N3 t - 681.3ms turn_front=75( W& j1 y7 Y5 F# [& z
- 6920ms的时基脉冲,如果想让舵机转90度,就应该发生一个高电平为1.5ms,周期为20ms的方波,duty=1.5/20=7.5% ,而定时器自动重装载寄存器arr的值为 1000 ,所以duty=75,时占空比为75/1000=7.5%.
' I% ^4 l3 a8 v2 v, ]% E - 70*/
. m7 y9 a" m( P3 B0 o" X - 71void SERVO_Init(void)# Z: w- [9 O4 }" p/ [: ?7 B# x% v& H
- 72{
8 G% L, J+ ]2 C2 |; u - 73 SERVO_Gpio_Init();9 R7 @% v; f5 j
- 74 SERVO_TIM_Config(TIM3,1000,1440,turn_front);2 Q; O6 l$ r& x" V
- 75/** 我们把定时器设置自动重装载寄存器 arr 的值为 1000,设置时钟预分频器为 1440,则
: L! U. K6 s* z5 ? - 76驱动计数器的时钟:CK_CNT = CK_INT / (1440-1+1)=0.05M,则计数器计数一次的时间等于:$ F, C7 H3 s0 \# b6 b3 U
- 771/CK_CNT=20us,当计数器计数到 ARR 的值 1000 时,产生一次中断,则中断一次的时间
% ?6 I: f. w7 U, [ - 78为:1/CK_CNT*ARR=20ms。; Y5 J$ J1 X# P
- 79PWM 信号的频率 f = TIM_CLK/{(ARR+1)*(PSC+1)} TIM_CLK=72MHZ7 E# J1 |' q3 t( k% b. F: F/ ]
- 80 = 72 000 000/(1000*1440)=5KHZ ; _9 m3 H9 A* p1 w
- 81*/ 9 n0 f2 I) P% f- ]( P$ c
- 82}) C1 L; X8 L7 `4 ?
- 83/*舵机角度控制*/
0 d' n* h; B' w, f - 84void SERVO_Angle_Control(uint16_t Compare2)& Y. D/ T X4 B. e' q7 D! P
- 85{
, U! i' s* f" L5 _/ Z - 86 TIM_SetCompare2(TIM3,Compare2);' |- ^! @& X0 R* A
- 87}
复制代码
/ ^* x" G6 y; T' m
编码器模块
调节小车前进的速度和避障快慢我们采用EC11旋转式编码器,可以用于光度、湿度、音量调节等参数的调节。EC11编码器的形状类似于电位器,中心有一个旋钮可以调节PWM信号,光电码盘利用光电转换原理输出三组方波脉冲。EC11编码器的实物图如下:" @2 w& J: m2 p$ V6 i0 U P
; |" I3 o6 p' n9 \! U$ c8 V. D
; D3 L+ F( E3 L3 }
OLED显示模块
用来显示小车转速,以及左右编码器数值和电池电压等参数所用的是OLED显示模块,分辨率较高,而且功耗低,正常显示时仅0.06W,供电电压范围在3.3V-5V,有IIC和SPI两种通信协议可供选择。显示模块的亮度和对比度可以通过程序设置。由于它使用寿命长以及其他的优点,OLED更加适合小系统,本系统由于单片机引脚有限,不适合利用简单的LCD1602或者12864来显示,在多方对比之下OLED效果更好。OLED显示部分相对比较简单,大家参考中景园的例程就可以实现。 $ w( I- i& v" z# O5 c
转载自:果果小师弟
; x$ O6 N$ u$ m7 K) x: p) X |