1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date        Author    Email                    Notes
8  * 2022-04-16  Kevin.Liu kevin.liu.mchp@gmail.com First Release
9  */
10 
11 #include <rtthread.h>
12 
13 #include "atmel_start.h"
14 #include "driver_init.h"
15 #include "utils.h"
16 
17 #include "can_demo.h"
18 
19 #ifdef SAM_CAN_EXAMPLE
20 
21 #if defined(SOC_SAMC21) || defined(SOC_SAME54)
22 #define CAN_HARDWARE            (void *)CAN1
23 #elif defined(SOC_SAME70)
24 #define CAN_HARDWARE            (void *)MCAN1
25 #else
26 #error "CAN undefined SOC Platform"
27 #endif
28 
29 static volatile enum can_async_interrupt_type can_errors;
30 static rt_sem_t   can_txdone;
31 static rt_sem_t   can_rxdone;
32 static rt_uint8_t can_stack[ 512 ];
33 static struct rt_thread can_thread;
34 
35 /**
36  * @brief    Callback function and should be invoked after call can_async_write.
37  *
38  * @note
39  *
40  * @param    descr is CAN device description.
41  *
42  * @return   None.
43  */
44 
can_tx_callback(struct can_async_descriptor * const descr)45 static void can_tx_callback(struct can_async_descriptor *const descr)
46 {
47     rt_err_t result;
48 
49     rt_interrupt_enter();
50     result = rt_sem_release(can_txdone);
51     if (RT_EOK != result)
52     {
53 #ifndef RT_USING_FINSH
54         rt_kprintf("rt_sem_release failed in %s %d\r\n",__FUNCTION__, __LINE__);
55 #endif
56     }
57     rt_interrupt_leave();
58 }
59 
60 /**
61  * @brief    Callback function and should be invoked after remote device send.
62  *
63  * @note     This callback function will be called in CAN interrupt function
64  *
65  * @param    descr is CAN device description.
66  *
67  * @return   None.
68  */
69 
can_rx_callback(struct can_async_descriptor * const descr)70 static void can_rx_callback(struct can_async_descriptor *const descr)
71 {
72     rt_err_t result;
73 
74     rt_interrupt_enter();
75     result = rt_sem_release(can_rxdone);
76     if (RT_EOK != result)
77     {
78 #ifndef RT_USING_FINSH
79         rt_kprintf("rt_sem_release failed in %s %d\r\n",__FUNCTION__, __LINE__);
80 #endif
81     }
82     rt_interrupt_leave();
83 }
84 
85 /**
86  * @brief    Callback function and should be invoked after CAN device IRQ handler detects errors happened.
87  *
88  * @note     This callback function will be called in CAN interrupt function
89  *
90  * @param    descr is CAN device description.
91  *
92  * @return   None.
93  */
94 
can_err_callback(struct can_async_descriptor * const descr,enum can_async_interrupt_type type)95 static void can_err_callback(struct can_async_descriptor *const descr,
96                              enum can_async_interrupt_type type)
97 {
98     rt_err_t result;
99 
100     if (type == CAN_IRQ_EW)
101     {
102       /* Error warning, Error counter has reached the error warning limit of 96,
103        * An error count value greater than about 96 indicates a heavily disturbed
104        * bus. It may be of advantage to provide means to test for this condition.
105        */
106     }
107     else if (type == CAN_IRQ_EA)
108     {
109         /* Error Active State, The CAN node normally take part in bus communication
110          * and sends an ACTIVE ERROR FLAG when an error has been detected.
111          */
112     }
113     else if (type == CAN_IRQ_EP)
114     {
115         /* Error Passive State, The Can node goes into error passive state if at least
116          * one of its error counters is greater than 127. It still takes part in bus
117          * activities, but it sends a passive error frame only, on errors.
118          */
119     }
120     else if (type == CAN_IRQ_BO)
121     {
122         /* Bus Off State, The CAN node is 'bus off' when the TRANSMIT ERROR COUNT is
123          * greater than or equal to 256.
124          */
125 
126         /* Suspend CAN task and re-initialize CAN module. */
127         can_errors = type;
128         rt_interrupt_enter();
129         result = rt_sem_release(can_rxdone);
130         if (RT_EOK != result)
131         {
132 #ifndef RT_USING_FINSH
133             rt_kprintf("rt_sem_release failed in %s %d\r\n",__FUNCTION__, __LINE__);
134 #endif
135         }
136         rt_interrupt_leave();
137     }
138     else if (type == CAN_IRQ_DO)
139     {
140         /* Data Overrun in receive queue. A message was lost because the messages in
141          * the queue was not reading and releasing fast enough. There is not enough
142          * space for a new message in receive queue.
143          */
144 
145         /* Suggest to delete CAN task and re-initialize it. */
146         can_errors = type;
147         rt_interrupt_enter();
148         result = rt_sem_release(can_rxdone);
149         if (RT_EOK != result)
150         {
151 #ifndef RT_USING_FINSH
152             rt_kprintf("rt_sem_release failed in %s %d\r\n",__FUNCTION__, __LINE__);
153 #endif
154         }
155         rt_interrupt_leave();
156     }
157 };
158 
159 /**
160  * @brief    Initialize CAN module before task run.
161  *
162  * @note     This function will set CAN Tx/Rx callback function and filters.
163  *
164  * @param    None.
165  *
166  * @return   None.
167  */
168 
can_demo_init(void)169 static inline void can_demo_init(void)
170 {
171     struct can_filter  filter;
172 
173     /**
174      * CAN_Node0_tx_callback callback should be invoked after call
175      * can_async_write, and remote device should receive message with ID=0x45A
176      */
177     can_async_register_callback(&CAN_0, CAN_ASYNC_TX_CB, (FUNC_PTR)can_tx_callback);
178 
179     /**
180      * CAN_0_rx_callback callback should be invoked after call
181      * can_async_set_filter and remote device send CAN Message with the same
182      * content as the filter.
183      */
184     can_async_register_callback(&CAN_0, CAN_ASYNC_RX_CB, (FUNC_PTR)can_rx_callback);
185 
186 
187     /* Should set at least one CAN standard & message filter before enable it. */
188 
189     filter.id   = 0x469;
190     filter.mask = 0;
191     can_async_set_filter(&CAN_0, 0, CAN_FMT_STDID, &filter);
192 
193     /* If set second standard message filter, should increase filter index
194      * and filter algorithm
195      * For example: index should set to 1, otherwise it will replace filter 0.
196      * can_async_set_filter(&CAN_0, 1, CAN_FMT_STDID, &filter); */
197 
198     filter.id   = 0x10000096;
199     filter.mask = 0;
200     can_async_set_filter(&CAN_0, 0, CAN_FMT_EXTID, &filter);
201 
202     can_async_enable(&CAN_0);
203 }
204 
205 /**
206  * @brief    CAN task.
207  *
208  * @note     This task will waiting for CAN RX semaphore and then process input.
209  *
210  * @param    parameter - task input parameter.
211  *
212  * @return   None.
213  */
214 
can_thread_entry(void * parameter)215 static void can_thread_entry(void* parameter)
216 {
217     int32_t  ret;
218     rt_err_t result;
219     uint8_t  data[64];
220     uint32_t count=0;
221     struct can_message msg;
222 
223     while (1)
224     {
225 #ifndef RT_USING_FINSH
226         rt_kprintf("can task run count : %d\r\n",count);
227 #endif
228         count++;
229 
230         result = rt_sem_take(can_rxdone, RT_WAITING_FOREVER);
231         if (RT_EOK != result)
232             continue;
233 
234         do
235         {
236             /* Process the incoming packet. */
237             ret = can_async_read(&CAN_0, &msg);
238             if (ret == ERR_NONE)
239             {
240 #ifndef RT_USING_FINSH
241                 rt_kprintf("CAN RX Message is % frame\r\n",
242                            msg.type == CAN_TYPE_DATA ? "data" : "remote");
243                 rt_kprintf("CAN RX Message is % frame\r\n",
244                            msg.type == CAN_FMT_STDID ? "Standard" : "Extended");
245                 rt_kprintf("can RX Message ID: 0x%X length: %d\r\n", msg.id, msg.len);
246                 rt_kprintf("CAN RX Message content: ");
247                 for (uint8_t i = 0; i < msg.len; i++)
248                     rt_kprintf("0x%02X ", data[i]);
249                 rt_kprintf("\r\n");
250 #endif
251             }
252         } while (ret == ERR_NONE); /* Get all data stored in CAN RX FIFO */
253 
254         /* CAN task got CAN error message, handler CAN Error Status */
255         if ((can_errors == CAN_IRQ_BO) || (can_errors == CAN_IRQ_DO))
256         {
257             can_async_init(&CAN_0, CAN_HARDWARE);
258         }
259     }
260 }
261 
262 /**
263  * @brief    Call this function will to send a CAN message.
264  *
265  * @note
266  *
267  * @param    msg - message to be sent, timeouts - wait timeouts for Tx completion.
268  *
269  * @return   RT_OK or -RT_ERROR.
270  */
271 
can_send_message(struct can_message * msg,rt_uint32_t timeouts)272 rt_err_t can_send_message(struct can_message *msg, rt_uint32_t timeouts)
273 {
274     rt_err_t result;
275 
276     if (RT_NULL == msg)
277     {
278         rt_kprintf("can_send_message input message error\r\n");
279         return -RT_ERROR;
280     }
281 
282     can_async_write(&CAN_0, msg);
283     result = rt_sem_take(can_rxdone, timeouts);
284 
285     return result;
286 }
287 
288 /**
289  * @brief    Call this function will create a CAN task.
290  *
291  * @note     Should create Tx/Rx semaphore before run task.
292  *
293  * @param    None.
294  *
295  * @return   RT_OK or -RT_ERROR.
296  */
297 
can_demo_run(void)298 rt_err_t can_demo_run(void)
299 {
300     rt_err_t result;
301 
302     can_rxdone = rt_sem_create("can_rx", 0, RT_IPC_FLAG_FIFO);
303     if (RT_NULL == can_rxdone)
304     {
305         rt_kprintf("can_rx semaphore create failed\r\n");
306         return (-RT_ERROR);
307     }
308 
309     can_txdone = rt_sem_create("can_tx", 0, RT_IPC_FLAG_FIFO);
310     if (RT_NULL == can_txdone)
311     {
312         rt_kprintf("can_tx semaphore create failed\r\n");
313         return (-RT_ERROR);
314     }
315 
316     can_demo_init();
317 
318     /* initialize CAN thread */
319     result = rt_thread_init(&can_thread,
320                             "can",
321                             can_thread_entry,
322                             RT_NULL,
323                             (rt_uint8_t*)&can_stack[0],
324                             sizeof(can_stack),
325                             RT_THREAD_PRIORITY_MAX/3,
326                             5);
327     if (result == RT_EOK)
328     {
329         rt_thread_startup(&can_thread);
330     }
331 
332     return result;
333 }
334 #endif
335 
336 /*@}*/
337