你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32与PC端、HC-06、ROS进行USART串口通信

[复制链接]
STMCU小助手 发布时间:2022-4-26 22:26
前言
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  \. ^ 4c96242ab38648818d029a7ea10cab27.jpg
- 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
94]6SASVE]E8VUPMKX_YD40.png
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
  1. void My_Usart1_Init(int bound)1 D+ Q* g. s. f$ ~" w0 E
  2. {' s3 l: ^( j( i. V1 T1 b. s
  3.         /*1.串口时钟、GPIOA使能*/7 }; S1 t4 c9 Y9 g% ~3 ^" ^6 H) `
  4.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA,ENABLE);
    . d! s5 ]- F0 }7 ]0 Y6 M
  5.         # N) a3 ]- t, P. m! h
  6.         4 \: C6 _! x' |2 y" a- F1 e( r
  7.         /*2.GPIO端口模式设置*/
    " p, k8 o( v; ?7 i! i% i5 o
  8.         GPIO_InitTypeDef GPIO_InitStruct;
    . e2 _# b( G; a. }$ ^# D
  9.         /*TX*/
    ! b( |7 ^7 ^- j0 D' s: B8 _* T
  10.         GPIO_InitStruct.GPIO_Pin = Usart1_TX;
    ! U3 v6 S0 g# Q! |, W" D
  11.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;: [; ]) x( Y+ c; X$ b1 V& G
  12.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    ; j  v4 @" _0 ]8 x$ c; }
  13.         GPIO_Init(GPIOA, &GPIO_InitStruct);
    * J' L- u$ ~8 Z1 ?/ M" I; w
  14.         /*RX*/- [, J1 |& j4 t& M4 o1 h
  15.         GPIO_InitStruct.GPIO_Pin = Usart1_RX;+ S% c5 u# @$ f- z  d
  16.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;5 |- u4 I7 f* v( ?2 [: L
  17.         //GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;5 u5 p8 k  J: d  ?
  18.         GPIO_Init(GPIOA, &GPIO_InitStruct);
    # |6 C5 R* t1 M% E4 R4 p
  19.         / W6 Y1 C2 D# V# z% v1 q' v
  20.         
    4 r# S+ {) q7 E' Z) @8 s0 i" b
  21.         /*3.串口参数初始化*/
    - f* F- q- _$ ]+ O  U' S$ k
  22.         USART_InitTypeDef USART_InitStruct;
    6 x+ f1 K% T* M" C! [- i
  23.         USART_InitStruct.USART_BaudRate = bound;//波特率& N4 j& Z" s* e% l
  24.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制4 t& h5 c* [4 ]" }1 }" \
  25.         USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//USART模式- R! T6 N4 K5 [6 ]9 T' b
  26.         USART_InitStruct.USART_Parity = USART_Parity_No;//校验位# a8 J$ z# [5 b! ]
  27.         USART_InitStruct.USART_StopBits = USART_StopBits_1;//终止位' j) D& ~) Q3 Y. B- p# D# E# C
  28.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;//数据长度,如果有奇偶校验就设置为9位
    4 p% x) m) ]7 u2 v0 j
  29.         USART_Init(USART1, &USART_InitStruct);8 u, a+ w6 @- }5 A! w: d
  30.         ' Z4 B( {- `0 r- }9 P
  31.         /*4.开启中断并初始化NVIC*/8 ]. D& e( i7 z7 q' e
  32.         USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开中断,开启接受中断后,接收到数据则会进入中断服务函数" Y1 a/ S. z# F+ H/ t
  33.         ' C/ w' m& h2 l2 Q" b( @. {7 ?7 l
  34.         NVIC_InitTypeDef NVIC_InitStruct;
    ; z, J9 T* `0 ^5 w6 F- g
  35.         NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;) }+ p% h& C, ?& _
  36.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    ; h6 ^% E; A: Y3 ~1 G
  37.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
    5 A7 e6 b5 d9 \& }' i
  38.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;( Y  h& x6 _/ {( M2 l6 v5 i
  39.         NVIC_Init(&NVIC_InitStruct);//初始化NVIC
    * r% d6 ?3 t  O* N. B  p4 q6 y
  40.         - M/ I1 f" @( Y3 s$ H  A" l  F
  41.         /*5.使能串口*/
      K7 z! Z: g# g. X
  42.         USART_Cmd(USART1, ENABLE);) I8 y% G6 I+ E  f
  43.         
    / ?% Z# O& ?7 u4 [0 ^
  44.         /*6.编写中断服务函数*/
      {! r# {2 V. p" x) {0 L
  45.          8 ]* R5 f: t* z9 j" a+ X6 c9 N
  46. }
复制代码

" 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 ?
  1. void USART1_IRQHandler(void)
    $ S# o" C' i! C+ r1 u
  2. {
    5 d% [9 i9 [" s% C- u1 e
  3.         u16 RX_From_PC;
    2 c6 N. n8 m+ j+ a
  4.         if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)( R/ y" J( ^: i* T5 }8 n# z
  5.         {
    " r* ?( f# j. W2 ]8 e( t4 E
  6.                 USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清空中断标志位
    . Q' ?: V4 X: r! J
  7.                 RX_From_PC = USART_ReceiveByte(USART1);//接收PC端发来的消息
    + z. M- {6 T: G
  8.                 USART_SendByte(USART1,RX_From_PC);//将数据发回到PC端
    % Z, T) `! Q. _& I# M$ d& U. X
  9.                 9 S, @& k7 M' }
  10.                 //usartReceiveOneData(&TargetVelocity,&TargetVelocity,&RX_Cmd_Form_Ros);//接收ROS发来的消息; ]! G! @9 V* v5 K+ r# X' A
  11.         }
    . y* h! m( p% a
  12. }
