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

STM32的CAN环回测试

[复制链接]
攻城狮Melo 发布时间:2023-6-28 14:42
CAN基础知识介绍文中介绍了CAN协议的基础知识,以及STM32F4芯片的CAN控制器相关知识,下面将通过实例,利用STM32CubeMX图形化配置工具,来实现CAN通讯的环回测试; T1 j& O. g" L' o" p1 P  A  L
9 }/ v& p/ G8 L! h: F( W
1. STM32CubeMX配置, j: s# Q6 y' p

: `( m7 }. \0 ^) z1 N

0 P, V1 t" E; D# L& C& ?. u8 z⏩ CAN是挂载在APB1总线上,设置PCLK1时钟频率到最大45MHz& n/ c9 y9 Y- s: U

& k- y. P9 k  I( e. Z 微信图片_20230628143758.png . F+ O- u) [! G; ?0 ~/ g" o1 h0 T
! e" Q  Y+ u# R" ]/ ]

! o; z- d, R" n: m& C( Q  _⏩ 激活CAN1,配置位时序参数,其他基本参数以及工作模式(此处设置为Loopback环回模式)
) u  v7 P. x1 j4 ^8 ^' s4 R( \" d2 V' a% V0 S- B  f% g

) l* v5 E5 U! q6 C2 Y" h 微信图片_20230628143755.png
0 a# b! b6 u* N. d& s7 \
+ p7 ~7 ]; [) R3 A& h( {2 iCAN波特率的计算公式:只需要知道BS1和BS2的设置,以及APB1的时钟频率,就可以方便的计算出波特率。比如设置TS1=8、TS2=6和BRP=6,在APB1频率为45Mhz的条件下,即可得到CAN通信的波特率=45000/6/(8+6+1)=500Kbps; l, _) T; z& [5 f. p

& F- m* g" o7 v, }: S' ^ 微信图片_20230628143751.png 2 ?4 E& b3 M0 Q& R

7 N. t$ w% w1 r
% i; g' e4 M5 E& a! R: t
⏩ 激活USART1作为调试串口,配置相关LED对应的GPIO引脚作为指示灯2 {% Q; J9 X  }8 m, I

* ]0 F4 W8 ]! }0 u) N

5 E# g- Y' |+ n; s9 s, J2. MDK-ARM编程
& D3 q* W# y; n0 |1 s; s) p5 l$ h" L5 u$ z. W- U: \
2.1 几个重要的结构体3 E/ u' k5 a# X& t6 w1 S/ ~( z7 e

$ ^1 v' ^# [& E6 Q
6 J6 f  d7 ]: e  m3 V( h
⏩ 主控寄存器 CAN_MCR:负责管理 CAN 的工作模式0 A1 L3 x* e6 s$ V; {: ^
  1. typedef struct
    + \/ M9 _. f- O; H
  2. {2 ]) M8 H$ }4 z; R$ w6 H8 k- ~
  3.   uint32_t Prescaler;       /* 配置 CAN 外设的时钟分频,可设置为 1-1024*/7 x  L& Q4 C" o" r) I# }( e7 Z
  4.   uint32_t Mode;            /* 配置 CAN 的工作模式,回环或正常模式 */" |( j# x4 v& ?* y# u& G% M' }" {; S
  5.   uint32_t SyncJumpWidth;   /* 配置 SJW 极限值 */. Q) b: O9 E. [1 T
  6.   uint32_t TimeSeg1;        /* 配置 BS1 段长度 */
    & L0 I* Q4 {* |* D' Q/ v: i
  7.   uint32_t TimeSeg2;        /* 配置 BS2 段长度 */
    ' x3 U4 x/ g3 L9 o9 Q+ p5 ^
  8.   FunctionalState TimeTriggeredMode;    /* 是否使能 TTCM 时间触发功能 */. ~2 F3 C7 \! f, i0 e) Q( u! R* K
  9.   FunctionalState AutoBusOff;           /* 是否使能 ABOM 自动离线管理功能 */
    0 l4 C$ b" K  a* y& O$ N
  10.   FunctionalState AutoWakeUp;           /* 是否使能 AWUM 自动唤醒功能 */" e! q, x) Y# ]. ?
  11.   FunctionalState AutoRetransmission;   /* 是否使能 NART 自动重传功能 */
    7 x* j; F  [. c* _0 [- T  a
  12.   FunctionalState ReceiveFifoLocked;    /* 是否使能 RFLM 锁定 FIFO 功能 */
    $ M& A- }2 I, N, {
  13.   FunctionalState TransmitFifoPriority; /* 配置 TXFP 报文优先级的判定方法 */5 P. R* h* ?* F, o! z
  14. } CAN_InitTypeDef;
复制代码
) j. C, O: r" O8 ]1 {
⏩ 发送及接收头结构体:主要用于构造发送报文,以及接收报文。收发发文时,需要自定义头结构体变量) O7 q4 Q1 X. n9 B8 m) J! R
% u' \1 X* v) I2 P/ F) @, E

+ T5 k. ?5 Z/ E
  1. typedef struct8 N# C0 j6 B# S! F
  2. {
      `2 f) d8 Q4 K) Y' F6 ^8 R8 m
  3.   uint32_t StdId;       /* 存储报文的标准标识符 11 位,0-0x7FF. */9 `9 h+ u4 ?. Y( L: k+ E1 K. }4 t
  4.   uint32_t ExtId;       /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
    7 x7 }5 g$ l" _$ _7 ]# I1 ^: ]
  5.   uint32_t IDE;        /* 存储 IDE 扩展标志 */
    * j- k" U5 |2 |
  6.   uint32_t RTR;        /* 存储 RTR 远程帧标志 */
    - R' }5 x- F& y- h  ^5 A
  7.   uint32_t DLC;        /* 存储报文数据段的长度,0-8 */
    - A% H7 p+ j& i* Z
  8.   FunctionalState TransmitGlobalTime;
    * g$ }$ a; c) R3 h) d& S" y
  9. } CAN_TxHeaderTypeDef;5 [8 h- ^# e$ F& ~; t
  10. & _# _; V$ c1 J: Z2 b% w$ X
  11. typedef struct
    ! t) |- [% z# M  Q9 z5 q6 E5 r
  12. {
    / v! p" [" ^+ T7 y7 u
  13.   uint32_t StdId;      /* 存储报文的标准标识符 11 位,0-0x7FF. */% S0 D8 N0 r4 R7 I2 A7 W
  14.   uint32_t ExtId;      /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
    , |! i& [0 _* T3 `+ _; T
  15.   uint32_t IDE;        /* 存储 IDE 扩展标志 */" [0 J2 D8 V1 S, Y
  16.   uint32_t RTR;        /* 存储 RTR 远程帧标志 */
      ^: c& z9 o4 U% S/ d
  17.   uint32_t DLC;        /* 存储报文数据段的长度,0-8 */
    $ h0 e' E# Y; Y0 M. S! U
  18.   uint32_t Timestamp;
    4 `2 d: h0 k( I) d
  19.   uint32_t FilterMatchIndex;
    % L# `& g$ v8 ?7 Q: q) t* ?
  20. } CAN_RxHeaderTypeDef;
