/* * Copyright (c) 2006-2022, Synwit Technology Co.,Ltd. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2022-02-16 lik first version */ #include "drv_can.h" #ifdef RT_USING_CAN #ifdef BSP_USING_CAN //#define DRV_DEBUG #define LOG_TAG "drv.can" #include #if !defined(BSP_USING_CAN0) && !defined(BSP_USING_CAN1) #error "Please define at least one BSP_USING_CANx" /* this driver can be disabled at menuconfig ? RT-Thread Components ? Device Drivers */ #endif #ifdef BSP_USING_CAN0 #ifndef CAN0_CFG #define CAN0_CFG \ { \ .name = "can0", \ .CANx = CAN0, \ } #endif /* CAN0_CFG */ #endif /* BSP_USING_CAN0 */ #ifdef BSP_USING_CAN1 #ifndef CAN1_CFG #define CAN1_CFG \ { \ .name = "can1", \ .CANx = CAN1, \ } #endif /* CAN1_CFG */ #endif /* BSP_USING_CAN1 */ #define PRESCL_Pos 0 #define BS1_Pos 16 #define BS2_Pos 20 #define SJW_Pos 24 #define PRESCL_Msk (0x3FF << PRESCL_Pos) #define BS1_Msk ((0x0F) << BS1_Pos) #define BS2_Msk ((0x07) << BS2_Pos) #define SJW_Msk (0x3 << SJW_Pos) struct swm_baud_rate_tab { rt_uint32_t baud_rate; rt_uint32_t config_data; }; #define BAUD_DATA(TYPE, NO) ((can_baud_rate_tab[NO].config_data & TYPE##_Msk) >> TYPE##_Pos) struct swm_can_cfg { const char *name; CAN_TypeDef *CANx; CAN_InitStructure CAN_initstruct; }; struct swm_can_device { struct swm_can_cfg *can_cfg; struct rt_can_device can_device; }; /* SystemCoreClock 152MHz(max) 150MHz不能生成CAN1MBaud */ static const struct swm_baud_rate_tab can_baud_rate_tab[] = { {CAN1MBaud, ((CAN_SJW_4tq << SJW_Pos) | (CAN_BS1_12tq << BS1_Pos) | (CAN_BS2_6tq << BS2_Pos) | (1 << PRESCL_Pos))}, {CAN500kBaud, ((CAN_SJW_4tq << SJW_Pos) | (CAN_BS1_12tq << BS1_Pos) | (CAN_BS2_6tq << BS2_Pos) | (3 << PRESCL_Pos))}, {CAN250kBaud, ((CAN_SJW_4tq << SJW_Pos) | (CAN_BS1_12tq << BS1_Pos) | (CAN_BS2_6tq << BS2_Pos) | (7 << PRESCL_Pos))}, {CAN125kBaud, ((CAN_SJW_4tq << SJW_Pos) | (CAN_BS1_12tq << BS1_Pos) | (CAN_BS2_6tq << BS2_Pos) | (15 << PRESCL_Pos))}, {CAN100kBaud, ((CAN_SJW_4tq << SJW_Pos) | (CAN_BS1_12tq << BS1_Pos) | (CAN_BS2_6tq << BS2_Pos) | (19 << PRESCL_Pos))}, {CAN50kBaud, ((CAN_SJW_4tq << SJW_Pos) | (CAN_BS1_12tq << BS1_Pos) | (CAN_BS2_6tq << BS2_Pos) | (39 << PRESCL_Pos))}, {CAN20kBaud, ((CAN_SJW_4tq << SJW_Pos) | (CAN_BS1_12tq << BS1_Pos) | (CAN_BS2_6tq << BS2_Pos) | (99 << PRESCL_Pos))}, {CAN10kBaud, ((CAN_SJW_4tq << SJW_Pos) | (CAN_BS1_12tq << BS1_Pos) | (CAN_BS2_6tq << BS2_Pos) | (199 << PRESCL_Pos))}}; enum { #ifdef BSP_USING_CAN0 CAN0_INDEX, #endif #ifdef BSP_USING_CAN1 CAN1_INDEX, #endif }; static struct swm_can_cfg swm_can_cfg[] = { #ifdef BSP_USING_CAN0 CAN0_CFG, #endif #ifdef BSP_USING_CAN1 CAN1_CFG, #endif }; static struct swm_can_device can_obj[sizeof(swm_can_cfg) / sizeof(swm_can_cfg[0])]; static rt_uint32_t get_can_baud_index(rt_uint32_t baud) { rt_uint32_t len, index; len = sizeof(can_baud_rate_tab) / sizeof(can_baud_rate_tab[0]); for (index = 0; index < len; index++) { if (can_baud_rate_tab[index].baud_rate == baud) return index; } return 0; /* default baud is CAN1MBaud */ } static rt_err_t swm_can_config(struct rt_can_device *can_device, struct can_configure *cfg) { struct swm_can_device *can_dev; rt_uint32_t baud_index; rt_uint32_t can_mode; RT_ASSERT(can_device); RT_ASSERT(cfg); can_dev = (struct swm_can_device *)can_device->parent.user_data; RT_ASSERT(can_dev); switch (cfg->mode) { case RT_CAN_MODE_NORMAL: can_mode = CAN_MODE_NORMAL; break; case RT_CAN_MODE_LISTEN: can_mode = CAN_MODE_LISTEN; break; case RT_CAN_MODE_LOOPBACK: can_mode = CAN_MODE_SELFTEST; break; case RT_CAN_MODE_LOOPBACKANLISTEN: can_mode = CAN_MODE_SELFTEST; break; } baud_index = get_can_baud_index(cfg->baud_rate); CAN_Close(can_dev->can_cfg->CANx); //一些关键寄存器只能在CAN关闭时设置 can_dev->can_cfg->CANx->CR &= ~(CAN_CR_LOM_Msk | CAN_CR_STM_Msk); can_dev->can_cfg->CANx->CR |= (can_mode << CAN_CR_LOM_Pos); can_dev->can_cfg->CANx->BT1 = (0 << CAN_BT1_SAM_Pos) | (BAUD_DATA(BS1, baud_index) << CAN_BT1_TSEG1_Pos) | (BAUD_DATA(BS2, baud_index) << CAN_BT1_TSEG2_Pos); can_dev->can_cfg->CANx->BT0 = (BAUD_DATA(SJW, baud_index) << CAN_BT0_SJW_Pos) | ((BAUD_DATA(PRESCL, baud_index) & 0x3F) << CAN_BT0_BRP_Pos); can_dev->can_cfg->CANx->BT2 = ((BAUD_DATA(PRESCL, baud_index) >> 6) << CAN_BT2_BRP_Pos); can_dev->can_cfg->CANx->RXERR = 0; //只能在复位模式下清除 can_dev->can_cfg->CANx->TXERR = 0; /* can start */ CAN_Open(can_dev->can_cfg->CANx); return RT_EOK; } static rt_err_t swm_can_control(struct rt_can_device *can_device, int cmd, void *arg) { rt_uint32_t argval; struct swm_can_device *can_dev; struct rt_can_filter_config *filter_cfg; RT_ASSERT(can_device != RT_NULL); can_dev = (struct swm_can_device *)can_device->parent.user_data; RT_ASSERT(can_dev != RT_NULL); switch (cmd) { case RT_DEVICE_CTRL_CLR_INT: argval = (rt_uint32_t)arg; if (argval == RT_DEVICE_FLAG_INT_RX) { can_dev->can_cfg->CANx->IE &= ~(CAN_IE_RXDA_Msk | CAN_IE_RXOV_Msk); } else if (argval == RT_DEVICE_FLAG_INT_TX) { can_dev->can_cfg->CANx->IE &= ~CAN_IE_TXBR_Msk; } else if (argval == RT_DEVICE_CAN_INT_ERR) { can_dev->can_cfg->CANx->IE &= ~(CAN_IE_ARBLOST_Msk | CAN_IE_BUSERR_Msk | CAN_IE_ERRWARN_Msk | CAN_IE_ERRPASS_Msk); } break; case RT_DEVICE_CTRL_SET_INT: argval = (rt_uint32_t)arg; if (argval == RT_DEVICE_FLAG_INT_RX) { can_dev->can_cfg->CANx->IE |= (CAN_IE_RXDA_Msk | CAN_IE_RXOV_Msk); } else if (argval == RT_DEVICE_FLAG_INT_TX) { can_dev->can_cfg->CANx->IE |= CAN_IE_TXBR_Msk; } else if (argval == RT_DEVICE_CAN_INT_ERR) { can_dev->can_cfg->CANx->IE |= (CAN_IE_ARBLOST_Msk | CAN_IE_BUSERR_Msk | CAN_IE_ERRWARN_Msk | CAN_IE_ERRPASS_Msk); } break; case RT_CAN_CMD_SET_FILTER: { rt_uint32_t filter_idx = 0; if (RT_NULL == arg) { /* default filter config */ } else { filter_cfg = (struct rt_can_filter_config *)arg; /* get default filter */ for (int i = 0; i < filter_cfg->count; i++) { if (filter_cfg->items[i].hdr_bank == -1) { filter_idx = i; } else { filter_idx = filter_cfg->items[i].hdr_bank; } if (filter_cfg->items[i].ide == RT_CAN_STDID) { can_dev->can_cfg->CANx->AFM &= ~(1 << filter_idx); can_dev->can_cfg->CANx->ACR[filter_idx] = __REV(filter_cfg->items[i].id << 5); can_dev->can_cfg->CANx->AMR[filter_idx] = __REV(~(filter_cfg->items[i].mask << 5)); can_dev->can_cfg->CANx->AFE |= (1 << filter_idx); } else if (filter_cfg->items[i].ide == RT_CAN_EXTID) { can_dev->can_cfg->CANx->AFM |= (1 << filter_idx); can_dev->can_cfg->CANx->ACR[filter_idx] = __REV(filter_cfg->items[i].id << 3); can_dev->can_cfg->CANx->AMR[filter_idx] = __REV(~(filter_cfg->items[i].mask << 3)); can_dev->can_cfg->CANx->AFE |= (1 << filter_idx); } } } break; } case RT_CAN_CMD_SET_MODE: argval = (rt_uint32_t)arg; if (argval != RT_CAN_MODE_NORMAL && argval != RT_CAN_MODE_LISTEN && argval != RT_CAN_MODE_LOOPBACK && argval != RT_CAN_MODE_LOOPBACKANLISTEN) { return -RT_ERROR; } if (argval != can_dev->can_device.config.mode) { can_dev->can_device.config.mode = argval; return swm_can_config(&can_dev->can_device, &can_dev->can_device.config); } break; case RT_CAN_CMD_SET_BAUD: argval = (rt_uint32_t)arg; if (argval != CAN1MBaud && argval != CAN800kBaud && argval != CAN500kBaud && argval != CAN250kBaud && argval != CAN125kBaud && argval != CAN100kBaud && argval != CAN50kBaud && argval != CAN20kBaud && argval != CAN10kBaud) { return -RT_ERROR; } if (argval != can_dev->can_device.config.baud_rate) { can_dev->can_device.config.baud_rate = argval; return swm_can_config(&can_dev->can_device, &can_dev->can_device.config); } break; case RT_CAN_CMD_SET_PRIV: argval = (rt_uint32_t)arg; if (argval != RT_CAN_MODE_PRIV && argval != RT_CAN_MODE_NOPRIV) { return -RT_ERROR; } if (argval != can_dev->can_device.config.privmode) { can_dev->can_device.config.privmode = argval; return swm_can_config(&can_dev->can_device, &can_dev->can_device.config); } break; case RT_CAN_CMD_GET_STATUS: { can_dev->can_device.status.rcverrcnt = can_dev->can_cfg->CANx->RXERR; can_dev->can_device.status.snderrcnt = can_dev->can_cfg->CANx->TXERR; can_dev->can_device.status.lasterrtype = (can_dev->can_cfg->CANx->ECC >> 6) & 0x03; can_dev->can_device.status.errcode = can_dev->can_cfg->CANx->ECC & 0x1F; rt_memcpy(arg, &can_dev->can_device.status, sizeof(can_dev->can_device.status)); } break; } return RT_EOK; } static int swm_can_sendmsg(struct rt_can_device *can_device, const void *buf, rt_uint32_t box_num) { uint32_t i; struct swm_can_device *can_dev; RT_ASSERT(can_device != RT_NULL); can_dev = (struct swm_can_device *)can_device->parent.user_data; struct rt_can_msg *pmsg = (struct rt_can_msg *) buf; if (RT_CAN_STDID == pmsg->ide) { can_dev->can_cfg->CANx->FRAME.DATA[0] = pmsg->id >> 3; can_dev->can_cfg->CANx->FRAME.DATA[1] = pmsg->id << 5; if (RT_CAN_DTR == pmsg->rtr) { can_dev->can_cfg->CANx->FRAME.INFO = (0 << CAN_INFO_FF_Pos) | (0 << CAN_INFO_RTR_Pos) | (pmsg->len << CAN_INFO_DLC_Pos); for(i = 0; i < pmsg->len; i++) { can_dev->can_cfg->CANx->FRAME.DATA[i+2] = pmsg->data[i]; } if(can_dev->can_cfg->CANx->CR & CAN_CR_STM_Msk) { can_dev->can_cfg->CANx->CMD = (1 << CAN_CMD_SRR_Pos); } else { can_dev->can_cfg->CANx->CMD = (1 << CAN_CMD_TXREQ_Pos); } } else { can_dev->can_cfg->CANx->FRAME.INFO = (0 << CAN_INFO_FF_Pos) | (1 << CAN_INFO_RTR_Pos) | (0 << CAN_INFO_DLC_Pos); can_dev->can_cfg->CANx->CMD = (1 << CAN_CMD_TXREQ_Pos); } } else { can_dev->can_cfg->CANx->FRAME.DATA[0] = pmsg->id >> 21; can_dev->can_cfg->CANx->FRAME.DATA[1] = pmsg->id >> 13; can_dev->can_cfg->CANx->FRAME.DATA[2] = pmsg->id >> 5; can_dev->can_cfg->CANx->FRAME.DATA[3] = pmsg->id << 3; if (RT_CAN_DTR == pmsg->rtr) { can_dev->can_cfg->CANx->FRAME.INFO = (1 << CAN_INFO_FF_Pos) | (0 << CAN_INFO_RTR_Pos) | (pmsg->len << CAN_INFO_DLC_Pos); for(i = 0; i < pmsg->len; i++) { can_dev->can_cfg->CANx->FRAME.DATA[i+4] = pmsg->data[i]; } if(can_dev->can_cfg->CANx->CR & CAN_CR_STM_Msk) { can_dev->can_cfg->CANx->CMD = (1 << CAN_CMD_SRR_Pos); } else { can_dev->can_cfg->CANx->CMD = (1 << CAN_CMD_TXREQ_Pos); } } else { can_dev->can_cfg->CANx->FRAME.INFO = (1 << CAN_INFO_FF_Pos) | (1 << CAN_INFO_RTR_Pos) | (0 << CAN_INFO_DLC_Pos); can_dev->can_cfg->CANx->CMD = (1 << CAN_CMD_TXREQ_Pos); } } return RT_EOK; } static int swm_can_recvmsg(struct rt_can_device *can_device, void *buf, rt_uint32_t fifo) { uint32_t i; struct swm_can_device *can_dev; RT_ASSERT(can_device != RT_NULL); can_dev = (struct swm_can_device *)can_device->parent.user_data; struct rt_can_msg *pmsg = (struct rt_can_msg *) buf; CAN_RXMessage CAN_RXMsg; /* get data */ CAN_Receive(can_dev->can_cfg->CANx, &CAN_RXMsg); /* get id */ if (CAN_RXMsg.format == CAN_FRAME_STD) { pmsg->ide = RT_CAN_STDID; } else { pmsg->ide = RT_CAN_EXTID; } pmsg->id = CAN_RXMsg.id; /* get type */ if (CAN_RXMsg.remote == 0) { pmsg->rtr = RT_CAN_DTR; } else { pmsg->rtr = RT_CAN_RTR; } /* get len */ pmsg->len = CAN_RXMsg.size; for(i = 0; i < pmsg->len; i++) { pmsg->data[i] = CAN_RXMsg.data[i]; } return RT_EOK; } static const struct rt_can_ops swm_can_ops = { .configure = swm_can_config, .control = swm_can_control, .sendmsg = swm_can_sendmsg, .recvmsg = swm_can_recvmsg, }; static void swm_can_isr(struct rt_can_device *can_device) { struct swm_can_device *can_dev; RT_ASSERT(can_device != RT_NULL); can_dev = (struct swm_can_device *)can_device->parent.user_data; uint32_t int_sr = CAN_INTStat(can_dev->can_cfg->CANx); if(int_sr & CAN_IF_RXDA_Msk) { rt_hw_can_isr(can_device, RT_CAN_EVENT_RX_IND); } else if (int_sr & CAN_IF_RXOV_Msk) { rt_hw_can_isr(can_device, RT_CAN_EVENT_RXOF_IND); } else if (int_sr & CAN_IF_TXBR_Msk) { rt_hw_can_isr(can_device, RT_CAN_EVENT_TX_DONE); } else if (int_sr & CAN_IE_ERRWARN_Msk) { } else if (int_sr & CAN_IE_ERRPASS_Msk) { } else if (int_sr & CAN_IE_ARBLOST_Msk) { } else if (int_sr & CAN_IE_BUSERR_Msk) { } } #ifdef BSP_USING_CAN0 /** * @brief This function handles CAN0 interrupts. */ void CAN0_Handler(void) { rt_interrupt_enter(); swm_can_isr(&(can_obj[CAN0_INDEX].can_device)); rt_interrupt_leave(); } #endif /* BSP_USING_CAN0 */ #ifdef BSP_USING_CAN1 /** * @brief This function handles CAN1 interrupts. */ void CAN1_Handler(void) { rt_interrupt_enter(); swm_can_isr(&(can_obj[CAN0_INDEX].can_device)); rt_interrupt_leave(); } #endif /* BSP_USING_CAN1 */ int swm_can_init(void) { int i = 0; int result = RT_EOK; struct can_configure config = CANDEFAULTCONFIG; config.privmode = RT_CAN_MODE_NOPRIV; config.ticks = 50; #ifdef RT_CAN_USING_HDR config.maxhdr = 16; #endif #ifdef BSP_USING_CAN0 PORT_Init(PORTB, PIN5, PORTB_PIN5_CAN0_RX, 1); PORT_Init(PORTB, PIN4, PORTB_PIN4_CAN0_TX, 0); SYS->CLKEN0 |= (0x01 << SYS_CLKEN0_CAN0_Pos); NVIC_EnableIRQ(CAN0_IRQn); #endif #ifdef BSP_USING_CAN1 PORT_Init(PORTB, PIN3, PORTB_PIN3_CAN1_RX, 1); PORT_Init(PORTB, PIN2, PORTB_PIN2_CAN1_TX, 0); SYS->CLKEN1 |= (0x01 << SYS_CLKEN1_CAN1_Pos); NVIC_EnableIRQ(CAN1_IRQn); #endif for (i = 0; i < sizeof(swm_can_cfg) / sizeof(swm_can_cfg[0]); i++) { can_obj[i].can_device.config = config; can_obj[i].can_cfg = &swm_can_cfg[i]; result = rt_hw_can_register(&can_obj[i].can_device, can_obj[i].can_cfg->name, &swm_can_ops, &can_obj[i]); if (result != RT_EOK) { LOG_E("%s register fail.", can_obj[i].can_cfg->name); } else { LOG_D("%s register success.", can_obj[i].can_cfg->name); } } return result; return 0; } INIT_BOARD_EXPORT(swm_can_init); #endif /* BSP_USING_CAN */ #endif /* RT_USING_CAN */