复制代码
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
  1. void USART_SendByte(USART_TypeDef* USARTx, uint16_t Data)/ l& V- m" O2 g0 w/ o' c
  2. {, ^) \; ^' B) H$ ~1 |2 g$ \8 x8 s
  3.   /* Check the parameters */7 B$ M7 v* A3 V# h4 ?
  4.   assert_param(IS_USART_ALL_PERIPH(USARTx));9 ~6 V3 ~/ b9 L& a5 M/ l
  5.   assert_param(IS_USART_DATA(Data)); 0 r4 ^  v* a8 W: g
  6.   b9 E3 P9 w& f" t! G2 u
  7.   /* Transmit Data */
    . E% b3 ]+ P9 _: g
  8.   USARTx->DR = (Data & (uint16_t)0x01FF);3 E! k/ S' [, H0 [! U% J, g7 }
  9.   while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);7 c% P, W9 X- n5 T$ g
  10. }" W/ V1 e: [! p$ t" T$ t: Z5 Q: B
  11. 5 s% ~/ j( ^& n( Y0 v0 y7 H
  12. void USART_SendString(USART_TypeDef* USARTx,char* str)! k/ \5 k) N( d6 C; O
  13. {* _; M/ }0 ]& j1 i# y/ Z
  14.         while((*str) != '\0')
    9 z1 U7 ~0 z: l+ ]7 v" x5 W
  15.         {
    9 z: H6 Z3 j3 Y( g5 u
  16.                 USART_SendByte(USARTx, *str);
    5 }, K: w' s: E# m. P
  17.                 str++;
    ( }# O/ q: j1 f- V+ @! K
  18.         }- h3 \, j3 D5 b, D' c$ Z
  19.         while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
    + z, Y; C9 y4 n' t% [
  20. }
      x7 }* j( r0 d* ^4 V! m4 l

  21. ' ]$ D. {' E, e: o. Q

  22. & U& L- R$ y+ |7 R) L$ i
  23. * K/ Z" Y  p3 D+ A7 y. V
  24. uint16_t USART_ReceiveByte(USART_TypeDef* USARTx)
    # ]& Q4 I  s" |* ?7 S) v9 E5 g) e
  25. {% H% @1 Q4 g5 m  h. G9 e; c
  26.   /* Check the parameters */
    / n( E! {+ V$ ~' h; ]8 [% c+ D  q
  27.   assert_param(IS_USART_ALL_PERIPH(USARTx));
    % k0 v! j0 j, ?/ M- l0 K9 s
  28.         " F- R" D6 m  T+ x- A# f
  29.   while(USART_GetFlagStatus(USARTx,USART_FLAG_RXNE)==RESET);
    6 n  I6 F: Y1 L& Y* c0 i' a

  30. 0 e7 I% S$ u" _% U0 y
  31.   /* Receive Data */
    : }7 T* t, q) z
  32.   return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
    8 \1 A. o' q8 p/ s+ y; }$ o) C
  33. }
    + Q' A1 m" H2 D& q
