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

【经验分享】STM32 CAN通讯测试(环回模式)

[复制链接]
STMCU小助手 发布时间:2022-2-5 23:00
1.CAN通讯的理解
 想学习CAN通讯,那么要对通讯协议有一定的认知。通讯协议是指通信双方对数据传送控制的一种约定。约定中包括对数据格式,同步方式,传输速度,传送步骤,检纠错方式以及控制字符定义等问题做出统一规定,通信双方必须共同遵守。
 CAN通讯全称控制器局域网通讯,是用来在局域网中高效传输,处理信息的一种通讯方式。它采用数据块编码的方式,数据块根据帧类型的不同有四种格式,可使不同的节点接收到相同的数据,然后再根据各节点内CAN配置选择处理还是丢弃该信息(这与TCP/IP协议栈的链路层的MAC地址过滤很相似,是可以互通理解的),CAN的位流是按照非归零(NRZ)码方式编码,一个完整的位电平有显性和隐性两种方式。显性和隐性是根据CAN总线上的差分电压VCAN1H-VCAN1L, 若小于阈值则为隐性位,代表逻辑1,大于阈值则为显性位,代表逻辑0,这种将单电平转换成两根差分线的方式提高了电路的可靠性,不过也决定局域网里同时只能有一路数据传输,因此CAN通讯是半双工的。
2.CAN通讯帧格式
 CAN报文有四种不同的帧类型:
 (1).数据帧:数据帧将数据从发送器传输到接收器。
 数据帧和可以使用标准帧和扩展帧两种格式。它们用一个帧间空间与前面的帧分隔。

0 g+ F# x9 \! Q
786514-20151018103121991-1243402744.png

; C0 H+ F* b9 d% R% J' v
       1).帧起始(SOF) 标志帧的开始,由一个“显性(0)”位构成。只有在总线空闲时才允许节点发送(信号),其它所有节点必须同步于首先开始发送报文的节点的帧起始前沿。
     2).仲裁场 由标识符和传送帧类型(RTR)组成的仲裁场
     标准帧格式:
    
786514-20151018103958179-1352434789.png
! v% R1 H" R$ t) k0 F6 Y4 k3 U
    扩展帧格式:
* n  W; C5 e! f% W# e$ {& K3 @
786514-20151018104028429-1256746731.png
    
           对于数据帧 RTR恒为0,SRR恒为1,因此可以根据仲裁场起始第12个字符数判断是标准帧还是扩展帧。
    3).控制场 保留位R1,R0(恒为0),以及帧长度选择位DLC(4位)构成的。
    4).数据场 由数据帧里的发送数据组成,长度由DLC控制,但小于等于8字节。
    5).CRC场 由CRC序列(CRC Sequence),以及CRC界定符(CRC Delimiter)构成。CRC序列之后是CRC界定符,它包含一个单独的“隐性(1)”位。
    6).应答场(ACK Field) 2位,包含应答间隙(ACK Slot)和应答界定符(ACK Delimiter),当接收器正确地接收到有效的报文,接收器就会在应答间隙(ACK Slot)中写入显性位(0),在返回给发送器,完成一次通讯(半双工).
      ① 应答间隙
     所有接收到匹配CRC序列的节点会在应答间隙期间用“显性(0)”的位写入发送器的“隐性(1)”位来组成应答数据。
      ② 应答界定符
    应答界定符是应答场的第二位,并且必须为“隐性(1)”的位。因此,应答间隙(ACK Slot)被两个“隐性”的位所包围,也就是CRC界定符和应答界定符。
        7).帧结束  由7个隐性位构成,代表帧的结束。
  (2).远程帧:总线节点发出远程帧,请求发送具有同一识别符的数据帧。
        
786514-20151018105841897-138322512.png
  
             远程帧除了RTR位默认为1,没有数据场外,其它与数据帧相同,不在赘述。
  (3).错误帧:报文发送过程中,检测到任一节点出错,即于下一位发送出错帧,通知发送端停止发送。
           
786514-20151018110054835-1359426977.png
/ f6 @' O* _  |: J
           错误标志:有两种形式的错误标志:激活错误标志和认可错误标志。
    1).激活错误”标志由6个连续的“显性”位组成
    2).“认可错误”标志由6个连续的“隐性”的位组成,除非被其他节点的“显性”位重写。
           错误界定符:错误界定符包括8个“隐性”的位。
    错误标志传送了以后,每一个节点就发送一个“隐性”的位,并一直监视总线直到检测出一个“隐性”的位为止,然后就开始发送其余7个“隐性”位。
 (4).过载帧:接收端用于要求发送端延缓发送下一个数据帧或者远程帧。

