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