本帖最后由 xld0932 于 2024-2-19 11:32 编辑
1.概述
当前CAN通讯应用越来越广泛、汽车电子、工业控制,到近些年的电动车,都开始使用CAN组网通讯了,这都得益于CAN的高效、稳定和规范。国民技术N32A455系列MCU带有2组CAN通讯接口,最高波特率可达到1Mbit/s,支持CAN协议2.0A和2.0B,支持时间触发通讯功能;每个CAN都有两个3级深度的接收FIFO、共有14个过滤器组、3个发送邮箱,通过接收FIFO和过滤机制,降低了CAN消息的实时响应要求,减少MCU不必要的消耗,同时也提升了MCU的利用率。
2.熟悉CAN模块
2.1.工作模式
初始化、正常和睡眠模式是CAN的三种主要工作模式。软件只有在CAN处于初始化模式时才能进行初始化配置、在初始化完成后,软件配置CAN进入正常模式、通过配置寄存器相应位可以使CAN进入睡眠模式,在进入睡眠模式后,可以通过硬件自动唤醒(检测到CAN总线的活动),也可以通过软件唤醒(操作寄存器位),需要注意的是在进入正常模式之前,CAN必须与CAN总线同步。另外还有测试模式(回环模式、静默模式、回环静默模式)、调试模式。
2.2.发送邮箱
应用程序可以通过三个发送邮箱发送消息,发送三个邮箱消息的顺序是由发送调度器根据消息的优先级决定的,优先级可以由消息的标识符决定,也可以由发送请求的顺序决定。
2.3.接收过滤器
每个 CAN 有 14 个可配置的标识符过滤器组。应用配置标识符过滤器组后,接收邮箱会自动接收需要的邮件,丢弃其他邮件。
2.4.接收FIFO
每个 CAN 有两个接收 FIFO,每个 FIFO 可以存储三个完整的报文。无需应用程序管理,由硬件管理。
3.CAN通讯示例
3.1.GPIO初始化配置
/* PD8_CAN1_RX */
#define CAN1_RX_RCC RCC_APB2_PERIPH_GPIOD
#define CAN1_RX_GPIO GPIOD
#define CAN1_RX_PIN GPIO_PIN_8
/* PD9_CAN1_TX */
#define CAN1_TX_RCC RCC_APB2_PERIPH_GPIOD
#define CAN1_TX_GPIO GPIOD
#define CAN1_TX_PIN GPIO_PIN_9
/* PE0_CAN1_STB */
#define CAN1_STB_RCC RCC_APB2_PERIPH_GPIOE
#define CAN1_STB_GPIO GPIOE
#define CAN1_STB_PIN GPIO_PIN_0
void CAN_Configuration(void)
{
GPIO_InitType GPIO_InitStructure;
RCC_EnableAPB2PeriphClk(CAN1_RX_RCC | RCC_APB2_PERIPH_AFIO, ENABLE);
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = CAN1_RX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitPeripheral(CAN1_RX_GPIO, &GPIO_InitStructure);
RCC_EnableAPB2PeriphClk(CAN1_TX_RCC | RCC_APB2_PERIPH_AFIO, ENABLE);
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = CAN1_TX_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitPeripheral(CAN1_TX_GPIO, &GPIO_InitStructure);
RCC_EnableAPB2PeriphClk(CAN1_STB_RCC, ENABLE);
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = CAN1_STB_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitPeripheral(CAN1_STB_GPIO, &GPIO_InitStructure);
GPIO_WriteBit(CAN1_STB_GPIO, CAN1_STB_PIN, Bit_RESET);
GPIO_ConfigPinRemap(GPIO_RMP1_CAN1, ENABLE);
......
}
3.2.CAN通讯参数配置
void CAN_Configuration(void)
{
......
CAN_InitType CAN_InitStructure;
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_CAN1, ENABLE);
CAN_DeInit(CAN1);
CAN_InitStruct(&CAN_InitStructure);
CAN_InitStructure.TTCM = DISABLE;
CAN_InitStructure.ABOM = DISABLE;
CAN_InitStructure.AWKUM = DISABLE;
CAN_InitStructure.NART = DISABLE;
CAN_InitStructure.RFLM = DISABLE;
CAN_InitStructure.TXFP = ENABLE;
CAN_InitStructure.OperatingMode = CAN_Normal_Mode;
CAN_InitStructure.RSJW = CAN_RSJW_1tq;
CAN_InitStructure.TBS1 = CAN_TBS1_14tq;
CAN_InitStructure.TBS2 = CAN_TBS2_3tq;
CAN_InitStructure.BaudRatePrescaler = 4;
CAN_Init(CAN1, &CAN_InitStructure);
......
}
3.3.CAN过滤器配置
#define CAN_FILTER_STDID(STDID) ((STDID & 0x7FF) << 5)
#define CAN_FILTER_EXTID_H(EXTID) ((uint16_t)(((EXTID <<3) | 0x04) >> 16))
#define CAN_FILTER_EXTID_L(EXTID) ((uint16_t)(( EXTID <<3) | 0x04))
#define CAN_STD_ID_H_MASK_CARE (0xFFE0)
#define CAN_STD_ID_L_MASK_CARE (0x0000)
#define CAN_STD_ID_H_MASK_DONT_CARE (0x0000)
#define CAN_STD_ID_L_MASK_DONT_CARE (0x0000)
#define CAN_EXT_ID_H_MASK_CARE (0xFFFF)
#define CAN_EXT_ID_L_MASK_CARE (0xFFF8)
#define CAN_EXT_ID_H_MASK_DONT_CARE (0x0000)
#define CAN_EXT_ID_L_MASK_DONT_CARE (0x0000)
void CAN_Configuration(void)
{
......
CAN_FilterInitType CAN_FilterInitStructure;
CAN_FilterInitStructure.Filter_Num = 0;
CAN_FilterInitStructure.Filter_Mode = CAN_Filter_IdMaskMode;
CAN_FilterInitStructure.Filter_Scale = CAN_Filter_32bitScale;
CAN_FilterInitStructure.Filter_HighId = CAN_FILTER_STDID(0x400);
CAN_FilterInitStructure.Filter_LowId = CAN_FILTER_STDID(0x400);
CAN_FilterInitStructure.FilterMask_HighId = CAN_STD_ID_H_MASK_DONT_CARE;
CAN_FilterInitStructure.FilterMask_LowId = CAN_STD_ID_H_MASK_DONT_CARE;
CAN_FilterInitStructure.Filter_FIFOAssignment = CAN_FIFO0;
CAN_FilterInitStructure.Filter_Act = ENABLE;
CAN1_InitFilter(&CAN_FilterInitStructure);
CAN_INTConfig(CAN1, CAN_INT_FMP0, ENABLE);
......
}
3.4.CAN中断配置及处理
CanRxMessage CAN_RxMessage[10];
uint8_t CAN_RxMessage_WriteCursor = 0;
uint8_t CAN_RxMessage_ReadCursor = 0;
void CAN_Configuration(void)
{
......
NVIC_InitType NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void CAN1_RX0_IRQHandler(void)
{
CAN_ReceiveMessage(CAN1, CAN_FIFO0, &CAN_RxMessage[CAN_RxMessage_WriteCursor++]);
if (CAN_RxMessage_WriteCursor > 10)
{
CAN_RxMessage_WriteCursor = 0;
}
}
3.5.接收发送处理
void CAN_RxMessageHandler(void)
{
uint8_t i = 0;
if (CAN_RxMessage_ReadCursor != CAN_RxMessage_WriteCursor)
{
printf("\r\n");
if (CAN_RxMessage[CAN_RxMessage_ReadCursor].IDE == CAN_Extended_Id)
{
printf("\r\nReceive Frame[0x%X, %d]:", CAN_RxMessage[CAN_RxMessage_ReadCursor].ExtId, CAN_RxMessage[CAN_RxMessage_ReadCursor].DLC);
}
else
{
printf("\r\nReceive Frame[0x%X, %d]:", CAN_RxMessage[CAN_RxMessage_ReadCursor].StdId, CAN_RxMessage[CAN_RxMessage_ReadCursor].DLC);
}
for (i = 0; i < CAN_RxMessage[CAN_RxMessage_ReadCursor].DLC; i++)
{
printf(" 0x%02X", CAN_RxMessage[CAN_RxMessage_ReadCursor].Data[i]);
}
CAN_RxMessage_ReadCursor++;
if (CAN_RxMessage_ReadCursor > 10)
{
CAN_RxMessage_ReadCursor = 0;
}
printf("\r\n");
}
}
void CAN_TxStdMessage(void)
{
CanTxMessage CAN_TxMessage;
uint8_t TxMailbox = 0;
uint8_t i = 0;
CAN_TxMessage.StdId = 0x0400;
CAN_TxMessage.IDE = CAN_ID_STD;
CAN_TxMessage.RTR = CAN_RTRQ_DATA;
CAN_TxMessage.DLC = 8;
for (i = 0; i < CAN_TxMessage.DLC; i++)
{
CAN_TxMessage.Data[i] = i;
}
TxMailbox = CAN_TransmitMessage(CAN1, &CAN_TxMessage);
if (TxMailbox != CAN_TxSTS_NoMailBox)
{
while (CAN_TransmitSTS(CAN1, TxMailbox) != CANTXSTSOK)
{
__ASM("nop");
}
}
else
{
printf("\r\n%s CAN_TxSTS_NoMailBox!!!", __FUNCTION__);
}
}
void CAN_TxExtMessage(void)
{
CanTxMessage CAN_TxMessage;
uint8_t TxMailbox = 0;
uint8_t i = 0;
CAN_TxMessage.ExtId = 0x12345678;
CAN_TxMessage.IDE = CAN_ID_EXT;
CAN_TxMessage.RTR = CAN_RTRQ_DATA;
CAN_TxMessage.DLC = 8;
for (i = 0; i < CAN_TxMessage.DLC; i++)
{
CAN_TxMessage.Data[i] = i;
}
TxMailbox = CAN_TransmitMessage(CAN1, &CAN_TxMessage);
if (TxMailbox != CAN_TxSTS_NoMailBox)
{
while (CAN_TransmitSTS(CAN1, TxMailbox) != CANTXSTSOK)
{
__ASM("nop");
}
}
else
{
printf("\r\n%s CAN_TxSTS_NoMailBox!!!", __FUNCTION__);
}
}
4.测试结果
4.1.PC端使用PCAN工具收发数据
4.2.MCU在接收到CAN数据后,通过串口打印输出
4.3.硬件连接
需要注意的是我们使用的是CAN1,需要将J10这两个跳帽给短接上!!!
5.附件程序