# }! L* r. F7 Y$ d2 R
786514-20151018110435632-1510780758.png
         
         1).超载标志: 过载标志由6个“显性”的位组成。过载标志的所有形式和“激活错误”标志的一样
       2).过载界定符包括8个“隐性”的位,具体动作与错误界定符一致
  CAN通讯是数据块编码的半双工通讯方式,没有主从设备区别,因此发出报文的节点为该报文的发送器,该节点在总线空闲或丢失仲裁前恒为发送器;如果一个节点不是报文发送器,并且总线不处于空闲状态,则该节点为接收器。
3.CAN通讯的STM32实现
CAN协议是比较复杂的一种通讯协议,因此需要在学习如何使用stm32实现前了解协议本身的很多内容,下面就可以开始stm32中CAN协议环回测试,用来简单的理解CAN协议的测试。既然是要STM32实现,那么步骤的设计如下:
   (1).工作原理图
  了解了CAN通讯,下面进入正题,CAN通讯连接首先看原理图如下:
+ M3 U" _1 S2 ?& p9 t" ]
786514-20151018112329366-259251431.png
         
      从上面可以看出CAN1_TX: PD1    CAN1_RX PD0
  (2).CAN硬件驱动配置
  CAN通讯端口配置还是比较简单的:
  1. GPIO_InitTypeDef GPIO_InitStructure;: s7 T8 I# A' s
  2.     & g: x- d8 g5 R" V5 L/ P/ O
  3. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);5 I4 T8 H- n& U, m( x) {- O
  4.     $ Y' {$ a! Y1 k0 `
  5. GPIO_InitStructure.GPIO_Pin = CAN1_TX_Pin;; ~, a! W7 `% ]1 T, u5 P# o! L9 e
  6. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    ) \" w3 j" T$ N5 n4 C3 h& p  P' C
  7. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     //将CAN1输出端PD1配置为推挽输出模式  % h$ w5 \2 c/ a5 a- Z4 P
  8. GPIO_Init(CAN1_TX_Port,&GPIO_InitStructure);   
    / {1 K3 g1 I( i2 b* J
  9.     9 p6 ?1 L) }* \  D8 ~( _
  10. GPIO_InitStructure.GPIO_Pin = CAN1_RX_Pin;
    ( D* s* E8 h: G
  11. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //将CAN1输入端PD0配置为浮空输入
    7 b2 S8 ]7 b0 {+ D" ]
  12. GPIO_Init(CAN1_RX_Port, &GPIO_InitStructure);
复制代码

& ~1 `! W. _+ a' b! P8 U
     CAN通讯模式配置(因为是简单的测试,因此配置为环回模式,过滤器配置为屏蔽位模式)
  1. void CAN1_MODE_Config(void)
    : J% U6 t( p% E, ?/ T6 v  `
  2. {
    5 o- G5 C  n1 e' i) L* [2 q
  3.     CAN_InitTypeDef CAN_InitStructure;                                
    0 x9 V) X* F# X8 r! t  G7 g# ^
  4.     CAN_FilterInitTypeDef CAN_FilterInitStructure;        , W* \" I7 `# y  @+ Z  H" h0 L
  5.     NVIC_InitTypeDef NVIC_InitStructure;
    , Y/ p" S! d& [) O9 }
  6.    
    8 o5 z& ?( n: K$ r) t3 _
  7.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);- x& r" U# J9 a/ Z- ?7 t5 u
  8.     CAN_DeInit(CAN1);* f' C, C3 y) K3 H. ]
  9.    
    / Q$ w& H; @- M, y; K
  10.     CAN_InitStructure.CAN_ABOM = DISABLE;                     //离线模式由软件实现  [& p6 r. m" _! G
  11.     CAN_InitStructure.CAN_AWUM = DISABLE;                     //软件唤醒
    0 _' Q9 f. B) o
  12.     CAN_InitStructure.CAN_TTCM = DISABLE;                     //禁止时间触发通信模式
    5 r, [8 A5 R( H3 j9 m+ R8 I% f. Y
  13.     CAN_InitStructure.CAN_NART = ENABLE;                      //禁止自动重传
    * z. B3 h7 j9 h: Q1 `
  14.     CAN_InitStructure.CAN_TXFP = DISABLE;                     //优先级由报文的标识符来决定
    0 a: w, v. n# I4 Y, s3 t+ s6 m" i- l
  15.     CAN_InitStructure.CAN_RFLM = DISABLE;                     //接受溢出时FIFO不锁定,下一个收到的报文覆盖原有报文
      _( @' ^  P' v3 o: a
  16.    CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;           //CAN硬件工作环回模式$ a% }6 @$ l& C& i, Z; h* Y
  17.     CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;                  //重新同步跳跃宽度为2个时间单位1 Z/ O9 T9 z* d. }2 ~% M# X
  18.     CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq;                  //时间段为8个时间单位
    - Q/ k1 n0 J3 L( L% k
  19.     CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq;                  //时间段为7个时间单位
    % D' P* `+ }. f  g
  20.    CAN_InitStructure.CAN_Prescaler = 5;                      //设定一个时间单位的长度为5,范围(1~1024)
    / g! \3 n9 m2 m8 m& }3 y0 y
  21.     CAN_Init(CAN1, &CAN_InitStructure);% J2 J( b8 Y  y
  22.    
    1 E  Y0 N8 h3 |* }5 I- O3 M% x
  23.     CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;             //设定过滤器组为屏蔽位模式
    & B9 b% G+ k; n& I) R5 ?, b9 u5 C
  24.     CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;            //过滤器位宽为32位过滤器一个
    : s$ ?# g! N, z0 b# [0 b, W5 H
  25.     CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;                          //设定过滤器标识符高位(32为高位段,16位为第一个)! c" H; f' V; n- o( j/ e6 }
  26.     CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;                           //设定过滤器标识符低位(32为低位段,16位为第二个)3 u% ]# ?3 k# i% @) ~9 J: F* a
  27.     CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0x0000;                       //设定过滤器标识符高位(32为高位段,16位为第一个)* H$ W) J0 {" J- d# D. x
  28.     CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0x0000;                        //设定过滤器标识符低位(32为低位段,16位为第二个)$ @; T9 u' k5 ?1 _% W
  29.     CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;        //过滤器FIFO0指向过滤器03 D) Z4 t' @( H1 F  L
  30.     CAN_FilterInitStructure.CAN_FilterNumber = 1;                               //指定待初始化的过滤器,范围1~13
    , E' @! g5 W/ z: {
  31.     CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;                      //使能过滤器, V# ~$ k& W/ Z% v0 L+ @6 p
  32.     CAN_FilterInit(&CAN_FilterInitStructure);
    ; \8 c8 Z( B! Q3 y' G
  33.    
    / q/ j( Q# S5 \) |
  34.     CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); //FIF0消息挂号中断允许0 i* R" ~: H* A" \$ t
  35.    
    # k" @( i1 @7 R4 G
  36.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;   
    ) `7 o2 ^  Y- n% Q/ p
  37.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;# I$ O: ^$ w  H) o
  38.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;8 Q* {0 L; s* M7 e
  39.     NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;             //CAN1_RX0中断向量表中开启
    , O2 H1 e$ |8 L
  40.     NVIC_Init(&NVIC_InitStructure);
      O# h' F( m4 p9 t, P
  41. }
复制代码
8 a1 O7 e0 }, r
完成了驱动方面的配置,下面要进行的就是发送CAN数据和接收CAN数据的生成和实现了:
  1. //CAN发送帧构成$ _% j; |/ T% l
  2. void CAN_TxMessageInit(uint32_t std_id, uint32_t ext_id, uint8_t ide, uint8_t rtr, uint8_t dlc, uint8_t *pdata)3 f6 [3 c% K6 ?' [& i6 s) N
  3. {" E5 e& u2 O3 u# Z6 p6 T
  4.     uint8_t i;
    + l: [! J" K" u* S, A1 {& l
  5.     assert_param(dlc>8);      9 E- r/ ^! ?6 I5 _( _5 B7 ]
  6.     CanTxMessage.StdId = std_id&0x7ff; //设定标准标识符0~0x7ff  11位/ y8 F  W- @: W1 M5 O
  7.     CanTxMessage.ExtId = ext_id&0x3ffff; //设定额外标识符0~0x3ffff 18位
    % g# \- e+ y2 u. P
  8.     CanTxMessage.IDE = ide; //输出标识符类型,STD(标准标识符)或EXT(额外标识符)4 R8 Y) y% \% ?( p; a$ B$ }5 g2 r
  9.     CanTxMessage.RTR = rtr; //输出帧类型,DATA(数据帧)或者REMOTE(远程帧)
    / |, |/ F/ z2 O8 i' P  n
  10.     CanTxMessage.DLC = dlc; //帧长度,0~87 Q" p( U2 _* k$ j' F
  11.    for(i=0; i<dlc; i++)3 R. k  y' T+ z- z- ?
  12.     {5 j0 c3 j% m. O; M$ `% l
  13.         CanTxMessage.Data[i] = *(pdata+i);( _, ~- t" j/ L8 z+ {
  14.     }
    2 e) C. P$ t' C" t
  15. }
    , Y4 x- H: P; I. Q1 X6 C* q. m
  16.   \1 S: x7 ^( ]3 c: c/ f
  17. //CAN中断接收函数: ^. t) G8 R+ ^: I& I% |) ], e; q- V
  18. void CAN1_RX0_IRQHandler(void)
    ! O0 j% @) M8 Y9 m8 H! c
  19. {
    . n2 ]' U4 h- }" j
  20.     ITStatus Status;
    ( R/ V" Z$ o1 B8 Z4 j
  21.     9 k  q4 v# ~$ D- }, `6 `& X
  22.     Status = CAN_GetITStatus(CAN1, CAN_IT_FMP0);             //判断接受到过滤器中断信号: j! v9 g5 `( l5 r
  23.     ! F! S4 [  B* D( f- t
  24.     if(Status == SET)* m! Y' A5 V% F' u. \
  25.     {, B& B' p" m$ Y+ d& g" ~8 W' O
  26.       CAN_Receive(CAN1, CAN_FIFO0, &CanRxMessage);        //接收一个CAN报文5 F7 i2 ?6 O# E4 ~7 P+ b5 ^
  27.         memcpy(rdata, &(CanRxMessage.Data[0]), 8);            //将接收到的数据转存到rdata数组中
    1 ^6 v+ r% @  ]
  28.     }
    0 U. Y' B, h4 Y$ @( D/ r7 w
  29.    
      O6 b( j4 E+ u) y3 g
  30.     CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0);
    $ J) E! _5 V1 W4 \# ^
  31. }
