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

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

[复制链接]
STMCU小助手 发布时间:2022-4-26 22:26
前言8 e1 X9 Y5 y( G% ~7 l: C3 R2 C
串口通讯对于任何开发板都是非常重要的,也是必学知识之一,通过串口通信可以实现上位机与下位机之间、开发板之间的通讯,可以让我们实时掌握机器人的各个关机的运动状态和传感器的信息。0 r: E. [3 s% k) Y% C4 w+ ]7 P2 {
' q% C, {9 w$ b! t: d
现在的通信协议有很多,比如:UART、USART、CAN、SPI等等,它们功能不同,适用于不同的场合,USART作为单片机之间、下位机与上位机之间最常用的通讯方式之一,它对于数据的收发十分方便,应用日益广泛。/ a" G0 O7 g1 C! W

. Y& d  ^! |5 d$ ?% I  H0 b一、USART通信原理; l6 V' v  O- ]4 T
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)中文名叫:全双工通用同步/异步串行收发模块。- n3 x( P1 f- @; O! D6 O1 |. k: C, f
/ k. G, f$ C9 ]* _) q
先对这个名字的各个部分进行解释:
  Y8 ^: Y5 O/ z# w2 q
0 B/ e) J( p6 b; `0 w$ g$ s 4c96242ab38648818d029a7ea10cab27.jpg
# e1 ~) q5 D. u) s
+ J' ]8 H& Q+ t) @0 R 在平时的工程项目中,我们常用的是全双工串行异步通信方式,虽然串行通信数据是一位一位的发送,但随着近几年科技的高速发展,串行通讯的速度已经逐渐赶超并行通讯了,而且串行通讯方式适用于远距离通讯,比较常用。) E0 m( N( ~4 C3 U  b3 m
* V! A1 Y* U0 q( N& i
USART通讯的数据格式大致是这样:
8 l8 y: }+ w, i. G2 U$ @
+ I; w* g# W7 M8 {: {- W 起始位(0)+串行数据帧(从低位到高位传输)+停止位(1)3 f+ G. b0 L( E* Q! n4 e% b+ ~1 \
: E0 r. ~& ^! I0 }0 ]  f
串行数据帧可以人为设置为8位或者9位,9位是8位数据加上1位校验位(奇偶校验)。
' `9 Z- {* @# w) T( s: G% q  y9 H8 H5 Y( N
另外一个比较重要的概念是波特率,在任何通讯开始之前,通讯双方都要约定好波特率,波特率是每秒发送有效数据的位数(bit/s),双方如果没有约定好一致的波特率,在传输过程中则会出现乱码的情况。# y1 I' K6 `( p9 k

0 ?4 H1 B. J: Y$ q在STM32中,有专门的数据寄存器和特定的引脚负责USART通讯,并配合有相应的标志位,用于帮我们判断数据是否发送/接收完毕,并且也有相关的库函数帮助我们对串口进行配置。
5 _* z) G; x5 T5 r9 e- O* q, O5 |3 u/ z6 V, s& x3 _- ]7 h" [+ k# S2 {
相关寄存器有:发送数据寄存器(TDR)、发送移位寄存器、接收数据寄存器(RDR)、接受移位寄存器。5 [3 r( ~8 V% g. x7 p

4 w& K' r8 _; r相关标志位有:  T8 e9 }4 F) |( k" j( h
) l/ L' v; X% x( r# T# P) o- D3 d0 j
TEX标志位:1:数据寄存器无数据;0:数据寄存器有数据
5 u3 e6 g) E; D3 @4 T, r" JTX标志位:1:发送完成;0:发送未完成
6 p6 S( P, q6 [6 O( Z, O2 wRXNE标志位:1:数据接收完成,可以读出;0:数据未收到& G& N; i+ W" n% i0 R; e
具体知识在中文参考手册P517,大家可以详细查看
* Q% u6 Q  }4 F2 \7 W
- X& u$ Q  h. a5 b9 v+ R4 l二、STM32与PC通讯

& L* k6 t& M2 j4 R, N! U( u/ ^STM32与PC通讯需要进行一些配置,这里实现由PC端向STM32发送一个数据,STM32接收到后再发回到PC端,该实验需要用到串口调试助手。- J. q. B' x0 W- t* \# h' [
# q0 g) I( K; u/ X2 m2 A7 N
STM32可以作为串口通讯的引脚大家可以通过数据手册进行查看,比如PA9(TX)和PA10(RX)$ l: |/ G! ?5 O, k8 |9 y
5 j' b9 f7 X1 p" R
94]6SASVE]E8VUPMKX_YD40.png ! M* m; e+ e' d% v

+ _1 h$ N+ u& w6 Q) v# X8 Y串口通讯一般都配合中断进行使用,下面讲解串口配置过程:+ ^# {" X- [0 R0 C8 }3 o

$ v3 P2 a$ I0 t% `3 WUSART配置步骤
: f4 T, o# Q$ j$ N% i
& h1 D& s# ?% o/ M步骤 函数 1.串口时钟、GPIO时钟使能 RCC_APB2PeriphClockCmd 2.GPIO端口模式配置 GPIO_Init 3.串口参数初始化 USART_Init 4.开启终端并初始化NVIC USART_ITConfig、NVIC_Init 5.使能串口 USART_Cmd 6.编写中断服务函数 USART1_IRQHandler* w6 }8 h& e. W2 o

  r! R. q2 r$ f; bmy_usart.c 代码如下:
! H4 V7 H6 g# b6 r' Y3 r& i2 R* b+ }) G- g, l4 F& O- w
  1. void My_Usart1_Init(int bound)
    0 L# b! s; w2 p: o% G' O  {' m
  2. {
    , P! C9 Y. q+ j7 M/ t) N# w
  3.         /*1.串口时钟、GPIOA使能*/
    : e# N9 {* P" L( }  }) R
  4.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA,ENABLE);
    : Z/ Z5 @4 ~6 n' K1 q7 r- Q
  5.         0 m  f% b, E* X+ \
  6.         + E7 _; O. O5 g6 N8 e. q% [9 w
  7.         /*2.GPIO端口模式设置*/
    ) A6 S: c! E. q6 }+ Z$ o2 a' w
  8.         GPIO_InitTypeDef GPIO_InitStruct;
    % h4 a* X. f$ r/ x
  9.         /*TX*/
    - i! L* y' w4 v/ v. Y, `) A
  10.         GPIO_InitStruct.GPIO_Pin = Usart1_TX;
    1 c9 ^* v5 ?3 U
  11.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;7 l# S# Y3 a6 E- D' t
  12.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    7 @/ @2 J: H! \9 ^5 s) o
  13.         GPIO_Init(GPIOA, &GPIO_InitStruct);
    & S! N5 ^! e& p! V% v: X- D4 p; d6 j
  14.         /*RX*/
    & L' }8 b, H9 |5 r
  15.         GPIO_InitStruct.GPIO_Pin = Usart1_RX;
    * M9 [0 @3 n. b6 Y: ?
  16.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;& ]" I7 r2 o& U5 W" C6 V) ^
  17.         //GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;( l( c" I* |  M+ x
  18.         GPIO_Init(GPIOA, &GPIO_InitStruct);
    % @6 k5 |8 u1 o
  19.         
    1 r% X8 A! N  n9 Q; y" E
  20.         
    # _  z+ G3 C, ^; H( Z3 V7 N; G
  21.         /*3.串口参数初始化*/8 j. ~! T" ~' I# r9 q
  22.         USART_InitTypeDef USART_InitStruct;
    + {. B. b+ `. Y; w: k$ [
  23.         USART_InitStruct.USART_BaudRate = bound;//波特率
    0 A7 e) S/ K0 L4 z( @
  24.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制0 ^$ h2 b& q0 }6 D5 K$ q* n
  25.         USART_InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//USART模式
    2 i% i. B( ]/ |
  26.         USART_InitStruct.USART_Parity = USART_Parity_No;//校验位& M# |7 |3 l9 \' O7 G
  27.         USART_InitStruct.USART_StopBits = USART_StopBits_1;//终止位8 g$ k8 \: B) N
  28.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;//数据长度,如果有奇偶校验就设置为9位
    . y5 X, f$ M7 j
  29.         USART_Init(USART1, &USART_InitStruct);
    " R. N: N2 Q+ I6 K# ?
  30.         
    " S; i8 D% d# U4 f9 F
  31.         /*4.开启中断并初始化NVIC*/
    , \+ o$ D$ ]+ z* }
  32.         USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开中断,开启接受中断后,接收到数据则会进入中断服务函数
    % q7 R5 [  b9 g$ \& S7 C5 T
  33.         
    ( M3 ]  d: y% ?: Y7 |
  34.         NVIC_InitTypeDef NVIC_InitStruct;: ]& u) W' B! @
  35.         NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    + w& J4 G  D/ i4 j! K
  36.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;9 ?9 S* f6 N/ \& W: c
  37.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
    7 O3 b3 n& P' B6 I) b
  38.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;' a8 ]$ X5 l! k; l: \& `
  39.         NVIC_Init(&NVIC_InitStruct);//初始化NVIC' x' E9 V* ?8 E, @" `5 J# G+ u
  40.         
    1 C9 S7 F/ V  r1 W; O& _+ G& T8 C
  41.         /*5.使能串口*// j9 W% U1 e/ {: H( Z+ k
  42.         USART_Cmd(USART1, ENABLE);# G: Y  Z% q. P8 W
  43.         3 n) i) c% Y4 n9 k* `7 C
  44.         /*6.编写中断服务函数*/
    ; ^8 h/ G) g  d+ L4 Y  [
  45.          3 K8 L% |0 Z! S+ Q# D: ^
  46. }
复制代码

& l1 p% ?/ o% X( ]! ~0 d$ Y* B% V1 A( ?' C
进行如上配置后,当STM32接收到PC发送的信息就会进入中断服务函数,中断服务函数接收到数据后再进行发送。: F7 V$ S$ ~, J0 D, T5 ?
+ _. e8 {3 M( ~' K* t5 J! _+ ^" F. _. x
中断服务函数代码:' O) v! N! B8 m

: E& c  |/ f+ e4 q/ [
  1. void USART1_IRQHandler(void)" z5 l$ o/ b+ m" K) X* {$ S
  2. {
    7 d! T1 w: n2 s" K+ V: z
  3.         u16 RX_From_PC;1 I4 c6 ]1 O/ X; {
  4.         if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)! s" w+ [  g& F6 @
  5.         {' a7 Q8 q% v$ q6 Z$ `6 P
  6.                 USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清空中断标志位
    9 H3 [  V% u0 ~7 c3 h& u! [3 O6 T+ A
  7.                 RX_From_PC = USART_ReceiveByte(USART1);//接收PC端发来的消息0 C; Q! ~2 e* n2 y, |; y! Q
  8.                 USART_SendByte(USART1,RX_From_PC);//将数据发回到PC端
    ' C' v" }; J4 H1 Z4 z- w
  9.                 ! ~1 C: e. v# X  O! b2 ~% s( t$ ^9 w
  10.                 //usartReceiveOneData(&TargetVelocity,&TargetVelocity,&RX_Cmd_Form_Ros);//接收ROS发来的消息
    % w7 T5 v7 E1 v3 t8 A
  11.         }
    0 y4 F# l" l6 F, [2 Y& P# ~
  12. }
复制代码

! Y' R* W5 c8 e6 W+ I在这里我们没有使用官方固件库中提供的收发函数 USART_ReceiveData 和 USART_SendData,而是使用了我自己对他们进行重写的函数,在其中加入了相关标志位的判断,这样可以保证收发过程中不会发生数据覆盖。; D+ Z. j5 q3 C% J8 c" @
2 m" G, z( G" d, r- r& @
重写后的函数:+ P' r. m* d6 z
3 w! d5 y$ T" R2 C
  1. void USART_SendByte(USART_TypeDef* USARTx, uint16_t Data)
    3 {9 v4 a/ V0 }% i1 \* Q) A
  2. {5 F0 y2 Y: f, ~( z2 w3 |% i3 g
  3.   /* Check the parameters */
    ' A4 D* d" d7 M) V4 s) Z
  4.   assert_param(IS_USART_ALL_PERIPH(USARTx));# N3 p+ b" l( ~) W% b
  5.   assert_param(IS_USART_DATA(Data));
    / K3 X/ i4 ^7 O4 u  R

  6. . b' U4 }# c8 T8 A1 U
  7.   /* Transmit Data */
    ( r: j2 x  A7 Y+ p5 H0 E
  8.   USARTx->DR = (Data & (uint16_t)0x01FF);3 c# t* o; r! n! I5 D6 T" S: m
  9.   while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
    % A5 D/ }5 G4 |! Z
  10. }
    4 p! x8 m5 s' e8 w+ Q

  11. % ?+ d$ i% ~2 H! ^. p
  12. void USART_SendString(USART_TypeDef* USARTx,char* str)
    ! y# m, ^7 X" e' w) S% P
  13. {* j6 ]) \' L/ T! @* t0 r
  14.         while((*str) != '\0')! U9 C% R% Y9 R. q9 P! Q
  15.         {, d7 J- z( u# c5 W/ k
  16.                 USART_SendByte(USARTx, *str);
    # f3 f4 L7 R$ W8 ?
  17.                 str++;
    5 D) L. a* {/ D4 E, S  z" U3 e
  18.         }
    . `7 H) O  `! u- @+ G
  19.         while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
    ( A0 c. C! B0 V  }
  20. }
    ; h- M. o- H( z9 ?7 d

  21. ) U9 q/ n; x$ K) V! q
  22. 7 |; X2 Y" a0 [/ l4 T6 V

  23. 6 X0 ^4 }$ `# B6 F( k% a
  24. uint16_t USART_ReceiveByte(USART_TypeDef* USARTx)
    2 d7 S5 h/ I/ {* W* z
  25. {
    . J! i9 U- u+ @
  26.   /* Check the parameters */- j+ B6 [6 n& p3 J- H0 e
  27.   assert_param(IS_USART_ALL_PERIPH(USARTx));4 X$ N, ^! A2 ?" E
  28.         
    2 ?  q. W0 @6 q# ?
  29.   while(USART_GetFlagStatus(USARTx,USART_FLAG_RXNE)==RESET);0 m2 g5 I- W, L% y1 t

  30. ) J! q6 @% A! F
  31.   /* Receive Data */5 o3 r5 x$ p6 n9 H
  32.   return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);7 {6 g" v: `1 U; I8 E9 s; H
  33. }
    # y. x  Q' l0 x" l* [7 E