复制代码

5 s# L) l" H9 D% O
# Y$ D& _! P) k: s2 U/ C' N
⏩ 过滤器结构体:STM32CubeMX不会初始化过滤器的相关内容,需要自己添加
# N4 w+ P3 M" G1 ?/ h4 D0 ~2 @5 [/ {* H& |3 b$ C
* M* g- ?4 U7 q$ F6 n
  1. typedef struct
    , n' v' {5 O8 w1 I. W2 t/ F
  2. {
    $ i! Q# P( D% O) T$ V
  3.   uint32_t FilterIdHigh;         /*CAN_FxR1 寄存器的高 16 位 */
    6 @8 x, O) g# ^' L* f
  4.   uint32_t FilterIdLow;          /*CAN_FxR1 寄存器的低 16 位 */" |4 [1 N! M, n9 i  Z# j' f
  5.   uint32_t FilterMaskIdHigh;     /*CAN_FxR2 寄存器的高 16 位 */0 Y* G7 k, ]% p* a9 ~" v- @6 i
  6.   uint32_t FilterMaskIdLow;      /*CAN_FxR2 寄存器的低 16 位 */( n. Y1 n3 n  u' e3 Y
  7.   uint32_t FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收 FIFO */' \# C1 A$ p: t3 Z
  8.   uint32_t FilterBank;           /* 筛选器编号,范围0-27,CAN1是0-13,CAN2是14-27 */
    3 g0 s" ~9 j  p7 W  k: i- ?* X. c
  9.   uint32_t FilterMode;           /* 筛选器模式 */. h7 h6 q0 p5 q) Z+ r2 w( S
  10.   uint32_t FilterScale;          /* 设置筛选器的尺度 */$ k' ?8 h( e8 x* a
  11.   uint32_t FilterActivation;     /* 是否使能本筛选器 */
    5 Z! `) d) i, s, i% c
  12.   uint32_t SlaveStartFilterBank; /* CAN2起始过滤器组 */
    ( Z: [* Q2 S; p6 Z- j
  13. } CAN_FilterTypeDef;
复制代码
/ I9 E. o* z9 ]  r* b! V
& o6 O3 o8 Q3 k' ~- R: L8 v" |! l
2.2 程序编写3 {9 c2 D# ?" D2 O

; J( [$ q# @  V7 P4 [
' j. y" k" c+ N% W
⏩ 生成工程后,打开can.c文件,可见STM32CubeMX已经对位时序参数、其他基本参数以及工作模式进行了初始化。但是并没有初始化过滤器的相关内容,因此需要我们自己添加,并在CAN初始化时调用7 y! }" w: c4 k% _0 c: K
& E' n( e4 B- ~$ J5 x# j

# C9 D2 x- M9 n) `: f
  1. //下面的设置只使能了FIFO0,并不过滤任何消息
    ) e( y9 W! H: m. V& l$ _, L
  2. void CAN_Filter_Config(){
    ! k+ b! k& o. P% y
  3.   CAN_FilterTypeDef sFilterConfig;% w  m! ^2 A# Y7 h" J* A
  4. ' T5 T# F- C0 o
  5.   sFilterConfig.FilterBank = 0;      //筛选器编号, CAN1是0-13, CAN2是14-27
    + E3 C* c8 D: n; O3 L: g$ D
  6.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //采用掩码模式
    ) k0 V8 s. P3 l7 }9 K* C) |
  7.   sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置筛选器的尺度, 采用32位
    6 [; n- C4 h: u
  8.   sFilterConfig.FilterIdHigh = 0X0000;    //过滤器ID高16位,即CAN_FxR1寄存器的高16位
    0 V& e1 B9 w7 k8 a& h5 G" [% x
  9.   sFilterConfig.FilterIdLow = 0X0000;     //过滤器ID低16位,即CAN_FxR1寄存器的低16位# }" p( y- f/ S" l4 G
  10.   sFilterConfig.FilterMaskIdHigh = 0X0000;   //过滤器掩码高16位,即CAN_FxR2寄存器的高16位& [1 {0 d: @+ S8 O% d3 s! Z
  11.   sFilterConfig.FilterMaskIdLow = 0X0000;    //过滤器掩码低16位,即CAN_FxR2寄存器的低16位. [; u! O, X% s: f
  12.   sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //设置经过筛选后数据存储到哪个接收FIFO3 ~. }3 U/ Y3 m% B5 X
  13.   sFilterConfig.FilterActivation = ENABLE;   //是否使能本筛选器
    & P% `0 [: t- Q% f% i- n+ R
  14.   sFilterConfig.SlaveStartFilterBank = 14;   //指定为CAN1分配多少个滤波器组
    2 X2 ^% r8 {% `, c

  15. 9 n; I0 F. V! D% `
  16.   if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)5 h/ B5 g, x! E5 d& a5 q+ J
  17.   {* P+ h! A/ a" n5 Y( n& A( ]8 [
  18.     Error_Handler();: z2 w  E4 z5 H$ W8 q
  19.   }
    ' o. W& @& |3 v$ x" \7 ^. \8 e' }
  20. }
复制代码
+ v. g% V1 B: F) t5 Q7 c
⏩ 编写发送和接收数据函数:此处将格式固定为标准数据帧,ID为12
' Y# y8 ~( \9 Z1 ]2 j( r0 P0 a2 ?( x6 T5 B9 D! p

& y# j/ \; a; l
  1. uint8_t CAN1_Send_Msg(uint8_t *msg, uint8_t len){
    . f0 f; h1 N; r9 [" e2 p
  2.   uint16_t i = 0;9 G6 m+ b2 o5 \
  3.   uint32_t txMailBox;1 }5 U9 B; ?/ d1 _' @7 |
  4.   uint8_t send_buf[8];/ T- r: U$ ]( f/ T

  5. # p; m2 X2 f6 f. v: K% o2 F9 n
  6.   txHeader.StdId = 12;9 w: B. f; c% _. E9 q
  7.   txHeader.ExtId = 12;! K2 r1 e) ^& Q% p# `3 P
  8.   txHeader.IDE = CAN_ID_STD;
    7 ~$ R; a0 ?7 l5 k+ C1 z2 K
  9.   txHeader.RTR = CAN_RTR_DATA;5 Y6 U  p, w8 y; o1 ]+ Q
  10.   txHeader.DLC = len;
    ! p& g% L2 a" `. {+ H$ `4 k" Z

  11. " }# r1 x) s1 @
  12.   for(i = 0; i < len; i++). M/ V) s( m' _& b
  13.     send_buf[i] = msg[i];
    % w/ A# t0 N9 @+ f- b6 ?4 t( C

  14. & B! }, l+ R* k+ @' L3 ]
  15.   if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, send_buf, &txMailBox) != HAL_OK)
    / M. |! g/ U0 K- B9 M' U/ A; [$ s
  16.     return 1;
    # E2 a4 {% s$ Y5 ~
  17.   return 0;6 K9 p, I- l9 B+ B$ F5 u3 ~! ]) U
  18. }
      P/ @  v' f8 G1 e3 E( `+ r

  19. # h# k, x  B+ x: B* |; |
  20. uint8_t CAN1_Recv_Msg(uint8_t *buf){
    , w; ~# k1 P; c! Z& ?1 T* [% r
  21.   uint16_t i = 0;
    : w% W' L0 d2 }6 N5 u+ H& ?1 u
  22.   HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rxHeader, buf);# m5 D0 k4 s9 G1 G# s
  23. + v  n6 z( Z) }1 @
  24.   if(rxHeader.IDE == CAN_ID_STD)$ R$ P2 K: s5 }  b
  25.     printf("StdId ID: %d\r\n", rxHeader.StdId);
    ) y1 P  O9 x' \7 J2 c8 M
  26.   else
    # f! f$ Q0 q( X7 w- B& F
  27.     printf("ExtId ID: %d\r\n", rxHeader.ExtId);
    2 E% P; G& X- h+ q9 h0 C
  28. ( j; h1 a: ~( Z
  29.   printf("CAN IDE: %d\r\n", rxHeader.IDE);: b. f* H  I' o& W: o$ I7 Q* ?6 x. M
  30.   printf("CAN RTR: %d\r\n", rxHeader.RTR);& J1 L9 v6 M% y: R, P% V
  31.   printf("CAN DLC: %d\r\n", rxHeader.DLC);
    , t$ s- A( ~/ ~# E
  32.   printf("Recv Data: ");
    ( J9 F' V$ T% L* A9 V: I$ k

  33. * ]2 V/ O' V" h% [8 l1 U( H7 d7 E
  34.   for(i = 0; i < rxHeader.DLC; i++)
    3 \' j( b. h! W+ E, e
  35.     printf("%c",buf[i]);
    8 V+ x# T" E$ o( T# v4 n0 n- g

  36. 8 R$ ]/ L/ U4 i1 |3 C: d6 O7 S
  37.   printf("\n");
    ' W- R* P/ p! l
  38.   return rxHeader.DLC;# ^0 E: V, {0 H, N! a
  39. }
复制代码
! J3 q5 D/ q, e" g0 N/ a
⏩ 默认Cubemx生成的代码并没有can start,没有调用HAL_CAN_Start(&hcan1) 来使能CAN,因此需要在CAN初始化代码中添加
, B% Y; _6 ^; @# h8 H) H8 P
& H" ^! q" S$ ~7 P. Q6 o
( i( |3 o) P/ P
  1. void MX_CAN1_Init(void){& J7 B$ T5 l  t- T4 i/ J: D) ~
  2.   ......
    , F7 ]/ k5 n4 F
  3.   /* USER CODE BEGIN CAN1_Init 2 */
    1 \4 e% f! A2 C
  4.   CAN_Filter_Config();' p9 N9 F- l$ C3 D5 J& ~
  5.   HAL_CAN_Start(&hcan1); 4 k$ O; h1 U0 e0 }
  6.   /* USER CODE END CAN1_Init 2 */
    3 r8 u: c# @5 O, v
  7. }
