rt-thread-official/bsp/ft2004/drivers/drv_can.c

272 lines
6.7 KiB
C

/*
* 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 <string.h>
#ifdef BSP_USE_CAN
#define LOG_TAG "drv_can"
#include <drv_log.h>
#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_can1.device.config.ticks = 20000;
drv_can1.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