由于鄙人比较懒,因此本文章只是对 FDCAN 的 经典模式 的简单使用介绍。对于我不需要使用的功能 我就没有深入研究,因此本文只是 CAN 的常用方式的笔记,深入研究的话可以详细阅读手册,本文章没有介绍。
如果有介绍错误的地方,希望各位大佬指正,鄙人一定修改,必要时删除本文章,防止误导他人。欢迎讨论!
FDCAN 与 经典CAN 的最直观差别就是数据帧部分,FDCAN的数据帧波特率可变,并且数据大小不局限于 8 Bytes ,最高支持 64 Bytes 的数据传输。详细差别见 传送门: CAN FD 与 CAN协议区别–简单易懂协议详解 。
1. FDCAN的框图
根据上图可以看到,FDCAN 有两个时钟域,APB总线接口和CAN内核时钟。FDCAN的波特率取决于内核时钟,寄存器的配置、消息RAM等,走的是APB时钟。FDCAN的APB时钟在RCC_APB寄存器中配置,内核时钟在RCC_CCIPR寄存器中配置(下一章)。 FDCAN有两个中断线,这两个中断线可以单独的打开或关闭,在FDCAN_ILE寄存器中配置。 其他具体介绍参考DM00355726_ENV4手册的1947页(RM0440手册官方下载传送门)。
2. FDCAN的时钟与波特率
2.1 fdcan_clk
几乎所有通信都可在RCC中配置时钟选择,FDCAN的时钟(fdcan_clk)可在RCC_CCIPR寄存器中进行配置。
2.2 fdcan_tq_ck
根据系统框图可看到,系统输入的时钟可通过 fdcan_clk 分频得到 fdcan_tq_ck ,在 FDCAN_CKDIV 寄存器中配置分频系数。
2.3 波特率
FDCAN模块的bit采样分三段:
波特率公式如下:
波
特
率
=
1
t
q
+
t
B
S
1
+
t
B
S
2
波特率 = \frac{1}{t_q + t_{BS1} + t_{BS2}}
波特率=tq?+tBS1?+tBS2?1?
t
q
=
(
B
R
P
+
1
)
?
f
d
c
a
n
_
c
l
k
t_q = (BRP + 1)*fdcan\_clk
tq?=(BRP+1)?fdcan_clk
注意: 1、由于 FDCAN 与 经典CAN 的帧格式有点差异,其数据帧波特率可变,因此在这里分出普通CAN和数据bit时间 2、数据 和 普通 bit 时间寄存器名称不一样,数据波特率寄存器 FDCAN_DBTP ,一般帧波特率寄存器为 FDCAN_NBTP 。
3. FDCAN的地址分布
3.1 总体分布
G474的FDCAN外设一共有3组(FDCAN1,FDCAN2,FDCAN3),寄存器配置分为3个,消息RAM也分为3组。 这里可以先把 消息RAM 的宏定义写好
#define FDCANs_MESSAGE_RAM_BASE 0x4000A400
#define FDCAN1_RAM_BASE (FDCANs_MESSAGE_RAM_BASE)
#define FDCAN2_RAM_BASE (FDCAN1_RAM_BASE + 0x00000400)
#define FDCAN3_RAM_BASE (FDCAN2_RAM_BASE + 0x00000400)
3.2 Message RAM
1个 信息RAM 有 212 words 的大小,即 212 × 4 = 848 Bytes 。 根据该图,可以创建一个对应上述地址的结构体,方便后期地址的管理:
typedef struct
{
uint32_t FILTER_11BIT[28];
uint32_t FILTER_29BIT[16];
uint32_t Rx_FIFO_0[54];
uint32_t Rx_FIFO_1[54];
uint32_t Tx_FIFO[6];
uint32_t Tx_BUFFER[54];
}FDCAN_RAM_Struct;
4. FDCAN的筛选器
4.1 筛选器的特征
- 每个筛选器可配置为
? 范围筛选 ? 指定ID筛选 ? 屏蔽典型bit筛选 - 每个过滤器元件可配置为接受或拒绝过滤
- 每个过滤器元素可以单独启用/禁用
- 过滤器是按顺序检查的,执行在第一个匹配的过滤器元素时停止
相关的寄存器:全局筛选配置(RXGFC)、拓展ID和掩码(XIDAM)
如果需要接收数据,一定要配置筛选器,否则会什么也接受不到。
4.2 11-bit 筛选器的格式
这里根据手册的翻译进行配置就好,29-bit 的筛选器也是如此,手册里全有,我就不放图片了,太占地方。
对于筛选器的掩码模式,可通过传送门了解到:CAN通讯难点———验收筛选器
通过配置筛选器,可以使FDCAN外设接收其他模块发来的信息,在筛选器中配置接收消息的FIFO或者拒收消息。配置筛选器是FDCAN接收的关键。
5. TX FIFO 的寄存器格式
具体说明见手册1969页,我就不复制了,翻一下就能懂每个BIT代表的意思。 根据上表,我们就可以写出对应各个标志位的结构体,方便后期指针使用。 由于其buffer支持 11-bit ID 和 29-bit ID ,因此将其写为共同体。
typedef union
{
uint32_t HallfWord[54];
struct
{
uint32_t T0_ID:29;
uint32_t T0_RTR:1;
uint32_t T0_XTD:1;
uint32_t T0_ESI:1;
uint32_t T1_Reserved1:16;
uint32_t T1_DLC:4;
uint32_t T1_BRS:1;
uint32_t T1_FDF:1;
uint32_t T1_Reserved2:1;
uint32_t T1_EFC:1;
uint32_t T1_MM:8;
uint32_t DataWord[16];
}bit_29ID;
struct
{
uint32_t T0_Reserved:18;
uint32_t T0_ID:11;
uint32_t T0_RTR:1;
uint32_t T0_XTD:1;
uint32_t T0_ESI:1;
uint32_t T1_Reserved1:16;
uint32_t T1_DLC:4;
uint32_t T1_BRS:1;
uint32_t T1_FDF:1;
uint32_t T1_Reserved2:1;
uint32_t T1_EFC:1;
uint32_t T1_MM:8;
uint32_t DataWord[16];
}bit_11ID;
}FDCAN_Buffer_Union;
6. FDCAN的初始化
IO口配置我就不介绍了,想必大家都会配置IO。
6.1 配置时钟
- 首先在RCC->APB1RSTR1寄存器中使能RCC_APB1ENR1_FDCANEN,这样FDCAN的寄存器就可以配置了。
- 然后选择FDCAN内核时钟的时钟源,在RCC->CCIPR寄存器的RCC_CCIPR_FDCANSEL中配置。
- 置位INIT标志位,开始配置寄存器(只有INIT标志位置位,才可置位CCE标志位)
- 置位CCE标志位,解锁受保护的寄存器
- 配置时钟分频
- 配置波特率
- 配置中断
- 配置筛选器
- 清除INIT标志位
初始化代码见 附录1.1 RCC配置 附录1.2 FDCAN寄存器配置
7. 发送操作
7.1 FDCAN帧模式
FDCAN帧模式由下表各个标志位配置:
根据上表所示,可以通过 CCCR寄存器 和 Tx Buffer 内部的标志位 配合,实现不同模式的CAN。 由于鄙人只想使用 经典CAN,因此将 CCCR寄存器 中的 FDOE 配置为 0,其他标志位我就不管了。
7.2 发送步骤
- 将要发送的数据写入 Tx Buffer 中(在这里可以配置发送长度、ID等信息)
- 置位发送请求
- 等待发送完成(可以不等待,配置发送完成中断使能后,在中断中置位发送完成标志来判断是否发送完毕)
示例代码见 附录2 发送函数 中断中判断是否发送完毕程序见 附录3 中断程序
8. 接收操作
首先配置筛选器来选择接收的消息和接收后存放的FIFO。
接收分为 覆盖模式 和 阻塞模式 。 覆盖模式 顾名思义,就是当FIFO满了以后,接收到新消息后覆盖老消息。 阻塞模式 就是当FIFO满了之后,忽略新消息,保留老消息。
当 Rx FIFO 0 接收到消息后,将会在 FDCAN_RXF0S 寄存器中 F0GI 标志位指示新消息存储索引,当 CPU 读取这些消息后,务必将最后 F0GI 的值写入到 FDCAN_RXF0A 的 F0AI 标志位,来确认来读取完毕,这会将 FDCAN_RXF0S寄存器中的索引标志位 F0GI 设置为 F0AI + 1 并更新 FIFO 0 的存储计数 (FDCAN_RXF0S 寄存器中 F0FL 标志位,范围0~3),如果不确认读取完毕,将会导致指示索引 F0GI 失效。
接收操作可以在中断中进行,通过配置 FDCAN_IE 寄存器中的相应 中断使能标志位 来开启中断。如:使能 FIFO0 接收新消息中断就置位 FDCAN_IE_RF0NE 标志位。
因此,接收操作的步骤为:
- 读取接收的数据
- 确认读取完毕
接收程序见 附录3 中断程序
9. 参考资料传送门
9.1 代码下载(0积分)
代码下载(0积分)
9.2 其他 CAN 知识了解
其他 CAN 知识了解
附录1 初始化
附录1.1 RCC配置
为方便其他模块配置,将其他模块的RCC列表了出来,方便当库文件使用
返回第6章
void RCC_Init(void)
{
RCC->AHB1RSTR |= 0
;
RCC->AHB1RSTR &= 0xffffffff
;
RCC->AHB1ENR |= 0
;
RCC->AHB2RSTR |= 0
|RCC_AHB2RSTR_ADC345RST
|RCC_AHB2RSTR_ADC12RST
|RCC_AHB2RSTR_GPIOFRST
|RCC_AHB2RSTR_GPIOERST
|RCC_AHB2RSTR_GPIODRST
|RCC_AHB2RSTR_GPIOCRST
|RCC_AHB2RSTR_GPIOBRST
|RCC_AHB2RSTR_GPIOARST
;
RCC->AHB2RSTR &= 0xffffffff
&~RCC_AHB2RSTR_ADC345RST
&~RCC_AHB2RSTR_ADC12RST
&~RCC_AHB2RSTR_GPIOFRST
&~RCC_AHB2RSTR_GPIOERST
&~RCC_AHB2RSTR_GPIODRST
&~RCC_AHB2RSTR_GPIOCRST
&~RCC_AHB2RSTR_GPIOBRST
&~RCC_AHB2RSTR_GPIOARST
;
RCC->AHB2ENR = 0
|RCC_AHB2ENR_ADC345EN
|RCC_AHB2ENR_ADC12EN
|RCC_AHB2ENR_GPIOFEN
|RCC_AHB2ENR_GPIOEEN
|RCC_AHB2ENR_GPIODEN
|RCC_AHB2ENR_GPIOCEN
|RCC_AHB2ENR_GPIOBEN
|RCC_AHB2ENR_GPIOAEN
;
RCC->AHB3RSTR |= 0
;
RCC->AHB3RSTR &= 0xffffffff
;
RCC->AHB3ENR |= 0
;
RCC->APB1RSTR1 |= 0
|RCC_APB1RSTR1_FDCANRST
|RCC_APB1RSTR1_TIM7RST
|RCC_APB1RSTR1_TIM6RST
|RCC_APB1RSTR1_TIM4RST
|RCC_APB1RSTR1_TIM3RST
|RCC_APB1RSTR1_TIM2RST
;
RCC->APB1RSTR1 &= 0xffffffff
&~RCC_APB1RSTR1_FDCANRST
&~RCC_APB1RSTR1_TIM7RST
&~RCC_APB1RSTR1_TIM6RST
&~RCC_APB1RSTR1_TIM4RST
&~RCC_APB1RSTR1_TIM3RST
&~RCC_APB1RSTR1_TIM2RST
;
RCC->APB1ENR1 |= 0
|RCC_APB1ENR1_FDCANEN
|RCC_APB1ENR1_TIM7EN
|RCC_APB1ENR1_TIM6EN
|RCC_APB1ENR1_TIM4EN
|RCC_APB1ENR1_TIM3EN
|RCC_APB1ENR1_TIM2EN
;
RCC->APB1RSTR2 |= 0
;
RCC->APB1RSTR2 &= 0xffffffff
;
RCC->APB1ENR2 |= 0
;
RCC->APB2RSTR |= 0
|RCC_APB2RSTR_TIM17RST
|RCC_APB2RSTR_TIM16RST
|RCC_APB2RSTR_TIM15RST
|RCC_APB2RSTR_USART1RST
|RCC_APB2RSTR_TIM8RST
|RCC_APB2RSTR_TIM1RST
|RCC_APB2RSTR_SYSCFGRST
;
RCC->APB2RSTR &= 0xffffffff
&~RCC_APB2RSTR_TIM17RST
&~RCC_APB2RSTR_TIM16RST
&~RCC_APB2RSTR_TIM15RST
&~RCC_APB2RSTR_USART1RST
&~RCC_APB2RSTR_TIM8RST
&~RCC_APB2RSTR_TIM1RST
&~RCC_APB2RSTR_SYSCFGRST
;
RCC->APB2ENR |= 0
|RCC_APB2ENR_TIM17EN
|RCC_APB2ENR_TIM16EN
|RCC_APB2ENR_TIM15EN
|RCC_APB2ENR_USART1EN
|RCC_APB2ENR_TIM8EN
|RCC_APB2ENR_TIM1EN
|RCC_APB2ENR_SYSCFGEN
;
RCC->CCIPR |= 0
|(0 << RCC_CCIPR_FDCANSEL_Pos)
;
}
返回第6章
附录1.2 FDCAN寄存器配置
返回第6章
void FDCAN1_Function_Init(void)
{
FDCAN1->CCCR |= 0
|FDCAN_CCCR_CCE
|FDCAN_CCCR_INIT
;
FDCAN1->CCCR |= 0
|FDCAN_CCCR_DAR
;
FDCAN_CONFIG->CKDIV = FDCAN_CKDIV_PDIV;
FDCAN1->DBTP = 0
|FDCAN_DBTP_TDC
|(0 << FDCAN_DBTP_DBRP_Pos)
|(22 << FDCAN_DBTP_DTSEG1_Pos)
|(7 << FDCAN_DBTP_DTSEG2_Pos)
|(7 << FDCAN_DBTP_DSJW_Pos)
;
FDCAN1->NBTP = 0
|(7 << FDCAN_NBTP_NSJW_Pos)
|(0 << FDCAN_NBTP_NBRP_Pos)
|(22 << FDCAN_NBTP_NTSEG1_Pos)
|(7 << FDCAN_NBTP_NTSEG2_Pos)
;
FDCAN1->IE |= 0
|FDCAN_IE_TCE
|FDCAN_IE_RF1NE
|FDCAN_IE_RF0NE
;
FDCAN1->ILE = 0x00000000
|FDCAN_ILE_EINT1
|FDCAN_ILE_EINT0
;
FDCAN1->RXGFC = 0
;
FDCAN1->XIDAM = 0x1FFFFFFF;
FDCAN1->TXBTIE = 0
|0x00000001
;
FDCAN1->TXBCIE = 0
;
FDCAN1_RAM->FILTER_11BIT[0] = 0x00000000
|(1 << FDCANx_RAM_FILTER11_S0_SFT_Pos)
|(1 << FDCANx_RAM_FILTER11_S0_SFEC_Pos)
|(0x209 << FDCANx_RAM_FILTER11_S0_SFID1_Pos)
|(0x609 << FDCANx_RAM_FILTER11_S0_SFID2_Pos)
;
FDCAN1->RXGFC = 0x00000000
|(0 << FDCAN_RXGFC_LSE_Pos)
|(1 << FDCAN_RXGFC_LSS_Pos)
|FDCAN_RXGFC_F0OM
|FDCAN_RXGFC_F1OM
|(1 << FDCAN_RXGFC_ANFS_Pos)
|(1 << FDCAN_RXGFC_ANFE_Pos)
;
FDCAN1->CCCR &= ~FDCAN_CCCR_INIT;
}
返回第6章
附录2 发送函数
返回第7章
void FDCAN1_TxBuffer1_SendData_11ID(uint16_t ID,uint8_t RTR,uint8_t DLC,Buffer_64Byte_Struct *buffer)
{
FDCAN1_TXBUFFER1->bit_11ID.T0_ESI = 0;
FDCAN1_TXBUFFER1->bit_11ID.T0_XTD = 0;
FDCAN1_TXBUFFER1->bit_11ID.T0_RTR = 0;
FDCAN1_TXBUFFER1->bit_11ID.T0_ID = ID;
FDCAN1_TXBUFFER1->bit_11ID.T1_MM = 0;
FDCAN1_TXBUFFER1->bit_11ID.T1_EFC = 0;
FDCAN1_TXBUFFER1->bit_11ID.T1_FDF = 0;
FDCAN1_TXBUFFER1->bit_11ID.T1_BRS = 0;
FDCAN1_TXBUFFER1->bit_11ID.T1_DLC = DLC;
FDCAN1_TXBUFFER1->bit_11ID.DataWord[0] = buffer->DataWord[0];
FDCAN1_TXBUFFER1->bit_11ID.DataWord[1] = buffer->DataWord[1];
FDCAN1->TXBAR |= 0x00000000
|BIT0
;
}
返回第7章
附录3 中断程序
返回第7章 返回第8章
void FDCAN1_IT0_IRQHandler(void)
{
if(FDCAN1->IR & FDCAN_IR_RF0N)
{
FDCAN1->IR |= FDCAN_IR_RF0N;
switch((FDCAN1->RXF0S & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos)
{
case 0:
CAN_Rxbuffer[0] = FDCAN1_RAM->Rx_FIFO_0[0];
CAN_Rxbuffer[1] = FDCAN1_RAM->Rx_FIFO_0[1];
CAN_Rxbuffer[2] = FDCAN1_RAM->Rx_FIFO_0[2];
CAN_Rxbuffer[3] = FDCAN1_RAM->Rx_FIFO_0[3];
CAN_Rxbuffer[4] = FDCAN1_RAM->Rx_FIFO_0[4];
CAN_Rxbuffer[5] = FDCAN1_RAM->Rx_FIFO_0[5];
CAN_Rxbuffer[6] = FDCAN1_RAM->Rx_FIFO_0[6];
CAN_Rxbuffer[7] = FDCAN1_RAM->Rx_FIFO_0[7];
CAN_Rxbuffer[8] = FDCAN1_RAM->Rx_FIFO_0[8];
CAN_Rxbuffer[9] = FDCAN1_RAM->Rx_FIFO_0[9];
CAN_Rxbuffer[10] = FDCAN1_RAM->Rx_FIFO_0[10];
CAN_Rxbuffer[11] = FDCAN1_RAM->Rx_FIFO_0[11];
CAN_Rxbuffer[12] = FDCAN1_RAM->Rx_FIFO_0[12];
CAN_Rxbuffer[13] = FDCAN1_RAM->Rx_FIFO_0[13];
CAN_Rxbuffer[14] = FDCAN1_RAM->Rx_FIFO_0[14];
CAN_Rxbuffer[15] = FDCAN1_RAM->Rx_FIFO_0[15];
CAN_Rxbuffer[16] = FDCAN1_RAM->Rx_FIFO_0[16];
CAN_Rxbuffer[17] = FDCAN1_RAM->Rx_FIFO_0[17];
FDCAN1->RXF0A = 0;
break;
case 1:
CAN_Rxbuffer[0] = FDCAN1_RAM->Rx_FIFO_0[18];
CAN_Rxbuffer[1] = FDCAN1_RAM->Rx_FIFO_0[19];
CAN_Rxbuffer[2] = FDCAN1_RAM->Rx_FIFO_0[20];
CAN_Rxbuffer[3] = FDCAN1_RAM->Rx_FIFO_0[21];
FDCAN1->RXF0A = 1;
break;
case 2:
CAN_Rxbuffer[0] = FDCAN1_RAM->Rx_FIFO_0[36];
CAN_Rxbuffer[1] = FDCAN1_RAM->Rx_FIFO_0[37];
CAN_Rxbuffer[2] = FDCAN1_RAM->Rx_FIFO_0[38];
CAN_Rxbuffer[3] = FDCAN1_RAM->Rx_FIFO_0[39];
FDCAN1->RXF0A = 2;
break;
default:break;
}
}
else if(FDCAN1->IR & FDCAN_IR_TC)
{
FDCAN1->IR |= FDCAN_IR_TC;
}
}
返回第7章 返回第8章
|