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

STM32的CAN环回测试

[复制链接]
攻城狮Melo 发布时间:2023-6-28 14:42
CAN基础知识介绍文中介绍了CAN协议的基础知识,以及STM32F4芯片的CAN控制器相关知识,下面将通过实例,利用STM32CubeMX图形化配置工具,来实现CAN通讯的环回测试" O& f! C9 k2 |3 j  D: W3 U/ p

3 h9 @$ x; o: l) M1. STM32CubeMX配置
$ E7 }: k/ ^5 k2 {5 `
% }3 w4 S% S- _' q+ F
+ ?8 Q( r3 `- z/ l: o" |
⏩ CAN是挂载在APB1总线上,设置PCLK1时钟频率到最大45MHz2 g  V& S6 R' ^7 J7 W  G+ T  T
6 [% }  @/ V$ m* a" t
微信图片_20230628143758.png
* p- I8 S! h/ S% S% @  J7 T6 a
  \: `9 K+ S0 j2 y
5 H7 @! O9 x& ?3 h! |$ r
⏩ 激活CAN1,配置位时序参数,其他基本参数以及工作模式(此处设置为Loopback环回模式)
. p) t! `/ n+ Z( J" S9 f9 w' `( L2 }' i3 P4 x

  d9 J8 f6 f5 n; @ 微信图片_20230628143755.png
- X1 y( m" Q' t* W' G3 D
7 }0 S$ d  C. s! B, e0 w- {CAN波特率的计算公式:只需要知道BS1和BS2的设置,以及APB1的时钟频率,就可以方便的计算出波特率。比如设置TS1=8、TS2=6和BRP=6,在APB1频率为45Mhz的条件下,即可得到CAN通信的波特率=45000/6/(8+6+1)=500Kbps; C$ ]# f2 l+ n7 F

# h# ]2 ?( O2 Q 微信图片_20230628143751.png
4 L9 E$ I7 N5 ?+ x5 U: R
" M8 L6 \# H- I& V4 P: \  h4 Y

$ ~) Z8 O7 Y- v) ?/ H⏩ 激活USART1作为调试串口,配置相关LED对应的GPIO引脚作为指示灯
! W7 T6 t  d3 C  V1 U* c
- S' K" }0 c4 Q% }

/ [6 L- {/ k6 g6 l3 t) @2. MDK-ARM编程
; O- |2 S3 h2 Y: |# X3 Z$ y
) i! ?: [, `8 b/ R+ F4 m0 U2.1 几个重要的结构体
9 O) u7 o1 F7 G* D& \% k2 H9 G" j% _

+ k9 `0 L0 E4 N6 l6 s8 g, m( a⏩ 主控寄存器 CAN_MCR:负责管理 CAN 的工作模式: D9 }: S, p/ M8 ]* K
  1. typedef struct: L7 e5 r( d3 t4 P5 u; l9 ~
  2. {
    9 l+ W) K+ l2 n4 u, P" \
  3.   uint32_t Prescaler;       /* 配置 CAN 外设的时钟分频,可设置为 1-1024*/
    # W! Q1 R( |! y; D
  4.   uint32_t Mode;            /* 配置 CAN 的工作模式,回环或正常模式 */
    4 X7 k- V3 N' @
  5.   uint32_t SyncJumpWidth;   /* 配置 SJW 极限值 */
    4 w: _7 |4 f" ~# G0 w! v) h7 l2 _" R
  6.   uint32_t TimeSeg1;        /* 配置 BS1 段长度 */* p1 _* U: E( O% A6 X/ P
  7.   uint32_t TimeSeg2;        /* 配置 BS2 段长度 */; W6 y2 k1 l3 K5 y: z2 k7 Q
  8.   FunctionalState TimeTriggeredMode;    /* 是否使能 TTCM 时间触发功能 */5 L- P, I# @" v% [
  9.   FunctionalState AutoBusOff;           /* 是否使能 ABOM 自动离线管理功能 */
    # N5 `" g& [- S( o1 l
  10.   FunctionalState AutoWakeUp;           /* 是否使能 AWUM 自动唤醒功能 */
    & K. ?& O8 u8 n6 ]7 U; V7 g
  11.   FunctionalState AutoRetransmission;   /* 是否使能 NART 自动重传功能 */2 y$ w3 _& C$ ]# _! F) f& q- M
  12.   FunctionalState ReceiveFifoLocked;    /* 是否使能 RFLM 锁定 FIFO 功能 */
    8 V- m2 T( X+ x0 Q2 b
  13.   FunctionalState TransmitFifoPriority; /* 配置 TXFP 报文优先级的判定方法 */
    7 y, E* V, g* I! V
  14. } CAN_InitTypeDef;
