|
原理图 9 T" k& x7 E$ J n" G" q
- z9 g# S* X e8 T- C2 t7 E0 Z
代码实现思路发送思路:定时发送 按键测试发送 接收思路:中断接收
! k* ~+ S, F; v2 A* { CAN代码实现- 第一步
: k- ^3 l" b' W2 y/ _* y" m" x
定义了两个全局变量TxMessage和RxMessage, 分别用于发送和接收CAN消息的邮箱结构体。 - CanTxMsg TxMessage = {0};//发送邮箱结构体
/ H! ` E5 o/ y& e8 F. E - CanRxMsg RxMessage = {0};//接收邮箱结构体
复制代码 ' c+ V& J; y7 B% s7 D" b# k
- 第二步
, A3 [+ z g* |, H5 p3 W
配置对应的引脚模式 通过GPIO_InitTypeDef结构体初始化了GPIOA的引脚12,配置为复用推挽输出。接着,初始化了引脚11,配置为浮空输入。 6 W: e9 m: N) L$ u6 H$ G
CAN_RX的模式对于CAN接收引脚,可以选择使用浮空输入模式或上拉输入模式,具体取决我们硬件设计和应用要求。 浮空输入模式(Floating Input)是指将引脚置为高阻态,不连接到任何电平源,允许外部信号自由驱动引脚电平。在CAN总线中,通常使用终端电阻(通常为120欧姆)来驱动CAN引脚电平。这样可以确保在总线空闲状态时,引脚电平为高阻态。 上拉输入模式(Pull-up Input)是指在引脚和VDD之间连接一个上拉电阻,将引脚电平拉高到VDD(逻辑高电平)或外部信号源。在CAN总线中,上拉输入模式可用于引脚电平的恢复和传输。 选择浮空输入模式还是上拉输入模式取决于具体应用的要求。通常,如果CAN总线上的其他设备已经提供了终端电阻,可以选择使用浮空输入模式。如果的硬件设计中没有终端电阻,您可以选择使用上拉输入模式,并使用外部上拉电阻将引脚电平拉高到逻辑高电平。
( @2 h4 K; g5 Q4 Z CAN_TX的模式在CAN通信中,CAN_TX模式是用于配置CAN发送引脚的模式。选择适当的CAN_TX模式取决于我们的硬件设计和应用需求。 常见的CAN_TX模式有以下两种: 推挽输出模式(Push-Pull Output):在该模式下,CAN发送引脚被配置为推挽输出,可以提供较高的驱动能力,能够输出高电平和低电平两种电平状态。这种模式适用于直接驱动总线上的终端电阻,提供较强的信号驱动能力。 开漏输出模式(Open-Drain Output):在该模式下,CAN发送引脚被配置为开漏输出,只能提供低电平输出,而高电平状态是通过外部上拉电阻拉高到逻辑高电平。这种模式适用于与其他设备共享总线的情况,需要使用外部上拉电阻来拉高总线电平。 ' p3 ?# w& p8 ]' V0 W* f7 C$ g
选择CAN_TX模式取决于我们的硬件设计和连接配置。如果我们的硬件设计中包含终端电阻,并且CAN总线上没有其他设备需要共享总线,您可以选择推挽输出模式。如果我们的硬件设计中没有终端电阻或需要与其他设备共享总线,则可以选择开漏输出模式。 - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟
# p* v& O. V' M1 R0 b. y - RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
4 k; H9 D4 a! w) ^ - : s+ z+ Q) m" g' L
0 E6 `5 @; r1 C- GPIO_InitTypeDef GPIO_InitStructure;/ e d5 O' s6 K. K+ K& Q0 Z6 S
- , X, ?2 h8 H, N# s h
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
0 Z/ ]% C0 q/ T- b- n, E4 h( ~ - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;- s, ]9 m& B" j' J$ }8 X
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
$ ^3 G1 X' w$ @) A - GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO* U% a3 z( b1 [8 U! s
- 0 H# C1 ?6 t+ \2 _
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;9 [$ s$ K1 ?- P0 s. @0 c% k, k
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//上拉输入) D8 w$ j8 K& T2 L
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化IO
复制代码
) O/ R: R3 e( c/ L7 B; j" Z- 第三步配置CAN相关参数 定义了一个CAN_InitTypeDef结构体CAN_InitStructure,用于配置CAN的工作模式和速率。在这里,设置了CAN的模式为正常模式,速率为1Mbps。同时,设置了同步跳转宽度(SJW)为1个时间单位,位时间1(BS1)为2个时间单位,位时间2(BS2)为3个时间单位,预分频器(Prescaler)为6。
7 a5 o0 T& j& s; i) @: n
通过CAN_Init函数对CAN进行初始化,传入了上述配置结构体。 然后,定义了一个CAN_FilterInitTypeDef结构体CAN_FilterInitStructure,用于配置CAN的过滤器。在这里,设置了过滤器0,使用标识符屏蔽模式,并且过滤掉所有的标识符和掩码。将过滤器与FIFO0相关联,并激活过滤器。通过CAN_FilterInit函数对CAN过滤器进行初始化,传入了上述配置结构体。 - CAN_InitTypeDef CAN_InitStructure;8 t3 S: @7 L0 t4 Q8 n. Y- r
-
0 Q0 E. {; a5 v0 { - CAN_InitStructure.CAN_TTCM=DISABLE;
4 j, I! r3 i. }4 a$ `9 z# W - CAN_InitStructure.CAN_ABOM=DISABLE;
) a2 p; K' I4 B- O7 {8 ]1 g" P, ~# I - CAN_InitStructure.CAN_AWUM=ENABLE;
1 J* t. q7 U5 @5 M - CAN_InitStructure.CAN_NART=DISABLE;
. G9 P5 [- z. Z: L: V; w - CAN_InitStructure.CAN_RFLM=DISABLE;
8 r) V7 _: z" p/ a - CAN_InitStructure.CAN_TXFP=DISABLE;
0 h! o, y( A9 I - 6 U5 c% Q; P1 g& B( y6 J
- CAN_InitStructure.CAN_Mode= CAN_Mode_Normal; //模式设置
6 R/ ]" W/ ?) i- D: J4 g2 i
6 A" O8 A7 k2 D( p9 M- //1Mbits 500K a6 E* h$ R, Y" c
- CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
' x; \6 C/ C6 w - CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;' l, h# `" w4 x# S; Y. ]" L
- CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq; / J- d! I, d4 `+ [# M$ W
- CAN_InitStructure.CAN_Prescaler = 6; //6分频
" O& L- q# \2 g - v; ^0 b5 z/ u' a7 }+ {" X; S
- CAN_Init(CAN1, &CAN_InitStructure); ! Z! t& R9 ^8 s0 d' h
- ( }+ G8 a9 p. {, P
- CAN_FilterInitTypeDef CAN_FilterInitStructure;
4 b2 {8 y7 Z) x% V" c! K% O - 4 u0 L1 f/ g F/ u. S
- CAN_FilterInitStructure.CAN_FilterNumber = 0; //过滤器0
3 u/ `. ~- L0 W3 Y2 a - CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
3 X% j2 x& P6 \4 |6 n5 F - CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //32位 8 }7 @9 I% J' A8 Q* ]6 V, X! l
- CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;////32位ID* z% o6 h9 g7 A! f: P
- CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;$ L7 Q0 Q0 w) F0 v
- CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;//32位MASK
6 n5 h- B9 w8 b* _- s - CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
3 J% M+ h3 i, Q+ V- l - CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//过滤器0关联到FIFO0# t, I6 s6 f/ W. }; y
- CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
# r7 K2 Z) r1 j5 m0 c - % Z0 }0 w% e0 h( x: f
- CAN_FilterInit(&CAN_FilterInitStructure);
复制代码
* `& k7 R/ I2 o" ~( K. c# w- 第四步
- 通过CAN_ITConfig函数使能CAN1的FIFO0接收中断。4 K, K* P0 e) _6 `: u6 T
然后,定义了一个NVIC_InitTypeDef结构体NVIC_InitStructure,用于配置CAN接收中断的中断优先级。在这里,设置了中断通道为USB_LP_CAN1_RX0_IRQn,主优先级为1,次优先级为0,并使能中断。 最后,在CAN_Config函数中进行了CAN总线的配置和初始化,并完成了中断的设置。 ; S, S3 v- g0 d# r
CAN接收逻辑实现中断方式 - //CAN接收数据的函数
' y. G. x( c1 x* _5 A# S9 R3 u - void USB_LP_CAN1_RX0_IRQHandler(void)
% r) ~- [5 Q" D1 r - {+ @5 ^0 v3 K% X
- if(CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)//查询标志位
1 k. v$ g# {# L2 r* m8 @: h/ ] - {4 q& n2 R6 A% i1 `
- CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); //接收信息
0 M# x! Q7 `9 W( w4 J# C/ ]; `9 p K - - ~$ ?, q: V; x& ]7 \- O0 x
- // 打印接收到的数据, ?% E! T8 K3 D. m
- printf("Rece CAN message: ID:0x%02X, DLC:%d, Data:", RxMessage.StdId, RxMessage.DLC);- a5 ?( U) H k% {" O0 @( V4 d
- for(uint8_t i = 0; i < RxMessage.DLC; i++)
- J6 Y+ Q% R+ z5 ?* V; h* f - {
4 s, `# U z0 u3 I0 V. ^6 ^. m/ W3 b - printf("%02X ", RxMessage.Data[i]);% I9 w$ G& f/ F9 W0 h! m% C/ V
- }/ e/ R B( t) B, u2 a9 o+ p
- printf("\n"); I F1 R/ R1 Z; l
- //CAN_BAOWENJIANCE()
/ Z# |; ?/ ?6 R - //打印信息 串口1 串口2 printf都可以 ! A; ^4 Q* H2 F" P6 E
-
1 c' ]4 }0 P$ R6 U0 a - CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); // 清除中断标志位9 R5 [: F: n5 q& a: N2 t
- }
6 m# x0 K3 F& K: j - }
复制代码
/ U! G6 a& I2 z) rCAN发送逻辑的实现- //CAN发送数据的接口函数* _2 ~9 v+ G7 Z
- uint8_t CAN_SendMessage(uint32_t StdId, uint8_t* pData, uint8_t Length)* J. F, I5 [" w2 _/ Y4 O
- {
. Y1 t/ T% [2 T" w& ]# R5 Y; b - uint8_t MailBox = 0;
# ], N/ i; |6 Y5 i) D - TxMessage.StdId = StdId;9 t0 T1 s# o* r* ^5 D: @! e
- TxMessage.ExtId = 0x00;
' D# ? c% k) F. l' r0 u$ P7 H$ c - TxMessage.IDE = CAN_ID_STD;
1 D: P: g' z; Q/ v+ H j5 d - TxMessage.RTR = CAN_RTR_DATA;
) y! Z0 Z- K& s2 ~/ ^ - TxMessage.DLC = Length;
* Y4 A5 t% |8 Q3 A - for (uint8_t i = 0; i < Length; i++): w4 j" V2 `# K
- {5 J$ q* B+ l1 `! }
- TxMessage.Data[i] = pData[i];//发送的数据赋值
5 o* K5 Z% |1 P1 M - }
5 l$ |9 ^, D" e& B o4 W -
" l' ?" |7 A2 ~ - // 打印接发送的数据* Y: u- W6 ]- e$ W7 A, }% K
- printf("Send CAN message: ID:0x%02X, DLC:%d, Data:", TxMessage.StdId, TxMessage.DLC);+ t; G1 w/ i6 U( Y
- for(uint8_t i = 0; i < TxMessage.DLC; i++)
: q! q1 o; F( q+ L+ v; P0 z& h - {4 m- [& ]+ ]/ }: d
- printf("%02X ", TxMessage.Data[i]);" H. I3 `! P" G' P# }
- }
Q( H9 p5 h% `) y5 Z" l - printf("\n");
/ n+ a; y0 y( H+ ^ -
# o. M. v. {5 U0 }- U - MailBox = CAN_Transmit(CAN1, &TxMessage);- p' J; |8 ]6 M
- while((CAN_TransmitStatus(CAN1, MailBox)==CAN_TxStatus_Failed));
1 D$ ~9 V; V |% e- @$ W, l4 i7 w3 C$ } - return TxMessage.DLC; //返回值是发送了几个字节: w1 O& \5 A& U- P% W- |; K
- }
复制代码
2 s D, O$ ^% ^) a3 K' T发送信息这是一条标准数据帧 由MCU发出 - //发送信息在这里填写
; W5 `6 Z8 W* w/ X - uint32_t StdId = 0x55;//数据帧ID
0 l# l) m# B, U - uint8_t TxData[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};//8字节数据内容" \5 W6 s" @. c. t
- uint8_t Length = 8;//发送字节个数
复制代码 5 ]5 g2 U2 m) a9 k' f( B) z
接收信息这是一条标准数据帧 由
# Q1 f9 Y( V' v. ^; Z$ P& N
0 A/ s5 S' }9 J) t
上位机CAN调试助手发出
# ]$ t5 t/ L& K7 V( s' J 测试效果$ {4 K7 A! g# M: l2 ^# j7 L% w
! l V' @# v8 {( ?; ^
; e7 C- J) J# l4 B! e+ P* Q
7 D4 S' P- {6 o: d( ~8 Z- q
转载自: 小李的创客实验室
; g( M$ [, ?. i2 }: n" X如有侵权请联系删除' F& ^9 z/ @" M: I, R4 l2 Z
P' _, ^- K) D) m$ ?9 Z* X+ Y( X, s. H B/ w
) i9 M# S& w1 A- i1 a5 Z2 X' r5 S1 \. S9 w- B
5 N* T+ g) L9 ^" n |