/* * Copyright (c) 2006-2024, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2024-08-22 QT-one first version */ #include #include "drv_can.h" #include "ht32_can_config.h" #ifdef BSP_USING_CAN #if !defined(BSP_USING_CAN) #error "Please define at least one BSP_USING_CAN" #endif #define CAN_UMASK_MODE 0 #define CAN_MASK_MODE 1 struct ht32_can_msg_type { CAN_MSG_TypeDef cfg_msg; uint32_t data_len; uint8_t data[8]; }; /* Baud rate mapping structure */ struct ht32_baud_rate { enum CANBAUD rt_baud_rate; uint32_t us_baus_rate; }; /* CAN Filter Table Configuration Structure */ struct ht32_can_filter_config { /* Each bit represents a message;1: the message is occupied;0: the message is not occupied */ uint32_t filter_flag; /* Filter table configuration information */ CAN_MSG_TypeDef filter_mag[MSG_OBJ_TOTAL_NUM]; }; /* CAN Object Structures */ struct ht32_can { char *name; /* Equipment name */ HT_CAN_TypeDef *can_x; /* peripheral base address */ struct can_configure cfg; /* CAN Configuration Structure */ struct rt_can_device device; /* Inherited device options */ struct ht32_can_filter_config filter_cfg; /* Filter Table Configuration */ }; /* CAN Baud Rate Mapping Table */ static const struct ht32_baud_rate can_baud_rate_tab[] = { {CAN1MBaud, 1000000}, {CAN800kBaud, 800000}, {CAN500kBaud, 500000}, {CAN250kBaud, 250000}, {CAN125kBaud, 125000}, {CAN100kBaud, 100000}, {CAN50kBaud, 50000}, {CAN20kBaud, 20000}, {CAN10kBaud, 10000}, }; /* CAN Object Information */ static struct ht32_can ht32_can_config = { .name = BSP_USING_CAN_NAME, .can_x = HT_CAN0, .cfg = {0}, .device = RT_NULL, .filter_cfg = {0}, }; /** * @brief Default Filter Table Configuration * @param can_instance:CAN object * @retval */ static rt_uint32_t cfg_can_default_filter(struct ht32_can *can_instance) { uint8_t filter_num = BSP_USING_CAN_MSG_NUM; can_instance->filter_cfg.filter_flag |= 1 << filter_num; can_instance->filter_cfg.filter_mag[filter_num].MsgNum = filter_num + 1; can_instance->filter_cfg.filter_mag[filter_num].IdType = (CAN_IdType_Enum)BSP_USING_CAN_ID_MODE; can_instance->filter_cfg.filter_mag[filter_num].IdMask = BSP_USING_CAN_MASK; can_instance->filter_cfg.filter_mag[filter_num].FrameType = (CAN_FrameType_Enum)BSP_USING_CAN_FRAME_MODE; can_instance->filter_cfg.filter_mag[filter_num].Id = BSP_USING_CAN_ID; CAN_SetRxMsg(can_instance->can_x, &can_instance->filter_cfg.filter_mag[filter_num], 1); return RT_EOK; } /** * @brief Get baud rate mapping parameters for CAN * @info This function is mainly used to convert the baud rate of RTT format to HT32 format baud rate * @param baud:CAN baud rate in RTT format * @retval Returns the CAN baud rate in HT32 format. */ 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].rt_baud_rate == baud) return can_baud_rate_tab[index].us_baus_rate; } return 0; } /** * @brief Configuring CAN Structures * @info This function depends on the ht32_can_config.h file * @param can_ck:System clock for CAN * @param can_buad:CAN baud rate to be configured * @param mode:Modes of CAN * @param nart:enable or disable the no automatic retransmission * @param CAN_InitStruct:Structures to be configured * @retval 1:success;0:error */ static rt_uint32_t config_can_struct(uint32_t can_ck, uint32_t can_buad, uint8_t mode, ControlStatus nart, CAN_InitTypeDef* CAN_InitStruct) { uint8_t cf0_nbt = 0; uint32_t nominal_bit_time = 0; for (cf0_nbt = 25; cf0_nbt > 8; cf0_nbt--) { if ((can_ck / can_buad / cf0_nbt) > 0) { if (((can_ck / (can_ck / can_buad / cf0_nbt)) / cf0_nbt) <= can_buad) { nominal_bit_time = cf0_nbt; break; } } } if (cf0_nbt < 8) { return 0; } CAN_InitStruct->CAN_BRPrescaler = (can_ck / (can_buad * nominal_bit_time)); CAN_InitStruct->CAN_SJW = HTCFG_CAN_CF0_BIT_TIME_SJW; CAN_InitStruct->CAN_TSEG1 = (nominal_bit_time - (nominal_bit_time * HTCFG_CAN_CF0_SAMPLE_POINT) / 100); CAN_InitStruct->CAN_TSEG0 = (nominal_bit_time - 1 - CAN_InitStruct->CAN_TSEG1); CAN_InitStruct->CAN_NART = nart; CAN_InitStruct->CAN_Mode = mode; return 1; } /** * @brief CAN Configuration Functions * @param * @retval */ static rt_err_t ht32_can_configure(struct rt_can_device *can, struct can_configure *cfg) { CKCU_PeripClockConfig_TypeDef CKCUClock = {{ 0 }}; struct ht32_can *can_instance = RT_NULL; rt_uint32_t can_baud = 0; rt_uint8_t can_mode = 0; CAN_InitTypeDef CAN_InitStruct = {0}; RT_ASSERT(can); RT_ASSERT(cfg); can_instance = (struct ht32_can *)can->parent.user_data; RT_ASSERT(can_instance != RT_NULL); CKCUClock.Bit.AFIO = 1; CKCUClock.Bit.CAN0 = 1; CKCU_PeripClockConfig(CKCUClock, ENABLE); ht32_can_gpio_init(can_instance->can_x); /* Get baud rate */ can_baud = get_can_baud_index(cfg->baud_rate); if (can_baud == 0) { return -RT_ERROR; } can_instance->cfg.baud_rate = cfg->baud_rate; can_instance->cfg.mode = cfg->mode; /* Configuring the operating mode of CAN */ switch (cfg->mode) { case RT_CAN_MODE_NORMAL: can_mode = CAN_MODE_NORMAL; break; case RT_CAN_MODE_LISTEN: can_mode = CAN_MODE_SILENT; break; case RT_CAN_MODE_LOOPBACK: can_mode = CAN_MODE_LBACK; break; case RT_CAN_MODE_LOOPBACKANLISTEN: can_mode = CAN_MODE_SILENT | CAN_MODE_LBACK; break; default: return -RT_ERROR; } if (0 == (config_can_struct(_HTCFG_CF0_CK_CAN, can_baud, can_mode, DISABLE, &CAN_InitStruct))) { return -RT_ERROR; } /* Reset CAN */ CAN_DeInit(can_instance->can_x); /* Initialising CAN */ CAN_Init(can_instance->can_x, &CAN_InitStruct); /* Configuring the Default Filter for CAN */ cfg_can_default_filter(can_instance); return RT_EOK; } /** * @brief CAN Control Functions * @param * @retval */ rt_err_t ht32_can_control(struct rt_can_device *can, int cmd, void *arg) { rt_uint32_t argval; struct ht32_can *can_instance; struct rt_can_filter_config *filter_cfg; RT_ASSERT(can != RT_NULL); can_instance = (struct ht32_can *)can->parent.user_data; RT_ASSERT(can_instance != RT_NULL); switch (cmd) { case RT_DEVICE_CTRL_CLR_INT:/* Clear Interrupt */ { argval = (rt_uint32_t) arg; if (argval == RT_DEVICE_FLAG_INT_RX) /* receive interruptions */ { if (CAN_GetFlagStatus(can_instance->can_x, CAN_FLAG_RXOK)) { /* Clear RXOK Flag */ CAN_ClearFlag(can_instance->can_x, CAN_FLAG_RXOK); } } else if (argval == RT_DEVICE_FLAG_INT_TX) /* Send Interrupt */ { if (CAN_GetFlagStatus(can_instance->can_x, CAN_FLAG_TXOK)) { /* Clear TXOK flag*/ CAN_ClearFlag(can_instance->can_x, CAN_FLAG_TXOK); } } else if (argval == RT_DEVICE_CAN_INT_ERR) /* false interruption */ { /* Error Process*/ CAN_LastErrorCode_TypeDef lec = CAN_GetLastErrorCode(can_instance->can_x); if (lec != NO_ERROR) { LOG_W("LEC: %d\r\n", lec); } if (CAN_GetFlagStatus(can_instance->can_x, CAN_FLAG_BOFF)) { /* Recover from Bus off state.*/ CAN_BusOffRecovery(can_instance->can_x); } } break; } case RT_DEVICE_CTRL_SET_INT:/* Setting Up Interruptions */ { argval = (rt_uint32_t) arg; if (argval == RT_DEVICE_FLAG_INT_RX) /* interrupt receive mode */ { LOG_W("Configuring Receive Interrupts!\r\n"); CAN_IntConfig(can_instance->can_x, CAN_INT_EIE | CAN_INT_SIE | CAN_INT_IE, ENABLE); NVIC_EnableIRQ(CAN0_IRQn); } else if (argval == RT_DEVICE_FLAG_INT_TX) /* interrupt transmission mode */ { LOG_W("Configuring Transmit Interrupts!\r\n"); } else if (argval == RT_DEVICE_CAN_INT_ERR) /* false interruption */ { LOG_W("Configuration error interrupt!\r\n"); } break; } case RT_CAN_CMD_SET_FILTER:/* Configuring the Hardware Filter Table */ { int i = 0; uint8_t filter_num = 0; uint32_t idmask = 0; if (RT_NULL == arg) { /* default filter config */ cfg_can_default_filter(can_instance); } else { filter_cfg = (struct rt_can_filter_config *)arg; if (filter_cfg->count > MSG_OBJ_TOTAL_NUM) { LOG_W("Filter list length exceeds the limit(max 32)!"); return -RT_ERROR; } for (i = 0; i < filter_cfg->count; i++) { /* Specify the filter table number or no */ if (filter_cfg->items[i].hdr_bank == -1) { filter_num = i; } else { if (filter_cfg->items[i].hdr_bank > MSG_OBJ_TOTAL_NUM) { LOG_W("Filter List Number Out of Limits(1-32)!"); return -RT_ERROR; } else { filter_num = filter_cfg->items[i].hdr_bank; } } if (can_instance->filter_cfg.filter_flag & (1 << filter_num)) { LOG_W("This filter channel will be changed(num:%d)!", filter_num); rt_kprintf("This filter channel will be changed(num:%d)!", filter_num); } can_instance->filter_cfg.filter_flag |= 1 << filter_num; can_instance->filter_cfg.filter_mag[filter_num].MsgNum = filter_num + 1; /* Standard or Extended Frames */ if (filter_cfg->items[i].ide == RT_CAN_STDID) { can_instance->filter_cfg.filter_mag[filter_num].IdType = CAN_STD_ID; idmask = 0x7FF; } else if (filter_cfg->items[i].ide == RT_CAN_EXTID) { can_instance->filter_cfg.filter_mag[filter_num].IdType = CAN_EXT_ID; idmask = 0x1FFFFFFF; } else { LOG_W("Frame pattern error(CAN_STD_ID/CAN_EXT_ID)!"); return -RT_ERROR; } /* Whether to use MASK mode */ if (filter_cfg->items[i].mode == CAN_UMASK_MODE) { can_instance->filter_cfg.filter_mag[filter_num].IdMask = idmask; } else if (filter_cfg->items[i].mode == CAN_MASK_MODE) { can_instance->filter_cfg.filter_mag[filter_num].IdMask = filter_cfg->items[i].mask; } else { LOG_W("MASK mode error(CAN_UMASK_MODE/CAN_MASK_MODE)!"); return -RT_ERROR; } /* Remote frames or data frames */ if (filter_cfg->items[i].rtr == RT_CAN_RTR) { can_instance->filter_cfg.filter_mag[filter_num].FrameType = CAN_REMOTE_FRAME; } else if (filter_cfg->items[i].rtr == RT_CAN_DTR) { can_instance->filter_cfg.filter_mag[filter_num].FrameType = CAN_DATA_FRAME; } /* Setting ID */ can_instance->filter_cfg.filter_mag[filter_num].Id = filter_cfg->items[i].id; /* Setting up the CAN filter table */ CAN_SetRxMsg(can_instance->can_x, &can_instance->filter_cfg.filter_mag[filter_num], 1); } } break; } case RT_CAN_CMD_SET_BAUD:/* Setting the baud rate */ { 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_instance->cfg.baud_rate) { can_instance->cfg.baud_rate = argval; return ht32_can_configure(&can_instance->device, &can_instance->cfg); } break; } case RT_CAN_CMD_SET_MODE:/* Setting the CAN Operating 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_instance->cfg.mode) { can_instance->cfg.mode = argval; return ht32_can_configure(&can_instance->device, &can_instance->cfg); } break; } case RT_CAN_CMD_GET_STATUS:/* Get CAN device status */ { rt_uint32_t errtype; errtype = can_instance->can_x->ECR; can_instance->device.status.rcverrcnt = ((errtype >> 8) & 0x7f); can_instance->device.status.snderrcnt = (errtype & 0xff); errtype = can_instance->can_x->SR; can_instance->device.status.lasterrtype = (errtype & 0x07); can_instance->device.status.errcode = ((errtype >> 5) & 0x07); rt_memcpy(arg, &can_instance->device.status, sizeof(can_instance->device.status)); break; } default: return -RT_ERROR; } return RT_EOK; } /** * @brief CAN sends data * @param * @retval */ rt_ssize_t ht32_can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t boxno) { struct ht32_can *can_instance = RT_NULL; struct rt_can_msg *pmsg = (struct rt_can_msg *) buf; struct ht32_can_msg_type tx_msg = {0}; RT_ASSERT(can != RT_NULL); can_instance = (struct ht32_can *)can->parent.user_data; RT_ASSERT(can_instance != RT_NULL); /* Standard and Extended Frames */ if (CAN_STD_ID == pmsg->ide) { tx_msg.cfg_msg.IdType = CAN_STD_ID; tx_msg.cfg_msg.Id = pmsg->id; } else if (CAN_EXT_ID == pmsg->ide) { tx_msg.cfg_msg.IdType = CAN_EXT_ID; tx_msg.cfg_msg.Id = pmsg->id; } else { LOG_W("Frame pattern error(CAN_STD_ID/CAN_EXT_ID)!"); return -RT_ERROR; } /* Teleframes and data frames */ if (RT_CAN_RTR == pmsg->rtr) { tx_msg.cfg_msg.FrameType = CAN_REMOTE_FRAME; } else if (RT_CAN_DTR == pmsg->rtr) { tx_msg.cfg_msg.FrameType = CAN_DATA_FRAME; } else { LOG_W("Remote frame setting error(CAN_REMOTE_FRAME/CAN_DATA_FRAME)!"); return -RT_ERROR; } /* Length of sent data */ tx_msg.data_len = pmsg->len & 0x0FU; /* data being sent */ tx_msg.data[0] = pmsg->data[0]; tx_msg.data[1] = pmsg->data[1]; tx_msg.data[2] = pmsg->data[2]; tx_msg.data[3] = pmsg->data[3]; tx_msg.data[4] = pmsg->data[4]; tx_msg.data[5] = pmsg->data[5]; tx_msg.data[6] = pmsg->data[6]; tx_msg.data[7] = pmsg->data[7]; /* Waiting tx Msg idle */ while (CAN_TransmitStatus(can_instance->can_x, &tx_msg.cfg_msg) == 0); /* Loopback data */ CAN_Transmit(can_instance->can_x, &tx_msg.cfg_msg, tx_msg.data, tx_msg.data_len); return RT_EOK; } /** * @brief CAN receive data * @param * @retval */ rt_ssize_t ht32_can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t boxno) { uint8_t i = 0; uint32_t msgnum = 0; CAN_RxStatus_TypeDef rx_status; struct ht32_can_msg_type rx_msg = {0}; struct ht32_can *can_instance = RT_NULL; struct rt_can_msg *pmsg = (struct rt_can_msg *) buf; RT_ASSERT(can != RT_NULL); RT_ASSERT(pmsg != RT_NULL); can_instance = (struct ht32_can *)can->parent.user_data; RT_ASSERT(can_instance != RT_NULL); msgnum = can_instance->filter_cfg.filter_flag; for (i = 0; i < MSG_OBJ_TOTAL_NUM; i++) { if ((msgnum & 1) == 1) { rx_status = CAN_Receive(can_instance->can_x, &can_instance->filter_cfg.filter_mag[i], rx_msg.data, &rx_msg.data_len); if (rx_status == MSG_OVER_RUN) { LOG_W("ID[%X] rx message over run\r\n", can_instance->filter_cfg.filter_mag[i].Id); } else if (rx_status == MSG_OBJ_NOT_SET) { LOG_W("rx message not set \r\n"); } else if (rx_status == MSG_RX_FINISH) { LOG_W("rx ok \r\n"); pmsg->data[0] = rx_msg.data[0]; pmsg->data[1] = rx_msg.data[1]; pmsg->data[2] = rx_msg.data[2]; pmsg->data[3] = rx_msg.data[3]; pmsg->data[4] = rx_msg.data[4]; pmsg->data[5] = rx_msg.data[5]; pmsg->data[6] = rx_msg.data[6]; pmsg->data[7] = rx_msg.data[7]; pmsg->len = rx_msg.data_len; if (can_instance->filter_cfg.filter_mag[i].IdType == CAN_EXT_ID) { pmsg->id = can_instance->filter_cfg.filter_mag[i].Id; pmsg->ide = RT_CAN_EXTID; } else if (can_instance->filter_cfg.filter_mag[i].IdType == CAN_STD_ID) { pmsg->id = can_instance->filter_cfg.filter_mag[i].Id; pmsg->ide = RT_CAN_EXTID; } if (can_instance->filter_cfg.filter_mag[i].FrameType == CAN_DATA_FRAME) { pmsg->rtr = RT_CAN_DTR; } else if (can_instance->filter_cfg.filter_mag[i].FrameType == CAN_REMOTE_FRAME) { pmsg->rtr = RT_CAN_RTR; } return RT_EOK; } } msgnum = msgnum >> 1; if (msgnum == 0) { return -1; } } return -1; } /* Mapping CAN interfaces */ static const struct rt_can_ops ht32_can_ops = { .configure = ht32_can_configure, /* CAN Configuration Functions */ .control = ht32_can_control, /* CAN Control Functions */ .sendmsg = ht32_can_sendmsg, /* CAN Transmit Data */ .recvmsg = ht32_can_recvmsg, /* CAN Receive Data */ }; int rt_hw_can_init(void) { struct can_configure config = CANDEFAULTCONFIG; config.mode = BSP_USING_CAN_MODE; config.baud_rate = BSP_USING_CAN_BAUD; config.privmode = RT_CAN_MODE_NOPRIV; config.ticks = 50; #ifdef RT_CAN_USING_HDR config.maxhdr = 14; #endif ht32_can_config.device.config = config; /* Registration of CAN devices */ rt_hw_can_register(&ht32_can_config.device, ht32_can_config.name, &ht32_can_ops, &ht32_can_config); return RT_EOK; } INIT_BOARD_EXPORT(rt_hw_can_init); void CAN0_IRQHandler(void) { CAN_LastErrorCode_TypeDef lec; rt_interrupt_enter(); /* Recover from Bus off state. */ if (CAN_GetFlagStatus(ht32_can_config.can_x, CAN_FLAG_BOFF)) { CAN_BusOffRecovery(ht32_can_config.can_x); } /* Transmit message finished */ if (CAN_GetFlagStatus(ht32_can_config.can_x, CAN_FLAG_TXOK)) { rt_hw_can_isr(&ht32_can_config.device, RT_CAN_EVENT_TX_DONE); CAN_ClearFlag(ht32_can_config.can_x, CAN_FLAG_TXOK); } /* Message received. */ if (CAN_GetFlagStatus(ht32_can_config.can_x, CAN_FLAG_RXOK)) { /* Clear all message objects' interrupt pending flag */ CAN_ClearAllMsgPendingFlag(ht32_can_config.can_x); rt_hw_can_isr(&ht32_can_config.device, RT_CAN_EVENT_RX_IND); CAN_ClearFlag(ht32_can_config.can_x, CAN_FLAG_RXOK); } lec = CAN_GetLastErrorCode(ht32_can_config.can_x); if (lec != NO_ERROR) { switch (lec) { case NO_ERROR: break; case STUFF_ERROR: ht32_can_config.device.status.bitpaderrcnt++; break; case FORM_ERROR: ht32_can_config.device.status.formaterrcnt++; break; case ACK_ERROR: ht32_can_config.device.status.ackerrcnt++; break; case BIT1_EROR: case BIT0_ERROR: ht32_can_config.device.status.biterrcnt++; break; case CRC_ERROR: ht32_can_config.device.status.crcerrcnt++; break; case NO_CHANGE: break; } ht32_can_config.device.status.lasterrtype = lec; ht32_can_config.device.status.rcverrcnt = CAN_GetReceiveErrorCounter(ht32_can_config.can_x); ht32_can_config.device.status.snderrcnt = CAN_GetLSBTransmitErrorCounter(ht32_can_config.can_x); ht32_can_config.device.status.errcode = lec; } rt_interrupt_leave(); } #endif /* BSP_USING_CAN */