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