|
原理图 6 Q) H& U$ e5 X' h L
! ~# w) J3 e0 @& Y4 u3 r y代码实现思路发送思路:定时发送 按键测试发送 接收思路:中断接收
' t! G. ^+ }& N0 v3 g CAN代码实现- 第一步2 i4 O- V* k* w. ~3 b. W& Y4 z" P
定义了两个全局变量TxMessage和RxMessage, 分别用于发送和接收CAN消息的邮箱结构体。 - CanTxMsg TxMessage = {0};//发送邮箱结构体
, c" J5 ]) w% X0 b: t - CanRxMsg RxMessage = {0};//接收邮箱结构体
复制代码 ; M" Q) D w- F
- 第二步1 t/ @! C9 g3 J, l b# `# N
配置对应的引脚模式 通过GPIO_InitTypeDef结构体初始化了GPIOA的引脚12,配置为复用推挽输出。接着,初始化了引脚11,配置为浮空输入。
6 @1 X+ D P q f CAN_RX的模式对于CAN接收引脚,可以选择使用浮空输入模式或上拉输入模式,具体取决我们硬件设计和应用要求。 浮空输入模式(Floating Input)是指将引脚置为高阻态,不连接到任何电平源,允许外部信号自由驱动引脚电平。在CAN总线中,通常使用终端电阻(通常为120欧姆)来驱动CAN引脚电平。这样可以确保在总线空闲状态时,引脚电平为高阻态。 上拉输入模式(Pull-up Input)是指在引脚和VDD之间连接一个上拉电阻,将引脚电平拉高到VDD(逻辑高电平)或外部信号源。在CAN总线中,上拉输入模式可用于引脚电平的恢复和传输。 选择浮空输入模式还是上拉输入模式取决于具体应用的要求。通常,如果CAN总线上的其他设备已经提供了终端电阻,可以选择使用浮空输入模式。如果的硬件设计中没有终端电阻,您可以选择使用上拉输入模式,并使用外部上拉电阻将引脚电平拉高到逻辑高电平。 # p' W- W) X6 R( D1 m `
CAN_TX的模式在CAN通信中,CAN_TX模式是用于配置CAN发送引脚的模式。选择适当的CAN_TX模式取决于我们的硬件设计和应用需求。 常见的CAN_TX模式有以下两种: 推挽输出模式(Push-Pull Output):在该模式下,CAN发送引脚被配置为推挽输出,可以提供较高的驱动能力,能够输出高电平和低电平两种电平状态。这种模式适用于直接驱动总线上的终端电阻,提供较强的信号驱动能力。 开漏输出模式(Open-Drain Output):在该模式下,CAN发送引脚被配置为开漏输出,只能提供低电平输出,而高电平状态是通过外部上拉电阻拉高到逻辑高电平。这种模式适用于与其他设备共享总线的情况,需要使用外部上拉电阻来拉高总线电平。 ' K) N9 o3 `3 N) W( z
选择CAN_TX模式取决于我们的硬件设计和连接配置。如果我们的硬件设计中包含终端电阻,并且CAN总线上没有其他设备需要共享总线,您可以选择推挽输出模式。如果我们的硬件设计中没有终端电阻或需要与其他设备共享总线,则可以选择开漏输出模式。 - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟
8 ?# v2 v( H& S( N0 f - RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟/ G+ x1 {3 B" y2 k! g
) V$ N6 H. P+ Q# K3 C& u" a- : o* k' { c$ D2 e3 ^9 Z
- GPIO_InitTypeDef GPIO_InitStructure;' [9 K* x% `; g% U" g' `
- ) n' P( z {5 W! \4 D+ L, T& X9 L, i( i
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;' l' W. `2 f1 O( `/ d
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
, k1 c3 D/ U% B2 b) k$ X: i - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽( U s7 B2 ]8 A* M! k0 p
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
* W! X# e* b0 N( C/ p; n
4 c, A5 _5 |" X) G7 W1 S5 v- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
/ |% l& e. r# O5 e8 Q9 Y9 A/ [ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//上拉输入9 d, K, Q( U* }! v! r
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化IO
复制代码
8 K" {) Z. `* [! s- 第三步配置CAN相关参数 定义了一个CAN_InitTypeDef结构体CAN_InitStructure,用于配置CAN的工作模式和速率。在这里,设置了CAN的模式为正常模式,速率为1Mbps。同时,设置了同步跳转宽度(SJW)为1个时间单位,位时间1(BS1)为2个时间单位,位时间2(BS2)为3个时间单位,预分频器(Prescaler)为6。8 P4 M/ y$ r* w7 X4 Y: M: F/ J: V
通过CAN_Init函数对CAN进行初始化,传入了上述配置结构体。 然后,定义了一个CAN_FilterInitTypeDef结构体CAN_FilterInitStructure,用于配置CAN的过滤器。在这里,设置了过滤器0,使用标识符屏蔽模式,并且过滤掉所有的标识符和掩码。将过滤器与FIFO0相关联,并激活过滤器。通过CAN_FilterInit函数对CAN过滤器进行初始化,传入了上述配置结构体。 - CAN_InitTypeDef CAN_InitStructure;$ c: f( |1 _+ ]$ i% p8 x+ ^0 L# ~8 N
-
/ f) ^$ Z! x3 h5 Z) P* X# h - CAN_InitStructure.CAN_TTCM=DISABLE; ! Z0 d! t) |9 C W' ]0 |
- CAN_InitStructure.CAN_ABOM=DISABLE;
( V1 b5 U: ?/ c( G/ c - CAN_InitStructure.CAN_AWUM=ENABLE;
: t. E! N9 D) |# @- J - CAN_InitStructure.CAN_NART=DISABLE;
9 F* q; z0 }! F3 \6 ]% `7 b - CAN_InitStructure.CAN_RFLM=DISABLE;
& P( m2 I1 ~5 p2 f7 j' K - CAN_InitStructure.CAN_TXFP=DISABLE;
& t) w- f6 S7 i' H7 i) |5 q
, z6 s# ~: E8 M- K3 g. h e- CAN_InitStructure.CAN_Mode= CAN_Mode_Normal; //模式设置% y/ M* K8 s: t( b/ G6 P
* \0 `; ^% e0 r! y r# ?- //1Mbits 500K
7 K6 k$ [2 u( k( ?" \- p+ i - CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;% Z% f/ a6 R1 h0 R4 i
- CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;" Y" v* C% n' e) H
- CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;
0 c( E; I3 k2 y' v1 h - CAN_InitStructure.CAN_Prescaler = 6; //6分频3 c6 I% w5 a" ]' G* B4 j$ u6 h4 a* `' l
- / E& R$ x2 s5 v8 q- o6 W
- CAN_Init(CAN1, &CAN_InitStructure); + l2 q8 [! M6 ^9 X
9 \0 V5 D" L% a* M0 J& }0 n* ~- H- CAN_FilterInitTypeDef CAN_FilterInitStructure;+ s9 V' L% h2 @7 o: o
- 7 J7 W2 d, r8 v6 ~) s
- CAN_FilterInitStructure.CAN_FilterNumber = 0; //过滤器0
5 r1 H. L- D! p - CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
) U% _, c% U" M' D+ Q; c - CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //32位
1 `$ J+ J7 ^' p/ _3 z4 c$ H { - CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;////32位ID
9 Y% L9 _) {2 V1 g+ M) C3 ?" Q" V - CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;1 H/ W' i' i0 ~- ]% ] [
- CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;//32位MASK
( Q: b0 D* E6 E$ T - CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
4 h- C0 s! }5 \7 E& Z2 p - CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//过滤器0关联到FIFO0
% A1 ]$ g3 F- K% L - CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
, |( f' G1 k3 {% y; Z3 N4 ~5 U& C# k - : ?' b$ {* B7 w/ F3 V( Q0 { \, C
- CAN_FilterInit(&CAN_FilterInitStructure);
复制代码 8 d* y: E6 l% e& [: ~
- 第四步
- 通过CAN_ITConfig函数使能CAN1的FIFO0接收中断。! K$ d& A) m' X) W! t N
然后,定义了一个NVIC_InitTypeDef结构体NVIC_InitStructure,用于配置CAN接收中断的中断优先级。在这里,设置了中断通道为USB_LP_CAN1_RX0_IRQn,主优先级为1,次优先级为0,并使能中断。 最后,在CAN_Config函数中进行了CAN总线的配置和初始化,并完成了中断的设置。 % s+ s( t L: T, b* @/ L! B
CAN接收逻辑实现中断方式 - //CAN接收数据的函数9 q! f8 l& I8 y1 _# R+ U5 ]/ O
- void USB_LP_CAN1_RX0_IRQHandler(void)* J% A3 {* N# i; I* h
- {
1 t- ^8 @! U j6 `/ d& C' j - if(CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET)//查询标志位
$ N g0 `3 R2 I6 g8 ] - {
9 T* _" X C: ?& _/ Q! R1 w2 @8 ~. A - CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); //接收信息
) A1 X" D- L3 W+ v0 W -
+ ^, C( O- ^% O5 X - // 打印接收到的数据
# r- D" B9 Q) o, R5 Y4 z1 a - printf("Rece CAN message: ID:0x%02X, DLC:%d, Data:", RxMessage.StdId, RxMessage.DLC);
0 G# l/ [4 W4 o; X2 {* F - for(uint8_t i = 0; i < RxMessage.DLC; i++)
( Z: G( E( I8 ]3 C0 M - {
; c0 {- x6 ?+ ?' l6 i+ [ - printf("%02X ", RxMessage.Data[i]);
7 j$ e4 Q c% h9 P+ F$ B - }& N9 V6 \+ t3 j! |/ d+ }: i
- printf("\n");
9 A( L+ N" S3 j) l - //CAN_BAOWENJIANCE()
( t U, Q0 R; F- h- f0 O- N" [ - //打印信息 串口1 串口2 printf都可以 / s3 V8 u: \' N: o# w3 H
-
$ I6 Q+ t! f8 q: ~" O9 ~* v! u2 f - CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); // 清除中断标志位% H$ h$ Z# o: P w) U
- }
6 M5 U5 r7 K% f8 C+ P- N1 K5 P - }
复制代码
- |" F1 o8 @8 v I4 P( E7 A& J8 RCAN发送逻辑的实现- //CAN发送数据的接口函数
! z6 m3 P7 D& X3 f/ K - uint8_t CAN_SendMessage(uint32_t StdId, uint8_t* pData, uint8_t Length)$ V9 b$ I0 S* f* k
- {1 |; m6 g, \1 Q3 }' F. g: F+ U
- uint8_t MailBox = 0;+ X& F( V9 x! q5 Q2 h+ b# l
- TxMessage.StdId = StdId;- Y, E* ~6 M+ j. x
- TxMessage.ExtId = 0x00;
+ K; w6 H, @& N! E1 X& b - TxMessage.IDE = CAN_ID_STD;
7 P' ~9 K. }: M, n - TxMessage.RTR = CAN_RTR_DATA;1 A4 _0 C# f% H
- TxMessage.DLC = Length;
2 y9 V3 F! g0 K - for (uint8_t i = 0; i < Length; i++)
# C b3 W6 [$ w; G& f - {
0 f, |5 p: a; {' P - TxMessage.Data[i] = pData[i];//发送的数据赋值
1 ?( } a, f4 V; @6 _ - }" H- _+ k* Z- N& h% p) \' L
- * E# ^* u' G* q. p; m
- // 打印接发送的数据. x6 A' p$ u+ z4 c0 B
- printf("Send CAN message: ID:0x%02X, DLC:%d, Data:", TxMessage.StdId, TxMessage.DLC);7 x: h- M5 N) a9 J# P N
- for(uint8_t i = 0; i < TxMessage.DLC; i++); c0 a9 n. N) P; M3 W
- {% p2 N) F" m3 i0 u$ d% F
- printf("%02X ", TxMessage.Data[i]);1 J# a$ B' _$ v W$ p+ \
- }
7 U- p; J* s* w# T: e - printf("\n");' o# z% _4 u- n3 F, s
-
6 Y3 T* g1 D, F5 k, Z) `( D! X - MailBox = CAN_Transmit(CAN1, &TxMessage);+ K; z1 p6 G+ d5 T; W
- while((CAN_TransmitStatus(CAN1, MailBox)==CAN_TxStatus_Failed));
' [* \/ ?" V. i t - return TxMessage.DLC; //返回值是发送了几个字节; D! @( m8 F6 L3 B, z3 v: n
- }
复制代码 5 F' ?. M6 i( P3 O$ ?/ l
发送信息这是一条标准数据帧 由MCU发出 - //发送信息在这里填写
' S* P& }$ A0 F# D7 v - uint32_t StdId = 0x55;//数据帧ID
|( h) S+ s5 R' ?- z% I - uint8_t TxData[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};//8字节数据内容4 p6 d# ~3 h- z
- uint8_t Length = 8;//发送字节个数
复制代码
3 V& J( G; T; O' w9 F接收信息这是一条标准数据帧 由
+ @3 |3 B' M1 y, E
8 Z9 ~6 |6 \4 E
上位机CAN调试助手发出
1 h3 s) T: w; F" j, p: f 测试效果6 v' T6 Z/ P) b6 S
' u0 ?/ E6 {9 a
$ @3 j- U$ I I5 \" a$ j8 k# R) A& T; s
转载自: 小李的创客实验室
- g. x1 c! u* ^* ~$ u d2 |" Y9 _如有侵权请联系删除
8 @9 s% h7 n! f) W" O$ D; ~. `9 E! \% l& u- K! D
# k, a' y' ]# A; r) w/ W \7 H8 K) ?7 R5 \- L- B+ s# ?
" l8 [, @" G+ |6 b5 q
# l4 t2 C9 V* z: B# r& Y3 T |