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

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

[复制链接]
STMCU小助手 发布时间:2022-4-26 22:26
前言
, S1 r+ U9 c$ y9 r* l串口通讯对于任何开发板都是非常重要的,也是必学知识之一,通过串口通信可以实现上位机与下位机之间、开发板之间的通讯,可以让我们实时掌握机器人的各个关机的运动状态和传感器的信息。: ]; m: H6 w  F: v% e- H& H

( U" D  ~( K# a现在的通信协议有很多,比如:UART、USART、CAN、SPI等等,它们功能不同,适用于不同的场合,USART作为单片机之间、下位机与上位机之间最常用的通讯方式之一,它对于数据的收发十分方便,应用日益广泛。+ k5 X0 _9 w2 q: d1 J* `

! f7 \" O6 x) K7 o5 m; c4 c3 e一、USART通信原理
3 v' v( Y% h. K/ O3 v3 ^USART(Universal Synchronous/Asynchronous Receiver/Transmitter)中文名叫:全双工通用同步/异步串行收发模块。
- F' H/ q; }( g) |3 F& ~! Z
6 ^! B  |7 O7 J2 j. E1 w+ f先对这个名字的各个部分进行解释:
1 s% e* E, d! F: Z3 [1 b" T' N
4 H- f% ?0 G  x! \, n 4c96242ab38648818d029a7ea10cab27.jpg ) J+ Q; H" y! ]% B% E1 Y

1 t( V. V& E: W/ c 在平时的工程项目中,我们常用的是全双工串行异步通信方式,虽然串行通信数据是一位一位的发送,但随着近几年科技的高速发展,串行通讯的速度已经逐渐赶超并行通讯了,而且串行通讯方式适用于远距离通讯,比较常用。
0 [$ L5 D2 y9 `2 D/ F  z7 S: D* Y
/ c3 P  `4 `; E- H: Q  h- a( ^# G0 s. XUSART通讯的数据格式大致是这样:
% O- B3 t' z, e6 ~- d5 k. z! m+ e& f8 A8 x$ x! l2 K( i. ]8 o* @
起始位(0)+串行数据帧(从低位到高位传输)+停止位(1)
! g+ V6 X4 L5 v4 C, |/ c) P! `# c
7 H. [0 C) Q9 `( X7 [4 X串行数据帧可以人为设置为8位或者9位,9位是8位数据加上1位校验位(奇偶校验)。
) @4 H: M0 b+ H$ e5 ~/ u6 V: n" P2 I* g+ B% V$ n6 m
另外一个比较重要的概念是波特率,在任何通讯开始之前,通讯双方都要约定好波特率,波特率是每秒发送有效数据的位数(bit/s),双方如果没有约定好一致的波特率,在传输过程中则会出现乱码的情况。
8 [! A0 z/ i. O: K
' [* T& c/ e" v  N在STM32中,有专门的数据寄存器和特定的引脚负责USART通讯,并配合有相应的标志位,用于帮我们判断数据是否发送/接收完毕,并且也有相关的库函数帮助我们对串口进行配置。
* s! L7 I2 h4 x& x7 M  E3 y
* `0 w+ q& {% Z: y相关寄存器有:发送数据寄存器(TDR)、发送移位寄存器、接收数据寄存器(RDR)、接受移位寄存器。; t3 z* H4 c9 s
0 _7 e2 o. w6 S# s* E
相关标志位有:
# M0 X+ a3 H5 ?5 U1 }' L" `& T6 G, ^, R+ _2 ^. n% Z& z0 F
TEX标志位:1:数据寄存器无数据;0:数据寄存器有数据
3 T5 X; a% v4 s- a) qTX标志位:1:发送完成;0:发送未完成
8 Y6 I2 f# H  `8 T* F3 ~1 YRXNE标志位:1:数据接收完成,可以读出;0:数据未收到
0 `$ E; c) S( `2 P5 e; |具体知识在中文参考手册P517,大家可以详细查看
; V7 X" x1 m6 n$ w
  ~0 G2 ~' M: x* Q二、STM32与PC通讯
: p( i+ B  g/ h, p6 I, C8 K
STM32与PC通讯需要进行一些配置,这里实现由PC端向STM32发送一个数据,STM32接收到后再发回到PC端,该实验需要用到串口调试助手。# H4 |3 o7 q) a. Y% G  e
- u2 c4 ~6 a# L  ?
STM32可以作为串口通讯的引脚大家可以通过数据手册进行查看,比如PA9(TX)和PA10(RX)+ f/ I9 \5 Y. n+ n5 S! x5 b
* C3 e- _3 w! Z* k9 q
94]6SASVE]E8VUPMKX_YD40.png
/ M; W' }% q, P1 d. `: R/ e8 G2 ]- V' X7 Y
串口通讯一般都配合中断进行使用,下面讲解串口配置过程:
# N. [( k7 {$ e; h9 u" H% ?* t. ]; v5 X) d& [- ?& p" e6 C% Q: b. B
USART配置步骤* l4 O+ G, S+ H

1 W% R1 N1 @1 ~! `% |1 Z步骤 函数 1.串口时钟、GPIO时钟使能 RCC_APB2PeriphClockCmd 2.GPIO端口模式配置 GPIO_Init 3.串口参数初始化 USART_Init 4.开启终端并初始化NVIC USART_ITConfig、NVIC_Init 5.使能串口 USART_Cmd 6.编写中断服务函数 USART1_IRQHandler3 V" B: ~$ [( ~, f. h9 D
: w, I" `2 r8 C
my_usart.c 代码如下:
; X" J& `* g+ @; n; j$ D: C8 `. f  X6 a6 D; {9 w5 m
  1. void My_Usart1_Init(int bound)
    + M) p$ Z8 W5 O9 _* i
  2. {6 h: T. M9 D& z5 \, M+ Q* c
  3.         /*1.串口时钟、GPIOA使能*/
    2 n$ [% I9 w5 P( p9 L) Z7 _
  4.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA,ENABLE);
    . h, m' @" ^/ a6 f. M: P+ Q
  5.         3 _  |& z3 c9 l2 Q' Y$ o
  6.         & n" j$ K8 P& u/ c  X2 Z6 `
  7.         /*2.GPIO端口模式设置*/
    , x; F2 u5 T# o3 f: `$ K2 z. L7 ?
  8.         GPIO_InitTypeDef GPIO_InitStruct;& U! q6 w1 u+ f  O: n: {& p
  9.         /*TX*/" @  d5 M/ Z% F! f% X
  10.         GPIO_InitStruct.GPIO_Pin = Usart1_TX;
    % f) g1 `5 [3 f+ D& O. Y
  11.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;: b7 T/ _/ Q7 G) T$ ]: S
  12.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;% X0 D- f3 g8 I( y0 n/ }
  13.         GPIO_Init(GPIOA, &GPIO_InitStruct);' G. [( [5 }/ ?/ E& b
  14.         /*RX*/
    8 K. y9 V* t" ?8 o# z
  15.         GPIO_InitStruct.GPIO_Pin = Usart1_RX;6 B* b9 \2 i  a) |6 ~
  16.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;5 y3 K7 g$ Z( I6 E
  17.         //GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;( E$ i2 T1 F, |  m
  18.         GPIO_Init(GPIOA, &GPIO_InitStruct);6 h7 f* n4 N' x: \4 q
  19.         # b# ], @$ B7 S( ?# D1 Q
  20.         5 @3 j- O3 j0 k+ o$ D
  21.         /*3.串口参数初始化*/, r5 K$ c  i+ e9 ?5 J5 B+ L4 n; {
  22.         USART_InitTypeDef USART_InitStruct;
    . u4 N$ G* w" s# Q
  23.         USART_InitStruct.USART_BaudRate = bound;//波特率  Z; k4 x. h- t( I
  24.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制6 h  N9 I" {1 V8 M% `3 i" K5 v
  25.         USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//USART模式# Z* e1 U$ L: P( i2 Q0 c  S
  26.         USART_InitStruct.USART_Parity = USART_Parity_No;//校验位
    ; o2 a+ V$ G! H! s5 L8 [' O  a4 b4 w
  27.         USART_InitStruct.USART_StopBits = USART_StopBits_1;//终止位; }- [1 `+ M( l/ H. y
  28.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;//数据长度,如果有奇偶校验就设置为9位
    5 X: x- m# Z! Y& `! C: B7 P
  29.         USART_Init(USART1, &USART_InitStruct);
    : ]. p# F( d" h9 g" p
  30.         
    9 y. [9 q+ M- |6 p/ j5 Q# d
  31.         /*4.开启中断并初始化NVIC*/9 X* D. {1 e# i% e9 ^( A) @5 p( O
  32.         USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开中断,开启接受中断后,接收到数据则会进入中断服务函数$ i( X7 M0 X# a2 J! U# w
  33.         ; x, B" K+ H2 l8 c! |$ \4 [
  34.         NVIC_InitTypeDef NVIC_InitStruct;
    0 Q* e  x1 f& e8 C' \* \
  35.         NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    + V* M3 U/ P0 u" N. [
  36.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;) W* H2 _* ~8 q+ c, g1 C
  37.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;, W( k* o5 r. G1 R5 K* m' G6 s
  38.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;' B4 i3 R. n+ q8 P! {& q' R
  39.         NVIC_Init(&NVIC_InitStruct);//初始化NVIC
    & m  q% X: d* b2 }6 N
  40.         1 f+ E. L& Q/ R0 L+ A! @+ Q* J! r
  41.         /*5.使能串口*/
    3 p* x( D- B- a0 z% H4 t# R
  42.         USART_Cmd(USART1, ENABLE);
    : `- h) V4 R. P2 o
  43.         
    5 t& G  G6 @; `$ `2 t. M( L- p
  44.         /*6.编写中断服务函数*/
    ' a" C! B" M% W& m% N, T
  45.          9 `5 n4 [% `3 c' ]
  46. }
复制代码

! M/ J& {1 T9 h! y! l1 J: V3 l2 F- p! H3 y6 @9 e2 u9 O& F
进行如上配置后,当STM32接收到PC发送的信息就会进入中断服务函数,中断服务函数接收到数据后再进行发送。9 p5 R: G( m; J! l+ M
' Q+ P/ l4 ~1 G2 O6 e
中断服务函数代码:/ i& u: g8 d9 o# |3 [2 ~$ h

- i$ c! l% L; M) ]
  1. void USART1_IRQHandler(void)
    0 J" J" E& R# Q/ Z/ A; i4 L0 F3 f
  2. {9 y) T& s0 F9 w& R2 |
  3.         u16 RX_From_PC;# e. A5 `" Y+ R4 ~1 }) B9 }
  4.         if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET), d1 r4 ~4 F& C# e, d$ i
  5.         {1 H0 M& Y/ ~/ p- c
  6.                 USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清空中断标志位, \5 {0 r- L; P+ k( X
  7.                 RX_From_PC = USART_ReceiveByte(USART1);//接收PC端发来的消息" Y' S( f7 q0 S4 [9 B
  8.                 USART_SendByte(USART1,RX_From_PC);//将数据发回到PC端
    6 r9 m# r* }3 O- {2 I% X# r' f# _
  9.                 & y/ o2 w" I3 i! }6 O
  10.                 //usartReceiveOneData(&TargetVelocity,&TargetVelocity,&RX_Cmd_Form_Ros);//接收ROS发来的消息
    : l: w" l# i5 M* {; d
  11.         }
    $ x' A3 `, v( u) Y5 b" m
  12. }
复制代码

( Q/ j- L) @+ @. V" M& e# y在这里我们没有使用官方固件库中提供的收发函数 USART_ReceiveData 和 USART_SendData,而是使用了我自己对他们进行重写的函数,在其中加入了相关标志位的判断,这样可以保证收发过程中不会发生数据覆盖。
4 W9 o5 u2 K# {$ f. Z1 J2 t' K/ N% s3 A7 {# b
重写后的函数:
; N% \( z( ]3 |: `) l5 R) ^# ]! Q0 ]2 t2 t; J  _& q7 V: M. Z
  1. void USART_SendByte(USART_TypeDef* USARTx, uint16_t Data)9 \1 _  a+ i- [/ C1 j: N; `
  2. {. j* U0 g! p  H
  3.   /* Check the parameters */3 c+ V6 n  d( A8 C' V
  4.   assert_param(IS_USART_ALL_PERIPH(USARTx));
    9 I: k1 F, I8 I1 O
  5.   assert_param(IS_USART_DATA(Data));
    3 P1 f% y1 L7 {! Q8 |+ G

  6. 6 A  a; M7 _. M: E9 p3 g4 C
  7.   /* Transmit Data */
    4 o7 X8 y7 H8 [- `# [+ w8 ]9 W
  8.   USARTx->DR = (Data & (uint16_t)0x01FF);1 c, H% d7 f+ ^
  9.   while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
    : G" [" G- i8 J8 a7 ?3 A
  10. }; ~9 A! }& R/ N* D/ `5 M& `

  11. ; P( C" ]) r& O: p5 O
  12. void USART_SendString(USART_TypeDef* USARTx,char* str)  Y$ `$ l# r# q: z" d
  13. {
    % O5 f9 H5 Z& K4 f. e( c) k% R1 M
  14.         while((*str) != '\0')2 V- ~. {2 w4 \1 D% W
  15.         {
    " ?' f  Y8 K5 x! ]- i& z
  16.                 USART_SendByte(USARTx, *str);0 Y; ^. h- |8 W! [# e
  17.                 str++;0 r$ a& l2 V9 j3 [9 `- t
  18.         }; x/ g+ M8 }5 j9 K
  19.         while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
    1 i7 m6 F# [+ ^6 ^3 N! Y/ x
  20. }
    " O4 Y) k3 W4 G6 P' I

  21. * E3 }5 }, }( R0 V

  22. $ q7 H* s5 x6 K6 P6 `* E+ K
  23. + h7 D' V/ {  F
  24. uint16_t USART_ReceiveByte(USART_TypeDef* USARTx)( r, Z2 M: @& m0 J3 `$ l
  25. {
    3 {4 W+ ~) y+ S9 I
  26.   /* Check the parameters */, v3 [: Y6 E. j# x* G
  27.   assert_param(IS_USART_ALL_PERIPH(USARTx));& b5 |2 N! Y* X* d% Y* B* P
  28.         7 s8 c# y7 X: [" g0 }: c$ W% {1 h
  29.   while(USART_GetFlagStatus(USARTx,USART_FLAG_RXNE)==RESET);
    9 x, Z# Z! A+ ]* l% r
  30. : L" R! _+ Z6 k) h
  31.   /* Receive Data */9 ^( s. o# w8 n3 {8 k* w% [  y
  32.   return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
    , A2 L2 O8 M2 B/ g
  33. }
    , |; T7 `1 b+ }- S9 t, n
复制代码
% u: e7 [- T  u3 m! Z- G" J! b
在主函数中进行相应的初始化,我们就可以通过串口助手与STM32进行通讯了。4 J  b' K( P4 [* F
/ Q5 {$ x5 u* H5 D% q, v4 D
{[]G6A`}0`ZOOK8XC_%)6S9.png
6 g8 J# ~" `& q3 Q6 c5 R! e0 s6 d: {" M' {0 C8 g1 M
另外,Keil5是没有终端的,但我们可以通过一些设置也使用printf函数,让数据收发更加方便,我们需要对fputs和fgets这两个函数进行重定向(其实只重定向fputs即可),需要在程序中加入如下代码:
: M. v8 P) W8 ?" }7 R( k5 V& X- S2 r* A! o' o
  1. /*重定向这两个函数*/$ U. g$ S0 f+ G0 J& L) H
  2. int fputc(int ch,FILE *f)% S) Z5 m  J3 O5 v, {6 Q% B4 V
  3. {! t& \, o- h' E
  4.         while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);+ \. w: {; K0 |8 _
  5.         USART_SendData(USART1,(uint8_t) ch);
    4 j. N* h5 }, Y* ]2 P
  6.         return ch;
    9 ]+ S6 [6 L- I" h
  7. }; l: e" G! y8 @7 S6 f# H+ G% J8 n4 J- H

  8. ; d3 C* k. a0 W! J: S
  9. int fgetc(FILE *f), `# L  j* X1 J. e2 @( q0 L6 }
  10. {
    1 h9 T1 e/ V* T4 ?; A8 g2 U9 i/ b6 a
  11.         while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);
    1 C$ V# f  W  f7 A/ k: H9 Y# N
  12.         return (int)USART_ReceiveData(USART1);
    5 \( k1 O1 _8 T# c: f& [1 H" z
  13. }
复制代码

7 k) V  d1 p) D3 D- \ printf函数使用的是半主机工作模式,需要使用微控制器,需要进行如下配置:
; P! X, {; v& r
; ~! Y" D& g& a; `1 `: e; T {3{FUBC9AU]ED(F8UYKCYID.png
' ~" {2 X) ^& R0 m: v5 m/ b( i5 d3 \$ Y
这玩意功能少一些,如果不想用,则需要在程序中再加入以下代码,这是printf函数的底层程序:  l0 x" m& j$ j: u! W7 C. v. e

4 A% j3 V. O9 g2 i- R( Z" _
  1. #pragma import(__use_no_semihosting)! M- F" o+ J# Y/ `, n
  2. ) M  j5 {4 T6 T# u: L
  3. struct __FILE7 J- {* U; a+ ]  o1 ~4 P3 X
  4. {
    4 N- Q. S, X- O* b/ `
  5.         int handle;
    + V; t& u! M: S
  6. };
    & J( ?1 _* Y4 d, M

  7. * T4 i1 C, d2 r/ {3 D5 E! h; Q
  8. FILE __stdout;) W2 e; [6 ?4 l1 o
  9. _sys_exit(int x)
    1 n0 Z: q- b# X
  10. {* r  T3 k( M7 P
  11.         x = x;# l! n7 h0 f( R3 p/ e6 h* S" J& r
  12. }
复制代码

( T8 t' j8 ]3 o" T, t编译后,我们就可以与PC端进行愉快的通讯了。
5 w  s4 K# E1 p$ x, g) b. D3 E; r# }8 ^+ U/ Z
三、STM32与HC-06通讯* K4 L1 |/ O9 ]" E" w) b! m
STM32与HC-06的通讯与PC端的通讯类似,知识接线方式的不同而已,只需要进行如下正确的接线,即可完成顺利地通讯。
6 ^, |0 Q- x$ k3 P) i$ Z1 M
# ~* v6 i/ ^: W5 O 3EIZ2B@ALCG6F}(CGE(VO`W.png 4 `1 D% G. c- g! L' g  v
% c! I1 V; ^+ t4 F% J1 H( a
这里要注意的是,给HC-06供电时,需要提供3.6V-6V的电压;另外,要注意看一下STM32的引脚输出电压是多少,HC-06的输入输出电压都是3.3V,比如像Arduino的输出电压为5V,这时单片机的TX在接HC-06的RX时需要分压!
1 G" M" w# }( o7 V+ [; N/ F
0 @+ }. c' ?! g$ c' O0 n四、STM32与ROS通讯

% G+ X5 p3 ^/ B& x9 MSTM32与ROS通讯时,需要定义一个协议,以保证数据传输的可靠性,这个协议是STM32与ROS通讯时最广泛使用的协议,协议格式如下:
3 a) g$ s* f1 n1 Y) F' A' _+ o2 c8 m# X8 z3 Z2 b4 E
数据头55aa + 控制命令 + 数据字节数size + 数据 + 校验crc8 + 数据尾0d0a% I3 t4 J7 q4 o0 d$ B
- p/ O6 x, H( t  _8 g4 j' j6 X
在通讯时,我们只要对接收到的信息进行解码,即可获得有效数据信息,比如:设定的速度值、航向角等,保证了数据收发的可靠性。- E: X4 l/ {1 H  @

* G" X1 k& J; k& W先定义相关的变量:3 T2 Y% d0 E9 P6 F' Y/ v

& J8 B3 h: a# S
  1. //数据接收暂存区
    3 _4 {9 t0 ^) b) G8 z5 }# K: k; _* S
  2. unsigned char  receiveBuff[Message_Length-4] = {0};         . Q' }+ o- |- }  c# e& {. a
  3. //通信协议常量
    $ G7 D0 Z1 p9 T
  4. const unsigned char header[2]  = {0x55, 0xaa};
    & J* W, `& \5 f8 ]
  5. const unsigned char ender[2]   = {0x0d, 0x0a};: f/ a# F+ l) I+ K( T# `
  6. ; G. b6 r, K& y4 J
  7. //发送数据(左轮速、右轮速、角度)共用体(-32767 - +32768)6 b5 x2 ~. r3 m) T5 l% c4 _
  8. union sendData* L* ?, G; {& b3 L. m- M
  9. {
      m5 B6 D# G" O& X$ z$ y0 j. Q
  10.         float d;2 A1 i2 l( A5 J$ e+ N  O( g
  11.         char data[4];
    ) w6 d  f. Y  g9 z* ?  S! O6 c4 \
  12. }leftVelNow,rightVelNow,angleNow;
    6 j# O& M5 Y% C: u, N' q

  13. 8 n9 \2 I* |. F# z
  14. //左右轮速控制速度共用体+ Y5 e) Z7 R+ T4 [& ?" m
  15. union receiveData) _+ O( x1 f( k) r- d1 c
  16. {; G9 D, X1 k9 U4 d- E
  17.         float d;
    ; B- a0 B/ K+ n$ t3 X
  18.         char data[4];9 g4 [" S! \0 b7 G4 j
  19. }leftVelSet,rightVelSet;
复制代码
& K4 L1 B. v  T

& v1 Q2 E+ @( l! {& M1 M1 j, \& o接收信息函数:
: m- ~+ F( ]& s3 x

  1. ! F/ s) }' d" H1 V0 B
  2. int usartReceiveOneData(float *p_leftSpeedSet,float *p_rightSpeedSet,unsigned char *p_crtlFlag): F( l4 R, y3 M/ N1 l) u
  3. {3 F: {. q& j& i/ Q
  4.         unsigned char USART_Receiver              = 0;          //接收数据
    6 d* E5 X' u: S5 L+ b! |, |$ b
  5.         static unsigned char checkSum             = 0;8 y$ P3 g: x0 }0 p* D# v: }
  6.         static unsigned char USARTBufferIndex     = 0;
    + [) T5 r) c: W
  7.         static short j=0,k=0;- u, ^" t% o# O; M7 O: v" J% n
  8.         static unsigned char USARTReceiverFront   = 0;: r5 p) Q, Q3 d' b: x1 }
  9.         static unsigned char Start_Flag           = START;      //一帧数据传送开始标志位
      w0 Y# X# }- g3 E* C6 b. J2 }
  10.         static short dataLength                   = 0;
    7 t: c7 |' {  U2 Z' n- Y7 ^! \5 w
  11. 0 W+ w6 ^" T/ A9 w
  12.         USART_Receiver = USART_ReceiveByte(USART1);! n& y  Z' e# g' m; `/ |
  13.         //接收消息头+ L5 d2 [' s; h; R
  14.         if(Start_Flag == START)
    , @6 w1 `; F% b  l& s
  15.         {
    : v! A1 _) n: N6 g7 l4 i! A
  16.                 if(USART_Receiver == 0xaa)                             //buf[1]
    - r0 n9 P# N- @7 ~& ~/ {! W  M
  17.                 {  9 Z) e5 r) g2 q, F  X; L7 @, C- O
  18.                         if(USARTReceiverFront == 0x55)        //数据头两位 //buf[0]
    . {& c" @% t% ?) W" b  \, c
  19.                         {4 e( h  y# e: |5 ]# N
  20.                                 Start_Flag = !START;              //收到数据头,开始接收数据
    4 g9 x& X* q0 v& G3 B+ w# r
  21.                                 //printf("header ok\n");; K5 H& _# g8 ~- w5 h9 ^
  22.                                 receiveBuff[0]=header[0];         //buf[0]& }3 p5 H" R! j- ]  P& w( I% o
  23.                                 receiveBuff[1]=header[1];         //buf[1]5 R( D% R: h( I5 D/ G! H  I; X
  24.                                 USARTBufferIndex = 0;             //缓冲区初始化
    - x8 @+ }: ?. N4 Q. ?) D
  25.                                 checkSum = 0x00;                                  //校验和初始化
      ]4 \  V2 J$ G# T
  26.                         }
    # _- l) z5 P0 o1 ]( R- Y
  27.                 }! }: P+ U9 o4 p: i) s2 g
  28.                 else
    2 S! O* F0 m; w: {2 R
  29.                 {
    6 K6 d9 H9 ]0 H7 g( i5 n- {
  30.                         USARTReceiverFront = USART_Receiver;  
    ( q( {- ?: X2 n+ O9 W+ h, D8 v' P
  31.                 }' w9 O6 I) V5 I% s
  32.         }
    " u# p8 k6 I: }$ c4 p
  33.         else
    1 J) h$ t* s4 |* l' K
  34.     {
    6 L' X' g9 r/ y- \* }
  35.                 switch(USARTBufferIndex)
    ) O# p0 E6 o/ Q, w! f! E1 `
  36.                 {
    , k% o' p" u- C* G/ ~
  37.                         case 0://接收控制指令+ k# A# D( \! K" I; E; f
  38.                                 receiveBuff[2] = USART_Receiver;
    1 ~  h& w# l/ c/ l9 W& U4 e
  39.                                 *p_crtlFlag = receiveBuff[2];                                //buf[2]
    $ w- U. s/ y9 Y. A" S5 c
  40.                                 USARTBufferIndex++;/ t! E- H+ V3 \: l1 N
  41.                         case 1://接收左右轮速度数据的长度
    * c$ [$ i8 j$ b0 R. b3 z9 l" E+ \
  42.                                 receiveBuff[3] = USART_Receiver;/ e4 f8 T( W( z3 x7 ?
  43.                                 dataLength     = receiveBuff[3];            //buf[3]
    # U& P4 J" f4 l+ I8 A1 }
  44.                                 USARTBufferIndex++;
    # W  W1 T& ^6 h. j
  45.                                 break;                                7 t; b' K& i9 M3 ]" g5 Z3 n
  46.                         case 2://接收所有数据,并赋值处理 0 H) o' O$ `; Q
  47.                                 receiveBuff[j + 4] = USART_Receiver;        //buf[4] - buf[11]                                       
    ; ^$ A0 b! d# l  @8 [9 e5 ~8 J
  48.                                 j++;
    2 O4 @; _1 d# a/ t! ^
  49.                                 if(j >= dataLength)                        
    0 S1 R5 C* D" b1 B
  50.                                 {0 h3 M! S( k  Z( {6 Y" `
  51.                                         j = 0;
    9 }) v/ A. F1 t, [$ ^6 g  t7 \- D
  52.                                         USARTBufferIndex++;
    # R2 M8 A- P& G1 @" V' x6 {
  53.                                 }
    ! ~. m1 i. o& d
  54.                                 break;3 y( s. K3 H3 P# O+ y- u
  55.                         case 3://接收校验值信息* Y* g9 N, h4 X& C! ~
  56.                                 receiveBuff[4 + dataLength] = USART_Receiver;
    3 R" _$ J9 i8 x, M) h
  57.                                 checkSum = getCrc8(receiveBuff, 4 + dataLength);
    # y; L; |  y3 F  M& j
  58.                                   // 检查信息校验值# a: Y& b! r( y" V
  59.                                 if (checkSum != receiveBuff[4 + dataLength]) //buf[12]* h. n4 o0 ?! u: e1 d* L9 j
  60.                                 {% u, w7 [' \" P0 Q9 k! h
  61.                                         printf("Received data check sum error!");: M# }4 }9 O9 Y; @* C
  62.                                         return 0;  w0 p% c9 v3 {# Q. ]' ^
  63.                                 }, h  Y9 {3 t( ^2 P
  64.                                 USARTBufferIndex++;" {$ ^  c  w/ P: Q: K
  65.                                 break;
    & M* @6 C3 E' }
  66.                                 
    ' k  J% _. O, z
  67.                         case 4://接收信息尾
    " u  r" F) O7 D: u5 V! z- N
  68.                                 if(k==0)
    9 |' Y, K9 V& I/ }" Z! m  P
  69.                                 {: r1 d9 Z# x; a2 p* W5 r. n
  70.                                         //数据0d     buf[13]  无需判断
    4 }# B# t& Z7 `
  71.                                         k++;  {: G$ m2 D' J! ^1 `
  72.                                 }
    + V; l6 h/ S& T4 u1 I4 n
  73.                                 else if (k==1)' N4 [+ z9 h% z9 D
  74.                                 {
    - n' u" N& R' N7 `
  75.                                         //数据0a     buf[14] 无需判断( `/ g- _% Q9 y: W) r* J% [9 g4 T

  76. & W& I9 a% }1 F# S2 s0 u
  77.                                         //进行速度赋值操作                                        * J2 R/ \% H/ d0 @+ d3 ^, c( h
  78.                                          for(k = 0; k < 4; k++)
    ) |! I7 R8 n, E6 y$ `+ d
  79.                                         {' Z5 N* f% t4 E6 t- K. m
  80.                                                 leftVelSet.data[k]  = receiveBuff[k + 4]; //buf[4] - buf[7]- p' Z; R  }: x$ ?6 P+ J5 r% c4 X
  81.                                                 rightVelSet.data[k] = receiveBuff[k + 8]; //buf[8] - buf[11]
    # T9 q' Y% P$ Y0 n
  82.                                         }                                & R; _4 n, U. A- S$ U
  83.                                         0 r- w$ K* d8 A% ^
  84.                                         //速度赋值操作) Q: h! E2 u( w) ~6 U+ Z+ I4 a& M1 t
  85.                                         sscanf(leftVelSet.data,"%f",&(*p_leftSpeedSet));2 V5 W' J) N8 Y* y( o
  86.                                         sscanf(leftVelSet.data,"%f",&(*p_rightSpeedSet));9 q% p" }) Z* q+ k1 g" ^0 M$ Q! r* `
  87.                                        
    9 L9 v8 L( E+ \4 f
  88.                                        
    ) v4 o% @* K* |) e' ^
  89.                                         //-----------------------------------------------------------------9 Q0 T- O# J3 }' _! P, R
  90.                                         //完成一个数据包的接收,相关变量清零,等待下一字节数据
    / a& F) u$ S& ]/ g: X
  91.                                         USARTBufferIndex   = 0;
    ' B) d1 j: I8 Y
  92.                                         USARTReceiverFront = 0;
    6 a" l# z  T9 y; A0 F5 i3 D. D" ]: t; ^
  93.                                         Start_Flag         = START;
    ' l" X3 m. _0 y! K6 N
  94.                                         checkSum           = 0;
    - ?: X; R- ]- C, [# z, O" z
  95.                                         dataLength         = 0;6 K# b3 m) a5 U. b- T! i* h$ X( V
  96.                                         j = 0;0 `& Y7 i& G, J
  97.                                         k = 0;
    ' \% A4 x3 o$ a" y" T
  98.                                         //-----------------------------------------------------------------                                       
    - x8 E3 E2 [* i  r; H: @" Q
  99.                                 }
    ! @- `+ L6 ]) }; {1 P7 |- @
  100.                                 break;! w2 @5 g# G' T0 W7 J9 t1 }3 _  J8 ?
  101.                          default:break;
    # R; x4 q, r+ ^& ?/ o7 L
  102.                 }                " ?7 A9 P- U! m
  103.         }
    + ?' }( A8 p+ L' |) ?3 ^$ U& y
  104.         return 0;
    ! M6 e" }9 ~6 r: ~7 a
  105. }" t+ L- b. ~* b  P# s
复制代码

9 m3 H5 e; L1 w6 {7 A发送信息函数:2 V' F% }2 X$ Y8 m/ P

0 ]# F: u" P9 V4 x3 u- i+ m* s
  1. void usartSendData(float leftVel, float rightVel,float angle,unsigned char ctrlFlag)
    8 p: e! F" c9 i4 Z3 \
  2. {& ]# W. L% w3 n/ D8 {0 y5 `; ^
  3.         // 协议数据缓存数组  D+ B5 i" H2 N1 Z
  4.         unsigned char buf[Message_Length] = {0};
    ) C9 A6 q: U- Y! o% u. t( T& ]
  5.         int i, length = 0;
    7 e% W. a- S. E1 d# B7 F
  6. : d( X' v6 F! a" L* D6 p% c
  7.         // 计算左右轮期望速度
    : P4 Y" ?+ U4 ]' c6 J2 t/ c
  8.         leftVelNow.d  = leftVel;
    ) I+ P9 a; W4 w0 E) |
  9.         rightVelNow.d = rightVel;$ k0 {8 u& c8 H. W  I9 b0 a7 F
  10.         angleNow.d    = angle;
    ( c: Z, E5 m. |! z
  11.         , g/ W% _2 M2 C# s  x
  12.         // 设置消息头$ X! S) I& T/ g% o- i. M
  13.         for(i = 0; i < 2; i++)7 f) @- t2 Z1 s
  14.                 buf<i> = header<i>;                      // buf[0] buf[1]
    % n% q" f$ _% ]) v+ {. m- S0 `& H
  15. + [' D: ^1 B+ w/ f
  16.         //设置命令位: Q- i1 @* J5 `) S7 f9 m/ L. n* H
  17.         buf[2] = ctrlFlag;                                                         // buf[2]0 N# Q4 z9 D2 q- B  U7 z
  18.         
    # e7 T) v' A2 b5 ]# E4 z
  19.         //设置数据长度" i. E0 i- q. y$ ?2 b6 I! `; X
  20.         length = 3 * sizeof(float);
    : O5 C) ]% Z" H! T/ p
  21.         buf[3] = length;                                                         // buf[3]6 h' k1 N. c& w! g
  22.         
    ' p7 J$ y% z6 k
  23.         // 设置机器人左右轮速度、角度
    2 X. r* U! K: |" Y
  24.         for(i = 0; i < 4; i++)6 k$ j0 I. C$ N/ D8 H: q
  25.         {
    ) O, M6 L9 X- Z! ?7 A) C
  26.                 buf[i + 4] = leftVelNow.data<i>;         // buf[4] buf[5] buf[6] buf[7]
      K% K: J) z; v
  27.                 buf[i + 8] = rightVelNow.data<i>;        // buf[8] buf[9] buf[10] buf[11]
    . C: `- d7 d8 h/ @. J1 [& R& r
  28.                 buf[i + 12] = angleNow.data<i>;           // buf[12] buf[13] buf[14] buf[15]
    - K* q3 G5 [/ v1 x8 t$ ]
  29.         }
    - ^& ^' n* p+ S6 X7 ]/ g
  30.         7 n4 T& i' @7 p  O& y
  31.         // 设置校验值、消息尾' P( l+ C9 q# v
  32.         buf[Message_Length - 3] = getCrc8(buf, 4 + length);  // buf[16]
    * k. _! W) a/ s' P3 F
  33.         buf[Message_Length - 2] = ender[0];              // buf[17]- q; t6 q2 ]3 H3 Z4 f7 \0 d
  34.         buf[Message_Length - 1] = ender[1];              // buf[18]
    ! ~+ B, H9 v' ~2 R8 |, z
  35.         
    5 f9 h% M/ h' E9 n$ R& w+ T
  36.         //发送字符串数据
    0 T' t' D8 s( o: F3 u
  37.         USART_Send_String(buf,sizeof(buf));
    5 I2 @5 t0 Y5 ?6 b2 V* v
  38. }/ a6 X4 O3 }4 C# ~" x; E& S
  39. 4 ]7 ]5 h7 g; E2 W
  40. 循环冗余校验:; G0 ?, ?9 c2 Q/ f; p3 X9 N

  41. * s" F5 U8 R5 a- n
  42. unsigned char getCrc8(unsigned char *ptr, unsigned short len), t0 Y2 |8 J+ l$ `
  43. {
    5 W: j! V0 g# c4 u3 s( m6 e+ z/ I
  44.         unsigned char crc;2 H( u1 C' }' s0 q) G6 R
  45.         unsigned char i;% t, R1 U& [! {% `! s$ i
  46.         crc = 0;
    & G3 q4 K2 c" r/ z* P4 q! W
  47.         while(len--)
    * i1 D$ f; M* `2 X9 L* C
  48.         {
    ! }' z* D: q- I9 y) j5 V
  49.                 crc ^= *ptr++;2 W8 W8 v: F& S5 @3 c, t8 h7 L" `7 W
  50.                 for(i = 0; i < 8; i++)4 m7 P0 t8 d9 Y9 [
  51.                 {- g9 r3 v6 i4 s2 g9 [( r6 ^
  52.                         if(crc&0x01), K' U' J# h+ k, |( J  ~, M! l) i- P
  53.                 crc=(crc>>1)^0x8C;
    ! Y4 m" ?5 v- ?: P/ z6 Y
  54.                         else
      o' Q! X7 B: U' A8 G
  55.                 crc >>= 1;2 T. c3 j5 Z( Z3 p4 d
  56.                 }3 G$ T. d9 A! i0 A2 ~
  57.         }
    : U% `8 O# q$ ^
  58.         return crc;
    7 q. K8 T: @* F- D( |$ U1 e2 G
  59. }</i></i></i></i></i>
复制代码

& c+ ~+ t5 K+ a- L然后我们只要在中断服务函数中调用收发信息函数,即可实现与ROS的通讯。
( R# {& @) C: M0 b/ Y' p$ [2 Y7 L) M. c5 S
总结
) U" ?7 m9 E5 u# ?# Z: D% l; j串口通讯的ROS部分,使用到了boost::asio库,等我学会了再更。% E% Z. [  W& u

$ }( W! q, K/ D
( @1 |) [7 p8 j1 \8 A6 W5 e+ G. N+ Z/ h3 ?
收藏 评论0 发布时间:2022-4-26 22:26

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版