复制代码

6 r% S- V* K/ x; {在主函数中进行相应的初始化,我们就可以通过串口助手与STM32进行通讯了。$ F3 r: h7 Y  S

4 L( k$ p3 Y& ~ {[]G6A`}0`ZOOK8XC_%)6S9.png
; 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: _
  1. /*重定向这两个函数*/: B% ~7 E% z/ k& t0 e, ?! N
  2. int fputc(int ch,FILE *f)
    % a& T' m0 W" e
  3. {
    . o' n" ?! b. Z: W1 X$ l* S; }
  4.         while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
    # K; A- z1 H) g4 L
  5.         USART_SendData(USART1,(uint8_t) ch);
    9 F, ^4 I0 u2 h; ^8 b
  6.         return ch;7 H/ N& o8 B8 H% F+ G
  7. }
    6 t4 L# o: k4 \2 b* I* K, w# m, Z! i

  8. 7 L* \1 y( h/ ?  H8 K+ [
  9. int fgetc(FILE *f): o& k* H0 b! |! }& j  j+ \
  10. {
    " |$ N: o" G, t( H3 ~
  11.         while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);
    2 v9 w  B4 ]! W4 N2 M
  12.         return (int)USART_ReceiveData(USART1);7 J2 B( I) G  r, c
  13. }
复制代码
6 {# u2 Q  {1 E; P- t, [- I
printf函数使用的是半主机工作模式,需要使用微控制器,需要进行如下配置:
$ q  Z- a- P: ?( }5 ]' b- O. M$ x: ^8 u- `
{3{FUBC9AU]ED(F8UYKCYID.png
, 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
  1. #pragma import(__use_no_semihosting)
    # d3 V% ^& E: e

  2. 6 p9 m! F) d& d. O
  3. struct __FILE
    ! r& b7 D( v: R) J2 ~
  4. {- ?. w/ @/ \: c
  5.         int handle;; B1 C4 P, `$ ]; W/ I7 v
  6. };- `. B2 Y- k# U; b/ H

  7. + _. Z2 c8 m5 M6 b
  8. FILE __stdout;
    " [+ S5 p/ c2 O$ j: p
  9. _sys_exit(int x)
    ! p  ?) Z; ]$ Q! H. d. X- g1 ~
  10. {
    $ H' V6 z; {6 p
  11.         x = x;
    , W7 r- W+ P3 y* K% ~1 v2 ]
  12. }
复制代码

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 3EIZ2B@ALCG6F}(CGE(VO`W.png - 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
  1. //数据接收暂存区
    ' H/ ~0 x. e, s, s
  2. unsigned char  receiveBuff[Message_Length-4] = {0};         
    2 [8 c: ]' S$ @, N( @1 O
  3. //通信协议常量
    , H5 _! p8 Z* m
  4. const unsigned char header[2]  = {0x55, 0xaa};9 Y- i9 S6 e9 j/ b: m' I
  5. const unsigned char ender[2]   = {0x0d, 0x0a};6 s4 J3 F& A, i) V+ u/ O

  6. * G; g: g# {* G& u* e; H$ H1 F
  7. //发送数据(左轮速、右轮速、角度)共用体(-32767 - +32768)% D3 m/ Q' L% W: o/ a# J
  8. union sendData: R( c4 `0 {- V! M1 n
  9. {+ q5 u" z6 d) L( ^$ N
  10.         float d;
    9 K# [8 t6 y, z
  11.         char data[4];* ^4 }1 W- d3 \0 C" {: ?
  12. }leftVelNow,rightVelNow,angleNow;
      R+ _4 T+ o/ \# S% K4 m
  13. ) o' R* k( y0 j5 x! y0 S: A9 L
  14. //左右轮速控制速度共用体5 U1 s( U( t* `
  15. union receiveData
    % c1 ~1 G  C( V, T/ i8 m9 h1 G7 y
  16. {. D& i7 O. ^) O% }8 l
  17.         float d;
    : y5 Y: ?. Y, a2 K5 T7 h
  18.         char data[4];  X! S0 H% l3 b$ H
  19. }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
  1. . _! F$ `1 P' I6 p
  2. int usartReceiveOneData(float *p_leftSpeedSet,float *p_rightSpeedSet,unsigned char *p_crtlFlag)7 z- X! \6 g$ U: _- Z6 ^4 Z* g
  3. {
    2 |/ e7 {2 l1 N+ N3 f- a
  4.         unsigned char USART_Receiver              = 0;          //接收数据
    ) L- X# f. t$ I& v
  5.         static unsigned char checkSum             = 0;
    ) H# Q8 N$ C: D2 j$ A! ?
  6.         static unsigned char USARTBufferIndex     = 0;" T+ U" V. R: G+ m  V
  7.         static short j=0,k=0;
    " `8 t% R$ A+ H" Y
  8.         static unsigned char USARTReceiverFront   = 0;
    ) i; q* G4 F8 D, l1 ^1 _: Z
  9.         static unsigned char Start_Flag           = START;      //一帧数据传送开始标志位1 j  D$ {- ]4 p% T/ q. ~, Q
  10.         static short dataLength                   = 0;5 D; A" u/ I+ l2 A! E3 p

  11. : P: ]( A, ^) M& m
  12.         USART_Receiver = USART_ReceiveByte(USART1);
    & x! N9 i; s& w5 u$ j) N3 Z! P& O8 K
  13.         //接收消息头, u% n5 A* H) s8 M  J7 V4 i
  14.         if(Start_Flag == START)
    4 V6 @; o1 p# i) S/ c" l! ]
  15.         {
    * Y, H" c, v& p+ ^9 Y
  16.                 if(USART_Receiver == 0xaa)                             //buf[1]7 z, [' N% }$ m" |2 ?5 K$ [% ]' i% k
  17.                 {  
    & \. U! @" `7 h3 k! _8 H0 L
  18.                         if(USARTReceiverFront == 0x55)        //数据头两位 //buf[0]
    ' _+ ^: J* {, P3 I& z
  19.                         {4 w0 \6 P5 _4 C/ r' ]9 T/ e" b
  20.                                 Start_Flag = !START;              //收到数据头,开始接收数据
    / S: @* n$ A4 D  V
  21.                                 //printf("header ok\n");3 }. m6 L5 _, r0 F, i. K
  22.                                 receiveBuff[0]=header[0];         //buf[0], L0 D3 o( f; k0 M3 L, \
  23.                                 receiveBuff[1]=header[1];         //buf[1]1 |" p4 X' o- a* U: G6 e
  24.                                 USARTBufferIndex = 0;             //缓冲区初始化: J. g9 F; J0 |8 W
  25.                                 checkSum = 0x00;                                  //校验和初始化+ H/ Q; h! t. u5 f8 K! a
  26.                         }
    0 X) Z& G- Z1 r* r
  27.                 }
    * q2 @0 a# {+ f$ W- b
  28.                 else
      I0 k, h+ k6 I4 b
  29.                 {
    & X* s1 @9 F  Z1 s6 o& j* A
  30.                         USARTReceiverFront = USART_Receiver;  
    $ T7 y. ?9 n- d$ b, x
  31.                 }! |' F& y! P# q/ ?' d! A& c
  32.         }
    ' v' Y# S3 d8 P6 S% u
  33.         else( f3 L- \) N/ W0 U5 X
  34.     {
    : R& T$ l( t2 x
  35.                 switch(USARTBufferIndex)
    ! j3 k3 r/ [2 W/ a& \: Z
  36.                 {
    0 B$ W* J% j5 S% `, z' n
  37.                         case 0://接收控制指令4 H# q" t" o; G2 f, J1 \$ f# E; g
  38.                                 receiveBuff[2] = USART_Receiver;
    , ]7 d- b. S) u9 G
  39.                                 *p_crtlFlag = receiveBuff[2];                                //buf[2]
    . z& W8 X4 @0 S1 C  x
  40.                                 USARTBufferIndex++;: z5 _0 k2 \5 N8 p# }
  41.                         case 1://接收左右轮速度数据的长度
    6 K. l* O( R- E$ O! a" l4 A
  42.                                 receiveBuff[3] = USART_Receiver;
    6 t0 f0 i# T5 ]. `( _2 {6 e
  43.                                 dataLength     = receiveBuff[3];            //buf[3]& d) ?" s- S, l4 m  B* @2 ^
  44.                                 USARTBufferIndex++;
    ) f3 q; \6 s" {0 n5 O( L- e4 P) ]
  45.                                 break;                                1 ~5 x* h4 ^0 V' O
  46.                         case 2://接收所有数据,并赋值处理
    1 k8 s& n  Y* ?+ o" }4 ~4 F4 p* B
  47.                                 receiveBuff[j + 4] = USART_Receiver;        //buf[4] - buf[11]                                       
    ( y0 u2 w, G' Q, \9 w
  48.                                 j++;
    4 z- R) Z. \1 _* @1 F& ], O
  49.                                 if(j >= dataLength)                         # x+ z9 @; l$ W! |
  50.                                 {; F' ]! B# o) ^
  51.                                         j = 0;; ]3 [" H9 c7 }& q3 y
  52.                                         USARTBufferIndex++;8 X: d. \2 M( T/ b7 E2 E1 t4 }
  53.                                 }
      T, {$ y2 X+ k4 ]/ j6 h- o
  54.                                 break;, c3 ?' a2 d) w5 ^3 _
  55.                         case 3://接收校验值信息
    5 H' h  q4 m; a# V6 T7 {- C4 E
  56.                                 receiveBuff[4 + dataLength] = USART_Receiver;
    0 P# a, [$ L' f4 Q
  57.                                 checkSum = getCrc8(receiveBuff, 4 + dataLength);# j2 v& F0 [" ?6 \
  58.                                   // 检查信息校验值# w) g  e. u: ?- n) Q% I& e
  59.                                 if (checkSum != receiveBuff[4 + dataLength]) //buf[12]; \6 _6 e) W' N1 c, t& C; R
  60.                                 {8 L) `; Q$ [* s6 ]
  61.                                         printf("Received data check sum error!");
    # r2 A' C9 h& t% ^- w
  62.                                         return 0;
    3 S. D  x3 o: Y, a$ U( @
  63.                                 }
      \! J% s. ^& l. o6 }; H( ~
  64.                                 USARTBufferIndex++;; Q( I: n( n9 ?
  65.                                 break;
    ' F, q! P, ]! @
  66.                                 1 ~# i  |: O3 e9 s, `8 A
  67.                         case 4://接收信息尾% q8 V! d; |, K6 t! |0 k7 z/ `) x: h/ w
  68.                                 if(k==0): ~; O& s. [  j% ?( i9 \. D) W. G
  69.                                 {
    * }/ e  x: C& m5 A' u) _
  70.                                         //数据0d     buf[13]  无需判断! \- L. V& E) U& {0 `3 E/ L
  71.                                         k++;
    $ E- P+ c9 n# s+ D9 c  N* h
  72.                                 }
    6 P, w1 t* s* m6 h" q' I
  73.                                 else if (k==1)
    * R1 N/ d( p3 r
  74.                                 {
    : C6 N# p- X# X6 y
  75.                                         //数据0a     buf[14] 无需判断
    9 Q7 }4 ?- j# w' l4 x" z
  76. 1 ^1 j: g* ?8 L
  77.                                         //进行速度赋值操作                                       
    ' k4 C6 |9 t9 R7 R4 D. }
  78.                                          for(k = 0; k < 4; k++)
    / D. M3 A' p3 h' K, k; l
  79.                                         {0 p- }3 h5 X/ O+ E& N- U
  80.                                                 leftVelSet.data[k]  = receiveBuff[k + 4]; //buf[4] - buf[7]
    7 b: O( e& d- \/ F1 X( [0 e
  81.                                                 rightVelSet.data[k] = receiveBuff[k + 8]; //buf[8] - buf[11]. u) f/ A5 V7 x1 N9 t
  82.                                         }                                1 k  [* P2 @- j  r9 h
  83.                                        
    : H% \; O4 D8 A, }
  84.                                         //速度赋值操作0 P% Q, x) }  ^
  85.                                         sscanf(leftVelSet.data,"%f",&(*p_leftSpeedSet));$ I& |  i1 D5 m
  86.                                         sscanf(leftVelSet.data,"%f",&(*p_rightSpeedSet));9 Y2 |: ]: E9 k7 \6 }& ?
  87.                                        
    : G% _2 v" z" X
  88.                                         2 b% G6 q* e" M) x
  89.                                         //-----------------------------------------------------------------
    ; |- S2 o5 y6 ^7 L& q5 X
  90.                                         //完成一个数据包的接收,相关变量清零,等待下一字节数据# l; H+ M; v! A& F: G8 s
  91.                                         USARTBufferIndex   = 0;
    ) @# }$ o# E* R7 f5 j
  92.                                         USARTReceiverFront = 0;$ [# K# ]2 \9 c) A. g8 ^
  93.                                         Start_Flag         = START;
    - n- J! b0 {7 R' H
  94.                                         checkSum           = 0;
    % o" j" P7 V9 T8 S* ?: l* @% p; R+ H! \
  95.                                         dataLength         = 0;
    0 |9 L( E& _+ E/ d
  96.                                         j = 0;
      l$ F9 r3 r: s, X
  97.                                         k = 0;6 \% A8 y' |9 z) I" j2 S. [8 b1 w
  98.                                         //-----------------------------------------------------------------                                        * Y% R0 \( [  R+ l4 ~; b+ O
  99.                                 }- Z- c/ W9 q. R! X$ y4 l
  100.                                 break;
    : A; r9 x1 c* `* k7 l3 e5 Q
  101.                          default:break;! s9 C0 X- O* B9 _# J5 w" [
  102.                 }                & v6 j7 A6 T% B
  103.         }/ P# d  I2 A& n4 |6 m! }6 O
  104.         return 0;
    * e$ E8 M2 S! N
  105. }
    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
  1. void usartSendData(float leftVel, float rightVel,float angle,unsigned char ctrlFlag)2 s# @/ d% J, c, \$ v( f
  2. {: @: X( b, I! |  a3 D' d2 c% m9 D
  3.         // 协议数据缓存数组" ^+ t1 x/ n$ X" ^, }: l
  4.         unsigned char buf[Message_Length] = {0};7 I  }; |) R& [' r: Y
  5.         int i, length = 0;
    $ i8 I& L+ t# l! Y
  6. 4 K/ ^  {; j0 Z: b2 `" p" P6 e
  7.         // 计算左右轮期望速度: v/ s( [" B2 {9 @2 {
  8.         leftVelNow.d  = leftVel;5 h6 U% K9 @5 D2 O5 U* Q) ^
  9.         rightVelNow.d = rightVel;. {: E% @; s" M/ d+ w7 N; V0 f
  10.         angleNow.d    = angle;
    $ C# j- a) P6 e% ?2 P
  11.         
    8 G  \3 c) D  T5 ^
  12.         // 设置消息头2 B" S, h8 \! ?9 ?) L6 ^3 y* G0 g5 [
  13.         for(i = 0; i < 2; i++)
    " e- J" M/ A" |6 ]
  14.                 buf<i> = header<i>;                      // buf[0] buf[1]
    ! o4 f* E% W( x0 _7 P
  15. : V7 x% [2 J9 ~8 z
  16.         //设置命令位' ^) \  A* n9 J. D# |0 J7 O
  17.         buf[2] = ctrlFlag;                                                         // buf[2]
    , y  K) `5 ~) f/ ~5 d
  18.         
    ; y* n  K: c6 Z7 y  x9 [* L$ l- s* A
  19.         //设置数据长度
    & g/ Q! m) M, B2 K
  20.         length = 3 * sizeof(float);
    6 E" T' J+ I% _- L$ J
  21.         buf[3] = length;                                                         // buf[3]. \, X9 f" c  v& j8 N9 }
  22.         $ E: K: F: I1 N/ b. F6 ^
  23.         // 设置机器人左右轮速度、角度
    ' |9 V" z/ M1 Q
  24.         for(i = 0; i < 4; i++), u) H( S1 `* o( w
  25.         {! o6 j( ?. |7 O; u7 U- i2 B  \) Y
  26.                 buf[i + 4] = leftVelNow.data<i>;         // buf[4] buf[5] buf[6] buf[7]5 ]1 S; W8 w: u& z8 b
  27.                 buf[i + 8] = rightVelNow.data<i>;        // buf[8] buf[9] buf[10] buf[11]4 p4 T' ~4 Y( ~. R" e
  28.                 buf[i + 12] = angleNow.data<i>;           // buf[12] buf[13] buf[14] buf[15]
    & S0 f& J$ C( q1 U
  29.         }1 v! a4 A1 A4 J) M& P" ]
  30.         
    2 _; q" G: {/ U) O" W9 s& i$ q
  31.         // 设置校验值、消息尾3 p+ c! m7 ~* d" q# V4 x
  32.         buf[Message_Length - 3] = getCrc8(buf, 4 + length);  // buf[16]
    - z: m+ e: L( }4 g" k& M
  33.         buf[Message_Length - 2] = ender[0];              // buf[17]9 k5 j2 T, I0 p0 \% y
  34.         buf[Message_Length - 1] = ender[1];              // buf[18]
    & h, O/ b4 Z4 `" {5 I
  35.         7 j' y/ y, ~. ?8 S
  36.         //发送字符串数据
    * P9 |$ Y$ R0 y0 A; C
  37.         USART_Send_String(buf,sizeof(buf));
    % Q8 K2 q2 D1 A1 K$ {. S7 ]" H. c
  38. }; j& J; J+ j4 F# u0 V6 k

  39. , t, F* z5 N4 h+ R8 p; a, x6 @* J
  40. 循环冗余校验:
    # {2 K- W! B- O) L9 _" S7 j, N
  41. 1 _+ N$ Q+ `# x( c0 s
  42. unsigned char getCrc8(unsigned char *ptr, unsigned short len)2 [8 @, M5 P! O' Z  l, ]
  43. {; O+ b! k8 S4 b; _% N) O
  44.         unsigned char crc;' X0 }  k5 Q$ t- S1 L) D
  45.         unsigned char i;
    * a- a6 @) r  P6 O3 G
  46.         crc = 0;
    . g' b$ M" \% J* h
  47.         while(len--), S& s& p, n4 a
  48.         {
    $ V' u/ w+ d% m3 g6 V  k
  49.                 crc ^= *ptr++;6 }2 I4 W0 u% @: ~
  50.                 for(i = 0; i < 8; i++)
    & g& o0 s4 Z0 ^% F8 L: _' w
  51.                 {
    " z- w7 j) q7 b) K7 Y0 h' z
  52.                         if(crc&0x01)
    , u0 ?( m' y+ n3 l8 {
  53.                 crc=(crc>>1)^0x8C;% K- {  S; {- Y7 z( l! a) D* S
  54.                         else $ D; s0 }2 ?, Y8 j5 G- k4 x# q
  55.                 crc >>= 1;" E* u! s9 e4 ]- _' _+ J  l4 E1 L. R
  56.                 }
    8 s, l( F6 [9 l- O
  57.         }. b; q4 J  V; q# d- b: y
  58.         return crc;' ~* U% O$ U! `
  59. }</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
赞 收藏 评论0 发布时间:2022-4-26 22:26

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版