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

基于STM32实现CAN收发通信经验分享

[复制链接]
攻城狮Melo 发布时间:2023-6-14 14:28
原理图


7 f+ E& k; k% ~! s- [

微信图片_20230614142827.png


  p0 a5 D; P4 [5 n6 q代码实现思路

发送思路:定时发送 按键测试发送 接收思路:中断接收


: ^( }$ |' C# K- @

CAN代码实现
  • 第一步8 y( j1 D" o% x

定义了两个全局变量TxMessage和RxMessage, 分别用于发送和接收CAN消息的邮箱结构体。

  1. CanTxMsg TxMessage = {0};//发送邮箱结构体' X9 T& F. l6 |" n, D
  2. CanRxMsg RxMessage = {0};//接收邮箱结构体
复制代码

& O7 a: H" C* z* G' e: D1 ?0 z
  • 第二步
    & U% H5 W- \  v& T! U4 p

配置对应的引脚模式 通过GPIO_InitTypeDef结构体初始化了GPIOA的引脚12,配置为复用推挽输出。接着,初始化了引脚11,配置为浮空输入。


- i* C9 q" c( L6 r: A3 ]9 r

CAN_RX的模式

对于CAN接收引脚,可以选择使用浮空输入模式或上拉输入模式,具体取决我们硬件设计和应用要求。

浮空输入模式(Floating Input)是指将引脚置为高阻态,不连接到任何电平源,允许外部信号自由驱动引脚电平。在CAN总线中,通常使用终端电阻(通常为120欧姆)来驱动CAN引脚电平。这样可以确保在总线空闲状态时,引脚电平为高阻态。

上拉输入模式(Pull-up Input)是指在引脚和VDD之间连接一个上拉电阻,将引脚电平拉高到VDD(逻辑高电平)或外部信号源。在CAN总线中,上拉输入模式可用于引脚电平的恢复和传输。

选择浮空输入模式还是上拉输入模式取决于具体应用的要求。通常,如果CAN总线上的其他设备已经提供了终端电阻,可以选择使用浮空输入模式。如果的硬件设计中没有终端电阻,您可以选择使用上拉输入模式,并使用外部上拉电阻将引脚电平拉高到逻辑高电平。

1 s1 x  q  l# N/ }9 S- c4 x5 t3 J

CAN_TX的模式

在CAN通信中,CAN_TX模式是用于配置CAN发送引脚的模式。选择适当的CAN_TX模式取决于我们的硬件设计和应用需求。

常见的CAN_TX模式有以下两种:

  • 推挽输出模式(Push-Pull Output):在该模式下,CAN发送引脚被配置为推挽输出,可以提供较高的驱动能力,能够输出高电平和低电平两种电平状态。这种模式适用于直接驱动总线上的终端电阻,提供较强的信号驱动能力。

  • 开漏输出模式(Open-Drain Output):在该模式下,CAN发送引脚被配置为开漏输出,只能提供低电平输出,而高电平状态是通过外部上拉电阻拉高到逻辑高电平。这种模式适用于与其他设备共享总线的情况,需要使用外部上拉电阻来拉高总线电平。

    . ~) [8 j0 R3 f* l3 R

选择CAN_TX模式取决于我们的硬件设计和连接配置。如果我们的硬件设计中包含终端电阻,并且CAN总线上没有其他设备需要共享总线,您可以选择推挽输出模式。如果我们的硬件设计中没有终端电阻或需要与其他设备共享总线,则可以选择开漏输出模式。

  1. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟. X4 N- I0 {9 C0 ~" N
  2.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,  ENABLE);//使能CAN1时钟  S/ K+ `5 E5 Z3 Z# L" j5 y- Y
  3. 3 S8 e/ U  F: {' F
  4. * G" ?% s- k& t' m+ f
  5.         GPIO_InitTypeDef GPIO_InitStructure;4 M- m2 L& j/ q

  6. / S( @; H4 p" u0 a, c
  7.         GPIO_InitStructure.GPIO_Pin    = GPIO_Pin_12;! {/ D7 T! `: m* \5 X1 j9 H
  8.         GPIO_InitStructure.GPIO_Speed  = GPIO_Speed_50MHz;
    ) k# u' F" C5 }% T/ {
  9.         GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_AF_PP;        //复用推挽3 K5 i. y) U/ R. E7 [
  10.         GPIO_Init(GPIOA, &GPIO_InitStructure);                //初始化IO5 U3 G* x8 ~  W  k

  11. 3 H) m7 c/ e0 r7 B. Y0 w
  12.         GPIO_InitStructure.GPIO_Pin    = GPIO_Pin_11;
    ! [4 |8 Q5 S* F
  13.         GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_IN_FLOATING;//上拉输入
    % ^  i8 Q" k% U3 G6 u9 h6 z1 N
  14.         GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化IO
复制代码

, P4 S$ v- v' @* @5 U
  • 第三步配置CAN相关参数 定义了一个CAN_InitTypeDef结构体CAN_InitStructure,用于配置CAN的工作模式和速率。在这里,设置了CAN的模式为正常模式,速率为1Mbps。同时,设置了同步跳转宽度(SJW)为1个时间单位,位时间1(BS1)为2个时间单位,位时间2(BS2)为3个时间单位,预分频器(Prescaler)为6。
    4 Q. A. n" _: I- A& ?

通过CAN_Init函数对CAN进行初始化,传入了上述配置结构体。

然后,定义了一个CAN_FilterInitTypeDef结构体CAN_FilterInitStructure,用于配置CAN的过滤器。在这里,设置了过滤器0,使用标识符屏蔽模式,并且过滤掉所有的标识符和掩码。将过滤器与FIFO0相关联,并激活过滤器。通过CAN_FilterInit函数对CAN过滤器进行初始化,传入了上述配置结构体。

  1. CAN_InitTypeDef   CAN_InitStructure;
    - @; ]9 K9 e, }$ M; T9 K1 C
  2.         3 Y6 k# h- v6 H9 @1 J) t  `
  3.         CAN_InitStructure.CAN_TTCM=DISABLE;                                                7 X$ W; I% s3 [1 _
  4.         CAN_InitStructure.CAN_ABOM=DISABLE;                                               
    ' H2 S0 u/ D0 C2 j- u: u
  5.         CAN_InitStructure.CAN_AWUM=ENABLE;                                                 ! J7 K: y/ a% r1 R! W- x5 m
  6.         CAN_InitStructure.CAN_NART=DISABLE;                                                 
    1 q$ C; m! w) v- c5 C+ V) m
  7.         CAN_InitStructure.CAN_RFLM=DISABLE;                                                  K  i& [- g3 I2 ~- K  {
  8.         CAN_InitStructure.CAN_TXFP=DISABLE;                                                 6 D% E. ?9 \" u
  9. : R' p7 @$ C+ L
  10.         CAN_InitStructure.CAN_Mode= CAN_Mode_Normal;                 //模式设置
    : W3 L5 {4 x( O  y
  11. 3 s. }% D# Y) L0 d# V2 b
  12.   //1Mbits  500K  
    ( E- F7 `$ W4 ~% Y3 G: b
  13.         CAN_InitStructure.CAN_SJW       = CAN_SJW_1tq;; x" a6 H8 b+ [) c* g) ?) Y
  14.         CAN_InitStructure.CAN_BS1       = CAN_BS1_2tq;- `- P) u7 r1 y) c" i' V5 O
  15.         CAN_InitStructure.CAN_BS2       = CAN_BS2_3tq;      
    $ M" \# W( U, e  X3 k
  16.         CAN_InitStructure.CAN_Prescaler = 6; //6分频& i( @# I4 U0 s7 ?- l

  17. # o3 s" [- T' f$ I$ p( Z$ n
  18.         CAN_Init(CAN1, &CAN_InitStructure);   ! k1 F( _; _& a8 v' {% T: t

  19. ! }1 z6 o. J- N3 q" r( n+ \) k
  20.         CAN_FilterInitTypeDef    CAN_FilterInitStructure;
    6 {% z+ c" B* f% j

  21.   t: R$ G7 h2 h* I( p6 M2 L6 H2 Z
  22.         CAN_FilterInitStructure.CAN_FilterNumber              = 0;          //过滤器08 @# I/ i. @& A$ m% C8 G3 Y1 t
  23.         CAN_FilterInitStructure.CAN_FilterMode                = CAN_FilterMode_IdMask;
    " V, j3 ]' d( d( {( \* t
  24.         CAN_FilterInitStructure.CAN_FilterScale               = CAN_FilterScale_32bit; //32位 : \, @/ {: v1 r; ]( W, g2 u/ ]/ c
  25.         CAN_FilterInitStructure.CAN_FilterIdHigh              = 0x0000;////32位ID4 y- _& \8 ]' a' ~  Z6 U
  26.         CAN_FilterInitStructure.CAN_FilterIdLow               = 0x0000;
    $ f& o6 k/ s$ F. u  Z' G
  27.         CAN_FilterInitStructure.CAN_FilterMaskIdHigh          = 0x0000;//32位MASK: H* E) ^7 L$ f  m* t- q. G( j
  28.         CAN_FilterInitStructure.CAN_FilterMaskIdLow           = 0x0000;
    3 F: X, @5 f- q# `5 I0 W1 c
  29.         CAN_FilterInitStructure.CAN_FilterFIFOAssignment      = CAN_Filter_FIFO0;//过滤器0关联到FIFO0
    ( g: n1 M% B7 b+ ]
  30.         CAN_FilterInitStructure.CAN_FilterActivation          = ENABLE;
    . J4 C2 M+ z1 d5 h

  31. , J( |$ o* V3 j' @+ d
  32.         CAN_FilterInit(&CAN_FilterInitStructure);
复制代码

8 E' \, J( Q# g* U  q7 K1 c
  • 第四步
  • 通过CAN_ITConfig函数使能CAN1的FIFO0接收中断。1 V! }- {  h' a/ @/ W  j: ^1 w; H

然后,定义了一个NVIC_InitTypeDef结构体NVIC_InitStructure,用于配置CAN接收中断的中断优先级。在这里,设置了中断通道为USB_LP_CAN1_RX0_IRQn,主优先级为1,次优先级为0,并使能中断。

最后,在CAN_Config函数中进行了CAN总线的配置和初始化,并完成了中断的设置。

" i: B! t2 B" J* E% W3 z

CAN接收逻辑实现

中断方式

  1. //CAN接收数据的函数
    8 T5 ^* Y& k2 J9 N! {& n
  2. void USB_LP_CAN1_RX0_IRQHandler(void)
    4 \4 ?7 [+ e% `, S) ^
  3. {
    & p+ H9 j' x, [
  4.         if(CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)//查询标志位 6 m9 L2 a( F8 ?# }  i6 t
  5.         {- O3 g9 b: V, ~$ H
  6.                 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); //接收信息
    9 M0 D: |/ B- `6 o' e
  7.                 ( o4 n8 `; b- w
  8.                 // 打印接收到的数据! d. t3 x% d% ^: d3 }/ E- T  O
  9.                 printf("Rece CAN message: ID:0x%02X, DLC:%d, Data:", RxMessage.StdId, RxMessage.DLC);
    " _- `5 Z5 S; y. y! w( H9 H
  10.                 for(uint8_t i = 0; i < RxMessage.DLC; i++)
    ) h5 `. D) k: ^, T4 j
  11.                 {3 r$ j# C# a2 P- Z8 |
  12.                         printf("%02X ", RxMessage.Data[i]);
    : Z1 s; I6 _0 Y2 D
  13.                 }" z9 O! d3 v) U2 c7 f: a
  14.                 printf("\n");
    / C: A+ J4 n% a( j
  15.                 //CAN_BAOWENJIANCE()
    : E2 D8 r, J5 f: U. q
  16.                 //打印信息 串口1 串口2 printf都可以
    1 N; S" b! P4 r- m+ j3 k
  17.        
    # H( d' G: R6 i9 g
  18.                 CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); // 清除中断标志位
    0 H/ F) k4 o) m
  19.         }6 I$ P  `/ G) s1 I
  20. }
复制代码

% t) G+ q/ L; [% P1 B' R+ X; KCAN发送逻辑的实现
  1. //CAN发送数据的接口函数
    8 a. o& \2 H' h" f
  2. uint8_t CAN_SendMessage(uint32_t StdId, uint8_t* pData, uint8_t Length)
    + b( d+ Q# A4 [: b' t
  3. {$ a8 ]3 Q9 D; J" ~' P' h
  4.         uint8_t MailBox   = 0;
    1 n8 M  W' e" R( R0 e
  5.         TxMessage.StdId   = StdId;
    , M8 P. g) J2 C/ R- w* g
  6.         TxMessage.ExtId   = 0x00;1 V" O, T: A4 e; Y4 G
  7.         TxMessage.IDE     = CAN_ID_STD;3 C9 }6 i* v! q9 ]* y
  8.         TxMessage.RTR     = CAN_RTR_DATA;, `; ~8 B7 {1 h: O
  9.         TxMessage.DLC     = Length;
    / K# _6 Q. x/ W! o" J7 U
  10.         for (uint8_t i = 0; i < Length; i++)+ E* P6 @: V! i
  11.         {
    1 B& G; c2 Y) x' [
  12.                 TxMessage.Data[i] = pData[i];//发送的数据赋值
    2 x/ g* }2 B0 e1 R
  13.         }5 w; t* O' @& u+ O
  14.        
    7 H# `0 A3 a) f) u
  15.   // 打印接发送的数据  s8 k4 y3 }. h4 ?  x: o
  16.         printf("Send CAN message: ID:0x%02X, DLC:%d, Data:", TxMessage.StdId, TxMessage.DLC);  l* a. e1 `* G2 g# w* i
  17.         for(uint8_t i = 0; i < TxMessage.DLC; i++)" D/ ^: t  M0 @
  18.         {$ K% W: {+ ?' J" x- ~& \6 d
  19.                 printf("%02X ", TxMessage.Data[i]);9 c, P( s; G7 f$ I# S
  20.         }* i3 i) n, g( [2 d
  21.         printf("\n");" C2 s7 [+ S6 A0 C- ~' Q! Y9 n' K
  22.        
    - S8 T$ w! g7 V
  23.         MailBox = CAN_Transmit(CAN1, &TxMessage);3 Q3 Q9 b( P4 E& e
  24.         while((CAN_TransmitStatus(CAN1, MailBox)==CAN_TxStatus_Failed));  G7 ?: f- ?  M4 g9 I: z
  25.         return TxMessage.DLC;                //返回值是发送了几个字节
    ( F/ e; s: Y$ E" _. D
  26. }
复制代码
3 b& H2 D, C) H  ]9 S; q& P
发送信息

这是一条标准数据帧 由MCU发出

  1. //发送信息在这里填写
    5 s4 m5 V1 k0 q' x, U% ]% S
  2. uint32_t StdId     =  0x55;//数据帧ID
    3 L: J- E& h1 F
  3. uint8_t  TxData[8] =  {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};//8字节数据内容
    ) ], {4 H+ [# l/ m4 h& A8 Q9 v/ R
  4. uint8_t  Length    =  8;//发送字节个数
复制代码

7 L" ]( l, n; i$ \: ~& L
接收信息

这是一条标准数据帧 由


( _: i  b' M7 B6 x. ]: j( d8 l

微信图片_20230614142823.png


# ]4 U0 ^& d, i) L% C6 ^. f: U

上位机CAN调试助手发出

2 R2 h1 |1 F6 y4 i3 ]! ~$ Y- B

测试效果. q. M* {+ Y, H1 N

) d6 w) c0 v& J9 T, i: t& | 微信图片_20230614142820.png 2 a6 _5 a* U5 l! u0 Q: ~
6 E4 I1 X$ N. U) Y9 z
转载自: 小李的创客实验室
6 c  t8 ?8 Z8 X3 \如有侵权请联系删除7 [6 u) [$ s3 b7 y0 K; V, K4 G
6 U- c7 ^/ |" F5 ~. i

+ V0 L$ J2 n$ r* U, y" k+ {' ?& }" C" e7 y5 `4 A! u

# ?0 l+ v4 R2 m/ {
; m% W/ P; f1 T. w" j+ O6 w
收藏 评论0 发布时间:2023-6-14 14:28

举报

0个回答

所属标签

相似分享

官网相关资源

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