复制代码
5 R; G  @: g: {# x% s
在主函数中进行相应的初始化,我们就可以通过串口助手与STM32进行通讯了。
# O+ y5 L; }# |0 D
3 ?. ?/ R; W: G2 H4 b {[]G6A`}0`ZOOK8XC_%)6S9.png
  Z+ \: d) N  C1 [$ M& i$ {, d6 S6 w- `
另外,Keil5是没有终端的,但我们可以通过一些设置也使用printf函数,让数据收发更加方便,我们需要对fputs和fgets这两个函数进行重定向(其实只重定向fputs即可),需要在程序中加入如下代码:# B, D0 I' A/ a% b

5 o/ ]6 Z5 m  I
  1. /*重定向这两个函数*/
    + v8 x" [/ h1 ]) R1 d
  2. int fputc(int ch,FILE *f)
    : m1 E+ F5 O* q% L+ r0 ]+ w
  3. {
    : R3 R! j0 C9 G. L
  4.         while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
    " m' ^* Y5 H/ u0 [
  5.         USART_SendData(USART1,(uint8_t) ch);
    " k: ^- L- E; ?8 H
  6.         return ch;: O* s" l( O: k5 H) a- i. d
  7. }
    9 n7 ?& h0 q. z: ?. ^& m, g

  8. + F# S, c4 \  T3 B5 m9 h& l& D4 g
  9. int fgetc(FILE *f)
    7 j+ Z* U6 U5 p4 h' S; O6 q6 A
  10. {2 I" r/ Z$ e5 D1 s
  11.         while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);. g% B& Z+ t, [
  12.         return (int)USART_ReceiveData(USART1);
    , @/ r, W! K7 r0 o! L, j! c
  13. }
复制代码
* Y! p  ^% a( q, Z4 H3 r  v1 ?: x! [
printf函数使用的是半主机工作模式,需要使用微控制器,需要进行如下配置:9 }' p8 i! V* m+ m- ?4 a

; W" {: C* D! \8 Z( |4 i7 p {3{FUBC9AU]ED(F8UYKCYID.png
/ a6 ~) j+ _' D. c/ x
0 k# \: g7 x8 ]4 ] 这玩意功能少一些,如果不想用,则需要在程序中再加入以下代码,这是printf函数的底层程序:) N# k2 L, o# W0 U7 A
! ~7 `# a* U* n7 G
  1. #pragma import(__use_no_semihosting)9 P. @( d( k6 w& v7 `: d: U

  2. ' D4 O/ K% w( D2 x* N- @
  3. struct __FILE" \' A9 A1 O+ v- Z3 c. h' t
  4. {, x! H$ K/ X1 G4 W
  5.         int handle;
      l% H% G/ N" F: Z4 `8 r0 P* \! [) Y6 H
  6. };
    3 ]7 v" |: e* A5 W9 }) b! a  I
  7. + D  j3 I! w, X# _# x5 ~0 M
  8. FILE __stdout;
    $ S# u. O) h( T: z; b: N
  9. _sys_exit(int x)
    : W- o& z; l$ B  C6 k
  10. {
    : o3 p) z. c/ P' ]/ T6 s. A
  11.         x = x;
    - D9 i1 Q" m: R4 i  _4 N: y
  12. }