复制代码
- G$ {' S/ Q: P+ `' S( x- }3 _
⏩ 主函数main.c中,代码如下$ ^) _  Y3 m, b. p* |

- x. ^; G3 b* K# u

0 h- g; y4 Y8 ]
  1. int main(void){
    ' `2 ~6 L3 k$ w5 J& Z* M# C+ G# U! y
  2.   HAL_Init();
    ; e' k  q' t0 \9 }
  3.   SystemClock_Config();5 |- [# u6 y/ @/ S
  4.   MX_GPIO_Init();
    8 s! m( |+ O. x1 y$ R
  5.   MX_CAN1_Init();
    5 u/ s2 [1 M) [2 N/ }
  6.   MX_USART1_UART_Init();
    9 _, N$ ^" h* ?7 l0 N# I" o
  7.   /* USER CODE BEGIN 2 */
    , e- Y6 \- n8 B9 V* T3 R
  8.   uint8_t ret,i;) }; U9 h8 h: Q0 n- g0 a# G
  9.   printf("CAN Testing....!\r\n");
    / ?6 Y3 g( p1 w" P: J; V
  10.   uint8_t txdata[8] = {76, 79, 79, 80, 66, 65, 67, 75};, ~) |' p4 F, P/ J. w
  11.   uint8_t rxdata[8];
    0 H9 v, Q% i* |0 Q4 |6 s& I
  12.    /* USER CODE END 2 */
    7 n2 L0 M( o1 T
  13.    /* Infinite loop */
    0 Q; Y- ^1 r2 Q* h9 ^1 l9 g
  14.    /* USER CODE BEGIN WHILE */
    1 ?5 ~4 R% ~/ g3 P% m
  15.   while (1)& O" ~8 ^: L+ c# ?, w  D2 W
  16.   {
    . j* z4 C/ |" k2 e" i; U
  17.     HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
    - t; N2 J6 v/ t3 P7 |1 \) B. M
  18.     HAL_Delay(1000);
    ( T/ B$ O# f5 v/ y& s  g
  19.     printf("Start Send data...\r\n");
    ! r' P! S+ A  ^0 z
  20.   
    2 v: M9 N. h+ t  o+ x0 a
  21.     ret = CAN1_Send_Msg(txdata, 8);
    # T5 a, v' c8 q0 b. @
  22.     if(ret == 0)
    , n$ c6 C, Z* G  L$ T
  23.       printf("CAN Send success!\r\n");
      m$ X- ?( J8 _% R. M" c: Q9 s
  24.     else
    4 I9 A* l& V1 m. L1 Z  m5 @
  25.       printf("CAN Send failed!\r\n");
    4 v) b4 L' s+ t
  26.   0 V% T) H/ M+ C6 {" \1 z
  27.     CAN1_Recv_Msg(rxdata); 3 I/ k* V, G, `, x  L( _
  28.     printf("+++++++++++++++++++++++++++++++\r\n");! g: J, D: X& G0 `
  29.     /* USER CODE END WHILE */
    , ~/ k, k; E" p) T! }8 C% }
  30.     /* USER CODE BEGIN 3 */) s9 h0 v9 R- X. L. h6 k
  31.   }
    / Q/ u+ b5 h, X. D3 Q) ^
  32.   /* USER CODE END 3 */5 \9 o* q! m. t' R' o
  33. }
复制代码

' `6 U; C  Z$ ^8 r1 z4 }3. 下载测试$ a4 o3 ^( u$ N% |( K* N) C  h
7 w) L- h) Y! D4 _) z8 A0 O  u
, u' d+ u3 B# j2 X
编译无误后下载到开发板,可以看到系统运行时D1指示灯不断闪烁,串口不断的打印CAN环回测试的数据
8 Y/ L8 i( G9 e  c
( I# Y1 H; k/ B4 j+ @

: y. h) W1 l& b* B 微信图片_20230628143747.png : B) U3 z; R4 d5 p
9 B! i, J4 S- A* o" o

& T* ~' T3 @/ @/ ?转载自: 嵌入式攻城狮
6 e) I8 J- \$ t! a! ^8 Q, G; J如有侵权请联系删除
8 z/ U( R% T  z* L: Q5 n6 u! O& W0 b2 O" G0 r
2 v3 m  M) X% [; k$ F
" j' M$ `) D! J8 ~& {3 R

1 V- k4 \) V3 f5 d3 I4 E
收藏 评论0 发布时间:2023-6-28 14:42

举报

0个回答

所属标签

相似技术帖

官网相关资源

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