概述
+ o2 R: t8 z0 c" |小车外形:9 o* W1 U" {. z5 s2 m+ T. a
# U. P' H% h* X" w2 V* g; U5 ~! s
' A2 y; a* T7 p! C' ^/ `0 m: B. n6 A
功能简介, G/ m" W, H% W5 X9 |, E
利用摄像头识别前车尾部的AprilTag,得到前车位置,传回stm32主控板处理,使两车在行驶时保持恒定距离,实现自动跟车。4 [6 p- ~/ |. \# c7 ?, f
* F7 u7 h2 B$ j$ F: _( n3 }# c' b4 M) q. `# n
}+ q! T7 E! H r5 a# t" h% K$ T3 U" N9 [, D
openMV4摄像头
, L3 a& b$ o5 Y1 t4 Y1.1 Apriltag识别与串口传输$ H4 z3 I2 Q4 A7 N4 `
' D; Z2 j1 y+ j9 g; y2 }4 Y0 G d; V9 {0 o" {3 }
AprilTag是一个视觉基准库,在AR,机器人,相机校准领域广泛使用。通过特定的标志(与二维码相似,但是降低了复杂度以满足实时性要求),可以快速地检测标志,并计算相对位置。
4 o2 x+ T- d6 U# W n4 G" D$ g
. W/ F1 ?8 ^8 k4 \6 U0 p
Apriltag示例:
% T) o5 O0 F. N* ^5 X
8 T# L; v a% L
通过识别Apriltag,可以得到x,y,z三个方向的距离以及偏移角度。这里只需要三维的距离即可,通过串口传回stm32., F8 x5 ]) n. q* @, S, {
- <font face="微软雅黑" size="3">$ `9 `) }% a' _% k
- import sensor, image, time, math,pyb6 d' o0 M& Y* h7 c, b# s/ }! e4 b' K
- from pyb import UART) v4 l, u- Y. d5 [. P
& L6 A; W/ s. D; L- sensor.reset()9 D2 q) t- z! Z P6 n
- sensor.set_pixformat(sensor.RGB565)
% x6 a: e2 N* q7 Y - sensor.set_framesize(sensor.QQVGA) # we run out of memory if the resolution is much bigger...2 w" ~9 q5 [# t: h+ J% j; y/ f
- sensor.skip_frames(time = 2000)
# l7 d6 _+ a! h" p; z; j - sensor.set_auto_gain(False) # must turn this off to prevent image washout.../ R( z( N5 J$ J% g' B
- sensor.set_auto_whitebal(False) # must turn this off to prevent image washout.... q/ y5 P; H8 j1 T
- clock = time.clock()
7 K- G% W, O4 Z2 h ~: X. T - uart = UART(3, 115200)#串口波特率5 T; C3 s8 `! E! E8 X% j) A
7 q: B; v0 j% P- f_x = (2.8 / 3.984) * 160 # find_apriltags defaults to this if not set
2 j7 q% [9 @; ]' e - f_y = (2.8 / 2.952) * 120 # find_apriltags defaults to this if not set9 C) N% U' m0 v+ t/ I7 u
- c_x = 160 * 0.5 # find_apriltags defaults to this if not set (the image.w * 0.5)
w0 r/ i& O' z - c_y = 120 * 0.5 # find_apriltags defaults to this if not set (the image.h * 0.5)# @ M& s4 v- W, m
- ' j8 p! r0 m }' c7 { G% `$ y
- def degrees(radians):6 F% ]; B9 w+ ~
- return (180 * radians) / math.pi" b0 a2 ^0 g5 W( y% f* P! ^+ I* ` H
- . I+ m/ m4 U! L9 p' \
- while(True):
; I$ p: q6 k* H; r w: ] - clock.tick()
5 \5 c0 n6 Z. i, a& G* }% y$ n - img = sensor.snapshot()! X" X5 F8 o3 _2 ?; b
- for tag in img.find_apriltags(fx=f_x, fy=f_y, cx=c_x, cy=c_y): # defaults to TAG36H11
* ?- d" i2 ]# s( v1 n - img.draw_rectangle(tag.rect(), color = (255, 0, 0))
& j$ I6 r6 T0 g3 m$ d6 j. J7 ?! Z1 |/ b - img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0))" j9 O$ c: q0 }3 c6 w
- print_args = (tag.x_translation(), tag.y_translation(), tag.z_translation())
. E. @* ^5 H4 ]8 Y - #degrees(tag.x_rotation()), degrees(tag.y_rotation()), degrees(tag.z_rotation()))
) ]4 A9 }) z0 ^' @; f( {* G - # Translation units are unknown. Rotation units are in degrees.
* J/ k! j$ u7 V- j$ O- e# b1 `7 m8 Y; w - # print("Tx %f, Ty %f, Tz %f" % print_args)
) T% G/ `: h# S0 K; V C1 D - uart.write("A%.2f,B%.2f,C%.2f," % print_args+'\r\n')#设置特定格式,以便于stm32分割取得数据, O' A5 z0 z, A
- #pyb.delay(500)9 A& }' O% G# b$ M$ A
- # print(clock.fps())</font>
复制代码 STM32主控板(型号为F407)
' _' y9 ` W8 }( T6 O0 p- t$ L2 ^/ P
% L% A- w+ v, H9 R' T4 o
7 S) W; G7 \2 U- T g+ y0 c2.1 时钟与中断配置
8 O+ K# O8 B" P& D9 ^3 G& r' h" O! a6 @
& E- [. Z" P: [附上stm32时钟示意图:
0 A' s: u! Z* y6 R
$ V0 C, L9 W6 r% E+ D$ \ h9 @' r
定时器示意图:
1 _' t7 |. @+ X0 I! A; R
% v4 H0 G$ _3 o+ t% I7 \' T5 v& ]
定时器分配:6 Q! |& L; ` B& |& I; o" X# u5 ?
+ }/ ]. B% `& r# r- ]
所有时钟初始化的函数:(每个函数的详细内容在后面)
6 A' Y: l+ r$ Z f+ I! C7 r; f+ [- <font face="微软雅黑" size="3">" E' L# v4 ]: J* ?8 h* V0 t
- TIM8_PWM_Init(400-1,20-1); //用于控制电机,168M/20=8.4Mhz的计数频率,重装载值400,所以PWM频率为 8.4M/400=21Khz.
0 s7 w2 S5 n8 n, A$ S: }7 n% C/ D; @ - TIM3_PWM_Init(200-1,8400-1);//用于控制舵机,50HZ
7 a& j9 k4 m4 A" i4 S: C - TIM2_Int_Init(400-1,20-1);//定时中断,21KHZ* D8 J" F$ B/ g. y* f8 M6 f. I- S
- TIM7_Int_Init(500-1,8400-1);//用于编码器计数,20HZ,50ms中断一次
! V6 b6 P7 p; F. m" S - uart_init(115200); //初始化串口1波特率为115200
$ h4 c, K' \7 X7 ~* l- G - Encoder_Init_TIM4();//编码器接口初始化</font>
复制代码 2.2 串口收发与数据处理6 k# s$ l: `" U0 f7 m1 D
( U5 V$ I' ?6 F U( F1 D
; a! _7 o6 Z, Y+ v+ L# D串口中断:USART1,USART2
4 F% _1 k2 A6 e i A, h串口初始化函数(以USART1为例):! F) @6 q) s: D1 A
- <font face="微软雅黑" size="3">
# Y$ r5 k, w1 G- N" x - void uart_init(u32 bound){
! z+ e: b& J/ O7 U$ p/ s* C4 b% W# v - //GPIO端口设置
; u) y" [) W3 F- D - GPIO_InitTypeDef GPIO_InitStructure;# D1 s# y/ R" X' t+ M- x; S9 n$ g
- USART_InitTypeDef USART_InitStructure;5 u* |1 u) p2 j. h. Q1 x% _
- NVIC_InitTypeDef NVIC_InitStructure;
`' h9 H% r7 d3 e5 `9 v - # h/ r3 N2 y. Y6 e4 ]. w
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟4 V) P' T- ~ P+ Z
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟
* {! v7 `+ @' F& e -
0 y2 l; x: A7 Z8 L - //串口1对应引脚复用映射8 x* z' w9 ?' M7 }
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9复用为USART1
. G/ ?% Z) M8 ~' j5 X$ P9 n. `0 M. [ - GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10复用为USART1
+ l' V0 ^" @( l% K2 P8 g6 r -
. V* g) v( f s1 C - //USART1端口配置
! k' t. A* m+ i" }6 D& ?& { - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9与GPIOA106 k9 ?/ `1 @. L- ~! y5 S7 H% f
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
9 m5 |7 Q& a7 M: X% p: r5 b - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
. t1 \" K6 p% ?9 L: ~5 ?7 j - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出+ d5 w; Y" E! s% _3 T. q
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉3 A* \0 n7 f/ P; G2 B1 D6 _
- GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
3 V3 f3 X5 w" u9 k/ G
, X4 k. q. T* V& u1 s; p$ P- //USART1 初始化设置
; `% v' \% {. v- D - USART_InitStructure.USART_BaudRate = bound;//波特率设置
' X% p" X% x' V9 l0 ? ? - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式( H& z8 D% X" s! z" z! K
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位+ h1 Y% F' N5 M, n0 t: e' o4 r
- USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
$ _* z6 d- Z( Q - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 B5 Q6 j. ?0 K$ ], K( v
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
9 y+ l: u/ S/ g$ g# Q* B - USART_Init(USART1, &USART_InitStructure); //初始化串口1
- O/ Y* b9 Q6 o7 B& H -
$ { K/ o Q% T- ^ - USART_Cmd(USART1, ENABLE); //使能串口1
! \, \5 J. s, }1 }# }# d - $ O% _4 O: f: Z1 t* p5 ?
- USART_ClearFlag(USART1, USART_FLAG_TC);/ @- v! `; ~; E( G7 {0 D; K5 s) ?$ d
-
+ W( K/ {. J/ _: j - #if EN_USART1_RX
* |- P. j/ f8 N' n - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
, ]! s" n L( `7 x" G" g1 F( ] - 3 n! ^7 |: x/ J$ Y
- //Usart1 NVIC 配置
/ ^1 w3 d' m8 z( G - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断通道" L! ]8 W# t G# |0 o& Y% y
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级35 {) M* n* E4 c. C% J1 h
- NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
+ c" i. y/ a5 ? - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
. z/ M8 X, g( L+ u8 A9 U - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
3 q. V4 ^9 O, o7 E; v4 m! l
8 S- P, h5 | o. z- #endif4 J+ _7 H" M: C: i
-
6 V( D% e4 U" {8 L& _ - }</font>
复制代码 串口中断处理函数:6 N# D( V6 p* y; Z1 m7 w
- <font face="微软雅黑" size="3">* `+ X5 U2 H1 [: z3 b4 X
- void USART1_IRQHandler(void) //串口1中断服务程序
+ @7 O4 Q1 W# u( @2 B - {" A% N$ F. t- s& M9 o9 L, _
- u8 Res;6 |4 P" }. N" q/ X0 B7 m& x
- #ifdef OS_TICKS_PER_SEC //如果时钟节拍数定义了,说明要使用ucosII了.
$ s2 S6 x4 K3 Q3 V - OSIntEnter(); ' @: B- P+ F: S( O1 s. R2 L0 v
- #endif$ s* \9 q9 `, c6 S+ Z
- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
0 H6 Z% D7 t6 U0 S7 b6 f - {( U9 ]1 _: [; J' n$ @
- Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
, N( _ e+ B7 t7 ? - 5 [& D G, R' U# }- z
- if((USART_RX_STA&0x8000)==0)//接收未完成2 S. G% e3 g) J2 n( R
- {
& n" M2 P9 Z# d. q# i+ [4 `6 V - if(USART_RX_STA&0x4000)//接收到了0x0d3 a/ d p$ B* ]3 g- x
- {
& `" V6 P! ^! x6 U - if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
. J# t% C; z A6 R1 q - else USART_RX_STA|=0x8000; //接收完成了 . H4 k2 A% c+ q
- }. J5 E) T$ ^- {+ ]! f% {
- else //还没收到0X0D
# G* Y! b* _! n! |7 |; w* Z/ ~8 ~ - { 3 n9 u1 F( u/ @
- if(Res==0x0d)USART_RX_STA|=0x4000;
4 W) F0 C" G6 y- y( x# i) ]3 i - else
( U0 z# @; u) V, v - {. P G* K$ n# J; \: z
- USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
2 s. i% w# ?" X$ e- { - USART_RX_STA++;: d B4 h6 y' H; P/ j0 t
- if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
% C" H7 h+ C, s! @, B$ z - }
* `: p( {6 u' Y9 N - } L% X/ Y/ S. Z' [1 \6 U# X
- } " Q2 a0 i' `4 D5 B
- } . R4 S8 f; ]+ `0 m$ F
- #ifdef OS_TICKS_PER_SEC //如果时钟节拍数定义了,说明要使用ucosII了." y5 I& a4 J8 ]: `9 F, m
- OSIntExit();
. Y9 X/ u; Q9 \* m: K - #endif
% P' m7 N2 c8 g- q5 t- v - }
3 f) K, h% v4 y* R- G+ ?) r - #endif </font>
复制代码 字符串接收与处理(从openMV接收到的数据):
/ B5 X% Z9 ^3 Q- <font face="微软雅黑" size="3">/*涉及到的全局变量+ F- g2 C0 D7 c' r/ U
- float data[3];//x,y,z方向的距离,浮点数形式5 ?% w' ~+ S1 `$ p$ R
- unsigned char data_string[3][7];//x,y,z方向的距离,字符串形式! N5 t. e" ~5 b
- */
M- q2 o, I: [3 i1 ]4 W% { - if(USART_RX_STA&0x8000)3 X: C! b( `; q! B% ~' @/ p
- {
4 K$ }6 k6 ]; p - //清空字符串
, R- U4 L2 k" n2 o! F - for(i=0;i<2;i++)
3 e. U5 W! e$ z* V; Y - {1 N. c9 [" Q8 v: |' ]0 U
- for(j=0;j<6;j++)
" H' T. g5 w& q3 S. G - {1 \* O& P; O& [' l3 b, h' X
- data_string[i][j]=' ';
! c( }0 {" M& S; L* ? - }( ^2 g M2 J, U" i& _# G" Z
- }/ f8 T; m, z* c1 J1 u g( t# [0 [
- len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
. N) u3 o5 l" S+ r8 I6 q - for(t=0,j=0;t<len;t++)% o; T) I8 O7 N' H# \1 W
- {
* y4 `0 U+ V: ~: N+ y2 W8 y - j=0;
/ z/ Y" K5 |( l2 {) v - if(USART_RX_BUF[t]=='A')4 D1 b; t# v: ^; W$ j) f! v/ w
- {
n" b+ J) b* \6 @* u8 z - t++;//去掉首字母+ N. O: v: Q( m3 ?
- while(USART_RX_BUF[t]!=',')' y. M6 V3 [, Q' D. S
- {" ~* j) `( x2 ^" h* O
- data_string[0][j]=USART_RX_BUF[t];: ]1 F; h, m6 h* K
- t++;, c/ ~1 W5 v2 ~$ E
- j++;
" `) Y: O8 s% H6 R) B - }* d8 x1 g1 G" a" f
- }
1 S/ c- s7 w! D( z; H - if(USART_RX_BUF[t]=='B')( R; N8 Q8 i- e( B
- { n1 I: G6 @# \. O9 f# x
- t++;//去掉首字母
( f, _! d' ~3 z0 F' c - while(USART_RX_BUF[t]!=',')
: }3 d# _+ o" Q* a - {
* s/ X+ I) t7 Q - data_string[1][j]=USART_RX_BUF[t];2 U# N5 r0 K: Q% n- m2 }. k' z- y
- t++;
0 z# _: g% ?! @) i6 v, a - j++;
' ? G j" b @( f - }
. H! U) M( T$ l: Z. s3 P - }
5 d7 w/ B1 _ P& f8 c. \+ ? - if(USART_RX_BUF[t]=='C')
% O4 R5 ~ s: t' F6 i" S - {
, k+ n2 {7 @$ A ?+ Y9 y - t++;//去掉首字母
/ |9 N. x9 |) A - while(USART_RX_BUF[t]!=',')% {. l0 P' n8 P& ]) J5 Q- H
- {
" E9 m+ o Z7 X - data_string[2][j]=USART_RX_BUF[t];
, K2 X" A+ B& U4 ?" _ - t++;& w& Z% D7 |6 o+ g+ B/ n
- j++;8 y: e$ S- c4 p2 \! ?
- }
; R$ @4 [: p' ~+ \ - }
# x; ]: ~' s3 ~! o- E) H+ X5 f& c - }' {, Q9 A( j" f: t
- //把字符串转化为浮点数6 ?$ y! { g3 c2 e
- data[0]=myatof(data_string[0])/100.0;//x! F7 r: O4 m; L. ]& h6 c
- data[1]=myatof(data_string[1])/100.0;//y
1 E# x6 |! o8 z: i9 n2 R% ] - data[2]=myatof(data_string[2])*(-1)/100.0;//z,输入是负数,转换为正1 M0 r* s# C" J( L7 t5 [) D
- //USART2发送数据! F& Y% q" W4 o5 g1 ?) d5 z- q4 y
- // Usart_SendString( RS232_USART, (uint8_t *)USART_RX_BUF );
0 z4 |+ s" P% ^) p. u! P; m - 1 |: I3 A, L+ |
- //LCD更新显示+ i' l6 ^4 t0 P7 K% }( }9 ?
- //显示xyz5 \8 R. R: [4 G! L
- // CLEAR(10);
1 v1 |% }! h2 \; g - // Gui_DrawFont_GBK16(30,0,BLACK,WHITE,clear);
2 l- F) [4 U+ A, q( G - // Gui_DrawFont_GBK16(30,0,BLACK,WHITE,data_string[0]);1 v, K- y) J. P
- // Gui_DrawFont_GBK16(30,20,BLACK,WHITE,clear);7 ~, ?1 g* y2 o) {+ h
- // Gui_DrawFont_GBK16(30,20,BLACK,WHITE,data_string[1]);0 Q, x$ g; \0 k$ U/ o
- // Gui_DrawFont_GBK16(30,40,BLACK,WHITE,clear);1 q' H4 t8 q e' Y2 }# F
- // Gui_DrawFont_GBK16(30,40,BLACK,WHITE,data_string[2]);4 l4 ~1 I7 [1 J$ x! s8 p: r
- & C2 q: t2 c6 o+ r4 O
- USART_RX_STA=0;//清除标志位: u3 @3 g8 t7 L! n" ?
- }0 e$ }3 r% d2 l* c ]9 i
- }
- N. i4 N6 Z1 z4 `3 T - </font>
复制代码 字符串转化为两位小数浮点数(用于后续PID控制):
2 E& r* ^4 Y9 ^ \6 t5 | ]3 d
* }' U& I( e, l) B' r J- <font face="微软雅黑" size="3">int myatof(const char *str)//此函数仅适用于两位小数的浮点数,返回的是乘100后的int值,因float返回有错误
" f$ m% F6 y, b; g& K3 K - {
% v# y* e K& c# u - int flag = 1;//表示正数. R" d" v% W$ M5 @3 ~& f1 v* _
- int res =0;
# N k. {0 r$ ] k3 ? - u8 i=1; //小数点后两位- H. m( P3 D$ w3 W0 @
- while(*str != '\0')0 y5 M6 y4 x. c i1 @2 r; F* c+ s
- {- a1 S* [$ X4 n9 G4 Q: e+ J7 `
- if( !(*str >= '0' && *str <= '9'))//找到字符串中的第一个数字9 N" d4 \) o# ~: ?: j# i
- {
: ~5 S) h! i% s$ j0 \ [ - str++;) ?# T+ l! _" a, b2 d% b: D
- continue;
6 U3 T* Y% ]+ Q2 S$ T4 n. ] - }
4 H: d; |6 |# H" D) O - if(*(str-1) == '-')" k' p& z6 H! Y( P3 }" t/ B: z
- {( u1 M8 v0 Z0 \1 p e
- flag=-1;//表示是一个负数
! f0 ~5 K9 D3 J. J9 a - }
9 `$ A1 K# ^ s R( l9 T - : H# \3 n B9 H& @, J1 k
- while(*str >= '0' && *str <= '9')
G) S5 \ u/ d - {
1 Q- x6 s, a( u) L5 X - res = res *10 + (*str - '0');
* ~' U) o l# Q3 Z0 f8 j4 N/ P - str++;
! L4 D* D* t, p; i" `, Q2 b# P) Q/ y - }5 n; m- `$ s- G: Z$ M
- if(*str == '.')
9 T' l# J# \/ f. k$ M: n - {& @6 n- N, V$ n) N* i+ L
- str++;. @2 `5 n5 V$ m, {( L
- res = res *10 + (*str - '0');
% ?# c9 j$ N O1 ^6 [" a - str++; s8 S$ O! T: x E; v- W+ y+ l
- res = res *10 + (*str - '0');//保留两位,故加两次
% g3 E) B) h! A8 n: B/ m- @ - return res*flag;5 {! K; r; F3 C' S2 @
- }9 d( b7 F- F+ J' u8 b& F0 B. F( R
- }
% ^' W* N2 ^% O - }
6 O% D) i- E; B4 W' g9 N
2 Y1 n x) O. Y# z4 _- + ^5 I' Y8 m9 P$ i7 M
- 2.3 LCD显示模块1 P8 q$ ~, Z8 @ w" J! n! j& b
9 h. @" ?4 h5 U2 g+ U9 ~- LCD模块用于调试时观察数据,调试完成可以删去,因为显示屏很耗时,使处理速度变慢
* t! ~) s( |& d) P2 J( v6 [2 u - 驱动函数总览:2 m, p3 ~, T! O( {; `
: u9 g/ I% ?, y2 n
' ]+ L7 P0 M! v# ]+ r; `+ l- : p3 B$ W2 l: H
- void LCD_GPIO_Init(void);
2 f4 c$ G' F. @! K z6 ` - void Lcd_WriteIndex(u8 Index);
9 v7 F# {# R! P! Z - void Lcd_WriteData(u8 Data);+ O- h* c+ C6 R9 ^* ~8 j9 s' z
- void Lcd_WriteReg(u8 Index,u8 Data);
' ^1 u$ @6 E! G( ^, u$ Z; p( s# j/ i. } - u16 Lcd_ReadReg(u8 LCD_Reg);
5 S- K2 t/ R6 u - void Lcd_Reset(void);
- q8 @9 t9 W( Y. y( w8 U8 x - void Lcd_Init(void);, t' F1 F! p( u9 X! {3 d% W
- void Lcd_Clear(u16 Color);3 P0 {2 f: H ~/ D0 o8 E, [
- void Lcd_SetXY(u16 x,u16 y);1 E+ m& |9 i2 K! j: g
- void Gui_DrawPoint(u16 x,u16 y,u16 Data);- \; o) h7 _( ~4 [* |7 d
- unsigned int Lcd_ReadPoint(u16 x,u16 y);
( A: K" F [) D+ N1 W - void Lcd_SetRegion(u16 x_start,u16 y_start,u16 x_end,u16 y_end);
3 c" E+ {6 m2 B4 B/ m - void LCD_WriteData_16Bit(u16 Data);</font>
复制代码 G$ w: t L/ H& T; \3 e5 a2 V
6 ]9 j' x# n! X* v
TFT屏幕初始化:- b9 D. [0 e8 J7 a$ C
" D3 Q3 P3 R) x( }* |- <font face="微软雅黑" size="3">void TFT_Init_Show(void)
2 `! O( o' {( V$ a% ]4 k1 K - {- L# ^+ K/ N/ p% E- H3 g
- Lcd_Clear(WHITE);
: Z6 z0 ]9 w: }+ S P - Gui_DrawFont_GBK16(16,70,BLACK,WHITE,"by WILL CHAN");( y7 E3 n) Y" D- U: \" q7 g1 ~, b
- delay_ms(1000);
0 d4 g7 s7 T+ r: { - Lcd_Clear(WHITE);
- X- L( \5 j& t/ H* _ - Gui_DrawFont_GBK16(3,0,RED,WHITE,"X:");
6 D3 t0 ^/ \7 ~6 U2 |& n) a8 p- M - Gui_DrawFont_GBK16(3,20,RED,WHITE,"Y:");
1 y5 @0 G/ @/ ~0 o - Gui_DrawFont_GBK16(3,40,RED,WHITE,"Z:");2 N# d8 o) e. K) }8 {
- Gui_DrawFont_GBK16(3,60,RED,WHITE,"speed:");
2 y" @' n* R& A* k3 y - }</font>
复制代码 字符串显示函数;; V4 ]' _* A/ ^- l |1 _7 W
- <font face="微软雅黑" size="3">1 `+ U; q$ O M( j7 P
- void Gui_DrawFont_GBK16(u16 x, u16 y, u16 fc, u16 bc, u8 *s) z1 x: Z( @/ R3 }+ T2 p0 ^4 j: Q
- {6 T! U0 K9 ~. F5 E; B! h! D- _
- unsigned char i,j;
) q$ S( p5 U4 e0 Y7 s - unsigned short k,x0;
: U R! s; _" l! G9 k - x0=x;0 X* y- N8 X7 A0 Q! v7 Z
* X1 f% X8 x* C' c- while(*s)
8 Q7 g& | A" V; |: p. t - { 0 ]8 |7 A$ `" E
- if((*s) < 128) 7 _* \( `" G$ ^1 g1 n4 p7 y y0 m& p
- {4 _! C% O5 d2 K
- k=*s;! `* T3 i, X# z5 s2 y$ a2 ~% C; w7 C
- if (k==13) * F0 U# J+ u* R. H: C
- {
7 U. k0 U$ i( d G% g8 x% T# J - x=x0;
, ^1 b o/ x3 R2 d - y+=16;' p& ~8 e# z, O& b
- }
% B! Q6 |5 g/ V/ C+ x& T, S5 ^ - else
0 ~, w: h, w1 H0 l/ a$ s - {1 p9 C$ w* ^- O+ \
- if (k>32) k-=32; else k=0;
, @1 g6 S. Q: X) t; S- f -
$ |1 ]6 C r9 H. r - for(i=0;i<16;i++)$ z* E( b- o: l8 S
- for(j=0;j<8;j++)
8 m$ f9 c! I! T3 o# o5 ] - {! c( X' y& ^" c/ W, i8 I# `
- if(asc16[k*16+i]&(0x80>>j)) Gui_DrawPoint(x+j,y+i,fc);# n/ e# u/ g7 T/ I
- else
( t* G7 g+ t6 k: x4 D - { p) u/ G1 [& e
- if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc);. B: a, K6 g, X% U
- }7 q) P. _- v: ^# _) ?2 ^% n, J) h
- }
( f, d% J7 F0 @0 x% W" B1 a - x+=8; Z! O. d8 R- G9 ?3 b8 {+ p3 i
- }: F5 E. t& Q) s9 [2 J9 u
- s++;
9 _$ v M. O5 v: n+ g - }
9 V* ^* _4 e1 D) J) ~6 Z; V1 _ -
& Y- A" j# c' D8 C" b1 { - else
& X. H5 z1 g* C4 O! n - {5 a$ v0 x7 G+ ]- z2 P7 j, S* C
-
7 m, a3 |! [7 n! J - ! y$ Y7 H% Z2 s& {) n5 n
- for (k=0;k<hz16_num;k++)
+ z c6 t5 @. g+ i6 q& u7 Q3 j - {( }% J, R9 a9 x' ]
- if ((hz16[k].Index[0]==*(s))&&(hz16[k].Index[1]==*(s+1)))- y3 P1 M% b0 g4 k; R# ~7 m* R/ _
- {
4 X; F+ u/ v1 {' {' E% H. ? - for(i=0;i<16;i++)* E7 S, y* E2 R7 d/ g7 ^3 @1 j: C$ T
- {
8 a9 k2 a( U! U+ \3 J. f - for(j=0;j<8;j++)
4 I* d1 c! c( V - {) [0 G2 `' S. N# C5 \* B) C0 Z
- if(hz16[k].Msk[i*2]&(0x80>>j)) Gui_DrawPoint(x+j,y+i,fc);
1 N. v: ]/ b9 ~ Q0 ` - else {0 [4 j% @+ q9 h1 B
- if (fc!=bc) Gui_DrawPoint(x+j,y+i,bc);
2 C' {1 }3 T7 q" I9 t - }! l; ^# O8 i! K/ h
- }4 t9 i- r c ?; E8 W' n
- for(j=0;j<8;j++)
& ~" e4 v: Q7 [4 r - {
5 s9 [3 c4 H9 `2 p, @; {& e - if(hz16[k].Msk[i*2+1]&(0x80>>j)) Gui_DrawPoint(x+j+8,y+i,fc);! L$ Q& D9 W8 c/ A
- else : G/ |( [5 L8 F! ^, i
- {" R& L: `! F2 K& _ ^
- if (fc!=bc) Gui_DrawPoint(x+j+8,y+i,bc);3 D/ a2 C( m+ Z
- }
% r& V2 ^ A7 P/ U( ^- b8 G2 K8 R - }& S2 M- C; Z) J( Z9 I
- }
) \# C% q' L/ Q h# v, | - }' T. l2 ?( Q. ^, x+ b
- }
* e+ o: V! a/ y" I7 s" e$ t. l4 u - s+=2;x+=16;. e+ F4 u d) [. c/ M
- } : O: f4 D1 }9 S( S4 W
-
. z7 H, F1 o$ e# J" T - }
x2 g6 O* u, l* x+ e5 Y - }, \! H% J1 q+ O4 \5 ~
- </font>
复制代码 2.4 电机、舵机与编码器" d; B- n9 i l, C3 ^
0 Z( ^4 T. Y' u/ |# l: b
2 Z3 v3 G9 q7 q& V定时中断:TIM2,用于修改电机和舵机的PWM占空比9 e7 _) ?; G5 \! {! [
初始化函数:
5 O& q* f2 g' B3 |" l
" Z9 _% |5 h3 H- <font face="微软雅黑" size="3">
3 f5 b* U! [1 }# P/ V - //通用定时器2中断初始化
I- o% }, S8 {2 p, Y - //arr:自动重装值。
9 Z5 @ Y5 X4 G- [0 _, V - //psc:时钟预分频数
+ a; i. c/ W& u) K" t3 ~, O" d - //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us./ m3 l" [" E1 y$ v5 O2 Q
- //Ft=定时器工作频率,单位:Mhz# t7 b" X0 @! H+ ~
- //这里使用的是定时器2!( @; A! b6 p% P6 R# r M* s" L
- void TIM2_Int_Init(u16 arr,u16 psc)5 r, b; M5 o2 r9 K# B
- {
; f0 g, R' h2 I1 } - TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;$ j# e9 W3 j# Y
- NVIC_InitTypeDef NVIC_InitStructure;
3 D: m$ T4 h$ p9 k4 @- Z* n, | -
% ?% Y6 Q( k4 B, g1 s) j - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); ///使能TIM2时钟
1 a: V. x$ Y1 j- R6 U, U -
- S; C. E; W7 c - TIM_TimeBaseInitStructure.TIM_Period = arr; //自动重装载值
4 l4 I# o9 ?: w1 { - TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //定时器分频
N& {5 B9 ^, ]2 A; w% n. v - TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式! X1 E" @/ @ Y
- TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; + G+ b' m3 @* |5 \
-
9 R) F1 E8 m0 L* e; Q) _7 u8 } - TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化TIM3, f) I+ S4 r3 @4 S9 j# P; O
- & M6 c) g) Y% e0 j0 E" i
- TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //允许定时器2更新中断
7 C s1 |" n4 }$ ]9 F: D; } - TIM_Cmd(TIM2,ENABLE); //使能定时器2& w$ P; y& i4 l, L9 F
- 4 T6 m H+ a0 l" L' K3 S8 m( _
- NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; //定时器2中断! e9 a, p7 u. J9 O) k7 L
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //抢占优先级1+ m! l6 M3 d- c- j
- NVIC_InitStructure.NVIC_IRQChannelSubPriority=2; //子优先级3
) q S5 e1 f% @5 g6 B - NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
t3 V+ f/ J" Z - NVIC_Init(&NVIC_InitStructure);
+ t9 F" W9 B5 ~% ~& Z. c6 ^
7 S' Q9 [# U5 C* s, r- }</font>
复制代码 TIM2中断处理函数:
# J( C/ ]7 M5 P: k p% l( f2 ?* j- <font face="微软雅黑" size="3">- @2 f# p/ `; ?. _# G0 a. p
- void TIM2_IRQHandler(void)6 e+ n, E' U. m8 W
- { : a( ^; q5 a: s- V2 n* B! _
- if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)//溢出中断- S2 m: q: F: \' Q0 b
- {( p9 r( E/ a9 i5 H* a: q
- if(motor_flag==1)//反转! c8 f" b$ c0 c- G) k. [/ o
- {2 i( p* [2 m9 D% r$ D
- TIM_SetCompare1(TIM8,motor_duty*PID_val_motor*400.0);//和定时器的自动重装载值进行比较,来设置占空比,引脚:PC64 G" Y) {. h$ _5 p7 J& `) f
- TIM_SetCompare2(TIM8,0);# f$ t* G' R* b7 _1 D
- }2 ~/ K! F( `$ ? h# m
- if(motor_flag==0)//正转
, g; d; g/ ~* ?+ D( O - {9 a I3 u! A; H2 q
- TIM_SetCompare1(TIM8,0);7 A+ X: D M! y& i9 C' S
- TIM_SetCompare2(TIM8,motor_duty*PID_val_motor*400.0);//和定时器的自动重装载值进行比较,来设置占空比,引脚:PC7; s L8 t" }4 C% T2 `
- }
5 W2 E. u' M1 A0 D1 V - TIM_SetCompare1(TIM3,200-(servo_angle/45.0+1)*5);//设置舵机角度,引脚:PA69 N! P |% s, q3 ?7 z' S/ ^% u: n
- }2 o1 T/ Z$ d" ?# [
- TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除中断标志位5 V8 c4 N5 `, g; y" A" J
- }
' i( X2 |5 z7 A# y( V - </font>
复制代码 PWM输出:TIM3(舵机),TIM8(电机)
" ~5 |; Y2 t- T/ h- u0 K1 a' z7 A初始化函数(以TIM8为例):
' q8 W& F! x& e% b9 B0 g
: l# ?7 W; ^0 Y5 \! i' K6 {- <font face="微软雅黑" size="3">
' E# i) J# y0 r, ]! ]/ c- w8 { - void TIM8_PWM_Init(u32 arr,u32 psc)
/ R! x- @' ^# i - { 0 |( l- L5 H, _+ m+ l& c. d1 R' H
- //此部分需手动修改IO口设置8 B$ _0 Q# U: p7 U8 v
- 2 n/ H1 n( S1 U ]
- GPIO_InitTypeDef GPIO_InitStructure;
$ a7 H/ o, r+ x& X" a3 j! Z; h% A - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;# B l9 b. o- }/ r+ X
- TIM_OCInitTypeDef TIM_OCInitStructure;
1 I7 w8 n/ _+ Y6 k3 z8 \ - TIM_BDTRInitTypeDef TIM_BDTRInitStructure;; ?: l$ x) c. ~6 C6 F" s& n1 i
-
9 W% u9 p }7 `7 y - RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8,ENABLE); //TIM8时钟使能 ( \: Q7 p+ d* C' Q
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC, ENABLE); //使能PORTA时钟
! D4 O/ P9 D0 U5 _ L* j' ` -
7 Y- B0 @6 {- B, L, ^ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIOFA
. Y# m; Z3 n1 ~$ k - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能, c- C4 W! l# T
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度100MHz
- l5 k8 X" J7 v$ m8 ? - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出- y) M9 c+ w: g2 _/ ~* n, `5 _
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
0 n+ E1 O1 f; ]$ A - GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA7% K/ j4 \& l" L9 m
6 Z. f1 t a( M# z% J4 P! e- Y- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;6 W8 P% ~$ S! H9 [1 g# L
- GPIO_Init(GPIOB,&GPIO_InitStructure);1 v. L$ [1 `, z) A
-
( s: P) R, B* M' t% _) o1 ^ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8;0 o5 }9 E. j6 A a- Q6 g8 K& o$ D: w
- GPIO_Init(GPIOC,&GPIO_InitStructure);
1 T, M1 m$ D! T! P - # U5 M8 k) a! g
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM8); //GPIOA8复用为定时器1' u- S: `% Y+ s$ W! P; o \
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM8); //GPIOA9复用为定时器1- A5 q+ i& V% g4 c0 Q% \3 e
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_TIM8); //GPIOA10复用为定时器1; o$ H" {2 W8 ]6 j @* ?' W
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM8); //GPIOB13复用为定时器17 W6 I- `1 k" S
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource0,GPIO_AF_TIM8); //GPIOB14复用为定时器1- [! P3 o# O, \: Y" j: d- [7 \9 h+ k
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource1,GPIO_AF_TIM8); //GPIOB15复用为定时器13 p% P5 t8 X8 p5 W) g; c1 T/ W
- + [& ~7 J- H) p: L" D
- TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频
/ V/ \! f- O% Y9 t - TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式6 g4 g, K y! Z; y! t; Z' @% z
- TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值* Z5 |* }* _0 T' @' S0 {+ t
- TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; % Y# G! S- y) s5 U& e6 z
-
% P7 U0 L0 U; f# f2 { - TIM_TimeBaseInit(TIM8,&TIM_TimeBaseStructure);//初始化定时器1
& e6 y% |) J! r1 D2 i7 B% e - 7 Q5 j4 N5 ]/ F& c$ H
- //初始化PWM模式
7 I D# g1 ~( ^0 p& T - TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
" D4 g4 `& H. T \' Q - TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;* t3 T4 j6 k* Z+ d [0 C
- TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;5 R( ~) r# E4 F5 _1 x
- TIM_OCInitStructure.TIM_Pulse = 0;
6 B- b' a4 D% F* v$ x - TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;2 r5 k/ P2 x+ \7 T8 F/ b
- TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;6 u, r' {. n0 X
- TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; + v% Q1 H [+ j
- TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;- L3 o* f. n; g3 D" T' [7 i7 v+ M
- 8 _2 ~0 @, e. D+ r
- TIM_OC1Init(TIM8, &TIM_OCInitStructure);
4 m- ] C! k6 r; a" F! e - TIM_OC2Init(TIM8, &TIM_OCInitStructure);
7 @" O( v9 _- Z8 E5 q: R& h) x - TIM_OC3Init(TIM8, &TIM_OCInitStructure);* w. W! \3 o& @1 Z; x) s
: J! l. W. A/ S; U- TIM_OC1PreloadConfig(TIM8,TIM_OCPreload_Enable);+ y5 q; t* d" L# }' `
- TIM_OC2PreloadConfig(TIM8,TIM_OCPreload_Enable);
l J: G! v' W q) B- L2 l - TIM_OC3PreloadConfig(TIM8,TIM_OCPreload_Enable);9 C# g% h. Q% U5 h* B
- 4 w7 B* w, r0 c8 }1 A' [
- TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; v: Y, Z7 r" |: ~; |
- TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
7 F! B' T9 K7 a - TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;9 w4 _9 w1 l+ M4 N
- TIM_BDTRInitStructure.TIM_DeadTime = 0;
7 f0 F0 t9 I! s$ U) L - TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable; . v2 V" d; Z' i% S
- TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;
4 X" U( j( T( z - TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable;
! `& |+ x, Z4 [$ W5 a3 V - TIM_BDTRConfig(TIM8,&TIM_BDTRInitStructure);
$ E7 T" i1 ]0 n - ( ^3 |6 r, A, h, W
- TIM_Cmd(TIM8,ENABLE);7 K) [$ W. w+ j; f1 ^0 c
- TIM_CCPreloadControl(TIM8,ENABLE);+ \3 A! Z# ]0 H- \
- TIM_CtrlPWMOutputs(TIM8,ENABLE);) S# r) _0 f. q" `
- 3 f% p9 C. h$ {8 n6 p" H3 w/ K, d
- }</font>
复制代码 编码器初始化函数:
, a* z4 C$ X& P% y- k! \- <font face="微软雅黑" size="3">
% p/ Q1 u, z0 n) G0 k9 l - void Encoder_Init_TIM4(void)
$ c1 N+ b+ t& C/ L, J3 K) i2 R$ g - {" P* x. l2 E0 `1 E
- GPIO_InitTypeDef GPIO_InitStructure; - F6 k2 Q( G: z5 T/ X
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;& w; F7 b5 ~% h: o( _: e! b i
- TIM_ICInitTypeDef TIM_ICInitStructure;2 z; R* N g; u# W' T6 b; q
- NVIC_InitTypeDef NVIC_InitStructure;
) D) ]) m H1 I# W, n - ) x; R# w- I2 j3 H) |5 C, }
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//开启TIM4时钟' M8 T2 N3 U% q, m
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//开启GPIOB时钟, v+ W+ y2 K/ C& y# h
- ( a9 O9 K4 A- L/ [( ~
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4);//PB6引脚复用
' e2 L0 V% [$ r' s, L - GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_TIM4);//PB7引脚服用$ }/ |$ S7 M& I J" H( b
-
! V: p9 |: `5 \- L - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //GPIOB6,GPIOB7
+ `" Q+ u5 s- P8 _( t* Q - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;$ T: |. ]1 \" B( N; r. R+ p# Y
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 N2 [) W. P0 K6 b# s+ h: l/ I
- GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
* t( t6 a' _2 T2 U \9 c - //GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
/ x7 ~. g4 B8 p - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;% k. U: g+ O5 i9 _
- GPIO_Init(GPIOB,&GPIO_InitStructure);
' X, ~1 N( Z6 b' o4 s/ g - ! f% o+ z: J1 l& p
- NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;! {# p) n, h: Y$ Z! x# w
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /*它的抢占优先级可以尽量设置低点*/
5 x5 ~% q8 e+ p$ G - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;' [! J9 L7 w, g4 ?' U2 N2 x8 D' Y
- NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;//禁用中断,防止计数溢出而没有相应函数,造成卡死) C5 ]- G0 |2 e7 x0 ^. {
- NVIC_Init(&NVIC_InitStructure);
3 l& E& }4 ~! X' [$ v/ q - 9 u- M r |: ~1 r: X
-
2 u+ B: m9 v) s ]9 B& M -
6 Z- ~: Y9 V y9 H9 n! c* b3 R4 d$ ` - TIM_TimeBaseStructure.TIM_Period = 4095; //设置下一个更新事件装入活动的自动重装载寄存器周期的值" ^ @0 j w* y" q$ I& P" V
- TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置用来作为TIMx时钟频率除数的预分频值 不分频" f" ]0 v! t+ N; ]
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
b; E5 R/ s% d" _+ i' X - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
' M% `6 s! [! g/ L( C - TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
+ M7 @# l7 c1 e/ z7 X$ I - 7 c0 i& v4 k7 @1 N% @: L, [
- TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Falling);
# ~4 ~! L! ]3 K - TIM_ICStructInit(&TIM_ICInitStructure);0 S& q# L) X9 B* c
- TIM_ICInitStructure.TIM_ICFilter = 10; //输入滤波器
' f& q+ G3 ~, _; l* u* `# W - TIM_ICInit(TIM4, &TIM_ICInitStructure);0 C9 ?" k9 V. g6 }# f
- TIM_ClearFlag(TIM4, TIM_FLAG_Update); //清除所有标志位
3 F8 ~/ D0 r2 Q( [8 j r# M - TIM_ITConfig(TIM4, TIM_IT_Update, DISABLE); //允许中断更新; w+ @# e1 q: G! X Y- s, @- e# c
- TIM4->CNT = 0;
6 G2 U1 f% F1 t; i) a- R8 F - TIM_Cmd(TIM4, ENABLE); //使能TIM4
9 }. x" ~" j; O: s) V - }
& }# m" K8 E+ H - </font>
复制代码 编码器返回速度值:
' ?9 m- N7 h- g1 q- <font face="微软雅黑" size="3">/**************************************************************************
4 A9 [$ ?2 d. ]# n" F- g; u y( o2 j - 函数功能:单位时间读取编码器计数
: L: X8 T# F. K4 \1 ` - 入口参数:定时器- b& |& q: `. a7 y: S1 S, |! i6 F# m
- 返回 值:速度值
$ r/ J; j7 O) j# v( L) } - **************************************************************************/
( g7 i7 S2 d8 V5 H( T - float Read_Encoder_Speed(uint8_t TIMX)7 U6 [, h9 a6 a
- {
2 @# ]: Q* T0 ?) z n) ^# ~ - int32_t Encoder_TIM;
, y I1 e5 K! j, i2 J - float res = 0;
: y" @2 Z m# R5 m& J" s - switch (TIMX)
6 N$ Z W4 d" m$ a4 { c - {
9 T: i1 {! K6 M+ O- G& u" X; m6 c1 D - case 5:
: Z! ?1 Y3 r- M0 f - Encoder_TIM = TIM_GetCounter(TIM5);
+ I" m* J; S( @- S# |& U/ Z - TIM5->CNT = ENCODER_BASE_COUNT;
: |9 ?5 e! s3 m1 _) k9 B4 r- m - res = (int32_t)Encoder_TIM - ENCODER_BASE_COUNT;
0 B8 |( W- N& ?3 U - break;. z D8 d4 o7 F
- case 4:* ]" d' h4 [ @$ i$ e8 J
- Encoder_TIM = TIM_GetCounter(TIM4);
! l3 B; N# X! ^: p3 _$ { - TIM4->CNT = ENCODER_BASE_COUNT; * X% Z% J$ M* f9 r
- res = (int32_t)Encoder_TIM - ENCODER_BASE_COUNT;
) d8 V; J7 t& l4 x# k0 b. f - break;
. P! C! ~( \, T - default:+ q4 t$ _. x) O8 x3 d
- Encoder_TIM = 0;7 ^+ Y1 i3 `3 p
- res = 0;+ t) d. f: W0 j
- }3 f* p! E$ o' T" A3 h3 x
- if(res>2048.0f). F$ K. k+ ?8 l- t! h
- res-=4096.0f;) p) n* w. f) P* M9 R! w7 B
- return res*360.0f/4096.0f;# H) [" I v& F, B4 A) p* K
- }( a ? A7 f3 H
- </font>
复制代码 定时从编码器取数,注意,时间不一样,取回的数值也不一样,取决于实际速度以及编码器线数。这里50ms取一次:% l* D$ F" K8 f( {
- <font face="微软雅黑" size="3">
# O5 F5 H6 Q# U; s0 }+ {2 Y6 W - void TIM7_IRQHandler(void)//频率20HZ,用于编码器计数) \/ E6 M8 x5 T( @# D
- { + N3 l: {0 D/ G$ U- @0 a
- if(TIM_GetITStatus(TIM7,TIM_IT_Update)==SET)//溢出中断
$ v! f& u) V0 p, i; Z$ A8 ~3 h - {
: ?1 M0 U2 R" S8 R! O - speed=Read_Encoder_Speed(4);" w' I9 n( b- j& \+ v8 X) K! A
- }
8 H" q4 \; k4 }3 R9 a1 Y% J% d1 ?0 U - TIM_ClearITPendingBit(TIM7,TIM_IT_Update); //清除中断标志位. d7 B, _% \( x& w5 }
- }</font>
复制代码 2.5 PID控制
2 W r5 f- T% y+ k, A% ?/ k6 `) v, l1 {9 {2 ]% h
; o! D4 r3 @' t. T+ [- k4 P
PID库函数:0 t8 e) d- x$ |# P+ a f$ J* t0 q! W+ K
1 z1 f7 ` k' f
- <font face="微软雅黑" size="3">! c. G E; \* ^5 g. d. O
- #define N 2 //需要对多少变量进行pid调节
0 w& h2 `6 U0 l5 y' G% x0 Y - 4 |1 M( g; }( W! q. {
- const float KP[N]={1.3,1.0};//这里只用了比例调节: U8 z- j& N w, |* l- t
- const float KI[N]={0,0};5 _7 C; g. |% b u; P3 ?; j, Z
- const float KD[N]={0,0};
% [1 ]5 O% ]! J5 O
4 H8 \( D% j" |7 [- s
- u8 I' \& D' O: w- d/ U$ [- struct _pid{8 s; u2 d1 x/ ?. `0 S" k' X( I. ^
- float SetVol; //定义设定值6 t; T" }/ Z$ y2 C# @( N
- float ActVol; //定义实际值2 P" i F2 v! A" `9 A+ R
- float Err; //定义误差# B" p! ]. [; I* W2 N* Q6 n
- float Err_Next; //定义上一个误差
8 J- @/ ?, h4 Z% J' x0 r5 w - float Err_Last; //定义上上一个误差# H4 A: w5 ?1 B! x7 V; U
- float Kp,Ki,Kd; //定义比例、积分、微分系数 X- I. B- l* r1 g/ N
- float integral; //定义积分值$ ]/ [$ B- ~, \8 w- k, O
- float actuator; //定义控制器执行变量
6 R" ]0 i, l! i$ j! U - }pid[N];: n& f( ?0 e: w$ T6 K ]
# {4 P; }; a% s) X* O& }; n) A/ o- void PID_Init(void)
/ g! Y- n0 n; g1 u# e! q' o - {6 |% l1 j4 Z- W$ G: u- I4 }
- for(int i=0;i<N;i++)/ T2 K7 D3 u z
- {
) I+ i/ ?* ]8 F; u# u: u% }) v- c - pid[i].SetVol=0.0;
/ Q1 i. m. N( L7 F' Z( @1 t - pid[i].ActVol=0.0;
: {2 ~: U2 D" n- }* Q4 a9 c - pid[i].Err=0.0;+ A# O" U6 V% @' D' H
- pid[i].Err_Next=0.0;; b8 Z) D/ m9 R H4 T( ]
- pid[i].Err_Last=0.0;- z0 J/ S. {( d2 r4 I
- pid[i].integral=0.0;; T! p2 m2 F% O
- pid[i].actuator=0.0; ] |7 l5 f9 g, {
- pid[i].Kp=KP[i];
% L( v1 F) `, w9 d* u - pid[i].Ki=KI[i];
. R8 L' s! E: D" c1 M) e! T0 \ - pid[i].Kd=KD[i];
/ y0 j$ A4 n8 M5 `* J - }+ ~: N9 l X% ?% G! k
- }% \$ R3 H8 M7 s" C4 t, _
- 0 z2 t8 W0 u$ J4 j# p) u
- float PID_realize(float set_val,float get_val,int i) //位置型PID算法实现
3 f% Z! z1 a1 H! }' l% z - {
( B: _. R9 d4 E* I6 e% i - pid[i].SetVol=set_val;2 x# {) J0 D1 p' L$ K2 ]1 ~
- pid[i].ActVol=get_val; e* _6 @; t$ V
- pid[i].Err=pid[i].SetVol-pid[i].ActVol;
9 L6 S. q0 o3 z: }, j; _ - float IncVol; //定义增量
. i. F$ l8 m* v3 R" N - pid[i].integral+=pid[i].Err;
9 H! U2 k2 I3 j( w - // IncVol=pid[i].Kp*(pid[i].Err-pid[i].Err_Next)+pid[i].Ki*pid[i].Err+pid[i].Kd*(pid[i].Err-2*pid[i].Err_Next+pid[i].Err_Last);
: P8 r7 S$ A" v8 e - pid[i].actuator=pid[i].Kp* pid[i].Err+pid[i].Ki*pid[i].integral+pid[i].Kd*(pid[i].Err-pid[i].Err_Next);$ q9 z$ J1 O, z+ @7 I
- // pid[i].actuator=adc_val+IncVol;2 K/ L" [/ h. M
- pid[i].ActVol=pid[i].actuator;
8 e4 Z9 j' U8 p9 V - pid[i].Err_Last=pid[i].Err_Next;
- X, i2 }2 I2 P- w6 l - pid[i].Err_Next=pid[i].Err;
- {9 B- O3 M- b0 R7 z# ] L - . G+ C; \) Q T$ M. W
- return pid[i].actuator;
+ ~+ o8 t5 N" X) r8 T, j - }</font>
复制代码 主函数中的PID调节:6 M/ Y1 q! ]- p0 X7 l
* m1 v4 R% U& p/ N- <font face="微软雅黑" size="3">, m5 h) j3 ?# s& |$ C& t6 n
- z_get=data[2];: {. ?; w; b, u9 ~ g. f% j: H5 M
- x_get=data[0];, S) i$ ]! T# h
- if(z_get-z_set>0.5||z_get-z_set<-0.5)//电机PID
+ Z" S- h. I% _. L' j - {; L8 c8 P1 N+ I+ V# q! I
- LED1=0; //调节时灯亮
. {8 G) @4 T A2 H6 C! |5 G4 }; p - PID_val_motor=PID_realize(z_set,z_get,0);/ C. u" Z7 j$ x/ |- h2 H2 I6 `& U
- PID_val_motor=PID_val_motor/10.0;
5 h0 _2 a5 Z6 j. h) S - if(PID_val_motor<=0)
" d& z: N; ~4 Y0 }6 @6 z( @ - motor_flag=0;//motor_flag控制电机正反转,PID_val_motor用于改变占空比,范围0~1, M }! _0 K1 B7 i2 C( F; y/ ~. w- A
- if(PID_val_motor>0)
6 ?. B% e& w" R" x9 Z8 R7 T4 F - motor_flag=1;, @, R6 U* m1 F9 c2 d; c1 [! J5 \8 C
- PID_val_motor=abs_float(PID_val_motor);
* n1 t/ o! n9 j+ S5 @4 A - if(PID_val_motor>2)PID_val_motor=0;//标志太远,让车停止
- T5 ? k7 s, G- {5 ]$ w5 z: h - if(PID_val_motor>1&&PID_val_motor<=2)PID_val_motor=1;; P/ M Y. k* L
- if(PID_val_motor<0.2)PID_val_motor=0;3 j* l7 E# n q6 ^1 T
- }
6 G+ M9 h9 N1 X* \, b+ j' e - if(x_get-x_set>0.1||x_get-x_set<-0.1)//舵机PID) J" u) Q, ?/ w' M) P3 |6 d
- {7 H" n5 o% F+ ]6 c# t3 S- ~
- LED1=0;
2 |4 K4 f0 [7 y0 D4 A( j/ d - PID_val_servo=PID_realize(x_set,x_get,1);, y+ n: d$ O" ?- f5 C& b) C9 \
- servo_angle=((140-35)/6)*PID_val_servo+35;//线性映射,把PID的值转化为角度35~140的舵机转角
; K9 R! f8 u4 i2 o9 `: F - if(servo_angle<35)servo_angle=35;7 X! \' r1 H( J
- if(servo_angle>140)servo_angle=140;9 p; ?% l" Q! M; \ w
- }
/ E( s0 L" O# ^ - LED1=1;</font>
复制代码 定时器TIM2中断里改变占空比:
9 A' j" s7 }) Y, ]) Y1 _- <font face="微软雅黑" size="3"> j- d" G t2 P& K
- void TIM2_IRQHandler(void)
" d- I. g! A, A8 @ - {
$ A6 y: N+ ]; I# P& J - if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)//溢出中断
4 o1 a* i, Z& o8 C( A - {- B% a6 ~3 A: N5 I. P% k+ G" k
- if(motor_flag==1)//反转" o3 c0 z+ ~3 h9 \
- {2 X# _; |6 S1 t- ?
- TIM_SetCompare1(TIM8,motor_duty*PID_val_motor*400.0);//和定时器的自动重装载值进行比较,来设置占空比,引脚:PC6
% U# E) K( i4 A6 p: Y8 r - TIM_SetCompare2(TIM8,0);
3 C3 b+ E3 `* e$ H& e" X - }
7 t g5 O+ p, k/ p+ X" I+ g - if(motor_flag==0)//正转
3 Y( Q! p% K4 |! X9 k - {
& _% {; ?5 {6 H- Q - TIM_SetCompare1(TIM8,0);3 R- ^$ N& ^9 P
- TIM_SetCompare2(TIM8,motor_duty*PID_val_motor*400.0);//和定时器的自动重装载值进行比较,来设置占空比,引脚:PC7' j' L/ k9 K. B5 ]
- }/ }- I5 b" C" P
- TIM_SetCompare1(TIM3,200-(servo_angle/45.0+1)*5);//设置舵机角度,根据舵机手册得到占空比与转角的关系,引脚:PA66 v; a5 t8 l( }7 ]- U( b; @
- }
. R0 Y, {3 a" z - TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除中断标志位9 m9 g8 E/ z x' W3 R, [
- }! A( j5 G- e7 H! E3 Y# ~
- </font>
复制代码 ( C; g5 V- o/ w
" q3 {9 v) @' ]' c: H' @( M! P1 F3 P1 c
|