复制代码
; W4 ~) Q6 ~* a, j' j$ v( ?5 j: k
⏩ 发送及接收头结构体:主要用于构造发送报文,以及接收报文。收发发文时,需要自定义头结构体变量
4 [& F" @6 H# G
$ I* U! C% ?8 p, k5 b! |: X
. @3 Y* }/ F! j' Z' h
  1. typedef struct
    + t0 A7 y" g/ m
  2. {
    0 d" D2 E- Q7 A! J: J
  3.   uint32_t StdId;       /* 存储报文的标准标识符 11 位,0-0x7FF. */
    4 F7 ^5 m. V6 K8 [
  4.   uint32_t ExtId;       /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */2 H6 N% K0 h" P1 `* d  [% `0 s
  5.   uint32_t IDE;        /* 存储 IDE 扩展标志 */5 Y4 B7 X' J/ L! G4 ?8 ?4 I) A
  6.   uint32_t RTR;        /* 存储 RTR 远程帧标志 */+ r+ |& n  C- s1 o# Y2 z+ }
  7.   uint32_t DLC;        /* 存储报文数据段的长度,0-8 */, x6 D# K9 O4 d7 p
  8.   FunctionalState TransmitGlobalTime; " U- U' q* C/ @
  9. } CAN_TxHeaderTypeDef;
    % I- d9 K7 n! _
  10. + H. S! y) a& \
  11. typedef struct
    " d0 T4 A" @! m) Q: G
  12. {' \0 M- T; B9 b* m
  13.   uint32_t StdId;      /* 存储报文的标准标识符 11 位,0-0x7FF. *// m* @8 |; h4 {6 S) h1 p
  14.   uint32_t ExtId;      /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */" ?- Z: B) W& B5 W( D2 `( J! W% I" o
  15.   uint32_t IDE;        /* 存储 IDE 扩展标志 */6 G4 X/ w, y- q9 _
  16.   uint32_t RTR;        /* 存储 RTR 远程帧标志 */
    % O. D) F, B0 t
  17.   uint32_t DLC;        /* 存储报文数据段的长度,0-8 */
      k. a: @6 c! @' s& E! U
  18.   uint32_t Timestamp;
      M! I! Y- E) y0 R. B/ E
  19.   uint32_t FilterMatchIndex;
    9 W6 [( O. N' H! Z1 Q. q/ [
  20. } CAN_RxHeaderTypeDef;
复制代码
4 G, m! E2 x+ j
1 y3 Z0 @" d5 G# }3 g
⏩ 过滤器结构体:STM32CubeMX不会初始化过滤器的相关内容,需要自己添加4 b5 }, G$ i# @3 a0 E

