刚好拿到一块瑞萨开发板,捣鼓玩下CAN,顺便试下固件升级。
A4M2 工程创建
详细可以参考,我之前写的文章 Renesa 瑞萨 A4M2 移植文件系统FAT32
CAN0 配置信息,使能FIFO,接收标准帧 ID为0x50,数据帧。
代码
void can_Init(void)
{
fsp_err_t err = R_CAN_Open(&g_can0_ctrl, &g_can0_cfg);
}
/* CAN 中断回调函数 */
void can_callback(can_callback_args_t * p_args)
{
switch (p_args->event)
{
case CAN_EVENT_RX_COMPLETE: //接收完成中断
{
can_rx_complete_flag = true; //can接收到数据
/* 读取接收帧 */
memcpy(&can_rx_frame, &(p_args->frame), sizeof(can_frame_t));
break;
}
case CAN_EVENT_TX_COMPLETE: //传输完成中断
{
can_tx_complete_flag = true; //can数据发送完成
break;
}
case CAN_EVENT_ERR_BUS_OFF: /* Bus error event. (bus off) */
case CAN_EVENT_ERR_PASSIVE: /* Bus error event. (error passive) */
case CAN_EVENT_ERR_WARNING: /* Bus error event. (error warning) */
case CAN_EVENT_BUS_RECOVERY: /* Bus error event. (bus recovery) */
case CAN_EVENT_MAILBOX_MESSAGE_LOST: /* Overwrite/overrun error */
{
can_err_status_flag = true; //设置标志位
/* 获取错误状态 */
can_err_status = (can_error_t) p_args->error;
break;
}
default:
{
break;
}
}
}
发送数据
can_tx_frame.id = 0x48;//发送ID
can_tx_frame.type = CAN_FRAME_TYPE_DATA;
can_tx_frame.data_length_code = 8; //一次最多发送8个字节
memcpy((uint8_t*)&can_tx_frame.data[0], (uint8_t*)"########", 8);
err = R_CAN_Write(&g_can0_ctrl, CAN_MAILBOX_NUMBER_0, &can_tx_frame);
/* Error trap */
if (FSP_SUCCESS != err)
{
CAN_MSG_PRINTF("CAN Write API FAILED");
//while(1);
}
/* 等待传输完成 */
while ((true != can_tx_complete_flag) && (--time_out));
can_tx_complete_flag = false;
//typedef struct st_can_frame
//{
// uint32_t id; ///< CAN ID.
// can_id_mode_t id_mode; ///< Standard or Extended ID (IDE).
// can_frame_type_t type; ///< Frame type (RTR).
// uint8_t data_length_code; ///< CAN Data Length Code (DLC).
// uint32_t options; ///< Implementation-specific options.
// uint8_t data[CAN_DATA_BUFFER_LENGTH]; ///< CAN data.
//} can_frame_t;
//接收数据
while (false == can_rx_complete_flag);
can_rx_complete_flag = false;
printf("%s",can_rx_frame.data)
STM32 CAN工程
CAN外设初始化
#define TX_CAN_UPDATE 0x50
#define RX_CAN_UPDATE 0x48
u8 CAN1_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
//初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11| GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化PA11,PA12
//引脚复用映射配置
GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1); //GPIOA11复用为CAN1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1); //GPIOA12复用为CAN1
//CAN单元设置
CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= mode; //模式设置
CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq~CAN_SJW_4tq
CAN_InitStructure.CAN_BS1=tbs1; //Tbs1范围CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2=tbs2;//Tbs2范围CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1
CAN_Init(CAN1, &CAN_InitStructure); // 初始化CAN1
//配置过滤器
CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0
u32 ID1 = RX_CAN_UPDATE;
u32 ID2 = TX_CAN_UPDATE;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdList;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
ID1 %= (1 << 11);
ID2%= (1 << 11);
//标准帧的帧ID长度是11位,帧ID的范围是000-7FF。
//扩展帧的帧ID长度是29位,帧ID的范围是0000 0000-1FFF FFFF
CAN_FilterInitStructure.CAN_FilterIdHigh = ID1 << 5;
CAN_FilterInitStructure.CAN_FilterIdLow = CAN_ID_STD | CAN_RTR_DATA;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = ID2 << 5;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = CAN_ID_STD | CAN_RTR_DATA;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_FIFO0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//FIFO0消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return 0;
}
void CAN1_RX0_IRQHandler(void)
{
u8 i =0;
CanRxMsg msg;
CAN_Receive(CAN1, CAN_FIFO0, &msg);
if(msg.IDE == CAN_ID_STD && msg.StdId == RX_CAN_UPDATE)
{
for(i=0;i<msg.DLC;i++){
Rx_Buffer[i] = msg.Data[i];
}
}
以上配置只会收到数据帧的ID为0x50和0x48,其他都会被过滤掉。
关于这部分,可以看我之前写 STM32 CAN通信理解(是半双工还是全双工?)
开发版间CAN通信发送和接收都正常。
OTA也正常。
使用CAN通信,主要是想为固件升级教程,使用CAN做通信升级做些准备。后面有空持续更新这个系列教程 MCU固件升级系列1(STM32)