/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2021-05-11 Carl the first version */ #include "drv_can.h" #include "interrupt.h" #include #ifdef BSP_USE_CAN #define LOG_TAG "drv_can" #include #define _CAN0_NAME "can0" #define _CAN1_NAME "can1" #define RTHW_CAN_WAIT(_can) rt_sem_take(&_can->recv_semaphore, RT_WAITING_FOREVER); #define RTHW_CAN_SEND(_can) rt_sem_release(&_can->recv_semaphore); #ifdef BSP_USING_CAN0 struct ft2004_can drv_can0 = { .name = _CAN0_NAME, .can_handle.Config.InstanceId = 0}; #endif #ifdef BSP_USING_CAN1 struct ft2004_can drv_can1 = { .name = _CAN1_NAME, .can_handle.Config.InstanceId = 1}; #endif static void _can_recv_irq(void *args) { struct ft2004_can *drv_can = (struct ft2004_can *)args; RTHW_CAN_SEND(drv_can); } static void rt_hw_inner_can_isr(int irqno, void *param) { FCan_IntrHandler(param); } static rt_err_t _can_config(struct rt_can_device *can, struct can_configure *cfg) { struct FCan_Bittiming bit_timing = {0}; struct ft2004_can *drv_can; RT_ASSERT(can); RT_ASSERT(cfg); drv_can = (struct ft2004_can *)can->parent.user_data; RT_ASSERT(drv_can); FCan_CfgInitialize(&drv_can->can_handle, FCan_LookupConfig(drv_can->can_handle.Config.InstanceId)); FCan_SetHandler(&drv_can->can_handle, FCAN_HANDLER_RECV, _can_recv_irq, drv_can); bit_timing.bitrate = cfg->baud_rate; if (FCan_CalcBittiming(&bit_timing) != FCAN_SUCCESS) { LOG_E("Setting baud rate %x is not valid \r\n", bit_timing.bitrate); return -RT_ERROR; } FCan_SetTiming(&drv_can->can_handle, &bit_timing); rt_hw_interrupt_set_priority(drv_can->can_handle.Config.IrqNum, 16); rt_hw_interrupt_install(drv_can->can_handle.Config.IrqNum, rt_hw_inner_can_isr, &drv_can->can_handle, drv_can->name); rt_hw_interrupt_umask(drv_can->can_handle.Config.IrqNum); FCan_Enable(&drv_can->can_handle); return RT_EOK; } static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg) { return RT_EOK; } static int _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num) { struct ft2004_can *drv_can; struct rt_can_msg *pmsg = (struct rt_can_msg *)buf; struct FCan_Frame can_frame = {0}; RT_ASSERT(can); drv_can = (struct ft2004_can *)can->parent.user_data; RT_ASSERT(drv_can); /* Check the parameters */ RT_ASSERT(pmsg->len <= 8U); if (RT_CAN_STDID == pmsg->ide) { can_frame.CanId = pmsg->id; } else { can_frame.CanId = pmsg->id; can_frame.CanId |= CAN_EFF_FLAG; } if (RT_CAN_DTR == pmsg->rtr) { } else { can_frame.CanId |= CAN_RTR_FLAG; } can_frame.CanDlc = pmsg->len & 0x0FU; memcpy(can_frame.data, pmsg->data, 8); return (FCan_SendByIrq(&drv_can->can_handle, &can_frame, 1, RT_NULL) == 1) ? RT_EOK : -RT_ERROR; } static int _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo) { struct ft2004_can *drv_can; struct rt_can_msg *pmsg = (struct rt_can_msg *)buf; RT_ASSERT(can); struct FCan_Frame recv_frame = {0}; drv_can = (struct ft2004_can *)can->parent.user_data; RT_ASSERT(drv_can); RTHW_CAN_WAIT(drv_can); if (FCan_RecvByIrq(&drv_can->can_handle, &recv_frame, 1) == 0) { LOG_E("rx msg is error"); return -RT_ERROR; } if (CAN_EFF_FLAG & recv_frame.CanId) { pmsg->ide = RT_CAN_EXTID; pmsg->id = (recv_frame.CanId & ~(RT_CAN_EXTID)); } else { pmsg->ide = RT_CAN_STDID; pmsg->id = recv_frame.CanId; } if (CAN_RTR_FLAG & recv_frame.CanId) { pmsg->id &= ~CAN_RTR_FLAG; pmsg->rtr = RT_CAN_RTR; } else { pmsg->rtr = RT_CAN_DTR; } /* get len */ pmsg->len = recv_frame.CanDlc; return RT_EOK; } static const struct rt_can_ops _can_ops = { _can_config, _can_control, _can_sendmsg, _can_recvmsg, }; int rt_hw_can_init(void) { #ifdef BSP_USING_CAN0 drv_can0.can_handle.Config.InstanceId = 0; rt_sem_init(&drv_can0.recv_semaphore, "can0_recv", 0, RT_IPC_FLAG_FIFO); drv_can0.device.config.ticks = 20000; drv_can0.device.config.baud_rate = 1000000; rt_hw_can_register(&drv_can0.device, drv_can0.name, &_can_ops, &drv_can0); #endif #ifdef BSP_USING_CAN1 drv_can1.can_handle.Config.InstanceId = 1; drv_can0.device.config.baud_rate = 1000000; rt_sem_init(&drv_can1.recv_semaphore, "can1_recv", 0, RT_IPC_FLAG_FIFO); rt_hw_can_register(&drv_can1.device, drv_can1.name, &_can_ops, &drv_can1); #endif return 0; } INIT_BOARD_EXPORT(rt_hw_can_init); #ifdef BSP_USING_CAN0_DEBUG struct can_test_struct { const char *name; struct rt_can_filter_config *filter; rt_device_t candev; struct rt_semaphore _sem; }; static struct can_test_struct can0_test_obj = { .name = _CAN0_NAME}; void can_recv_irq(void *param) { struct can_test_struct *_can_obj = (struct can_test_struct *)param; rt_kprintf("can_recv_iqr \r\n"); rt_sem_release(&_can_obj->_sem); } static void rt_can_test_loopback_thread_entry(void *param) { struct can_test_struct *_can_obj = (struct can_test_struct *)param; struct FCan_Frame recv_frame; struct ft2004_can *drv_can; rt_uint32_t i; _can_obj->candev = rt_device_find(_can_obj->name); RT_ASSERT(_can_obj->candev); drv_can = (struct ft2004_can *)_can_obj->candev->user_data; rt_sem_init(&_can_obj->_sem, "canrx_wait", 0, RT_IPC_FLAG_FIFO); rt_device_open(_can_obj->candev, RT_DEVICE_OFLAG_RDWR); while (1) { rt_kprintf(" start to wait loopback \r\n"); RTHW_CAN_WAIT(drv_can); while (0 != FCan_RecvByIrq(&drv_can->can_handle, &recv_frame, 1)) { rt_kprintf("CanId %x \r\n", recv_frame.CanId); rt_kprintf("CanDlc %x \r\n", recv_frame.CanDlc); for (i = 0; i < recv_frame.CanDlc; i++) { rt_kprintf("data [%d] %x \r\n", i, recv_frame.data[i]); } FCan_SendByIrq(&drv_can->can_handle, &recv_frame, 1, RT_NULL); } } } int rt_can0_test(void) { rt_thread_t tid; tid = rt_thread_create("can0_loopback", rt_can_test_loopback_thread_entry, &can0_test_obj, 1024, 16, 20); if (tid != RT_NULL) rt_thread_startup(tid); return 0; } INIT_APP_EXPORT(rt_can0_test); #endif #endif