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

STM32的CAN环回测试

[复制链接]
攻城狮Melo 发布时间:2023-6-28 14:42
CAN基础知识介绍文中介绍了CAN协议的基础知识,以及STM32F4芯片的CAN控制器相关知识,下面将通过实例,利用STM32CubeMX图形化配置工具,来实现CAN通讯的环回测试) s* q7 H9 P6 g" t6 s

4 V9 @+ s0 a# e9 q* K1. STM32CubeMX配置: |! V8 t4 m9 M' c5 Y7 h
3 [/ ?0 S% q' f2 y* R: c. o/ n

1 T9 _7 w# @& ^# M& X2 Q/ [⏩ CAN是挂载在APB1总线上,设置PCLK1时钟频率到最大45MHz
. q  i0 a) k, P4 J) w# ]6 F. t3 |  w7 Y5 L8 U# Y
微信图片_20230628143758.png $ c  m7 g$ m7 `7 C; z0 h3 d/ T

) e7 g3 q; |) O# F) E* A

8 @9 D# n* X! ~7 U+ T9 b' I2 y⏩ 激活CAN1,配置位时序参数,其他基本参数以及工作模式(此处设置为Loopback环回模式). i: G9 o1 @. c- U& u
. D5 w  M! j) G1 Z7 n/ h# s

+ ]( `* a- t+ e. s 微信图片_20230628143755.png 2 y2 j: Z& c; I% \* j" i6 W: m, x7 N
( A: f4 }% d* ]- i
CAN波特率的计算公式:只需要知道BS1和BS2的设置,以及APB1的时钟频率,就可以方便的计算出波特率。比如设置TS1=8、TS2=6和BRP=6,在APB1频率为45Mhz的条件下,即可得到CAN通信的波特率=45000/6/(8+6+1)=500Kbps
, _/ _7 P" `. H# e2 W8 a' h2 W
微信图片_20230628143751.png
% b! b0 O2 ~1 h/ C+ J3 i' M
) Q. A2 o7 x" D+ [/ [) `

' i6 R$ P! [; K- ]% f/ l; y0 }0 K⏩ 激活USART1作为调试串口,配置相关LED对应的GPIO引脚作为指示灯( D# r& O9 J8 z5 P- o( f
- p/ K( ]  X' i

