|
原理图
2 t$ A: c3 J8 M9 F
% P0 r! P+ H7 H; U' I: M$ e/ Y$ Y
代码实现思路发送思路:定时发送 按键测试发送 接收思路:中断接收
4 A/ P5 y$ i+ H1 D9 q CAN代码实现定义了两个全局变量TxMessage和RxMessage, 分别用于发送和接收CAN消息的邮箱结构体。 - CanTxMsg TxMessage = {0};//发送邮箱结构体
; r4 R. t- l" B n) s4 q5 H - CanRxMsg RxMessage = {0};//接收邮箱结构体
复制代码 ( Y( v6 M, M& _! T2 I
- 第二步
$ B) ?, j8 I" {. j6 e4 s
配置对应的引脚模式 通过GPIO_InitTypeDef结构体初始化了GPIOA的引脚12,配置为复用推挽输出。接着,初始化了引脚11,配置为浮空输入。 ' b5 Z% y3 d! ?7 J& C. }
CAN_RX的模式对于CAN接收引脚,可以选择使用浮空输入模式或上拉输入模式,具体取决我们硬件设计和应用要求。 浮空输入模式(Floating Input)是指将引脚置为高阻态,不连接到任何电平源,允许外部信号自由驱动引脚电平。在CAN总线中,通常使用终端电阻(通常为120欧姆)来驱动CAN引脚电平。这样可以确保在总线空闲状态时,引脚电平为高阻态。 上拉输入模式(Pull-up Input)是指在引脚和VDD之间连接一个上拉电阻,将引脚电平拉高到VDD(逻辑高电平)或外部信号源。在CAN总线中,上拉输入模式可用于引脚电平的恢复和传输。 选择浮空输入模式还是上拉输入模式取决于具体应用的要求。通常,如果CAN总线上的其他设备已经提供了终端电阻,可以选择使用浮空输入模式。如果的硬件设计中没有终端电阻,您可以选择使用上拉输入模式,并使用外部上拉电阻将引脚电平拉高到逻辑高电平。
) Q9 N5 `$ r( w% u' ]0 n CAN_TX的模式在CAN通信中,CAN_TX模式是用于配置CAN发送引脚的模式。选择适当的CAN_TX模式取决于我们的硬件设计和应用需求。 常见的CAN_TX模式有以下两种: 推挽输出模式(Push-Pull Output):在该模式下,CAN发送引脚被配置为推挽输出,可以提供较高的驱动能力,能够输出高电平和低电平两种电平状态。这种模式适用于直接驱动总线上的终端电阻,提供较强的信号驱动能力。 开漏输出模式(Open-Drain Output):在该模式下,CAN发送引脚被配置为开漏输出,只能提供低电平输出,而高电平状态是通过外部上拉电阻拉高到逻辑高电平。这种模式适用于与其他设备共享总线的情况,需要使用外部上拉电阻来拉高总线电平。
2 S2 t/ `. n6 f ]7 T; Y* T
选择CAN_TX模式取决于我们的硬件设计和连接配置。如果我们的硬件设计中包含终端电阻,并且CAN总线上没有其他设备需要共享总线,您可以选择推挽输出模式。如果我们的硬件设计中没有终端电阻或需要与其他设备共享总线,则可以选择开漏输出模式。 - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟' X' H& g+ I. f
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
}% d l. S+ q% Z8 W. c: ` - ; x# T8 ?" X1 ]& x; o) X% T
, C. g* K! B4 h$ P- GPIO_InitTypeDef GPIO_InitStructure;
$ {; k9 w( R! g# } - " E7 [/ Y9 P, I0 i) r* Q! \0 J
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
! g4 \) B7 M2 } v" E - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;; S7 K% @3 ~3 T+ W( ]3 L
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
* y% i& m/ j" E" P0 E - GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
9 e7 r' U7 L! E' g+ H - + N( ?6 L: n* u: k, b j
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;$ Y/ G1 ?5 V$ s0 b/ T: v$ d: Z
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//上拉输入
0 K, s1 a$ q" e7 P' k - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化IO
复制代码 ) w M: a5 @, W' X6 K; s. H
- 第三步配置CAN相关参数 定义了一个CAN_InitTypeDef结构体CAN_InitStructure,用于配置CAN的工作模式和速率。在这里,设置了CAN的模式为正常模式,速率为1Mbps。同时,设置了同步跳转宽度(SJW)为1个时间单位,位时间1(BS1)为2个时间单位,位时间2(BS2)为3个时间单位,预分频器(Prescaler)为6。* T7 g8 x5 | J x
通过CAN_Init函数对CAN进行初始化,传入了上述配置结构体。 然后,定义了一个CAN_FilterInitTypeDef结构体CAN_FilterInitStructure,用于配置CAN的过滤器。在这里,设置了过滤器0,使用标识符屏蔽模式,并且过滤掉所有的标识符和掩码。将过滤器与FIFO0相关联,并激活过滤器。通过CAN_FilterInit函数对CAN过滤器进行初始化,传入了上述配置结构体。 - CAN_InitTypeDef CAN_InitStructure;4 M' ^8 ~! D6 p' }/ l: o U0 _$ z
-
& T4 e3 ^: L; h! }2 q" R - CAN_InitStructure.CAN_TTCM=DISABLE; : k- A6 Y0 \6 s/ c6 B$ W
- CAN_InitStructure.CAN_ABOM=DISABLE; $ F$ r8 b! ^+ `5 M: w% L( K6 o7 J
- CAN_InitStructure.CAN_AWUM=ENABLE;
# b5 y! K6 u+ H: L+ ] - CAN_InitStructure.CAN_NART=DISABLE; 9 R6 f3 m; l; S2 _$ K, U/ w" ^
- CAN_InitStructure.CAN_RFLM=DISABLE; ' O% V3 G {) }) O
- CAN_InitStructure.CAN_TXFP=DISABLE; - {% E* O. p* A* ?" C0 j
- - Y: S# p) F6 M2 ]4 Q: ?& k$ y3 _6 x
- CAN_InitStructure.CAN_Mode= CAN_Mode_Normal; //模式设置6 }+ M5 s: H' U
- 9 ?- k/ y- l4 E8 {
- //1Mbits 500K ! B2 O6 {' ^2 t; o
- CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;1 ?8 G% {& _! b- |3 @! }7 N
- CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;6 p" l$ f$ c# y5 z7 h/ U
- CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq; - U5 @& v2 U4 x
- CAN_InitStructure.CAN_Prescaler = 6; //6分频/ [9 @ q M/ ?/ T0 N
* g0 T9 U, s* L& X2 k( Y: S- CAN_Init(CAN1, &CAN_InitStructure);
5 O$ ~# @( U P5 t, a/ k; p
7 Q; T4 z: H# J) Q- CAN_FilterInitTypeDef CAN_FilterInitStructure; D% [- A" x1 D% x. D1 s3 s/ p( }; k
- 2 R! d! l j* w; M& k
- CAN_FilterInitStructure.CAN_FilterNumber = 0; //过滤器0: o2 @! F% m5 @+ C; `
- CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
& n8 q w0 F6 g - CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //32位
: L" T1 b! b& J$ z* p - CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;////32位ID" R; {( e1 J' A4 I( A. `2 j
- CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
$ C- `! z6 c) G( I7 [1 _ - CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;//32位MASK
' x+ ?1 `2 M- M6 M/ O - CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
! p* w6 O2 P; [+ k2 C9 ?% x - CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//过滤器0关联到FIFO0
/ G* s* e2 y" {) a" x& ~! C& d' v: G - CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
2 W% B/ O# j$ K/ |
4 _( n Z/ q3 U5 B3 t- CAN_FilterInit(&CAN_FilterInitStructure);
复制代码
- @" y4 P8 Q) A L1 a- ?. w# n+ _- 第四步
- 通过CAN_ITConfig函数使能CAN1的FIFO0接收中断。4 P) y% U. [, B$ `: {
然后,定义了一个NVIC_InitTypeDef结构体NVIC_InitStructure,用于配置CAN接收中断的中断优先级。在这里,设置了中断通道为USB_LP_CAN1_RX0_IRQn,主优先级为1,次优先级为0,并使能中断。 最后,在CAN_Config函数中进行了CAN总线的配置和初始化,并完成了中断的设置。
& l! R+ I# K0 y% [( d% I0 t5 v# k+ e/ u CAN接收逻辑实现中断方式 - //CAN接收数据的函数9 p9 l. N& M6 G% R8 u. ]# `
- void USB_LP_CAN1_RX0_IRQHandler(void); i( H- y! C# N3 p
- {
9 j* B# P' ^* h' `& P - if(CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)//查询标志位
, K9 Q y4 z9 F$ R6 B% Q3 v - {% o8 T1 a9 Q% T4 z4 X, E" r
- CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); //接收信息
9 O! ?& G3 A) i% u5 P/ E( ^ -
# z$ H' e& }2 x, v/ {- @( H - // 打印接收到的数据
% `, {' c; a, M8 \( i/ Y' d8 e - printf("Rece CAN message: ID:0x%02X, DLC:%d, Data:", RxMessage.StdId, RxMessage.DLC);
4 j& R, f' _& k: P - for(uint8_t i = 0; i < RxMessage.DLC; i++)6 \$ S! [: f3 L
- {
0 `4 g' m% T9 x* V* ~" D7 l) n - printf("%02X ", RxMessage.Data[i]);
; e( r$ z! ?- p; X0 {: M - }4 ? @6 e2 ~$ W! k4 A O
- printf("\n");% H9 j( J7 `7 B" @- p
- //CAN_BAOWENJIANCE()
! x4 S+ a+ k( D( ]. S/ m - //打印信息 串口1 串口2 printf都可以 5 c+ R6 q& j; k' [1 }* ?+ S9 n$ D
-
/ ?1 k i6 I* q1 l9 h - CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); // 清除中断标志位
) Q4 L# n6 s" c; m/ r4 ` - }
9 e4 l# P& Q! y0 y0 g - }
复制代码
- ^2 n. ~7 b) ?/ rCAN发送逻辑的实现- //CAN发送数据的接口函数
% u) }& C2 p2 H; _ - uint8_t CAN_SendMessage(uint32_t StdId, uint8_t* pData, uint8_t Length)
2 Q. `) q5 `( c4 P. l+ |7 {5 g - {2 y$ i' A6 t$ S7 G- |5 @. f5 l: ]
- uint8_t MailBox = 0;
$ l$ N+ _0 ?0 E$ g4 L: K) M& S - TxMessage.StdId = StdId;) S' T) ?% J: [, k
- TxMessage.ExtId = 0x00;
" R. w" G7 m+ F3 x - TxMessage.IDE = CAN_ID_STD;
5 V2 h, E6 ^# l' v* T0 _ - TxMessage.RTR = CAN_RTR_DATA;
# j/ c4 C9 @# S5 p( X - TxMessage.DLC = Length;
1 e+ N! b8 R/ }$ W - for (uint8_t i = 0; i < Length; i++). S* Q `: f; v; S
- {1 ` P9 Z) m5 d2 g5 p! A
- TxMessage.Data[i] = pData[i];//发送的数据赋值; P- D' P6 X% B% d
- }
: R4 U0 n7 w: ]+ o6 e9 f - # l8 ^! D& Y7 r2 `4 K; h
- // 打印接发送的数据 {, K E$ K$ }9 m ]
- printf("Send CAN message: ID:0x%02X, DLC:%d, Data:", TxMessage.StdId, TxMessage.DLC);& n7 x6 J, B: n% `" q
- for(uint8_t i = 0; i < TxMessage.DLC; i++)
# s ?% J; L6 T9 c7 [6 E& Z - {
6 h( T" z+ y" A - printf("%02X ", TxMessage.Data[i]);
- V. {6 B, I, J% ~. k4 A - }# ?9 z8 C6 ?1 Y9 V2 Y9 q
- printf("\n");
$ ^+ L. e% O! o* s2 i -
. z/ ~! M! X6 H* v) C - MailBox = CAN_Transmit(CAN1, &TxMessage);
; a4 d9 ]+ V, e; b6 s# { - while((CAN_TransmitStatus(CAN1, MailBox)==CAN_TxStatus_Failed));
* A! F8 x) r% ]2 ~' u: l. Y - return TxMessage.DLC; //返回值是发送了几个字节& n8 U, B: J& Y3 ~5 R& g8 {1 y
- }
复制代码 ' f: I! n5 b: p
发送信息这是一条标准数据帧 由MCU发出 - //发送信息在这里填写- D( F4 q( C" k5 a
- uint32_t StdId = 0x55;//数据帧ID. P* R% c' T0 J% h% ?
- uint8_t TxData[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};//8字节数据内容2 E9 b, L4 }( Q; |) _6 b
- uint8_t Length = 8;//发送字节个数
复制代码
/ q+ p: Q/ w2 J1 S7 O接收信息这是一条标准数据帧 由 - S- E% [9 D. e/ k8 ]3 e: X! {# R+ t
& ?7 n, m. _- U: _; z
上位机CAN调试助手发出 0 `6 g2 l: i* c% C. R8 a% f
测试效果
) n4 x; O9 x7 c4 c) u
, p: \* |& P; T) P* T5 {1 R# w
! _ V- B4 G4 j
5 t5 |6 i" J8 J- ~ J% m) K转载自: 小李的创客实验室
( {' t0 n" }8 B如有侵权请联系删除
' T: W' r! J; D% Y; [0 q' \" P* s% W; Y$ S: t& J9 B0 A
! t( [- |7 w6 w* |
( B" k8 z/ @( w3 i+ @' u6 i0 ]/ A2 @9 U5 _5 p* ]
( |, O1 t/ U/ j' x$ t2 f# K+ J& h |