# @7 t' {! d# {+ V( ?2 p/ O( c  R
/ K+ b' [% V; B6 a$ d; j9 i
  1. typedef struct
    ) j' r0 g! E& j* }) c- Q& J+ p4 c
  2. {
      G; P& B  n" I! A
  3.   uint32_t FilterIdHigh;         /*CAN_FxR1 寄存器的高 16 位 */0 F; t4 R+ a3 A* ~  {" z2 }
  4.   uint32_t FilterIdLow;          /*CAN_FxR1 寄存器的低 16 位 */
    9 x- r% E( N1 q
  5.   uint32_t FilterMaskIdHigh;     /*CAN_FxR2 寄存器的高 16 位 */
    * Z$ R1 T) Z) d
  6.   uint32_t FilterMaskIdLow;      /*CAN_FxR2 寄存器的低 16 位 */: {( X! t# d( \
  7.   uint32_t FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收 FIFO */
    " r# y" X. v# m9 m! @" R# K
  8.   uint32_t FilterBank;           /* 筛选器编号,范围0-27,CAN1是0-13,CAN2是14-27 */
    8 Z( W9 U+ A! U
  9.   uint32_t FilterMode;           /* 筛选器模式 */; ?) V1 H9 X5 G) E# N
  10.   uint32_t FilterScale;          /* 设置筛选器的尺度 */' h# z1 O4 d( Z4 T
  11.   uint32_t FilterActivation;     /* 是否使能本筛选器 */
    ! f4 u9 S* ^" u; s
  12.   uint32_t SlaveStartFilterBank; /* CAN2起始过滤器组 */0 r9 H8 y% A5 w( s
  13. } CAN_FilterTypeDef;
复制代码
, d7 C. d) x" T% ?" `; q

. L* v6 T" R0 G6 c) u6 {. P, ^2.2 程序编写( a! _8 h4 W0 R- {) z
3 F  L$ s2 B, R% x6 e

' B2 }/ D  j8 ]9 D: v⏩ 生成工程后,打开can.c文件,可见STM32CubeMX已经对位时序参数、其他基本参数以及工作模式进行了初始化。但是并没有初始化过滤器的相关内容,因此需要我们自己添加,并在CAN初始化时调用
/ P3 E' {8 c1 Z% R! a) v0 |2 B* Y( t6 \7 ~, L

# ]& l# H# q  K( ~2 d$ P
  1. //下面的设置只使能了FIFO0,并不过滤任何消息
    ; X8 e- f  C: c3 G: G4 M0 s
  2. void CAN_Filter_Config(){8 ^7 @* F0 h' ?( ]7 |* n- K9 d
  3.   CAN_FilterTypeDef sFilterConfig;
    * W# [7 M9 A: r8 r5 K
  4. 3 R8 T6 Y6 `4 O
  5.   sFilterConfig.FilterBank = 0;      //筛选器编号, CAN1是0-13, CAN2是14-27
    6 p3 w! U5 g2 U2 x) A% D
  6.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //采用掩码模式
    4 n4 e3 a" Z/ L/ R( R! W* s' X
  7.   sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置筛选器的尺度, 采用32位
    ! f" l& D& P" h0 O& ?" A7 P& Q
  8.   sFilterConfig.FilterIdHigh = 0X0000;    //过滤器ID高16位,即CAN_FxR1寄存器的高16位
    ' Z5 \4 K( W: ?7 o7 y% Q
  9.   sFilterConfig.FilterIdLow = 0X0000;     //过滤器ID低16位,即CAN_FxR1寄存器的低16位; L% U& r  f$ O$ e
  10.   sFilterConfig.FilterMaskIdHigh = 0X0000;   //过滤器掩码高16位,即CAN_FxR2寄存器的高16位. e# U6 D6 W- e3 c
  11.   sFilterConfig.FilterMaskIdLow = 0X0000;    //过滤器掩码低16位,即CAN_FxR2寄存器的低16位
    , S5 V/ L9 n8 C8 T
  12.   sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //设置经过筛选后数据存储到哪个接收FIFO
    ( Q8 I4 n4 b% ^9 r# u8 E& K4 z* {2 F
  13.   sFilterConfig.FilterActivation = ENABLE;   //是否使能本筛选器: y& l3 v  O9 D+ o; G, A6 y
  14.   sFilterConfig.SlaveStartFilterBank = 14;   //指定为CAN1分配多少个滤波器组2 B8 U( `& {! Y6 v# }9 C9 U

  15. ! {) z6 Q4 P1 N
  16.   if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)& E7 a7 j8 w% x! i% d( }: O' W
  17.   {
    " h; I  m) k; K; j7 g( ^4 I
  18.     Error_Handler();/ k* H) g  z$ y7 u" z
  19.   }; X: @/ b7 W0 |4 f9 [  r  n7 c
  20. }