复制代码

5 D. M8 A9 ]" W. s编译后,我们就可以与PC端进行愉快的通讯了。
" M, C+ o6 R8 ^. c- l0 C9 u5 D( X! A$ @
三、STM32与HC-06通讯9 h, n* N# w3 A" Z3 u
STM32与HC-06的通讯与PC端的通讯类似,知识接线方式的不同而已,只需要进行如下正确的接线,即可完成顺利地通讯。8 P# a6 E, K  ~2 V! o1 Y
  u2 }7 }) y& K% h# A- y
3EIZ2B@ALCG6F}(CGE(VO`W.png
: t5 c8 t: b# {! Y
$ I( f7 M5 X  E" u+ r2 u: B5 v这里要注意的是,给HC-06供电时,需要提供3.6V-6V的电压;另外,要注意看一下STM32的引脚输出电压是多少,HC-06的输入输出电压都是3.3V,比如像Arduino的输出电压为5V,这时单片机的TX在接HC-06的RX时需要分压!
* D* D0 T# J5 [* b! j
5 z! n% \1 t! j% e, Z& \1 X1 u+ v" j四、STM32与ROS通讯

9 q; [7 X2 u2 D* t' a* U# nSTM32与ROS通讯时,需要定义一个协议,以保证数据传输的可靠性,这个协议是STM32与ROS通讯时最广泛使用的协议,协议格式如下:0 s2 x6 C; \+ `" w
7 u& q; U! a  T4 P0 ~9 C
数据头55aa + 控制命令 + 数据字节数size + 数据 + 校验crc8 + 数据尾0d0a
! Y1 }2 F: s+ z: H8 @8 s0 }4 g) V/ Z* _3 u
在通讯时,我们只要对接收到的信息进行解码,即可获得有效数据信息,比如:设定的速度值、航向角等,保证了数据收发的可靠性。
4 `+ h% `" q4 E6 X" H
: ]$ _* U/ U; H先定义相关的变量:
9 P, M5 L( l( N$ w0 L' k' [0 y
0 L0 C; N" R  M8 `+ P
  1. //数据接收暂存区
    9 v+ g7 B* Z% t1 X' `
  2. unsigned char  receiveBuff[Message_Length-4] = {0};         
    7 z8 K/ X( _. W! h, Y
  3. //通信协议常量. H  x6 e* I. Z. R
  4. const unsigned char header[2]  = {0x55, 0xaa};
    2 ^6 v) w" {0 V
  5. const unsigned char ender[2]   = {0x0d, 0x0a};8 t6 Y" M  \- g# m+ }

  6. ' f' b: Y* t4 g/ }( s7 g9 ^+ b( w( Q
  7. //发送数据(左轮速、右轮速、角度)共用体(-32767 - +32768)
    7 F9 `! x. y. p- B
  8. union sendData5 c: Z3 K8 ?9 b8 e( K( n
  9. {
    ) u5 _$ ~& @! o' b( F' q
  10.         float d;! M# _- W* A: o8 _' f
  11.         char data[4];
    3 n3 i6 E) K2 L
  12. }leftVelNow,rightVelNow,angleNow;
    / S& d- X; _8 e0 p9 Z
  13. + J: z* R! p2 R2 |0 \/ }8 h
  14. //左右轮速控制速度共用体8 L! B) Z3 ?) [6 w
  15. union receiveData. X8 d  Z/ h& I/ e
  16. {
    ( E1 t: `! ?" y* E9 H' e1 R. @4 i
  17.         float d;7 O1 g3 S# d; A( ?7 b: B
  18.         char data[4];  ]; i1 }# l, E! i( h" w
  19. }leftVelSet,rightVelSet;
复制代码
0 N1 ?. q+ a) ]& _+ I

