前言
8 X7 h0 W8 r- `' o9 }/ w串口通讯对于任何开发板都是非常重要的,也是必学知识之一,通过串口通信可以实现上位机与下位机之间、开发板之间的通讯,可以让我们实时掌握机器人的各个关机的运动状态和传感器的信息。
7 f9 T# g g: X& K# B$ P& [ D$ E' |. s- P5 @( V7 t
现在的通信协议有很多,比如:UART、USART、CAN、SPI等等,它们功能不同,适用于不同的场合,USART作为单片机之间、下位机与上位机之间最常用的通讯方式之一,它对于数据的收发十分方便,应用日益广泛。
$ e( A- I. s2 u- u! k3 V0 ^2 L, p( U9 Y# i
一、USART通信原理, N! ^2 O! x4 C' s" d" u
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)中文名叫:全双工通用同步/异步串行收发模块。
) \2 h0 T$ n9 G1 D
. M% d+ a+ G5 s- Z5 Z0 u* L, w# P先对这个名字的各个部分进行解释:2 V0 T( o- t( \/ q7 V
% s! |& {9 H5 h% T \. ^
- Y$ G# s7 P" a q
o9 M4 G8 x. t# l 在平时的工程项目中,我们常用的是全双工串行异步通信方式,虽然串行通信数据是一位一位的发送,但随着近几年科技的高速发展,串行通讯的速度已经逐渐赶超并行通讯了,而且串行通讯方式适用于远距离通讯,比较常用。0 d. L7 N9 t. f' T1 y
8 n+ f- d; @: J. y2 v2 o e5 nUSART通讯的数据格式大致是这样:2 n6 [$ ?4 ?- C0 f" y
5 D( R7 U& S0 q- l5 t) p, a
起始位(0)+串行数据帧(从低位到高位传输)+停止位(1) V' d$ J4 m# U
1 V _+ k, b, Z, j$ c
串行数据帧可以人为设置为8位或者9位,9位是8位数据加上1位校验位(奇偶校验)。
( e; h$ |4 ^& \5 o v8 _7 Y |4 c# T7 @' n& p7 x2 q6 q9 h
另外一个比较重要的概念是波特率,在任何通讯开始之前,通讯双方都要约定好波特率,波特率是每秒发送有效数据的位数(bit/s),双方如果没有约定好一致的波特率,在传输过程中则会出现乱码的情况。0 n! x( a5 ~$ c) V: Q9 l
4 J3 ^+ B9 T$ V' h4 B
在STM32中,有专门的数据寄存器和特定的引脚负责USART通讯,并配合有相应的标志位,用于帮我们判断数据是否发送/接收完毕,并且也有相关的库函数帮助我们对串口进行配置。' h+ R* v* ^7 i' t
! g7 w/ f7 M: z. E! a& E" @4 F相关寄存器有:发送数据寄存器(TDR)、发送移位寄存器、接收数据寄存器(RDR)、接受移位寄存器。7 O X( L$ ^. d6 n' }, I
- q) m" {1 P& I$ p7 ]5 I) B) o相关标志位有:
2 J5 k* J; }( f. _ Y
( }9 x7 e5 b9 s5 e0 bTEX标志位:1:数据寄存器无数据;0:数据寄存器有数据
6 f0 @0 |/ J$ Q+ P$ i, t. rTX标志位:1:发送完成;0:发送未完成
/ S' |/ U/ H2 z1 jRXNE标志位:1:数据接收完成,可以读出;0:数据未收到
4 F& l1 E9 B. C1 O* h- ~8 {. l6 g具体知识在中文参考手册P517,大家可以详细查看4 ^8 Z6 q! l: z9 `4 r" f8 S
) K$ x/ c0 V$ ]! [8 u3 ^; g$ q
二、STM32与PC通讯1 N4 h6 o+ {8 i# y, Y0 N$ M
STM32与PC通讯需要进行一些配置,这里实现由PC端向STM32发送一个数据,STM32接收到后再发回到PC端,该实验需要用到串口调试助手。& ~3 ~- ?! E7 Q% u9 x! A
' w9 K9 R( F! B, W2 ~; g W
STM32可以作为串口通讯的引脚大家可以通过数据手册进行查看,比如PA9(TX)和PA10(RX)0 R- Z/ ` R1 A
" Q b. T: q. {" ]# P
5 ^: r( D0 T8 c% s G* s+ ]' r
2 Q# x" @; c0 ^3 K; M串口通讯一般都配合中断进行使用,下面讲解串口配置过程:
1 t* m1 O+ l5 L5 [ F$ S: u8 I% F0 d' ?+ q2 u
USART配置步骤% ?9 D" p$ k' R, G! I" F
+ @* i9 e, Z, `$ N U2 Z2 W( i0 q
步骤 函数 1.串口时钟、GPIO时钟使能 RCC_APB2PeriphClockCmd 2.GPIO端口模式配置 GPIO_Init 3.串口参数初始化 USART_Init 4.开启终端并初始化NVIC USART_ITConfig、NVIC_Init 5.使能串口 USART_Cmd 6.编写中断服务函数 USART1_IRQHandler
0 j: b# N8 f6 s) k8 R* W2 i; c3 O. |7 J' y. m- h8 E: h1 Q9 l
my_usart.c 代码如下:
% z" D8 X" @: P x+ P, `6 i. b, C3 v; x( J9 S+ e
- void My_Usart1_Init(int bound)1 D+ Q* g. s. f$ ~" w0 E
- {' s3 l: ^( j( i. V1 T1 b. s
- /*1.串口时钟、GPIOA使能*/7 }; S1 t4 c9 Y9 g% ~3 ^" ^6 H) `
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA,ENABLE);
. d! s5 ]- F0 }7 ]0 Y6 M - # N) a3 ]- t, P. m! h
- 4 \: C6 _! x' |2 y" a- F1 e( r
- /*2.GPIO端口模式设置*/
" p, k8 o( v; ?7 i! i% i5 o - GPIO_InitTypeDef GPIO_InitStruct;
. e2 _# b( G; a. }$ ^# D - /*TX*/
! b( |7 ^7 ^- j0 D' s: B8 _* T - GPIO_InitStruct.GPIO_Pin = Usart1_TX;
! U3 v6 S0 g# Q! |, W" D - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;: [; ]) x( Y+ c; X$ b1 V& G
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
; j v4 @" _0 ]8 x$ c; } - GPIO_Init(GPIOA, &GPIO_InitStruct);
* J' L- u$ ~8 Z1 ?/ M" I; w - /*RX*/- [, J1 |& j4 t& M4 o1 h
- GPIO_InitStruct.GPIO_Pin = Usart1_RX;+ S% c5 u# @$ f- z d
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;5 |- u4 I7 f* v( ?2 [: L
- //GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;5 u5 p8 k J: d ?
- GPIO_Init(GPIOA, &GPIO_InitStruct);
# |6 C5 R* t1 M% E4 R4 p - / W6 Y1 C2 D# V# z% v1 q' v
-
4 r# S+ {) q7 E' Z) @8 s0 i" b - /*3.串口参数初始化*/
- f* F- q- _$ ]+ O U' S$ k - USART_InitTypeDef USART_InitStruct;
6 x+ f1 K% T* M" C! [- i - USART_InitStruct.USART_BaudRate = bound;//波特率& N4 j& Z" s* e% l
- USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制4 t& h5 c* [4 ]" }1 }" \
- USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//USART模式- R! T6 N4 K5 [6 ]9 T' b
- USART_InitStruct.USART_Parity = USART_Parity_No;//校验位# a8 J$ z# [5 b! ]
- USART_InitStruct.USART_StopBits = USART_StopBits_1;//终止位' j) D& ~) Q3 Y. B- p# D# E# C
- USART_InitStruct.USART_WordLength = USART_WordLength_8b;//数据长度,如果有奇偶校验就设置为9位
4 p% x) m) ]7 u2 v0 j - USART_Init(USART1, &USART_InitStruct);8 u, a+ w6 @- }5 A! w: d
- ' Z4 B( {- `0 r- }9 P
- /*4.开启中断并初始化NVIC*/8 ]. D& e( i7 z7 q' e
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开中断,开启接受中断后,接收到数据则会进入中断服务函数" Y1 a/ S. z# F+ H/ t
- ' C/ w' m& h2 l2 Q" b( @. {7 ?7 l
- NVIC_InitTypeDef NVIC_InitStruct;
; z, J9 T* `0 ^5 w6 F- g - NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;) }+ p% h& C, ?& _
- NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
; h6 ^% E; A: Y3 ~1 G - NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
5 A7 e6 b5 d9 \& }' i - NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;( Y h& x6 _/ {( M2 l6 v5 i
- NVIC_Init(&NVIC_InitStruct);//初始化NVIC
* r% d6 ?3 t O* N. B p4 q6 y - - M/ I1 f" @( Y3 s$ H A" l F
- /*5.使能串口*/
K7 z! Z: g# g. X - USART_Cmd(USART1, ENABLE);) I8 y% G6 I+ E f
-
/ ?% Z# O& ?7 u4 [0 ^ - /*6.编写中断服务函数*/
{! r# {2 V. p" x) {0 L - 8 ]* R5 f: t* z9 j" a+ X6 c9 N
- }
复制代码
" j% ]1 }$ r+ u# b+ D8 q+ w9 b
% ]$ ]# c P6 X: e+ _$ E( q3 r% \7 Q* o进行如上配置后,当STM32接收到PC发送的信息就会进入中断服务函数,中断服务函数接收到数据后再进行发送。
6 ]2 \- ^; M+ m7 P
- p0 k, h' w' X% E中断服务函数代码:
0 ]. P0 _# o( Y* }4 A) ?( Z: x8 Q, L! q4 k+ z" l! ], ?" q5 p0 ?
- void USART1_IRQHandler(void)
$ S# o" C' i! C+ r1 u - {
5 d% [9 i9 [" s% C- u1 e - u16 RX_From_PC;
2 c6 N. n8 m+ j+ a - if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)( R/ y" J( ^: i* T5 }8 n# z
- {
" r* ?( f# j. W2 ]8 e( t4 E - USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清空中断标志位
. Q' ?: V4 X: r! J - RX_From_PC = USART_ReceiveByte(USART1);//接收PC端发来的消息
+ z. M- {6 T: G - USART_SendByte(USART1,RX_From_PC);//将数据发回到PC端
% Z, T) `! Q. _& I# M$ d& U. X - 9 S, @& k7 M' }
- //usartReceiveOneData(&TargetVelocity,&TargetVelocity,&RX_Cmd_Form_Ros);//接收ROS发来的消息; ]! G! @9 V* v5 K+ r# X' A
- }
. y* h! m( p% a - }
复制代码 3 r& ^- t* m' ^) J6 m9 f8 z
在这里我们没有使用官方固件库中提供的收发函数 USART_ReceiveData 和 USART_SendData,而是使用了我自己对他们进行重写的函数,在其中加入了相关标志位的判断,这样可以保证收发过程中不会发生数据覆盖。$ \" ]9 m |- x2 D
* b! }# f, B5 A* G `2 Y/ Y( [
重写后的函数:
) Q2 i+ o5 y5 k
3 J1 N2 C) Z* W3 P- void USART_SendByte(USART_TypeDef* USARTx, uint16_t Data)/ l& V- m" O2 g0 w/ o' c
- {, ^) \; ^' B) H$ ~1 |2 g$ \8 x8 s
- /* Check the parameters */7 B$ M7 v* A3 V# h4 ?
- assert_param(IS_USART_ALL_PERIPH(USARTx));9 ~6 V3 ~/ b9 L& a5 M/ l
- assert_param(IS_USART_DATA(Data)); 0 r4 ^ v* a8 W: g
- b9 E3 P9 w& f" t! G2 u
- /* Transmit Data */
. E% b3 ]+ P9 _: g - USARTx->DR = (Data & (uint16_t)0x01FF);3 E! k/ S' [, H0 [! U% J, g7 }
- while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);7 c% P, W9 X- n5 T$ g
- }" W/ V1 e: [! p$ t" T$ t: Z5 Q: B
- 5 s% ~/ j( ^& n( Y0 v0 y7 H
- void USART_SendString(USART_TypeDef* USARTx,char* str)! k/ \5 k) N( d6 C; O
- {* _; M/ }0 ]& j1 i# y/ Z
- while((*str) != '\0')
9 z1 U7 ~0 z: l+ ]7 v" x5 W - {
9 z: H6 Z3 j3 Y( g5 u - USART_SendByte(USARTx, *str);
5 }, K: w' s: E# m. P - str++;
( }# O/ q: j1 f- V+ @! K - }- h3 \, j3 D5 b, D' c$ Z
- while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
+ z, Y; C9 y4 n' t% [ - }
x7 }* j( r0 d* ^4 V! m4 l
' ]$ D. {' E, e: o. Q
& U& L- R$ y+ |7 R) L$ i- * K/ Z" Y p3 D+ A7 y. V
- uint16_t USART_ReceiveByte(USART_TypeDef* USARTx)
# ]& Q4 I s" |* ?7 S) v9 E5 g) e - {% H% @1 Q4 g5 m h. G9 e; c
- /* Check the parameters */
/ n( E! {+ V$ ~' h; ]8 [% c+ D q - assert_param(IS_USART_ALL_PERIPH(USARTx));
% k0 v! j0 j, ?/ M- l0 K9 s - " F- R" D6 m T+ x- A# f
- while(USART_GetFlagStatus(USARTx,USART_FLAG_RXNE)==RESET);
6 n I6 F: Y1 L& Y* c0 i' a
0 e7 I% S$ u" _% U0 y- /* Receive Data */
: }7 T* t, q) z - return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
8 \1 A. o' q8 p/ s+ y; }$ o) C - }
+ Q' A1 m" H2 D& q
复制代码
6 r% S- V* K/ x; {在主函数中进行相应的初始化,我们就可以通过串口助手与STM32进行通讯了。$ F3 r: h7 Y S
4 L( k$ p3 Y& ~
; u0 t. C4 j+ p% A, X1 w- q' c7 A2 o! h! \2 H, ?
另外,Keil5是没有终端的,但我们可以通过一些设置也使用printf函数,让数据收发更加方便,我们需要对fputs和fgets这两个函数进行重定向(其实只重定向fputs即可),需要在程序中加入如下代码:* T! J9 o# |, I* v# o+ W
: W/ k8 F0 @9 T: _- /*重定向这两个函数*/: B% ~7 E% z/ k& t0 e, ?! N
- int fputc(int ch,FILE *f)
% a& T' m0 W" e - {
. o' n" ?! b. Z: W1 X$ l* S; } - while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
# K; A- z1 H) g4 L - USART_SendData(USART1,(uint8_t) ch);
9 F, ^4 I0 u2 h; ^8 b - return ch;7 H/ N& o8 B8 H% F+ G
- }
6 t4 L# o: k4 \2 b* I* K, w# m, Z! i
7 L* \1 y( h/ ? H8 K+ [- int fgetc(FILE *f): o& k* H0 b! |! }& j j+ \
- {
" |$ N: o" G, t( H3 ~ - while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);
2 v9 w B4 ]! W4 N2 M - return (int)USART_ReceiveData(USART1);7 J2 B( I) G r, c
- }
复制代码 6 {# u2 Q {1 E; P- t, [- I
printf函数使用的是半主机工作模式,需要使用微控制器,需要进行如下配置:
$ q Z- a- P: ?( }5 ]' b- O. M$ x: ^8 u- `
, G. F( a) b5 j+ J# j
3 N5 {; ^: j$ M- c" S* j7 B6 Z 这玩意功能少一些,如果不想用,则需要在程序中再加入以下代码,这是printf函数的底层程序:' T* `1 [; U* l4 }) P
/ m6 P! j) _. O# y4 z" f; f
- #pragma import(__use_no_semihosting)
# d3 V% ^& E: e
6 p9 m! F) d& d. O- struct __FILE
! r& b7 D( v: R) J2 ~ - {- ?. w/ @/ \: c
- int handle;; B1 C4 P, `$ ]; W/ I7 v
- };- `. B2 Y- k# U; b/ H
+ _. Z2 c8 m5 M6 b- FILE __stdout;
" [+ S5 p/ c2 O$ j: p - _sys_exit(int x)
! p ?) Z; ]$ Q! H. d. X- g1 ~ - {
$ H' V6 z; {6 p - x = x;
, W7 r- W+ P3 y* K% ~1 v2 ] - }
复制代码
4 a* \9 B t% f编译后,我们就可以与PC端进行愉快的通讯了。
" f" t' E% t/ m U8 G
2 [" K4 i- i9 i! F5 n三、STM32与HC-06通讯
_' V. w; y; ?STM32与HC-06的通讯与PC端的通讯类似,知识接线方式的不同而已,只需要进行如下正确的接线,即可完成顺利地通讯。- B+ y. S- D [! |& e _4 w2 O
; Z3 P0 S( C- ]: r, A- r' S
- l7 X |" p, F* ?$ H+ A
4 C7 r; C6 f1 J4 g9 y; v
这里要注意的是,给HC-06供电时,需要提供3.6V-6V的电压;另外,要注意看一下STM32的引脚输出电压是多少,HC-06的输入输出电压都是3.3V,比如像Arduino的输出电压为5V,这时单片机的TX在接HC-06的RX时需要分压!
o+ l! f. t4 \6 X/ R6 ]. \3 `- ?( U P3 i$ y& }* |
四、STM32与ROS通讯
8 s: `2 X6 Q$ @% f9 |/ ~% V/ tSTM32与ROS通讯时,需要定义一个协议,以保证数据传输的可靠性,这个协议是STM32与ROS通讯时最广泛使用的协议,协议格式如下:3 Y$ F) }& i, r8 K1 f, _
# l4 f6 I- q, U g数据头55aa + 控制命令 + 数据字节数size + 数据 + 校验crc8 + 数据尾0d0a
! X" J4 p) G9 C) |( g6 |- I
/ B! ]9 F0 G9 O% w% L, S# B$ J% \在通讯时,我们只要对接收到的信息进行解码,即可获得有效数据信息,比如:设定的速度值、航向角等,保证了数据收发的可靠性。
, `' H9 v! ]! I$ C& k2 w" C6 ^5 g9 @( a. R! T. \; k# J
先定义相关的变量:" Z% @9 M8 L( D% Y' n7 y y0 m
4 c L" [: c, M% R/ q
- //数据接收暂存区
' H/ ~0 x. e, s, s - unsigned char receiveBuff[Message_Length-4] = {0};
2 [8 c: ]' S$ @, N( @1 O - //通信协议常量
, H5 _! p8 Z* m - const unsigned char header[2] = {0x55, 0xaa};9 Y- i9 S6 e9 j/ b: m' I
- const unsigned char ender[2] = {0x0d, 0x0a};6 s4 J3 F& A, i) V+ u/ O
* G; g: g# {* G& u* e; H$ H1 F- //发送数据(左轮速、右轮速、角度)共用体(-32767 - +32768)% D3 m/ Q' L% W: o/ a# J
- union sendData: R( c4 `0 {- V! M1 n
- {+ q5 u" z6 d) L( ^$ N
- float d;
9 K# [8 t6 y, z - char data[4];* ^4 }1 W- d3 \0 C" {: ?
- }leftVelNow,rightVelNow,angleNow;
R+ _4 T+ o/ \# S% K4 m - ) o' R* k( y0 j5 x! y0 S: A9 L
- //左右轮速控制速度共用体5 U1 s( U( t* `
- union receiveData
% c1 ~1 G C( V, T/ i8 m9 h1 G7 y - {. D& i7 O. ^) O% }8 l
- float d;
: y5 Y: ?. Y, a2 K5 T7 h - char data[4]; X! S0 H% l3 b$ H
- }leftVelSet,rightVelSet;
复制代码
; i8 n6 f Z' z4 @# e9 @( {5 ~
1 C, \9 U; J G$ l# g: p接收信息函数:* f4 h8 Y* d- z( z6 N, O
- . _! F$ `1 P' I6 p
- int usartReceiveOneData(float *p_leftSpeedSet,float *p_rightSpeedSet,unsigned char *p_crtlFlag)7 z- X! \6 g$ U: _- Z6 ^4 Z* g
- {
2 |/ e7 {2 l1 N+ N3 f- a - unsigned char USART_Receiver = 0; //接收数据
) L- X# f. t$ I& v - static unsigned char checkSum = 0;
) H# Q8 N$ C: D2 j$ A! ? - static unsigned char USARTBufferIndex = 0;" T+ U" V. R: G+ m V
- static short j=0,k=0;
" `8 t% R$ A+ H" Y - static unsigned char USARTReceiverFront = 0;
) i; q* G4 F8 D, l1 ^1 _: Z - static unsigned char Start_Flag = START; //一帧数据传送开始标志位1 j D$ {- ]4 p% T/ q. ~, Q
- static short dataLength = 0;5 D; A" u/ I+ l2 A! E3 p
: P: ]( A, ^) M& m- USART_Receiver = USART_ReceiveByte(USART1);
& x! N9 i; s& w5 u$ j) N3 Z! P& O8 K - //接收消息头, u% n5 A* H) s8 M J7 V4 i
- if(Start_Flag == START)
4 V6 @; o1 p# i) S/ c" l! ] - {
* Y, H" c, v& p+ ^9 Y - if(USART_Receiver == 0xaa) //buf[1]7 z, [' N% }$ m" |2 ?5 K$ [% ]' i% k
- {
& \. U! @" `7 h3 k! _8 H0 L - if(USARTReceiverFront == 0x55) //数据头两位 //buf[0]
' _+ ^: J* {, P3 I& z - {4 w0 \6 P5 _4 C/ r' ]9 T/ e" b
- Start_Flag = !START; //收到数据头,开始接收数据
/ S: @* n$ A4 D V - //printf("header ok\n");3 }. m6 L5 _, r0 F, i. K
- receiveBuff[0]=header[0]; //buf[0], L0 D3 o( f; k0 M3 L, \
- receiveBuff[1]=header[1]; //buf[1]1 |" p4 X' o- a* U: G6 e
- USARTBufferIndex = 0; //缓冲区初始化: J. g9 F; J0 |8 W
- checkSum = 0x00; //校验和初始化+ H/ Q; h! t. u5 f8 K! a
- }
0 X) Z& G- Z1 r* r - }
* q2 @0 a# {+ f$ W- b - else
I0 k, h+ k6 I4 b - {
& X* s1 @9 F Z1 s6 o& j* A - USARTReceiverFront = USART_Receiver;
$ T7 y. ?9 n- d$ b, x - }! |' F& y! P# q/ ?' d! A& c
- }
' v' Y# S3 d8 P6 S% u - else( f3 L- \) N/ W0 U5 X
- {
: R& T$ l( t2 x - switch(USARTBufferIndex)
! j3 k3 r/ [2 W/ a& \: Z - {
0 B$ W* J% j5 S% `, z' n - case 0://接收控制指令4 H# q" t" o; G2 f, J1 \$ f# E; g
- receiveBuff[2] = USART_Receiver;
, ]7 d- b. S) u9 G - *p_crtlFlag = receiveBuff[2]; //buf[2]
. z& W8 X4 @0 S1 C x - USARTBufferIndex++;: z5 _0 k2 \5 N8 p# }
- case 1://接收左右轮速度数据的长度
6 K. l* O( R- E$ O! a" l4 A - receiveBuff[3] = USART_Receiver;
6 t0 f0 i# T5 ]. `( _2 {6 e - dataLength = receiveBuff[3]; //buf[3]& d) ?" s- S, l4 m B* @2 ^
- USARTBufferIndex++;
) f3 q; \6 s" {0 n5 O( L- e4 P) ] - break; 1 ~5 x* h4 ^0 V' O
- case 2://接收所有数据,并赋值处理
1 k8 s& n Y* ?+ o" }4 ~4 F4 p* B - receiveBuff[j + 4] = USART_Receiver; //buf[4] - buf[11]
( y0 u2 w, G' Q, \9 w - j++;
4 z- R) Z. \1 _* @1 F& ], O - if(j >= dataLength) # x+ z9 @; l$ W! |
- {; F' ]! B# o) ^
- j = 0;; ]3 [" H9 c7 }& q3 y
- USARTBufferIndex++;8 X: d. \2 M( T/ b7 E2 E1 t4 }
- }
T, {$ y2 X+ k4 ]/ j6 h- o - break;, c3 ?' a2 d) w5 ^3 _
- case 3://接收校验值信息
5 H' h q4 m; a# V6 T7 {- C4 E - receiveBuff[4 + dataLength] = USART_Receiver;
0 P# a, [$ L' f4 Q - checkSum = getCrc8(receiveBuff, 4 + dataLength);# j2 v& F0 [" ?6 \
- // 检查信息校验值# w) g e. u: ?- n) Q% I& e
- if (checkSum != receiveBuff[4 + dataLength]) //buf[12]; \6 _6 e) W' N1 c, t& C; R
- {8 L) `; Q$ [* s6 ]
- printf("Received data check sum error!");
# r2 A' C9 h& t% ^- w - return 0;
3 S. D x3 o: Y, a$ U( @ - }
\! J% s. ^& l. o6 }; H( ~ - USARTBufferIndex++;; Q( I: n( n9 ?
- break;
' F, q! P, ]! @ - 1 ~# i |: O3 e9 s, `8 A
- case 4://接收信息尾% q8 V! d; |, K6 t! |0 k7 z/ `) x: h/ w
- if(k==0): ~; O& s. [ j% ?( i9 \. D) W. G
- {
* }/ e x: C& m5 A' u) _ - //数据0d buf[13] 无需判断! \- L. V& E) U& {0 `3 E/ L
- k++;
$ E- P+ c9 n# s+ D9 c N* h - }
6 P, w1 t* s* m6 h" q' I - else if (k==1)
* R1 N/ d( p3 r - {
: C6 N# p- X# X6 y - //数据0a buf[14] 无需判断
9 Q7 }4 ?- j# w' l4 x" z - 1 ^1 j: g* ?8 L
- //进行速度赋值操作
' k4 C6 |9 t9 R7 R4 D. } - for(k = 0; k < 4; k++)
/ D. M3 A' p3 h' K, k; l - {0 p- }3 h5 X/ O+ E& N- U
- leftVelSet.data[k] = receiveBuff[k + 4]; //buf[4] - buf[7]
7 b: O( e& d- \/ F1 X( [0 e - rightVelSet.data[k] = receiveBuff[k + 8]; //buf[8] - buf[11]. u) f/ A5 V7 x1 N9 t
- } 1 k [* P2 @- j r9 h
-
: H% \; O4 D8 A, } - //速度赋值操作0 P% Q, x) } ^
- sscanf(leftVelSet.data,"%f",&(*p_leftSpeedSet));$ I& | i1 D5 m
- sscanf(leftVelSet.data,"%f",&(*p_rightSpeedSet));9 Y2 |: ]: E9 k7 \6 }& ?
-
: G% _2 v" z" X - 2 b% G6 q* e" M) x
- //-----------------------------------------------------------------
; |- S2 o5 y6 ^7 L& q5 X - //完成一个数据包的接收,相关变量清零,等待下一字节数据# l; H+ M; v! A& F: G8 s
- USARTBufferIndex = 0;
) @# }$ o# E* R7 f5 j - USARTReceiverFront = 0;$ [# K# ]2 \9 c) A. g8 ^
- Start_Flag = START;
- n- J! b0 {7 R' H - checkSum = 0;
% o" j" P7 V9 T8 S* ?: l* @% p; R+ H! \ - dataLength = 0;
0 |9 L( E& _+ E/ d - j = 0;
l$ F9 r3 r: s, X - k = 0;6 \% A8 y' |9 z) I" j2 S. [8 b1 w
- //----------------------------------------------------------------- * Y% R0 \( [ R+ l4 ~; b+ O
- }- Z- c/ W9 q. R! X$ y4 l
- break;
: A; r9 x1 c* `* k7 l3 e5 Q - default:break;! s9 C0 X- O* B9 _# J5 w" [
- } & v6 j7 A6 T% B
- }/ P# d I2 A& n4 |6 m! }6 O
- return 0;
* e$ E8 M2 S! N - }
0 _" Y6 s! e. j! k4 ^0 O
复制代码
1 h1 d7 T& M& w1 V发送信息函数:* y6 Q% K2 |. \$ t" b& s0 A
' p6 B& v: s3 i g% N# n
- void usartSendData(float leftVel, float rightVel,float angle,unsigned char ctrlFlag)2 s# @/ d% J, c, \$ v( f
- {: @: X( b, I! | a3 D' d2 c% m9 D
- // 协议数据缓存数组" ^+ t1 x/ n$ X" ^, }: l
- unsigned char buf[Message_Length] = {0};7 I }; |) R& [' r: Y
- int i, length = 0;
$ i8 I& L+ t# l! Y - 4 K/ ^ {; j0 Z: b2 `" p" P6 e
- // 计算左右轮期望速度: v/ s( [" B2 {9 @2 {
- leftVelNow.d = leftVel;5 h6 U% K9 @5 D2 O5 U* Q) ^
- rightVelNow.d = rightVel;. {: E% @; s" M/ d+ w7 N; V0 f
- angleNow.d = angle;
$ C# j- a) P6 e% ?2 P -
8 G \3 c) D T5 ^ - // 设置消息头2 B" S, h8 \! ?9 ?) L6 ^3 y* G0 g5 [
- for(i = 0; i < 2; i++)
" e- J" M/ A" |6 ] - buf<i> = header<i>; // buf[0] buf[1]
! o4 f* E% W( x0 _7 P - : V7 x% [2 J9 ~8 z
- //设置命令位' ^) \ A* n9 J. D# |0 J7 O
- buf[2] = ctrlFlag; // buf[2]
, y K) `5 ~) f/ ~5 d -
; y* n K: c6 Z7 y x9 [* L$ l- s* A - //设置数据长度
& g/ Q! m) M, B2 K - length = 3 * sizeof(float);
6 E" T' J+ I% _- L$ J - buf[3] = length; // buf[3]. \, X9 f" c v& j8 N9 }
- $ E: K: F: I1 N/ b. F6 ^
- // 设置机器人左右轮速度、角度
' |9 V" z/ M1 Q - for(i = 0; i < 4; i++), u) H( S1 `* o( w
- {! o6 j( ?. |7 O; u7 U- i2 B \) Y
- buf[i + 4] = leftVelNow.data<i>; // buf[4] buf[5] buf[6] buf[7]5 ]1 S; W8 w: u& z8 b
- buf[i + 8] = rightVelNow.data<i>; // buf[8] buf[9] buf[10] buf[11]4 p4 T' ~4 Y( ~. R" e
- buf[i + 12] = angleNow.data<i>; // buf[12] buf[13] buf[14] buf[15]
& S0 f& J$ C( q1 U - }1 v! a4 A1 A4 J) M& P" ]
-
2 _; q" G: {/ U) O" W9 s& i$ q - // 设置校验值、消息尾3 p+ c! m7 ~* d" q# V4 x
- buf[Message_Length - 3] = getCrc8(buf, 4 + length); // buf[16]
- z: m+ e: L( }4 g" k& M - buf[Message_Length - 2] = ender[0]; // buf[17]9 k5 j2 T, I0 p0 \% y
- buf[Message_Length - 1] = ender[1]; // buf[18]
& h, O/ b4 Z4 `" {5 I - 7 j' y/ y, ~. ?8 S
- //发送字符串数据
* P9 |$ Y$ R0 y0 A; C - USART_Send_String(buf,sizeof(buf));
% Q8 K2 q2 D1 A1 K$ {. S7 ]" H. c - }; j& J; J+ j4 F# u0 V6 k
, t, F* z5 N4 h+ R8 p; a, x6 @* J- 循环冗余校验:
# {2 K- W! B- O) L9 _" S7 j, N - 1 _+ N$ Q+ `# x( c0 s
- unsigned char getCrc8(unsigned char *ptr, unsigned short len)2 [8 @, M5 P! O' Z l, ]
- {; O+ b! k8 S4 b; _% N) O
- unsigned char crc;' X0 } k5 Q$ t- S1 L) D
- unsigned char i;
* a- a6 @) r P6 O3 G - crc = 0;
. g' b$ M" \% J* h - while(len--), S& s& p, n4 a
- {
$ V' u/ w+ d% m3 g6 V k - crc ^= *ptr++;6 }2 I4 W0 u% @: ~
- for(i = 0; i < 8; i++)
& g& o0 s4 Z0 ^% F8 L: _' w - {
" z- w7 j) q7 b) K7 Y0 h' z - if(crc&0x01)
, u0 ?( m' y+ n3 l8 { - crc=(crc>>1)^0x8C;% K- { S; {- Y7 z( l! a) D* S
- else $ D; s0 }2 ?, Y8 j5 G- k4 x# q
- crc >>= 1;" E* u! s9 e4 ]- _' _+ J l4 E1 L. R
- }
8 s, l( F6 [9 l- O - }. b; q4 J V; q# d- b: y
- return crc;' ~* U% O$ U! `
- }</i></i></i></i></i>
复制代码
: R3 \0 ^/ m; H然后我们只要在中断服务函数中调用收发信息函数,即可实现与ROS的通讯。
( W: R' f7 R3 E( e. F$ _# u+ u- Q0 ^ a: M, A" @/ d
总结
7 R3 f# c$ S" S, E) v: \( W8 s( `串口通讯的ROS部分,使用到了boost::asio库,等我学会了再更。
% ?$ q2 d) x% p" e
$ L7 @+ \/ |" M+ X4 Z' E/ k
- J0 x' N+ v) z5 L! }4 N: G
|8 z( ]7 r' O7 ], A/ h9 m6 O y |