复制代码
& b' Q- \7 r6 n' X5 K
⏩ 编写发送和接收数据函数:此处将格式固定为标准数据帧,ID为12; s" E" M5 J' t( o. K: Q' v4 A$ F& K
) A( S1 B+ X1 ^- x. q  S5 a
8 L4 ?% v7 H/ n; V6 p
  1. uint8_t CAN1_Send_Msg(uint8_t *msg, uint8_t len){
    7 I% Y1 l  M5 U/ F7 u
  2.   uint16_t i = 0;5 ^2 ]/ c, U& U8 |+ ~+ A9 }' v
  3.   uint32_t txMailBox;: U0 j) U1 Z$ n; F+ \; ~
  4.   uint8_t send_buf[8];5 L1 W/ X7 Z: f7 u" N3 Z$ Y
  5. 6 Z% L% j/ ]9 D
  6.   txHeader.StdId = 12;
    - G, y3 Q# ^. J" T& n
  7.   txHeader.ExtId = 12;* f8 ^  q/ c0 v% k# f0 V9 b
  8.   txHeader.IDE = CAN_ID_STD;
    " M& n  E# ?8 W& ^2 j/ D% u
  9.   txHeader.RTR = CAN_RTR_DATA;8 J% l$ D. ]/ ?- ?/ p4 S7 e8 i/ w( I
  10.   txHeader.DLC = len;* f- X" v6 }1 F1 Z" c: b

  11. " y* t- m5 m+ |1 H- I& @
  12.   for(i = 0; i < len; i++); M* P& }/ L2 J3 T) S3 V
  13.     send_buf[i] = msg[i];3 e9 i$ ^& c, t
  14. 5 S6 F. K3 u; Q  K
  15.   if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, send_buf, &txMailBox) != HAL_OK)
    ; I6 ]# f' F  M$ r! ~
  16.     return 1; 8 \! ?; W8 Q; e7 f! h) I/ c( `
  17.   return 0;
    / E4 E3 J- c4 x. A" h" W  L3 s
  18. }/ G+ A9 K) T6 F! H8 g$ e
  19. 3 U7 t. x( c, S' M) k" s# c
  20. uint8_t CAN1_Recv_Msg(uint8_t *buf){" L0 R9 y+ Q3 q# ?  Q3 I
  21.   uint16_t i = 0; ; W6 y. i. P! S# t
  22.   HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rxHeader, buf);8 G! C, \+ ]4 z: j
  23. 4 G/ j5 F# k4 p
  24.   if(rxHeader.IDE == CAN_ID_STD)
    . ^9 C3 w; A; l- m+ H
  25.     printf("StdId ID: %d\r\n", rxHeader.StdId);
    + f4 X/ P4 U1 ~# C, N6 R0 f4 o. t
  26.   else1 a+ x+ W9 W$ \; j9 m4 H0 Q  j
  27.     printf("ExtId ID: %d\r\n", rxHeader.ExtId);& x  R1 D  l2 s2 G! J2 k) B
  28. * K" S  J1 k" u; A  e9 K; `% ?
  29.   printf("CAN IDE: %d\r\n", rxHeader.IDE);
    0 [! |' h  }, s( y: Y7 H
  30.   printf("CAN RTR: %d\r\n", rxHeader.RTR);: r& p% |7 `. X% Z
  31.   printf("CAN DLC: %d\r\n", rxHeader.DLC);/ E+ q* o7 K% \/ d0 \+ o, ]- |
  32.   printf("Recv Data: ");
    ( \4 v! c: R( c4 G  _% @1 ^- X: I

  33.   @, b2 G  P- W  ?
  34.   for(i = 0; i < rxHeader.DLC; i++)1 A# N" x. C% s) a* a2 @0 d
  35.     printf("%c",buf[i]);: F, W4 S# G) p6 l2 R& k

  36. : g8 r( S  O" p) x% Q
  37.   printf("\n");! {# G: V! s7 @% u, i
  38.   return rxHeader.DLC;5 V- G" F4 y& C( D6 n9 }* u
  39. }
复制代码
$ s: f2 P  |/ ^0 B
⏩ 默认Cubemx生成的代码并没有can start,没有调用HAL_CAN_Start(&hcan1) 来使能CAN,因此需要在CAN初始化代码中添加0 j4 T7 ?; h; K  Q0 l! J  S

+ ?* x  X3 c& S9 t: x1 H
1 X% e9 j4 O  s' ^" d6 X) B+ G1 e+ w
  1. void MX_CAN1_Init(void){
    ; X, h/ H* Z8 ~7 F  e
  2.   ......' A* x# n. g% p& R
  3.   /* USER CODE BEGIN CAN1_Init 2 */% w3 x6 b3 ?+ N( L" F0 d$ ?  r
  4.   CAN_Filter_Config();- {* t$ S( B0 l' ?+ b
  5.   HAL_CAN_Start(&hcan1);
    / V! ?8 A/ ?/ y
  6.   /* USER CODE END CAN1_Init 2 */
      b* \( n; g9 B6 D  g* w. J: w
  7. }
复制代码

  X2 k; P& g* C! |! O⏩ 主函数main.c中,代码如下
1 @* k$ q$ m% H0 f# b* i6 z0 T
$ V0 O; i' b. s: j, ^* A7 L2 o  e

! a& S: J0 ]8 B& c
  1. int main(void){* \" S1 c1 w) _! J
  2.   HAL_Init();1 C& H) f7 r2 y9 Y1 i
  3.   SystemClock_Config();# W8 N: m4 D5 I' |  |5 v  b
  4.   MX_GPIO_Init();
    ; z% T& P' y# E3 N2 ?, i, s2 f
  5.   MX_CAN1_Init();
    1 r) z4 x: l9 I' K
  6.   MX_USART1_UART_Init();
    1 H- J  E! e: S7 L- b2 I% y, i. W
  7.   /* USER CODE BEGIN 2 */
    " E% Q7 r3 J& S7 L3 f; U8 V; G9 N% d6 |
  8.   uint8_t ret,i;/ o. W( u, L1 ?7 Y% {( z
  9.   printf("CAN Testing....!\r\n");
    * |) C0 ~6 g% _& }3 V6 `. a
  10.   uint8_t txdata[8] = {76, 79, 79, 80, 66, 65, 67, 75};
    / a( u4 C6 L% y6 O8 Q+ |3 ^
  11.   uint8_t rxdata[8];: N0 u1 u1 J* O9 \
  12.    /* USER CODE END 2 */
      s# ]2 E7 N% }$ X3 k2 O5 ^
  13.    /* Infinite loop */0 ]6 y; G& F! s. @
  14.    /* USER CODE BEGIN WHILE */) ]5 N! c; q, U: @! N- f4 h$ D
  15.   while (1)3 D  ]% \3 S; J+ M9 o
  16.   {3 x1 R0 J8 V  L+ y
  17.     HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
    - m- G* c; r9 N* a+ F4 p
  18.     HAL_Delay(1000);
    ; J7 y8 G3 H& u; m
  19.     printf("Start Send data...\r\n");
    / ~# e& k' ^4 j$ h
  20.   + Y6 z: `9 r7 T2 N! j+ f
  21.     ret = CAN1_Send_Msg(txdata, 8);
    , V3 Z- y$ K' H4 z
  22.     if(ret == 0)
    + |$ m/ ?" i; _
  23.       printf("CAN Send success!\r\n");$ \- @- r/ X' H8 Z/ g
  24.     else 5 x8 n$ Q3 W" `' O. o
  25.       printf("CAN Send failed!\r\n");/ S5 n/ U: K* Z* C5 L# ~
  26.   
    ' P! f# {( m3 {6 T8 J6 C
  27.     CAN1_Recv_Msg(rxdata);
    - |* B( m/ l# ~& E" w7 s
  28.     printf("+++++++++++++++++++++++++++++++\r\n");5 T/ A- T  R7 {, e7 e  q* p
  29.     /* USER CODE END WHILE */5 G5 {" Y( D! P: Z
  30.     /* USER CODE BEGIN 3 */
    / a2 F, q# W5 i( S; ~- g* f9 W) q+ z
  31.   }
    6 h# y& j) P) G& C+ g! ?1 H# P
  32.   /* USER CODE END 3 */8 v; J7 ~5 U6 c! ~# q2 D/ _
  33. }
复制代码

/ m% Z% q0 a! Q* l/ p3. 下载测试) j! m0 ^* c1 X' @$ @
" O& F7 N: N0 n
4 m# V' Q; {# e6 [, c; v& Q; |6 g2 r
编译无误后下载到开发板,可以看到系统运行时D1指示灯不断闪烁,串口不断的打印CAN环回测试的数据. M3 r3 d& r! y! G7 u; N8 S+ ]

: m; X3 W5 ?7 D3 q/ s# Z9 C* L

% b" F: R" q- \5 F 微信图片_20230628143747.png 5 ^- T7 H1 D* w7 Y, z4 n
/ @. N0 r, }# \& M5 O: h

6 ]: ]1 h9 C; C' W9 _2 z转载自: 嵌入式攻城狮
) P; P8 K# L- L如有侵权请联系删除
0 L) X1 n9 ~8 B, a5 |
. e1 h1 t+ `4 f) D/ h, r2 G& t' O* @

( @$ o  o3 B* j0 u. D: o
! l0 U1 L* ~* w8 K# e7 v" g; l
赞 收藏 评论0 发布时间:2023-6-28 14:42

举报

0个回答

所属标签

相似技术帖

官网相关资源

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