2 U6 h1 U/ z% M/ I接收信息函数:" a  C3 H; u/ A

  1. . g. X8 W5 Y# i! P  R: N
  2. int usartReceiveOneData(float *p_leftSpeedSet,float *p_rightSpeedSet,unsigned char *p_crtlFlag)
    4 p% A* O; D8 q1 M5 G( u
  3. {
    , B* m4 m, d7 d" P) Z
  4.         unsigned char USART_Receiver              = 0;          //接收数据; a8 ?4 i) ], p, \
  5.         static unsigned char checkSum             = 0;
    2 L6 j4 M" }; w/ U8 e
  6.         static unsigned char USARTBufferIndex     = 0;
    8 p7 t1 M+ z$ L3 J, X* n; Q/ ^$ f
  7.         static short j=0,k=0;7 k' d3 u, ~' @( u5 i2 H: z
  8.         static unsigned char USARTReceiverFront   = 0;- T7 x0 m/ ]9 l  l
  9.         static unsigned char Start_Flag           = START;      //一帧数据传送开始标志位$ e# W* r! t0 f7 q3 s
  10.         static short dataLength                   = 0;
    ' z2 D- M1 L( e0 w6 B. B. g

  11. 0 A# n' L- x6 X. L
  12.         USART_Receiver = USART_ReceiveByte(USART1);2 a, F* u1 c) z0 d* l8 `
  13.         //接收消息头
    $ E. y$ s3 ]# C2 u' S' l
  14.         if(Start_Flag == START)
    * @5 ^& S  n; W
  15.         {
    7 v' V3 C3 M& `# E4 g
  16.                 if(USART_Receiver == 0xaa)                             //buf[1]4 [( c" ?" Q# ]+ G- ~- g& k
  17.                 {  
    / C+ Q; E. c/ m; ~. H3 J
  18.                         if(USARTReceiverFront == 0x55)        //数据头两位 //buf[0]. e: }9 B4 U' O$ G7 `3 F, y8 y" t
  19.                         {! R; n1 B% U- ~
  20.                                 Start_Flag = !START;              //收到数据头,开始接收数据, k% }- i0 Y. y) s1 y/ \# Q
  21.                                 //printf("header ok\n");; a% \1 k6 G! Z% ^; [
  22.                                 receiveBuff[0]=header[0];         //buf[0]
    8 @# a+ D$ z6 C3 v5 ]( J8 H# J
  23.                                 receiveBuff[1]=header[1];         //buf[1]
    ) V" h9 v& @% |5 d
  24.                                 USARTBufferIndex = 0;             //缓冲区初始化
      [  L8 _' W2 P2 g1 l# t
  25.                                 checkSum = 0x00;                                  //校验和初始化/ _  z# p2 I0 S4 O# c8 {' B. J
  26.                         }
    ) X7 W$ N# W" G1 w- ^6 _
  27.                 }/ [8 H& A- ~. V1 b0 ?* V7 A
  28.                 else 6 {- R) I7 g  w5 k, B. Q
  29.                 {3 M  c3 R% q* `4 l& b( B
  30.                         USARTReceiverFront = USART_Receiver;  4 r  G8 y4 A, q. t. F& K
  31.                 }
    8 S8 C( u: g. c- g/ o- ?, z- @$ e
  32.         }
    1 s4 k+ m1 }4 N6 q" {
  33.         else2 Z: L# [+ Y  a: a' a8 Y; C
  34.     { # o7 K& U( \$ }4 |! k
  35.                 switch(USARTBufferIndex)
    - y$ b; `/ r, s# h7 ?
  36.                 {
    , F; U7 M# O9 t# @
  37.                         case 0://接收控制指令$ t5 }6 o0 k4 A
  38.                                 receiveBuff[2] = USART_Receiver;
    2 ~7 |! ^2 T$ f4 x
  39.                                 *p_crtlFlag = receiveBuff[2];                                //buf[2]# t/ Q4 m  x: a8 a' U3 t
  40.                                 USARTBufferIndex++;4 P# k  o4 Z' f6 g
  41.                         case 1://接收左右轮速度数据的长度
    % ~: E" s( |: F; `
  42.                                 receiveBuff[3] = USART_Receiver;
    % Q) q  Y. B$ @0 q) ~
  43.                                 dataLength     = receiveBuff[3];            //buf[3]& k# E" O7 x! O- J5 m! N5 B9 G
  44.                                 USARTBufferIndex++;
    ; U$ O2 K: K& Q& e
  45.                                 break;                                
    / i( f3 e& f. D% D- _1 k* @
  46.                         case 2://接收所有数据,并赋值处理 ( j( k! s+ f* \7 ^
  47.                                 receiveBuff[j + 4] = USART_Receiver;        //buf[4] - buf[11]                                       
    2 j$ _& ?: [( @& y
  48.                                 j++;4 b" m  t0 A. ]
  49.                                 if(j >= dataLength)                         + Q) e; {$ Z$ V. i
  50.                                 {
    * i" Z- x" X/ _
  51.                                         j = 0;
    1 Z8 r# ~1 [, m( M* ]( {
  52.                                         USARTBufferIndex++;
    6 h6 |3 x( u& x- d/ V7 [
  53.                                 }3 Q3 r3 A0 Q* x7 [: k
  54.                                 break;
    ; ^- S% d2 A, a$ d. B6 ^
  55.                         case 3://接收校验值信息
    5 P5 A' I* D0 K" ^/ G
  56.                                 receiveBuff[4 + dataLength] = USART_Receiver;& {+ x: g2 S; d# ^
  57.                                 checkSum = getCrc8(receiveBuff, 4 + dataLength);6 a2 B* J7 W* J+ s
  58.                                   // 检查信息校验值' d/ K7 |1 e% P$ [9 U( S; Z
  59.                                 if (checkSum != receiveBuff[4 + dataLength]) //buf[12]! `: f. `2 z' c9 {5 S) n
  60.                                 {
    , l4 w# `, t3 c9 F  h0 m* q' U
  61.                                         printf("Received data check sum error!");! o# t+ m+ A  @+ R; D6 S
  62.                                         return 0;# A# j' T9 ~+ U7 n9 ?
  63.                                 }
    8 o4 \, I, f, D. `1 }5 p0 q
  64.                                 USARTBufferIndex++;. N1 T# c; j, F6 c4 d
  65.                                 break;
    ' r+ M6 ^  s4 M; s- l+ [
  66.                                 
    ' Z; x6 n+ w, ?7 @" E( J
  67.                         case 4://接收信息尾
    ! E7 U! y; g1 `3 G0 z' D1 Y# |
  68.                                 if(k==0)$ v; Y3 y% b- g
  69.                                 {% d* O7 P* p* b. V
  70.                                         //数据0d     buf[13]  无需判断
    0 O# Q( ~  [' h
  71.                                         k++;  s  M2 q- }; D2 V
  72.                                 }/ Z1 Q  G' q2 I3 W+ y: [1 j
  73.                                 else if (k==1)
    1 N1 [0 b" L! S! @2 f; Z, s: N# b3 p
  74.                                 {/ j. _% X# T; ?/ a. E, W
  75.                                         //数据0a     buf[14] 无需判断
    9 `1 Z# R! C$ {) w5 J; S: o- C

  76. " j4 t( Y- n* e  z
  77.                                         //进行速度赋值操作                                        8 Y4 T6 w" y( s" ~' R
  78.                                          for(k = 0; k < 4; k++)
    ' r) o2 X4 \8 C- c* s
  79.                                         {, C) R( p* P- m3 Z: L& _
  80.                                                 leftVelSet.data[k]  = receiveBuff[k + 4]; //buf[4] - buf[7]2 L( V! b6 `( N+ X; A
  81.                                                 rightVelSet.data[k] = receiveBuff[k + 8]; //buf[8] - buf[11]
    6 }9 A6 A. K5 d- I; k
  82.                                         }                                
    7 J% v4 [: Q" R, o) T
  83.                                         . A! ]9 W: e# D, n6 o6 u+ L/ h
  84.                                         //速度赋值操作- R* ~0 a0 A6 @5 \
  85.                                         sscanf(leftVelSet.data,"%f",&(*p_leftSpeedSet));
    3 P$ r; J4 k. P  R6 _1 c+ t
  86.                                         sscanf(leftVelSet.data,"%f",&(*p_rightSpeedSet));
    2 @6 k- I. ~3 ?4 V8 H8 V3 i
  87.                                        
    9 [/ ~8 ?" I6 I4 G+ ^3 H# f5 h$ G
  88.                                        
    4 H( V* a9 d! m& k/ x. U& |
  89.                                         //-----------------------------------------------------------------! ^' F3 F+ e6 W) {8 c5 P
  90.                                         //完成一个数据包的接收,相关变量清零,等待下一字节数据$ _2 @' B: c8 H
  91.                                         USARTBufferIndex   = 0;# ]* M$ [6 o, |3 |/ W& S$ S1 S
  92.                                         USARTReceiverFront = 0;+ c0 ?1 ?8 P9 Q
  93.                                         Start_Flag         = START;
    , A) r% X( \  V  t
  94.                                         checkSum           = 0;
    3 k1 A5 R1 K& g5 G, k& d
  95.                                         dataLength         = 0;
    - o# i; w& i9 p, n( V, q
  96.                                         j = 0;. w  z( @  C6 D( n
  97.                                         k = 0;  P* f* \& O3 n0 T/ R( _
  98.                                         //-----------------------------------------------------------------                                       
    $ j. I3 P. V& T; x3 j
  99.                                 }% b' x& f( ]% y: {6 m/ _; e
  100.                                 break;/ `+ E) j8 v. B8 W5 Y& A% v6 \
  101.                          default:break;
    % t/ {% h8 ~  ?) G
  102.                 }                " e6 ]1 C: y7 M8 o$ C
  103.         }  i4 @; F+ J6 I, c( C
  104.         return 0;
    2 s$ U0 I0 }( ?' m1 ?7 H( }# n% X
  105. }
    : d7 _! }8 x% s' E$ K
复制代码
( R% t& p8 }8 \8 D; [" _
发送信息函数:& P/ G: q6 ]' t+ c" m4 [

8 ]+ y2 P9 `2 o2 T0 ~
  1. void usartSendData(float leftVel, float rightVel,float angle,unsigned char ctrlFlag)8 V5 S2 K! b4 a3 I" H7 @4 S% T
  2. {. r$ y) k6 }$ O0 x& j5 y
  3.         // 协议数据缓存数组; y, [) {# l. u0 f/ ~
  4.         unsigned char buf[Message_Length] = {0};
    2 w; @0 t- C# V" K& i. x
  5.         int i, length = 0;" u. n/ D: Q2 X. d% g0 U
  6. $ N1 j, s) m* q/ Z, K. P( r( ]
  7.         // 计算左右轮期望速度) q. r6 U! ?9 t/ `/ Z6 w. M
  8.         leftVelNow.d  = leftVel;
    & y/ k" N' D7 n1 u8 {* t7 D; K
  9.         rightVelNow.d = rightVel;5 q) L; b0 A4 j% s& Q2 z* z
  10.         angleNow.d    = angle;* l& l: I; F' O  I: ^  I
  11.         
    & q4 L$ w/ J* t4 e+ W- h
  12.         // 设置消息头6 N8 d/ @  d% s/ y
  13.         for(i = 0; i < 2; i++)
    ) e7 U+ c. ]) r9 U4 M' G- ]$ H
  14.                 buf<i> = header<i>;                      // buf[0] buf[1]
    , A" P4 n) Z6 q6 D* A. i1 L) c

  15. % j+ x- m5 C! s
  16.         //设置命令位* V" ]+ m6 c% a8 p( ?6 Y
  17.         buf[2] = ctrlFlag;                                                         // buf[2]
    & O5 W0 e$ B0 Z) i: Z
  18.         0 A' l% d. |4 }2 [# v4 ?
  19.         //设置数据长度: E2 l3 p- G5 b; `: F" ]" m3 F- c
  20.         length = 3 * sizeof(float);% F# I: A  o$ i% I/ [1 A+ ]
  21.         buf[3] = length;                                                         // buf[3]
    7 _( A! X9 Y" A& }6 d
  22.         
    ) v: l& [+ {3 K; k& j* H, h
  23.         // 设置机器人左右轮速度、角度
    4 p  T( E: l& b" d) H( P
  24.         for(i = 0; i < 4; i++)
    * c! K1 j; U/ L" N9 ]
  25.         {
      A# `$ K$ W' ~4 R
  26.                 buf[i + 4] = leftVelNow.data<i>;         // buf[4] buf[5] buf[6] buf[7]
    ( f/ ^. F' b& S2 m' K
  27.                 buf[i + 8] = rightVelNow.data<i>;        // buf[8] buf[9] buf[10] buf[11]! d, J8 ?/ z& {5 z* ~$ |+ e/ ?
  28.                 buf[i + 12] = angleNow.data<i>;           // buf[12] buf[13] buf[14] buf[15], D1 _. ^- `4 z  h( a
  29.         }% v3 C/ Y: }7 r2 V
  30.         7 H3 v$ C( i* n! m) p" a( L
  31.         // 设置校验值、消息尾
    " y7 @- _' E/ o9 @0 M
  32.         buf[Message_Length - 3] = getCrc8(buf, 4 + length);  // buf[16]
    ! ~( i; I" {4 i5 g) M% J5 U, S3 f
  33.         buf[Message_Length - 2] = ender[0];              // buf[17]
    0 N5 y$ A- w; i6 l" B5 {7 o! |/ p6 e. R6 [
  34.         buf[Message_Length - 1] = ender[1];              // buf[18]
    6 b. G* j1 Q$ R- C- `
  35.         ' e4 y# Z: t0 d% z. a7 K
  36.         //发送字符串数据/ m; V9 V- f: M
  37.         USART_Send_String(buf,sizeof(buf));
    ( f! ~% f/ I0 E0 q" i" {
  38. }
    & V; V/ N& ]2 s, o) a* S7 o

  39.   p# y# n& H" M- \8 P: t3 a2 ~
  40. 循环冗余校验:
    & k+ _& u; H9 u8 e* X6 ^

  41. $ R6 X6 B- H# W( D+ ]
  42. unsigned char getCrc8(unsigned char *ptr, unsigned short len)! Q& O+ J/ s$ ?. |
  43. {2 t& q2 u% ?( `, d& L  r. F
  44.         unsigned char crc;  B/ d. r) n- i
  45.         unsigned char i;9 C5 j5 k" t3 V
  46.         crc = 0;
    7 X/ K, R+ ^' R0 l9 z
  47.         while(len--)
      ~8 {! U* s" G- ^& d" _! A
  48.         {
    " u- l' w3 c! S. R* e5 e
  49.                 crc ^= *ptr++;; q; H1 K3 X7 ~5 z
  50.                 for(i = 0; i < 8; i++)4 m) Y" r5 K# N+ n, }
  51.                 {
      u, E0 J2 n) j0 ~1 a  J; ^3 I6 @3 M& [
  52.                         if(crc&0x01)
    ( [2 b! ]8 r+ \9 b$ s6 n0 M: C
  53.                 crc=(crc>>1)^0x8C;
    + F. U* {% p4 Y! r$ B. Q8 K
  54.                         else
    1 B9 t5 z# |" V
  55.                 crc >>= 1;
    , a/ Z, ~3 K; D4 B
  56.                 }9 V' v+ [, @) X  a
  57.         }, `" ^. D; C5 p1 q1 C
  58.         return crc;
      b, z) y2 J' s% V1 n+ Y
  59. }</i></i></i></i></i>
复制代码

' {9 B- S6 [' i6 R, k然后我们只要在中断服务函数中调用收发信息函数,即可实现与ROS的通讯。" {$ Y* ]2 Y; N
6 {4 P6 T4 ?% o+ ?0 O
总结5 H1 x& j4 C' ]9 D+ q2 o. H
串口通讯的ROS部分,使用到了boost::asio库,等我学会了再更。
8 K  @8 x: }) U7 p! ]4 e
/ h+ O% n! Y' ^9 @- }, {& C2 z0 m  p8 \) W. u" d2 p6 x

7 o6 s6 T/ _9 K5 B+ x# o6 E
收藏 评论0 发布时间:2022-4-26 22:26

举报

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