复制代码

( P# O$ k3 H2 E0 U' c' O! j
因为错误帧和超载帧是有stm32自带的硬件实现的,因此由我们提供的是远程帧和数据帧,单个帧内数据0~8字节,如果要发送多个数据,需要在其上添加传输层实现,具体实现我会在以后在研究(双CAN多帧通讯实验)。
主函数的实现如下:
  1. int main(void)
    / `% c' E0 k. V% @
  2. {      
    " Q% s; s, T- J. v* }. J( d& M
  3.     BSP_Init();                                       //硬件初始化6 ^: E( |) j: P7 {+ Q& Y+ W! [: H/ }
  4.     CAN_TxMessageInit(0x011, 0x0000, CAN_ID_STD,               CAN_RTR_DATA, sizeof(tdata), tdata);    //生成CAN报文
    : p" |. P: x$ B) D. ^  @- h
  5.     CAN_Transmit(CAN1, &CanTxMessage);                         //发送CAN报文
    1 U8 B  V- x- D! S1 Q
  6.     - Y+ w( I8 q1 K! F( V$ D
  7.     GPIO_ResetBits(GPIOD, GPIO_LED_1);2 A9 U1 \1 Z9 I  N# t: d: ?4 U
  8.     ARM_DELAY(1000000);1 ?# ]+ w# L( I3 H  d# p+ p) O
  9.     GPIO_SetBits(GPIOD, GPIO_LED_1);
      B, N$ s1 s7 ^
  10.     ARM_DELAY(1000000);
    $ h9 w5 n' S, V1 X% D' n! W' x( i

  11. ! d' o( ^" R- s2 K5 a& S
  12.     while(1)& H- z. ~( b/ M, u
  13.     {    : @& M- [" W+ g/ V  [. r$ V
  14.         if(CanRxMessage.StdId == 0x11 && CanRxMessage.DLC == sizeof(tdata))  //判断接收到的报文
    ! Y( v% o1 f) q+ {
  15.         {                                                                                                                                   4 s! r/ `% x2 [' C5 a
  16.             if(strncmp((char *)tdata, (char *)rdata, sizeof(rdata)) == 0)
    . g; M2 p; J: C8 t: B
  17.             {
    5 H+ E# D2 ~/ b: c% ]" Y5 a0 i
  18.                 GPIO_ResetBits(GPIOD, GPIO_LED_1);% I; `% R2 B# w* @) w; x; x: ^; `
  19.                 printf("stdID is %x, DIC is %x, receive data is %s \r\n",                CanRxMessage.StdId, CanRxMessage.DLC, rdata);
    % h4 ^9 E% j- e% \' ^! P7 ?
  20.             }
    0 N6 ]  l7 d( w$ ^3 X1 Y% n
  21.            memset(&CanRxMessage, 0, sizeof(CanRxMessage));     //清除接收到的报文            - H0 m( x& j- Q9 ]" b' \
  22.             CAN_Transmit(CAN1, &CanTxMessage);                  //发送CAN报文
    6 y% q' V) f  x1 \& i5 I( A
  23.         } 3 g1 y1 D7 g9 W6 N
  24.         ARM_DELAY(1000000);
    0 `$ ?  O% b$ M  H7 n, m
  25.         GPIO_SetBits(GPIOD, GPIO_LED_1);$ C; [3 F  M7 G0 f/ I
  26.         ARM_DELAY(1000000);
    2 p1 ~' B( j( R$ ^8 N
  27.     }
    + A* _( G5 ~( W
  28. }
复制代码
" F  m1 b4 g! N
如此就完成了简单的CAN环回测试实验,具体实验可通过串口接收端口查看,如下:
: j1 e9 E' z! N1 p8 i
786514-20151018120751210-864793667.png
赞 收藏 评论0 发布时间:2022-2-5 23:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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