334 lines
8.6 KiB
C
334 lines
8.6 KiB
C
|
/*
|
||
|
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*
|
||
|
* Change Logs:
|
||
|
* Date Author Email Notes
|
||
|
* 2019-07-16 Kevin.Liu kevin.liu.mchp@gmail.com First Release
|
||
|
*/
|
||
|
|
||
|
#include <rtthread.h>
|
||
|
|
||
|
#ifdef RT_USING_FINSH
|
||
|
#include <finsh.h>
|
||
|
#include <shell.h>
|
||
|
#endif
|
||
|
|
||
|
#include "atmel_start.h"
|
||
|
#include "driver_init.h"
|
||
|
#include "utils.h"
|
||
|
|
||
|
#include "can_demo.h"
|
||
|
|
||
|
#ifdef SAM_CAN_EXAMPLE
|
||
|
|
||
|
static volatile enum can_async_interrupt_type can_errors;
|
||
|
static rt_sem_t can_txdone;
|
||
|
static rt_sem_t can_rxdone;
|
||
|
static rt_uint8_t can_stack[ 512 ];
|
||
|
static struct rt_thread can_thread;
|
||
|
|
||
|
/**
|
||
|
* @brief Callback function and should be invoked after call can_async_write.
|
||
|
*
|
||
|
* @note
|
||
|
*
|
||
|
* @param descr is CAN device description.
|
||
|
*
|
||
|
* @return None.
|
||
|
*/
|
||
|
|
||
|
static void can_tx_callback(struct can_async_descriptor *const descr)
|
||
|
{
|
||
|
rt_err_t result;
|
||
|
|
||
|
rt_interrupt_enter();
|
||
|
result = rt_sem_release(can_txdone);
|
||
|
if (RT_EOK != result)
|
||
|
{
|
||
|
#ifndef RT_USING_FINSH
|
||
|
rt_kprintf("rt_sem_release failed in %s %d\r\n",__FUNCTION__, __LINE__);
|
||
|
#endif
|
||
|
}
|
||
|
rt_interrupt_leave();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Callback function and should be invoked after remote device send.
|
||
|
*
|
||
|
* @note This callback function will be called in CAN interrupt function
|
||
|
*
|
||
|
* @param descr is CAN device description.
|
||
|
*
|
||
|
* @return None.
|
||
|
*/
|
||
|
|
||
|
static void can_rx_callback(struct can_async_descriptor *const descr)
|
||
|
{
|
||
|
rt_err_t result;
|
||
|
|
||
|
rt_interrupt_enter();
|
||
|
result = rt_sem_release(can_rxdone);
|
||
|
if (RT_EOK != result)
|
||
|
{
|
||
|
#ifndef RT_USING_FINSH
|
||
|
rt_kprintf("rt_sem_release failed in %s %d\r\n",__FUNCTION__, __LINE__);
|
||
|
#endif
|
||
|
}
|
||
|
rt_interrupt_leave();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Callback function and should be invoked after CAN device IRQ handler detects errors happened.
|
||
|
*
|
||
|
* @note This callback function will be called in CAN interrupt function
|
||
|
*
|
||
|
* @param descr is CAN device description.
|
||
|
*
|
||
|
* @return None.
|
||
|
*/
|
||
|
|
||
|
static void can_err_callback(struct can_async_descriptor *const descr,
|
||
|
enum can_async_interrupt_type type)
|
||
|
{
|
||
|
rt_err_t result;
|
||
|
|
||
|
if (type == CAN_IRQ_EW)
|
||
|
{
|
||
|
/* Error warning, Error counter has reached the error warning limit of 96,
|
||
|
* An error count value greater than about 96 indicates a heavily disturbed
|
||
|
* bus. It may be of advantage to provide means to test for this condition.
|
||
|
*/
|
||
|
}
|
||
|
else if (type == CAN_IRQ_EA)
|
||
|
{
|
||
|
/* Error Active State, The CAN node normally take part in bus communication
|
||
|
* and sends an ACTIVE ERROR FLAG when an error has been detected.
|
||
|
*/
|
||
|
}
|
||
|
else if (type == CAN_IRQ_EP)
|
||
|
{
|
||
|
/* Error Passive State, The Can node goes into error passive state if at least
|
||
|
* one of its error counters is greater than 127. It still takes part in bus
|
||
|
* activities, but it sends a passive error frame only, on errors.
|
||
|
*/
|
||
|
}
|
||
|
else if (type == CAN_IRQ_BO)
|
||
|
{
|
||
|
/* Bus Off State, The CAN node is 'bus off' when the TRANSMIT ERROR COUNT is
|
||
|
* greater than or equal to 256.
|
||
|
*/
|
||
|
|
||
|
/* Suspend CAN task and re-initialize CAN module. */
|
||
|
can_errors = type;
|
||
|
rt_interrupt_enter();
|
||
|
result = rt_sem_release(can_rxdone);
|
||
|
if (RT_EOK != result)
|
||
|
{
|
||
|
#ifndef RT_USING_FINSH
|
||
|
rt_kprintf("rt_sem_release failed in %s %d\r\n",__FUNCTION__, __LINE__);
|
||
|
#endif
|
||
|
}
|
||
|
rt_interrupt_leave();
|
||
|
}
|
||
|
else if (type == CAN_IRQ_DO)
|
||
|
{
|
||
|
/* Data Overrun in receive queue. A message was lost because the messages in
|
||
|
* the queue was not reading and releasing fast enough. There is not enough
|
||
|
* space for a new message in receive queue.
|
||
|
*/
|
||
|
|
||
|
/* Suggest to delete CAN task and re-initialize it. */
|
||
|
can_errors = type;
|
||
|
rt_interrupt_enter();
|
||
|
result = rt_sem_release(can_rxdone);
|
||
|
if (RT_EOK != result)
|
||
|
{
|
||
|
#ifndef RT_USING_FINSH
|
||
|
rt_kprintf("rt_sem_release failed in %s %d\r\n",__FUNCTION__, __LINE__);
|
||
|
#endif
|
||
|
}
|
||
|
rt_interrupt_leave();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @brief Initialize CAN module before task run.
|
||
|
*
|
||
|
* @note This function will set CAN Tx/Rx callback function and filters.
|
||
|
*
|
||
|
* @param None.
|
||
|
*
|
||
|
* @return None.
|
||
|
*/
|
||
|
|
||
|
static inline void can_demo_init(void)
|
||
|
{
|
||
|
struct can_filter filter;
|
||
|
|
||
|
/**
|
||
|
* CAN_Node0_tx_callback callback should be invoked after call
|
||
|
* can_async_write, and remote device should receive message with ID=0x45A
|
||
|
*/
|
||
|
can_async_register_callback(&CAN_0, CAN_ASYNC_TX_CB, (FUNC_PTR)can_tx_callback);
|
||
|
|
||
|
/**
|
||
|
* CAN_0_rx_callback callback should be invoked after call
|
||
|
* can_async_set_filter and remote device send CAN Message with the same
|
||
|
* content as the filter.
|
||
|
*/
|
||
|
can_async_register_callback(&CAN_0, CAN_ASYNC_RX_CB, (FUNC_PTR)can_rx_callback);
|
||
|
|
||
|
|
||
|
/* Should set at least one CAN standard & message filter before enable it. */
|
||
|
|
||
|
filter.id = 0x469;
|
||
|
filter.mask = 0;
|
||
|
can_async_set_filter(&CAN_0, 0, CAN_FMT_STDID, &filter);
|
||
|
|
||
|
/* If set second standard message filter, should increase filter index
|
||
|
* and filter algorithm
|
||
|
* For example: index should set to 1, otherwise it will replace filter 0.
|
||
|
* can_async_set_filter(&CAN_0, 1, CAN_FMT_STDID, &filter); */
|
||
|
|
||
|
filter.id = 0x10000096;
|
||
|
filter.mask = 0;
|
||
|
can_async_set_filter(&CAN_0, 0, CAN_FMT_EXTID, &filter);
|
||
|
|
||
|
can_async_enable(&CAN_0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief CAN task.
|
||
|
*
|
||
|
* @note This task will waiting for CAN RX semaphore and then process input.
|
||
|
*
|
||
|
* @param parameter - task input parameter.
|
||
|
*
|
||
|
* @return None.
|
||
|
*/
|
||
|
|
||
|
static void can_thread_entry(void* parameter)
|
||
|
{
|
||
|
int32_t ret;
|
||
|
rt_err_t result;
|
||
|
uint8_t data[64];
|
||
|
uint32_t count=0;
|
||
|
struct can_message msg;
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
#ifndef RT_USING_FINSH
|
||
|
rt_kprintf("can task run count : %d\r\n",count);
|
||
|
#endif
|
||
|
count++;
|
||
|
|
||
|
result = rt_sem_take(can_rxdone, RT_WAITING_FOREVER);
|
||
|
if (RT_EOK != result)
|
||
|
continue;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
/* Process the incoming packet. */
|
||
|
ret = can_async_read(&CAN_0, &msg);
|
||
|
if (ret == ERR_NONE)
|
||
|
{
|
||
|
#ifndef RT_USING_FINSH
|
||
|
rt_kprintf("CAN RX Message is % frame\r\n",
|
||
|
msg.type == CAN_TYPE_DATA ? "data" : "remote");
|
||
|
rt_kprintf("CAN RX Message is % frame\r\n",
|
||
|
msg.type == CAN_FMT_STDID ? "Standard" : "Extended");
|
||
|
rt_kprintf("can RX Message ID: 0x%X length: %d\r\n", msg.id, msg.len);
|
||
|
rt_kprintf("CAN RX Message content: ");
|
||
|
for (uint8_t i = 0; i < msg.len; i++)
|
||
|
rt_kprintf("0x%02X ", data[i]);
|
||
|
rt_kprintf("\r\n");
|
||
|
#endif
|
||
|
}
|
||
|
} while (ret == ERR_NONE); /* Get all data stored in CAN RX FIFO */
|
||
|
|
||
|
/* CAN task got CAN error message, handler CAN Error Status */
|
||
|
if ((can_errors == CAN_IRQ_BO) || (can_errors == CAN_IRQ_DO))
|
||
|
{
|
||
|
can_async_init(&CAN_0, CAN1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Call this function will to send a CAN message.
|
||
|
*
|
||
|
* @note
|
||
|
*
|
||
|
* @param msg - message to be sent, timeouts - wait timeouts for Tx completion.
|
||
|
*
|
||
|
* @return RT_OK or RT_ERROR.
|
||
|
*/
|
||
|
|
||
|
rt_err_t can_send_message(struct can_message *msg, rt_uint32_t timeouts)
|
||
|
{
|
||
|
rt_err_t result;
|
||
|
|
||
|
if (RT_NULL == msg)
|
||
|
{
|
||
|
rt_kprintf("can_send_message input message error\r\n");
|
||
|
return RT_ERROR;
|
||
|
}
|
||
|
|
||
|
can_async_write(&CAN_0, msg);
|
||
|
result = rt_sem_take(can_rxdone, timeouts);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Call this function will create a CAN task.
|
||
|
*
|
||
|
* @note Should create Tx/Rx semaphore before run task.
|
||
|
*
|
||
|
* @param None.
|
||
|
*
|
||
|
* @return RT_OK or -RT_ERROR.
|
||
|
*/
|
||
|
|
||
|
rt_err_t can_demo_run(void)
|
||
|
{
|
||
|
rt_err_t result;
|
||
|
|
||
|
can_rxdone = rt_sem_create("can_rx", 0, RT_IPC_FLAG_FIFO);
|
||
|
if (RT_NULL == can_rxdone)
|
||
|
{
|
||
|
rt_kprintf("can_rx semaphore create failed\r\n");
|
||
|
return (-RT_ERROR);
|
||
|
}
|
||
|
|
||
|
can_txdone = rt_sem_create("can_tx", 0, RT_IPC_FLAG_FIFO);
|
||
|
if (RT_NULL == can_txdone)
|
||
|
{
|
||
|
rt_kprintf("can_tx semaphore create failed\r\n");
|
||
|
return (-RT_ERROR);
|
||
|
}
|
||
|
|
||
|
can_demo_init();
|
||
|
|
||
|
/* initialize CAN thread */
|
||
|
result = rt_thread_init(&can_thread,
|
||
|
"can",
|
||
|
can_thread_entry,
|
||
|
RT_NULL,
|
||
|
(rt_uint8_t*)&can_stack[0],
|
||
|
sizeof(can_stack),
|
||
|
RT_THREAD_PRIORITY_MAX/3,
|
||
|
5);
|
||
|
if (result == RT_EOK)
|
||
|
{
|
||
|
rt_thread_startup(&can_thread);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*@}*/
|