国民N32L43X系列MCU带有1路CAN接口,它支持CAN协议2.0A和2.0B。其在高效处理大量收到的报文的同时,极大地的降低了CPU资源的消耗。可以通过软件配置报文发送的优先级特性。对于一些安全要求较高的应用,bxCAN的硬件功能支持时间触发通信模式。其主要特点如下:
发送:
接收:
管理:
单CAN:
CAN收发测试
参考例程的示例代码对CAN进行参数配置,使其通讯波特率为500kbps、使能所有的接收地址不进行滤波,使用MCU的PB8和PB9这两个端口引脚,配置CAN接收中断,在中断中打印出当前接收到的CAN消息内容,然后使用CAN任务每间隔1秒钟向外发送一个消息,使用PC端的CAN_Test软件进行监控或操作。硬件连接如下所示:
实现代码
void bxCAN_Init(void)
{
CAN_InitType CAN_InitStructure;
CAN_FilterInitType CAN_FilterInitStructure;
GPIO_InitType GPIO_InitStructure;
NVIC_InitType NVIC_InitStructure;
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Input;
GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
GPIO_InitStructure.GPIO_Alternate = GPIO_AF5_CAN;
GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_PIN_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_CAN, ENABLE);
CAN_DeInit(CAN);
CAN_InitStruct(&CAN_InitStructure); /* 500kbps */
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_6tq;
CAN_InitStructure.TBS2 = CAN_TBS2_2tq;
CAN_InitStructure.BaudRatePrescaler = 6;
CAN_Init(CAN, &CAN_InitStructure);
CAN_FilterInitStructure.Filter_Num = 0;
CAN_FilterInitStructure.Filter_Mode = CAN_Filter_IdMaskMode;
CAN_FilterInitStructure.Filter_Scale = CAN_Filter_32bitScale;
CAN_FilterInitStructure.Filter_HighId = 0x0000;
CAN_FilterInitStructure.Filter_LowId = 0x0000;
CAN_FilterInitStructure.FilterMask_HighId = 0x0000;
CAN_FilterInitStructure.FilterMask_LowId = 0x0000;
CAN_FilterInitStructure.Filter_FIFOAssignment = CAN_FIFO0;
CAN_FilterInitStructure.Filter_Act = ENABLE;
CAN_InitFilter(&CAN_FilterInitStructure);
CAN_INTConfig(CAN, CAN_INT_FMP0, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = CAN_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
bxCAN_Baudrate(500000);
TASK_Append(TASK_ID_CAN, bxCAN_Handler, 1000);
}
void CAN_RX0_IRQHandler(void)
{
CanRxMessage CAN_RxMessage;
CAN_ReceiveMessage(CAN, CAN_FIFO0, &CAN_RxMessage);
if(CAN_RxMessage.IDE == CAN_Extended_Id)
{
printf("\r\nReceive ID[0x%02x], DLC[0x%02x]\r\n", CAN_RxMessage.ExtId, CAN_RxMessage.DLC);
}
else
{
printf("\r\nReceive ID[0x%02x], DLC[0x%02x]\r\n", CAN_RxMessage.StdId, CAN_RxMessage.DLC);
}
for(uint8_t i = 0; i < CAN_RxMessage.DLC; i++)
{
printf("0x%02x ", CAN_RxMessage.Data);
}
printf("\r\n");
}
void bxCAN_Handler(void)
{
static uint8_t StdId = 0;
static uint8_t Count = 0;
uint8_t TransmitMailbox = 0;
CanTxMessage CAN_TxMessage;
CAN_TxMessage.StdId = StdId++;
CAN_TxMessage.IDE = CAN_ID_STD;
CAN_TxMessage.RTR = CAN_RTRQ_DATA;
CAN_TxMessage.DLC = 0x08;
for(uint8_t i = 0; i < CAN_TxMessage.DLC; i++)
{
CAN_TxMessage.Data = Count++;
}
TransmitMailbox = CAN_TransmitMessage(CAN, &CAN_TxMessage);
while(CAN_TransmitSTS(CAN, TransmitMailbox) != CANTXSTSOK);
}
运行测试
我们在配置CAN参数时,官方的示例程序给出几个常用的通讯波特率和对应的配置参数值,如下图所示:
那我们需要使用其它通讯波特率时,该如何设置呢?是不是可以通过波特自动计算出这些配置参数呢?我们需要先了解一下这些配置参数和通讯波特率之间的关系,这样就可以通过编程的方式来自动获取配置参数了。
参考MCU用户手册中CAN位时间特性和CAN位时序寄存器的章节内容,如下所示:
我们可以根据最终需要的通讯波特率计算出正常的位时间,通过PCLK1时钟和波特率分频器可以计算出时间单元tq的时间长度,然后再通过TBS1、TBS2结合tq计算得到位时间,如下程序中通过穷举的方式列出了所有满足条件的配置值,同时根据不同的配置计算出了对应的采样点,具体程序如下所示:
void bxCAN_Baudrate(uint32_t Baudrate)
{
printf("\r\n%s : %d", __FUNCTION__, Baudrate);
const char *str_TBS1[16] =
{
"CAN_TBS1_1tq ", "CAN_TBS1_2tq ", "CAN_TBS1_3tq ", "CAN_TBS1_4tq ",
"CAN_TBS1_5tq ", "CAN_TBS1_6tq ", "CAN_TBS1_7tq ", "CAN_TBS1_8tq ",
"CAN_TBS1_9tq ", "CAN_TBS1_10tq", "CAN_TBS1_11tq", "CAN_TBS1_12tq",
"CAN_TBS1_13tq", "CAN_TBS1_14tq", "CAN_TBS1_15tq", "CAN_TBS1_16tq",
};
const char *str_TBS2[8] =
{
"CAN_TBS2_1tq", "CAN_TBS2_2tq", "CAN_TBS2_3tq", "CAN_TBS2_4tq",
"CAN_TBS2_5tq", "CAN_TBS2_6tq", "CAN_TBS2_7tq", "CAN_TBS2_8tq",
};
uint16_t BRTP = 0, TBS1 = 0, TBS2 = 0;
RCC_ClocksType RCC_Clocks;
RCC_GetClocksFreqValue(&RCC_Clocks);
for(BRTP = 0; BRTP < 1024; BRTP++)
{
for(TBS1 = 0; TBS1 < 16; TBS1++)
{
for(TBS2 = 0; TBS2 < 8; TBS2++)
{
if(Baudrate == (RCC_Clocks.Pclk1Freq / (BRTP + 1) / (TBS1 + TBS2 + 3)))
{
float SamplePoint = (float)(TBS1 + 2) / (float)(TBS1 + TBS2 + 3) * 100;
printf("\r\n-----------------------------------------------------------------------------------");
printf("\r\nBRTP : %d\tTBS1 : %s\tTBS2 : %s\tSamplePoint : %0.1f%%", BRTP + 1, str_TBS1[TBS1], str_TBS2[TBS2], SamplePoint);
}
}
}
}
printf("\r\n-----------------------------------------------------------------------------------");
}
运行结果
附件
软件工程源代码:
6666