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

STM32的CAN环回测试

[复制链接]
攻城狮Melo 发布时间:2023-6-28 14:42
CAN基础知识介绍文中介绍了CAN协议的基础知识,以及STM32F4芯片的CAN控制器相关知识,下面将通过实例,利用STM32CubeMX图形化配置工具,来实现CAN通讯的环回测试
/ j/ i2 t- [1 R: }) t$ F, C5 @( r# Q9 [  i3 {: h( ^- q5 f
1. STM32CubeMX配置5 L3 O% N( y: u2 h, M& y
7 ^! b9 i5 f3 _5 Z
. W9 N/ @8 s3 J% s
⏩ CAN是挂载在APB1总线上,设置PCLK1时钟频率到最大45MHz: J, ]# w" x4 n7 v8 c. ?, M
0 K1 t1 i  T# W7 K
微信图片_20230628143758.png
' `% l1 y4 U% e* i# F
7 ^8 g+ t' m8 G5 j9 S9 ?
, N' ^: U5 P- o1 e8 m
⏩ 激活CAN1,配置位时序参数,其他基本参数以及工作模式(此处设置为Loopback环回模式)5 s* h  r6 O: R( c

0 J( l: n8 D& D+ n9 W
) S1 r6 H* z. A! a* z0 t! y( A
微信图片_20230628143755.png
9 h( K% Z  Z" a' C1 z2 w6 N- G9 H) X$ x
CAN波特率的计算公式:只需要知道BS1和BS2的设置,以及APB1的时钟频率,就可以方便的计算出波特率。比如设置TS1=8、TS2=6和BRP=6,在APB1频率为45Mhz的条件下,即可得到CAN通信的波特率=45000/6/(8+6+1)=500Kbps
0 N+ B4 Z* C, Z8 `/ w) s/ [: t7 x  b3 f# n3 W$ M7 V
微信图片_20230628143751.png ; `4 b( }6 o! u0 C

6 R( M8 L# U% x* f
! x8 r2 {9 k7 W# O7 C) y
⏩ 激活USART1作为调试串口,配置相关LED对应的GPIO引脚作为指示灯5 O. r# T  D7 }" U2 O

+ X1 n7 @  `7 s/ L8 Z% W

. z& l$ E# B$ p  h2. MDK-ARM编程3 d) ?6 l$ w# y4 o

' k" y' O6 q: V6 o/ S! P2.1 几个重要的结构体
$ p/ u8 b$ j3 t. v# N, O$ T6 L' {8 f/ u! P/ l, h5 m
* y' D- `4 w: |1 w3 Y* H) w
⏩ 主控寄存器 CAN_MCR:负责管理 CAN 的工作模式& z9 g: E7 _; `! k) e( ~1 O4 s, F1 d
  1. typedef struct
    6 B7 k( ]  k& ^$ j0 i
  2. {, T! C. h  s4 T1 {/ v6 V! U5 ]( X
  3.   uint32_t Prescaler;       /* 配置 CAN 外设的时钟分频,可设置为 1-1024*/
    8 R8 M- J1 m1 y2 }
  4.   uint32_t Mode;            /* 配置 CAN 的工作模式,回环或正常模式 */& K, Z2 U/ j0 S( T
  5.   uint32_t SyncJumpWidth;   /* 配置 SJW 极限值 */) R; O" E  n4 i( E/ I
  6.   uint32_t TimeSeg1;        /* 配置 BS1 段长度 */! H; e5 C3 N( [! t
  7.   uint32_t TimeSeg2;        /* 配置 BS2 段长度 */
    . Y; X0 M9 d2 `3 P! W+ w
  8.   FunctionalState TimeTriggeredMode;    /* 是否使能 TTCM 时间触发功能 */
    3 M6 P2 L( n5 H2 e5 u; ^
  9.   FunctionalState AutoBusOff;           /* 是否使能 ABOM 自动离线管理功能 */
    : U  V0 O( X: D5 l
  10.   FunctionalState AutoWakeUp;           /* 是否使能 AWUM 自动唤醒功能 */
    ) h3 n& w6 T- D. f0 n
  11.   FunctionalState AutoRetransmission;   /* 是否使能 NART 自动重传功能 */
      Q+ ^1 E! r8 a* k
  12.   FunctionalState ReceiveFifoLocked;    /* 是否使能 RFLM 锁定 FIFO 功能 */
    ( |4 G8 G9 ]& `# R/ r- h' \
  13.   FunctionalState TransmitFifoPriority; /* 配置 TXFP 报文优先级的判定方法 */+ s6 s: _) f% y
  14. } CAN_InitTypeDef;
复制代码

7 o0 |& A& @; \" d⏩ 发送及接收头结构体:主要用于构造发送报文,以及接收报文。收发发文时,需要自定义头结构体变量
) d4 ^3 F6 `# i/ f$ P1 w2 \
2 w- d! J" x8 O. L+ P6 e8 {
/ p+ r2 b$ u( O) X/ y, W
  1. typedef struct: U( t, W, w+ m5 o1 P& g/ Y
  2. {
      e. M! ~0 }( {7 |: E8 l: i3 ]
  3.   uint32_t StdId;       /* 存储报文的标准标识符 11 位,0-0x7FF. */, U! H; [8 T; L* d
  4.   uint32_t ExtId;       /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
    " Z, M! W) o( v8 ~& U; m
  5.   uint32_t IDE;        /* 存储 IDE 扩展标志 */
    " w# \+ c# n! T  a2 O
  6.   uint32_t RTR;        /* 存储 RTR 远程帧标志 */, _2 a/ O: D0 y) ^% R
  7.   uint32_t DLC;        /* 存储报文数据段的长度,0-8 */7 \/ v* c3 n/ b" q
  8.   FunctionalState TransmitGlobalTime; 0 S, T2 {  e1 y  s
  9. } CAN_TxHeaderTypeDef;
    ( A) v0 O3 \" Q7 J2 W2 @5 z
  10. 2 L; z/ p0 J1 [7 a) p% a) i9 n
  11. typedef struct
    / D4 W# T8 B/ ?- F- k5 X8 l- X. v
  12. {  p8 S9 q$ h5 z8 `
  13.   uint32_t StdId;      /* 存储报文的标准标识符 11 位,0-0x7FF. */
    , ]' U7 m; k5 Y  k/ |
  14.   uint32_t ExtId;      /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
    2 s# J' l& y( R, X, u9 V3 R
  15.   uint32_t IDE;        /* 存储 IDE 扩展标志 */
    : V4 F+ H7 P& M  m0 d
  16.   uint32_t RTR;        /* 存储 RTR 远程帧标志 */% m1 X% u5 B; q" W: b5 H1 L
  17.   uint32_t DLC;        /* 存储报文数据段的长度,0-8 */
    ) N4 u3 ~/ j7 n/ [  l/ p* p% `2 {1 F
  18.   uint32_t Timestamp;
    " a. J' E  t# e2 @
  19.   uint32_t FilterMatchIndex;
    , V5 n1 o. K* i. |
  20. } CAN_RxHeaderTypeDef;
复制代码
; M3 i, `  x4 |2 h

. L8 t- t1 ~5 G& q1 U⏩ 过滤器结构体:STM32CubeMX不会初始化过滤器的相关内容,需要自己添加
! v, R/ O7 ^0 Y" b
* A: r9 e, D( t9 X* P" _+ V! j
' @- E, D2 u1 z9 D7 n
  1. typedef struct
    % k7 D& I3 a) _0 x) X5 @* T
  2. {
    % L- S! A2 ~) q/ f
  3.   uint32_t FilterIdHigh;         /*CAN_FxR1 寄存器的高 16 位 */
    * h1 f( y8 H0 D+ T0 F. H
  4.   uint32_t FilterIdLow;          /*CAN_FxR1 寄存器的低 16 位 */
    4 b' h# o& z8 g- r: L
  5.   uint32_t FilterMaskIdHigh;     /*CAN_FxR2 寄存器的高 16 位 */
    9 t& [6 l4 V  r2 R% \) `, k, Y$ o+ e
  6.   uint32_t FilterMaskIdLow;      /*CAN_FxR2 寄存器的低 16 位 */4 g& y6 C2 v# P7 Z
  7.   uint32_t FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收 FIFO */
    5 ?- [- D" U  s
  8.   uint32_t FilterBank;           /* 筛选器编号,范围0-27,CAN1是0-13,CAN2是14-27 */
    0 A0 v# R9 d% N. @& e( a0 ^5 i
  9.   uint32_t FilterMode;           /* 筛选器模式 */6 B. @7 p9 c0 [7 p$ J
  10.   uint32_t FilterScale;          /* 设置筛选器的尺度 */1 i/ v) V' n: a) j; g5 j5 U( [
  11.   uint32_t FilterActivation;     /* 是否使能本筛选器 */  ]/ G1 Q) s! f5 Y
  12.   uint32_t SlaveStartFilterBank; /* CAN2起始过滤器组 */
    2 r' c! S! R6 h
  13. } CAN_FilterTypeDef;
复制代码

* a* d* ]+ u& \. r

) [  E( }8 W4 a1 `) Q$ Q; e2.2 程序编写& P! h) c5 ]0 _0 n. D9 |
6 ?$ b. ]/ _6 y+ Q/ S# ?3 l/ r+ w. P

& t' F' j& N0 L1 K5 h4 w( f% l8 V) U⏩ 生成工程后,打开can.c文件,可见STM32CubeMX已经对位时序参数、其他基本参数以及工作模式进行了初始化。但是并没有初始化过滤器的相关内容,因此需要我们自己添加,并在CAN初始化时调用) u" O, {1 P0 ~. v+ l; Q
8 Z# {& M5 K( z0 m5 P( c7 Q

9 A. d- X5 G5 Z; N9 O! y% w# B5 ~
  1. //下面的设置只使能了FIFO0,并不过滤任何消息- Q% @$ n7 t# i& z, `/ i
  2. void CAN_Filter_Config(){
    / q! E- I" o7 c( S' \, S; J
  3.   CAN_FilterTypeDef sFilterConfig;
    1 @& C, N% D) \6 F9 Q% f- [1 u
  4. - P* @8 H4 u# V
  5.   sFilterConfig.FilterBank = 0;      //筛选器编号, CAN1是0-13, CAN2是14-27
    - U  R* M  E2 y" W" W
  6.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //采用掩码模式
    + p4 k9 s  K8 v7 n9 v& a4 i; [- ^
  7.   sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置筛选器的尺度, 采用32位! V" X2 }  Y( ^8 ?6 x& x
  8.   sFilterConfig.FilterIdHigh = 0X0000;    //过滤器ID高16位,即CAN_FxR1寄存器的高16位
    & q( \3 P' V6 b+ u2 t. U8 R
  9.   sFilterConfig.FilterIdLow = 0X0000;     //过滤器ID低16位,即CAN_FxR1寄存器的低16位
    4 d! X+ ]- ?2 F( t% A3 _0 a  Y
  10.   sFilterConfig.FilterMaskIdHigh = 0X0000;   //过滤器掩码高16位,即CAN_FxR2寄存器的高16位
    & I$ y8 U* ^% r  Z
  11.   sFilterConfig.FilterMaskIdLow = 0X0000;    //过滤器掩码低16位,即CAN_FxR2寄存器的低16位
    5 H6 x+ H) Z! r: @
  12.   sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //设置经过筛选后数据存储到哪个接收FIFO
    * N7 W: p  |3 b
  13.   sFilterConfig.FilterActivation = ENABLE;   //是否使能本筛选器
    5 {! f5 P0 S$ X/ I- H- t4 J
  14.   sFilterConfig.SlaveStartFilterBank = 14;   //指定为CAN1分配多少个滤波器组
    4 X: R# p0 A) L) T6 E& A* [, G

  15. - g1 H$ r$ X3 p- X
  16.   if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)0 p$ ?2 v( ^1 t. x7 Z" M
  17.   {
    + F: ?7 b% Q6 G
  18.     Error_Handler();6 q/ K3 D  n8 ?8 Y! D. M
  19.   }$ y: U& y. |" L( T$ D/ b
  20. }
复制代码
! B& F$ P( G& A, q& ]
⏩ 编写发送和接收数据函数:此处将格式固定为标准数据帧,ID为12
; L" p  f/ I" D* A; z( @- w9 s9 t1 d' Q" v  ?( r1 u7 B% u
, ^  B+ t) ]2 y0 L7 D* O! l$ @# R
  1. uint8_t CAN1_Send_Msg(uint8_t *msg, uint8_t len){# R' o5 n! l; F- G" ~- D
  2.   uint16_t i = 0;
    + Q6 I3 j. t: `' n% h
  3.   uint32_t txMailBox;
    8 x  `8 M. F; f* ?. \5 }
  4.   uint8_t send_buf[8];
    3 j2 {+ m# L0 B+ r0 [8 j5 m6 m! P
  5. % b# h+ y) W& d& i
  6.   txHeader.StdId = 12;
    # n7 J2 Q8 b7 @% k- v2 s" _
  7.   txHeader.ExtId = 12;1 {3 {) _' g" M9 c
  8.   txHeader.IDE = CAN_ID_STD;
    : O( c2 h, P  j$ ~2 q* r) W
  9.   txHeader.RTR = CAN_RTR_DATA;
    3 m/ G  O6 G6 d0 c- Q
  10.   txHeader.DLC = len;* ^5 l5 v  Z2 Z! z5 N0 r
  11. & j8 H' {3 e& r& [) t4 }) ]
  12.   for(i = 0; i < len; i++)# N" Z5 v. Y: S; N1 V, v9 j' @
  13.     send_buf[i] = msg[i];
    6 u" p- i- K. ^8 b9 _. k4 C1 \

  14. . X9 U& v: C% h0 i+ [
  15.   if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, send_buf, &txMailBox) != HAL_OK)1 \6 U* d6 }1 ^+ q5 A- Z$ `6 S4 V
  16.     return 1;
    " l* ?) o0 [# [8 ?$ H+ M
  17.   return 0;5 z8 l' c, d3 y) l
  18. }# H* N7 A) u+ f' E$ q& b/ z
  19. % K. E. n# x  y5 X
  20. uint8_t CAN1_Recv_Msg(uint8_t *buf){
    ( U2 n, Z) Q' l
  21.   uint16_t i = 0; 5 R  P# x' s. u- M
  22.   HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rxHeader, buf);8 D, i0 b/ d% t

  23. 0 s6 f- r* D' J( l- W, E
  24.   if(rxHeader.IDE == CAN_ID_STD)6 p' q. V, y5 R4 o1 G  ]5 m
  25.     printf("StdId ID: %d\r\n", rxHeader.StdId);5 Q4 s/ U# x, U+ y! G2 I, a' l, y
  26.   else
    8 Q. R6 N) A1 Q" t* {) o
  27.     printf("ExtId ID: %d\r\n", rxHeader.ExtId);! l9 I; ?5 K" o, p
  28. * \4 @( x9 b' A
  29.   printf("CAN IDE: %d\r\n", rxHeader.IDE);
    : B2 U/ E7 r5 E' |" ^
  30.   printf("CAN RTR: %d\r\n", rxHeader.RTR);
    # m1 K+ j& l! D9 W
  31.   printf("CAN DLC: %d\r\n", rxHeader.DLC);
    & o. f1 B0 _. s- I: x
  32.   printf("Recv Data: ");
    / F& U4 v# G4 e, g' J3 L6 }

  33. 3 b7 [! C! j# _% O8 q+ J
  34.   for(i = 0; i < rxHeader.DLC; i++): \8 j( M8 I2 D' R  A. G
  35.     printf("%c",buf[i]);0 S1 j2 p8 N; C
  36. * J' L, x% V0 a, H5 z' m& a1 K( [
  37.   printf("\n");
    : P* k( ]' W# ~
  38.   return rxHeader.DLC;
    8 O3 R  Y, o! D# s
  39. }
复制代码
9 X4 o5 `8 ]4 M# v+ @+ n
⏩ 默认Cubemx生成的代码并没有can start,没有调用HAL_CAN_Start(&hcan1) 来使能CAN,因此需要在CAN初始化代码中添加7 S4 B; V2 ^& x
7 ^. G1 Q: r$ J, i

3 m3 G& G* k2 F6 T( B) A
  1. void MX_CAN1_Init(void){2 z8 \% S* S8 Y# D# C* H
  2.   ......7 C2 E2 {! H- u& O3 p
  3.   /* USER CODE BEGIN CAN1_Init 2 *// z' |; V$ l" k2 @( J& m2 a! S/ @
  4.   CAN_Filter_Config();
    ( H1 t+ I: r; Q$ b
  5.   HAL_CAN_Start(&hcan1);
    ) @+ h2 F6 a- T
  6.   /* USER CODE END CAN1_Init 2 */
    , h) ^5 i( P& S1 E" y
  7. }
复制代码
% V2 c$ A, f- }6 E; O
⏩ 主函数main.c中,代码如下
2 x. b/ s1 `8 Q6 M) o4 k* `* w/ A6 z( _, F6 M% b

4 w" q( U$ i* L* m' Q& D7 Q: x
  1. int main(void){
    7 r" F+ q" \! Z# l. N
  2.   HAL_Init();
      r" R% J* [8 N! ?
  3.   SystemClock_Config();+ D6 l3 R* |# I/ A2 e
  4.   MX_GPIO_Init();1 x9 z$ O- h1 _7 f8 t  J3 m
  5.   MX_CAN1_Init();
      ^% p! T! D; u7 o7 h3 u4 L: v
  6.   MX_USART1_UART_Init();
    2 @: R% a+ x4 C
  7.   /* USER CODE BEGIN 2 */+ O! H6 d: i0 }8 g- H8 ~
  8.   uint8_t ret,i;
    $ u" _4 ], L$ M( G" x; f
  9.   printf("CAN Testing....!\r\n");
    " k( h& M0 u* C( d# {4 S! h
  10.   uint8_t txdata[8] = {76, 79, 79, 80, 66, 65, 67, 75};" e; m5 B8 U! l7 u5 C  @" `
  11.   uint8_t rxdata[8];/ Y. O8 p& Q% _0 y) W! @) j. ~
  12.    /* USER CODE END 2 */
    & D- t# I& @3 }1 U' P5 B
  13.    /* Infinite loop */
    " P; y9 I1 p6 L, E# c1 `
  14.    /* USER CODE BEGIN WHILE */' _/ q- s( z4 r5 j0 f6 d. x0 i
  15.   while (1)2 o0 a, C, }! \1 j
  16.   {
    / l0 T* r" _4 K' J% K1 p+ ^
  17.     HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);) R, U; e# R7 i2 d0 J! Z
  18.     HAL_Delay(1000);1 U+ `6 _+ F# W% [) J1 e2 |( v3 k
  19.     printf("Start Send data...\r\n");4 v/ @) a8 P. N# k* j
  20.   + R/ ]  j% {- F4 v
  21.     ret = CAN1_Send_Msg(txdata, 8);3 S$ `/ b8 |; p- V; {& o& p
  22.     if(ret == 0)4 K1 L  c1 _2 B3 C. S
  23.       printf("CAN Send success!\r\n");
    8 e, D! L7 ~/ {* j3 h9 ?
  24.     else 6 k* s7 D- M8 |1 x
  25.       printf("CAN Send failed!\r\n");
    1 _9 Z: ~; {' [
  26.     ]3 `0 e9 d1 J. \6 _( G0 j# {
  27.     CAN1_Recv_Msg(rxdata);
    / D! q6 ?0 ?1 S
  28.     printf("+++++++++++++++++++++++++++++++\r\n");
    4 e% o9 U( {: J8 D
  29.     /* USER CODE END WHILE */# l7 S' M3 N1 J; i+ ]7 c( q, A
  30.     /* USER CODE BEGIN 3 */. p9 j9 O# @/ z3 [* [
  31.   }
    6 j( P  }/ \( [4 U  H+ ^- N
  32.   /* USER CODE END 3 */* Y0 T2 q: l0 R$ g4 d
  33. }
复制代码

/ N" \% {. j+ z7 c% {# E8 X3. 下载测试8 C1 @8 O  \5 ~) S2 w
# m8 a, ]" R3 _. O  ~7 d
) t/ Q# ]2 ]8 r/ Q) g; U
编译无误后下载到开发板,可以看到系统运行时D1指示灯不断闪烁,串口不断的打印CAN环回测试的数据
  d  z$ |; T1 O
' {. a1 D3 ]2 [3 d6 c

2 q4 o+ G9 l' x% D% f  m 微信图片_20230628143747.png
: m, a: @5 p, V. d
9 ^4 [8 E* ?7 c* }5 r7 c9 e6 Z4 Q+ ^2 l6 S. f6 ~1 Z
转载自: 嵌入式攻城狮) d* v: i" v& v  i2 x; `& t* ~6 n
如有侵权请联系删除
7 g% n) y$ T. _( O( K6 z
3 {+ I! @) d% L7 D, b3 h# H$ u

$ ~# \7 D& r- U0 M" F0 o
8 E2 i4 \1 [7 u9 J4 G
赞 收藏 评论0 发布时间:2023-6-28 14:42

举报

0个回答

所属标签

相似技术帖

官网相关资源

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