+ ?, n% C9 Q0 ~5 k- X: V2. MDK-ARM编程% Q, |; `! Q, J/ P$ w/ c6 b

% i8 E0 e; ], l: e, F2.1 几个重要的结构体0 T4 X3 T) a* C! a1 e
+ u, x( C7 E) m. I8 B
* l+ F5 t% Z- o" H, K5 X/ P$ ?
⏩ 主控寄存器 CAN_MCR:负责管理 CAN 的工作模式
$ D. y+ \" `6 y. i" R' {3 v7 k
  1. typedef struct3 _! B+ I+ V1 k0 G1 M) D
  2. {/ F  V6 X' A$ P0 q3 h) D& }
  3.   uint32_t Prescaler;       /* 配置 CAN 外设的时钟分频,可设置为 1-1024*/6 y  @; u2 o- n2 j( L& Z- }  t
  4.   uint32_t Mode;            /* 配置 CAN 的工作模式,回环或正常模式 */
    - w, n+ z" e8 \* L
  5.   uint32_t SyncJumpWidth;   /* 配置 SJW 极限值 */! Z0 S0 t" @: Y1 \* R, d
  6.   uint32_t TimeSeg1;        /* 配置 BS1 段长度 */) U7 J/ U3 {0 a1 K( `" U
  7.   uint32_t TimeSeg2;        /* 配置 BS2 段长度 */& K$ Z2 [1 ~# O% _- `; C/ p
  8.   FunctionalState TimeTriggeredMode;    /* 是否使能 TTCM 时间触发功能 */
    # k: h' G3 R& J
  9.   FunctionalState AutoBusOff;           /* 是否使能 ABOM 自动离线管理功能 */
    7 S% h$ g' U9 P" T
  10.   FunctionalState AutoWakeUp;           /* 是否使能 AWUM 自动唤醒功能 */" U( w$ B' T* M- e4 H% Q# O- E$ w
  11.   FunctionalState AutoRetransmission;   /* 是否使能 NART 自动重传功能 */( j2 [, C+ f' i4 A% i- D& @
  12.   FunctionalState ReceiveFifoLocked;    /* 是否使能 RFLM 锁定 FIFO 功能 */
    $ D, H. A0 f8 \/ U1 d4 W1 f
  13.   FunctionalState TransmitFifoPriority; /* 配置 TXFP 报文优先级的判定方法 */2 Q4 {& I" a8 o" X
  14. } CAN_InitTypeDef;
复制代码
5 I* L& d) T% a; ]  A
⏩ 发送及接收头结构体:主要用于构造发送报文,以及接收报文。收发发文时,需要自定义头结构体变量  u0 q9 e5 ~' X! |  O

/ q+ u: y% M: S( w1 T: _2 }! n7 b
$ F; }) g0 }8 }8 t1 B  W( z$ u
  1. typedef struct2 d* R* m# V! q; _+ o
  2. {  j: x' D8 }; ?) q1 @' G, z, h2 g& x
  3.   uint32_t StdId;       /* 存储报文的标准标识符 11 位,0-0x7FF. */
    * \# _7 ~- [( r% }4 b
  4.   uint32_t ExtId;       /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
    + |/ M7 B! m+ k& z
  5.   uint32_t IDE;        /* 存储 IDE 扩展标志 */5 _5 Y, m' ?: _1 n& q
  6.   uint32_t RTR;        /* 存储 RTR 远程帧标志 */
    6 b# Q- G/ f* ?- s% X
  7.   uint32_t DLC;        /* 存储报文数据段的长度,0-8 */: M5 M! H" r6 n+ `- m- j
  8.   FunctionalState TransmitGlobalTime;
    ) B2 h6 V. m0 o/ u
  9. } CAN_TxHeaderTypeDef;8 D5 I" v% M& u0 S. h1 W

  10. . E  z8 v  ^2 F1 G; T( @8 B- g0 E
  11. typedef struct1 h; R( Y( Y2 c5 b: i9 c2 S
  12. {
    3 l5 n! J- O2 ~  ^
  13.   uint32_t StdId;      /* 存储报文的标准标识符 11 位,0-0x7FF. *// |( h( q) r0 s3 p
  14.   uint32_t ExtId;      /* 存储报文的扩展标识符 29 位,0-0x1FFFFFFF. */
    % ^3 ]. \; d+ s0 w/ ]$ ^
  15.   uint32_t IDE;        /* 存储 IDE 扩展标志 */! O; z" Z% _; E* J) _* Q8 R8 h% n
  16.   uint32_t RTR;        /* 存储 RTR 远程帧标志 */
    ' e+ D* f5 \  {  y4 ^3 h' @/ o  q
  17.   uint32_t DLC;        /* 存储报文数据段的长度,0-8 */
    2 n3 _1 A& m; J' F  a! u  r, |
  18.   uint32_t Timestamp; . J  r( m! H  P
  19.   uint32_t FilterMatchIndex;
    4 ?3 J# j4 K7 a! M, R* I! i, j- H
  20. } CAN_RxHeaderTypeDef;
复制代码
7 e0 i# }3 @4 t# Q4 w$ R$ t- U( B

( O7 F0 K$ _( W" Y7 v⏩ 过滤器结构体:STM32CubeMX不会初始化过滤器的相关内容,需要自己添加1 |; w1 [. }  H& Q# B4 G

% o3 c9 U& [3 E5 j# ~& w

/ h) T' {& Q7 r7 Z: L6 U
  1. typedef struct
    6 @& e) d* [% A6 A
  2. {0 `9 }9 J+ Z* ~' p
  3.   uint32_t FilterIdHigh;         /*CAN_FxR1 寄存器的高 16 位 */- g; f& V2 R5 n' i  O+ P
  4.   uint32_t FilterIdLow;          /*CAN_FxR1 寄存器的低 16 位 */9 w( J/ F: n9 ]3 h
  5.   uint32_t FilterMaskIdHigh;     /*CAN_FxR2 寄存器的高 16 位 */
      F8 e: P2 L$ _6 f3 U4 Y9 F
  6.   uint32_t FilterMaskIdLow;      /*CAN_FxR2 寄存器的低 16 位 */
    ! F0 `" d1 d3 R0 j1 G
  7.   uint32_t FilterFIFOAssignment; /* 设置经过筛选后数据存储到哪个接收 FIFO */
    6 @# s6 Z8 @# A* C# J2 y
  8.   uint32_t FilterBank;           /* 筛选器编号,范围0-27,CAN1是0-13,CAN2是14-27 */, Q+ Z) \9 o3 {, e# q- w
  9.   uint32_t FilterMode;           /* 筛选器模式 */- u' A: S6 _( c
  10.   uint32_t FilterScale;          /* 设置筛选器的尺度 */9 `& y- ?! Q: S6 ~: {0 M
  11.   uint32_t FilterActivation;     /* 是否使能本筛选器 */  W7 ^% B. M; w0 |7 l! z5 s
  12.   uint32_t SlaveStartFilterBank; /* CAN2起始过滤器组 */+ ?6 E/ u0 `  j) T# t) `* h
  13. } CAN_FilterTypeDef;
复制代码

& J! g5 w, u/ s  g$ o8 |

/ K& p6 ~; g7 _% S2.2 程序编写. G; X' `( g  t; d

) B% L" d! C' R/ s. D: _

7 j5 U( L8 ]# B. j5 ]3 {⏩ 生成工程后,打开can.c文件,可见STM32CubeMX已经对位时序参数、其他基本参数以及工作模式进行了初始化。但是并没有初始化过滤器的相关内容,因此需要我们自己添加,并在CAN初始化时调用
$ k" h: U6 n. G2 Z4 b, B! y$ F4 l) ]- {$ B( @& _" G

8 f8 L4 @& L( H5 v& x
  1. //下面的设置只使能了FIFO0,并不过滤任何消息! r3 V! H& U3 b) f& j5 P: j
  2. void CAN_Filter_Config(){; G& @8 U9 `! `3 G: S. F3 n& Y( V
  3.   CAN_FilterTypeDef sFilterConfig;' H1 c: f& t& J$ }: g8 G8 ]; f9 |; H

  4. 9 D' C' V9 O( u: p. J# W
  5.   sFilterConfig.FilterBank = 0;      //筛选器编号, CAN1是0-13, CAN2是14-27) \; ?5 b9 P' C# @% H4 x) ]2 b
  6.   sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; //采用掩码模式
    ' v  u; K- e4 [6 i: k! u
  7.   sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; //设置筛选器的尺度, 采用32位. ?+ F$ i6 c- q9 M/ h1 _$ c
  8.   sFilterConfig.FilterIdHigh = 0X0000;    //过滤器ID高16位,即CAN_FxR1寄存器的高16位
    * x  j# j0 f8 ~, M2 V" D3 ?4 V
  9.   sFilterConfig.FilterIdLow = 0X0000;     //过滤器ID低16位,即CAN_FxR1寄存器的低16位
    ' E& P/ D/ D/ C7 X8 w( o
  10.   sFilterConfig.FilterMaskIdHigh = 0X0000;   //过滤器掩码高16位,即CAN_FxR2寄存器的高16位( N9 U  S' r% a7 S+ r+ g% L8 Q
  11.   sFilterConfig.FilterMaskIdLow = 0X0000;    //过滤器掩码低16位,即CAN_FxR2寄存器的低16位
    6 k% k: F! h) R( g0 `2 T8 T
  12.   sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; //设置经过筛选后数据存储到哪个接收FIFO9 p5 |1 ?' L' _8 Z) u" s( ^1 ~$ l
  13.   sFilterConfig.FilterActivation = ENABLE;   //是否使能本筛选器
    " Y4 c1 O# v, ^
  14.   sFilterConfig.SlaveStartFilterBank = 14;   //指定为CAN1分配多少个滤波器组
    ) C( C. u1 q& M1 i( Q
  15. + ~+ O5 ]5 D/ B0 s6 a
  16.   if(HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
    2 k) d0 d5 @% s+ L# ^8 x: [' j2 k: U
  17.   {
    7 Z/ f3 L) {7 j% \+ k5 a" n
  18.     Error_Handler();% f6 ]7 h: Q( N' J8 B- P
  19.   }1 M+ k  u5 a! p+ X
  20. }
复制代码
& Z9 Q. f$ K4 C5 V
⏩ 编写发送和接收数据函数:此处将格式固定为标准数据帧,ID为12$ ?% W9 B5 `& k) J+ o+ m+ q2 u

7 _6 u& g  y( }2 {8 |7 F
2 _. G8 i3 \6 S' d2 M+ h) R' W
  1. uint8_t CAN1_Send_Msg(uint8_t *msg, uint8_t len){
    ; x  a6 H2 q) `% M% J0 s
  2.   uint16_t i = 0;
    0 m& q. l3 w; D1 U- v8 u8 B
  3.   uint32_t txMailBox;, r% V) L; Z* _* Q! j
  4.   uint8_t send_buf[8];
    0 l- P6 j4 A5 B4 x, q/ B8 l- g
  5. 3 L" `8 F1 b- Y
  6.   txHeader.StdId = 12;
    & d, A& p, j& G0 }" p  U; J% Z
  7.   txHeader.ExtId = 12;
    " I" `( c3 |3 W2 T& y1 F6 R
  8.   txHeader.IDE = CAN_ID_STD;
    + K8 }" b! g1 e; e" [8 s
  9.   txHeader.RTR = CAN_RTR_DATA;4 p# C" q& g( {; f. X
  10.   txHeader.DLC = len;
    $ B+ j7 I2 Y; W  E5 R

  11. . w) D) c! l3 e9 ?  V/ d8 Z
  12.   for(i = 0; i < len; i++)
      w, h" m  q* h7 L, X5 G" s
  13.     send_buf[i] = msg[i];1 W4 R% T& M, n" ?# n, ?

  14. . p8 Y! x' u, J& \
  15.   if(HAL_CAN_AddTxMessage(&hcan1, &txHeader, send_buf, &txMailBox) != HAL_OK)
    $ g; @2 G5 `& I, C2 ~
  16.     return 1;
    * L+ t8 V6 G- h& h* B4 \5 M" g
  17.   return 0;7 M" ^1 I/ j: b9 i) g
  18. }
    6 D' @  O  Z$ P1 i' g' c# Q
  19. ' T% h/ A" N- R7 D4 D7 \. ?! r
  20. uint8_t CAN1_Recv_Msg(uint8_t *buf){
    % ^1 f; m: d) F
  21.   uint16_t i = 0; # |6 u+ A6 q1 S5 A. o
  22.   HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rxHeader, buf);
    0 }$ g' V8 p3 R) \  g' Q0 |

  23. " E% }0 L- n+ C, Y; ~2 S% j1 ~
  24.   if(rxHeader.IDE == CAN_ID_STD)
    ' {) ~+ r5 n0 ]6 V8 T$ V0 _- T8 g
  25.     printf("StdId ID: %d\r\n", rxHeader.StdId);1 e6 v! {" f  B. P. Y9 c5 Z
  26.   else% D: |7 n% @( N/ \
  27.     printf("ExtId ID: %d\r\n", rxHeader.ExtId);
    6 y1 q3 s/ D, O7 l7 [1 E- M3 h% N( K
  28. + R* c* _# ]6 c; y: q
  29.   printf("CAN IDE: %d\r\n", rxHeader.IDE);5 s! C' _& n+ }8 o  q3 h/ l
  30.   printf("CAN RTR: %d\r\n", rxHeader.RTR);  g6 v: n; f* M+ f( `
  31.   printf("CAN DLC: %d\r\n", rxHeader.DLC);6 T3 Y/ ^& ?; |0 O) @
  32.   printf("Recv Data: ");
    ' W& I2 ^) B& N  V1 T% u2 E

  33. & r- ]2 W$ X6 D  V* Z# i
  34.   for(i = 0; i < rxHeader.DLC; i++)+ _4 F: j0 T2 @# a1 \1 h
  35.     printf("%c",buf[i]);! f# ~% m) j0 ~: h, a! _9 e6 b

  36. + U  g, K: p7 V: Z
  37.   printf("\n");) |7 w- g+ `. @8 H6 E3 e6 V
  38.   return rxHeader.DLC;2 j& R: P! I: z  h3 G
  39. }
复制代码
& m* E1 K4 a+ w/ N7 w% ]2 N" n
⏩ 默认Cubemx生成的代码并没有can start,没有调用HAL_CAN_Start(&hcan1) 来使能CAN,因此需要在CAN初始化代码中添加0 D  |7 r  o0 v! E3 S

$ j/ m/ D+ K, {" }9 [7 O* x$ C

6 j0 Q0 b% \+ k8 c8 R
  1. void MX_CAN1_Init(void){' r% U1 N& [2 D. r' X% j
  2.   ......
    , F8 L) }2 \; {8 j" v/ [6 [
  3.   /* USER CODE BEGIN CAN1_Init 2 */
    . P  F+ c, k) ^( n- B  N
  4.   CAN_Filter_Config();
    7 S, Y6 u) g- D
  5.   HAL_CAN_Start(&hcan1); ; A% S0 k9 B# y# b# l; v$ [
  6.   /* USER CODE END CAN1_Init 2 */
    9 q) l7 D% n  y* u
  7. }
复制代码
8 z: C) k9 i& [" Q( L/ ?
⏩ 主函数main.c中,代码如下5 @3 L3 q5 O# t4 h7 x
! p* L" q7 O4 s) n: s
9 |& y5 b7 n0 Y; y; f4 v0 h
  1. int main(void){
    6 q9 I, z( g) r
  2.   HAL_Init();9 i8 J" S" i3 ?9 r8 W
  3.   SystemClock_Config();
    1 k& O# X4 L4 E4 F- t! \1 z: C
  4.   MX_GPIO_Init();! I: Z2 J3 _) u6 j% `
  5.   MX_CAN1_Init();( x' X' T* C* V
  6.   MX_USART1_UART_Init();1 t, E; B# w" `7 |0 `
  7.   /* USER CODE BEGIN 2 */
    # b% {3 o) |9 r+ r% `$ G5 y
  8.   uint8_t ret,i;# h  e- R: h9 c5 f
  9.   printf("CAN Testing....!\r\n");: z- M# ]2 u3 S( \0 C
  10.   uint8_t txdata[8] = {76, 79, 79, 80, 66, 65, 67, 75};5 k1 E5 Y0 t' M# v
  11.   uint8_t rxdata[8];* e' [9 H9 p7 \, |
  12.    /* USER CODE END 2 */
    ! P/ Y1 j+ o% b; w8 I
  13.    /* Infinite loop */
    ; B6 a6 |( i4 q1 \: r7 W
  14.    /* USER CODE BEGIN WHILE */1 }/ U2 M) I1 q
  15.   while (1)) E5 G) e; e7 h: z5 }4 C! L" m
  16.   {, E( ?$ G! y, A) y$ B7 ~
  17.     HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
    / C. Y2 u* v) s5 y, b- D2 l- @
  18.     HAL_Delay(1000);. W: h& ~: R8 m8 d6 N/ |6 S: _
  19.     printf("Start Send data...\r\n");
    , X( X! k( o2 W! u9 z, b, B
  20.   
    " {: H5 u6 ~* u. D9 _
  21.     ret = CAN1_Send_Msg(txdata, 8);; n! N# z) ^1 g9 p  z
  22.     if(ret == 0)
    * W) A( G0 [) o# j" x8 R
  23.       printf("CAN Send success!\r\n");! P' s: U3 v% H% J' Q5 B
  24.     else 5 ?, c5 B: t- i, y2 x
  25.       printf("CAN Send failed!\r\n");
      _4 c' ~$ O1 Y) [) _9 M! D
  26.   
    " `# Q3 S9 a7 k( r1 ^3 B
  27.     CAN1_Recv_Msg(rxdata);
    + Z( Q3 S8 Y  _
  28.     printf("+++++++++++++++++++++++++++++++\r\n");4 g, Y4 M! K7 i9 [3 p% G
  29.     /* USER CODE END WHILE */( O) s( U; p! o5 {9 k9 s
  30.     /* USER CODE BEGIN 3 */3 N$ m; M  c, L
  31.   }! B, C, `" C; N2 [
  32.   /* USER CODE END 3 */  z# \$ o/ H7 m
  33. }
复制代码
# z$ i$ Y) l& X1 z
3. 下载测试
8 ?6 p; H: y( r# D1 U
$ G( Z6 k# r* j0 x: Y+ _. q4 ^
9 Z, L8 |/ y# E9 W. i
编译无误后下载到开发板,可以看到系统运行时D1指示灯不断闪烁,串口不断的打印CAN环回测试的数据8 j* r) ?! T1 Y# E6 ^- R
4 I. _1 W9 Z4 @, [' ]5 w
  c; l1 U5 F1 p5 R* d2 U
微信图片_20230628143747.png
/ v! G# i9 z. A: w( r( B) E  ?4 i

" E7 T3 u" T5 R7 J& q9 ^( u2 V& z) g转载自: 嵌入式攻城狮
/ J0 T' Y9 `2 S- j# y如有侵权请联系删除
9 n6 }' p( m% H
/ e+ b3 y& [$ I6 V3 Y) t
* K9 @4 w% U' L) U) J8 `: v6 j
7 X/ }* u% W' A, Y( b& [3 q
) ^) Z/ [9 d" Q; M
赞 收藏 评论0 发布时间:2023-6-28 14:42

举报

0个回答

所属标签

相似技术帖

官网相关资源

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