ChibiOS  21.6.0
hal_can.c
Go to the documentation of this file.
1 /*
2  ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 
17 /**
18  * @file hal_can.c
19  * @brief CAN Driver code.
20  *
21  * @addtogroup CAN
22  * @{
23  */
24 
25 #include "hal.h"
26 
27 #if (HAL_USE_CAN == TRUE) || defined(__DOXYGEN__)
28 
29 /*===========================================================================*/
30 /* Driver local definitions. */
31 /*===========================================================================*/
32 
33 /*===========================================================================*/
34 /* Driver exported variables. */
35 /*===========================================================================*/
36 
37 /*===========================================================================*/
38 /* Driver local variables and types. */
39 /*===========================================================================*/
40 
41 /*===========================================================================*/
42 /* Driver local functions. */
43 /*===========================================================================*/
44 
45 /*===========================================================================*/
46 /* Driver exported functions. */
47 /*===========================================================================*/
48 
49 /**
50  * @brief CAN Driver initialization.
51  * @note This function is implicitly invoked by @p halInit(), there is
52  * no need to explicitly initialize the driver.
53  *
54  * @init
55  */
56 void canInit(void) {
57 
58  can_lld_init();
59 }
60 
61 /**
62  * @brief Initializes the standard part of a @p CANDriver structure.
63  *
64  * @param[out] canp pointer to the @p CANDriver object
65  *
66  * @init
67  */
68 void canObjectInit(CANDriver *canp) {
69 
70  canp->state = CAN_STOP;
71  canp->config = NULL;
74 #if CAN_ENFORCE_USE_CALLBACKS == FALSE
78 #if CAN_USE_SLEEP_MODE == TRUE
81 #endif
82 #else /* CAN_ENFORCE_USE_CALLBACKS == TRUE */
83  canp->rxfull_cb = NULL;
84  canp->txempty_cb = NULL;
85  canp->error_cb = NULL;
86 #if CAN_USE_SLEEP_MODE == TRUE
87  canp->wakeup_cb = NULL;
88 #endif
89 #endif /* CAN_ENFORCE_USE_CALLBACKS == TRUE */
90 }
91 
92 /**
93  * @brief Configures and activates the CAN peripheral.
94  * @note Activating the CAN bus can be a slow operation.
95  * @note Unlike other drivers it is not possible to restart the CAN
96  * driver without first stopping it using canStop().
97  *
98  * @param[in] canp pointer to the @p CANDriver object
99  * @param[in] config pointer to the @p CANConfig object. Depending on
100  * the implementation the value can be @p NULL.
101  *
102  * @api
103  */
104 void canStart(CANDriver *canp, const CANConfig *config) {
105 
106  osalDbgCheck(canp != NULL);
107 
108  osalSysLock();
109  osalDbgAssert(canp->state == CAN_STOP, "invalid state");
110 
111  /* Entering initialization mode. */
112  canp->state = CAN_STARTING;
113  canp->config = config;
114 
115  /* Low level initialization, could be a slow process and sleeps could
116  be performed inside.*/
117  can_lld_start(canp);
118 
119  /* The driver finally goes into the ready state.*/
120  canp->state = CAN_READY;
121  osalSysUnlock();
122 }
123 
124 /**
125  * @brief Deactivates the CAN peripheral.
126  *
127  * @param[in] canp pointer to the @p CANDriver object
128  *
129  * @api
130  */
131 void canStop(CANDriver *canp) {
132 
133  osalDbgCheck(canp != NULL);
134 
135  osalSysLock();
136  osalDbgAssert((canp->state == CAN_STOP) || (canp->state == CAN_READY),
137  "invalid state");
138 
139  /* The low level driver is stopped.*/
140  canp->state = CAN_STOPPING;
141  can_lld_stop(canp);
142  canp->config = NULL;
143  canp->state = CAN_STOP;
144 
145  /* Threads waiting on CAN APIs are notified that the driver has been
146  stopped in order to not have stuck threads.*/
150  osalSysUnlock();
151 }
152 
153 /**
154  * @brief Can frame transmission attempt.
155  * @details The specified frame is queued for transmission, if the hardware
156  * queue is full then the function fails.
157  *
158  * @param[in] canp pointer to the @p CANDriver object
159  * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
160  * @param[in] ctfp pointer to the CAN frame to be transmitted
161  * @return The operation result.
162  * @retval false Frame transmitted.
163  * @retval true Mailbox full.
164  *
165  * @iclass
166  */
168  canmbx_t mailbox,
169  const CANTxFrame *ctfp) {
170 
172  osalDbgCheck((canp != NULL) && (ctfp != NULL) &&
173  (mailbox <= (canmbx_t)CAN_TX_MAILBOXES));
174  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
175  "invalid state");
176 
177  /* If the RX mailbox is full then the function fails.*/
178  if (!can_lld_is_tx_empty(canp, mailbox)) {
179  return true;
180  }
181 
182  /* Transmitting frame.*/
183  can_lld_transmit(canp, mailbox, ctfp);
184 
185  return false;
186 }
187 
188 /**
189  * @brief Can frame receive attempt.
190  * @details The function tries to fetch a frame from a mailbox.
191  *
192  * @param[in] canp pointer to the @p CANDriver object
193  * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
194  * @param[out] crfp pointer to the buffer where the CAN frame is copied
195  * @return The operation result.
196  * @retval false Frame fetched.
197  * @retval true Mailbox empty.
198  *
199  * @iclass
200  */
202  canmbx_t mailbox,
203  CANRxFrame *crfp) {
204 
206  osalDbgCheck((canp != NULL) && (crfp != NULL) &&
207  (mailbox <= (canmbx_t)CAN_RX_MAILBOXES));
208  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
209  "invalid state");
210 
211  /* If the RX mailbox is empty then the function fails.*/
212  if (!can_lld_is_rx_nonempty(canp, mailbox)) {
213  return true;
214  }
215 
216  /* Fetching the frame.*/
217  can_lld_receive(canp, mailbox, crfp);
218 
219  return false;
220 }
221 
222 /**
223  * @brief Tries to abort an ongoing transmission.
224  *
225  * @param[in] canp pointer to the @p CANDriver object
226  * @param[in] mailbox mailbox number
227  *
228  * @xclass
229  */
231  canmbx_t mailbox) {
232 
233  osalDbgCheck((canp != NULL) &&
234  (mailbox != CAN_ANY_MAILBOX) &&
235  (mailbox <= (canmbx_t)CAN_TX_MAILBOXES));
236 
237  can_lld_abort(canp, mailbox);
238 }
239 
240 /**
241  * @brief Can frame transmission.
242  * @details The specified frame is queued for transmission, if the hardware
243  * queue is full then the invoking thread is queued.
244  * @note Trying to transmit while in sleep mode simply enqueues the thread.
245  *
246  * @param[in] canp pointer to the @p CANDriver object
247  * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
248  * @param[in] ctfp pointer to the CAN frame to be transmitted
249  * @param[in] timeout the number of ticks before the operation timeouts,
250  * the following special values are allowed:
251  * - @a TIME_IMMEDIATE immediate timeout.
252  * - @a TIME_INFINITE no timeout.
253  * .
254  * @return The operation result.
255  * @retval MSG_OK the frame has been queued for transmission.
256  * @retval MSG_TIMEOUT The operation has timed out.
257  * @retval MSG_RESET The driver has been stopped while waiting.
258  *
259  * @api
260  */
262  canmbx_t mailbox,
263  const CANTxFrame *ctfp,
264  sysinterval_t timeout) {
265 
266  osalDbgCheck((canp != NULL) && (ctfp != NULL) &&
267  (mailbox <= (canmbx_t)CAN_TX_MAILBOXES));
268 
269  osalSysLock();
270  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
271  "invalid state");
272 
273  /*lint -save -e9007 [13.5] Right side is supposed to be pure.*/
274  while ((canp->state == CAN_SLEEP) || !can_lld_is_tx_empty(canp, mailbox)) {
275  /*lint -restore*/
276  msg_t msg = osalThreadEnqueueTimeoutS(&canp->txqueue, timeout);
277  if (msg != MSG_OK) {
278  osalSysUnlock();
279  return msg;
280  }
281  }
282  can_lld_transmit(canp, mailbox, ctfp);
283  osalSysUnlock();
284  return MSG_OK;
285 }
286 
287 /**
288  * @brief Can frame receive.
289  * @details The function waits until a frame is received.
290  * @note Trying to receive while in sleep mode simply enqueues the thread.
291  *
292  * @param[in] canp pointer to the @p CANDriver object
293  * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
294  * @param[out] crfp pointer to the buffer where the CAN frame is copied
295  * @param[in] timeout the number of ticks before the operation timeouts,
296  * the following special values are allowed:
297  * - @a TIME_IMMEDIATE immediate timeout (useful in an
298  * event driven scenario where a thread never blocks
299  * for I/O).
300  * - @a TIME_INFINITE no timeout.
301  * .
302  * @return The operation result.
303  * @retval MSG_OK a frame has been received and placed in the buffer.
304  * @retval MSG_TIMEOUT The operation has timed out.
305  * @retval MSG_RESET The driver has been stopped while waiting.
306  *
307  * @api
308  */
310  canmbx_t mailbox,
311  CANRxFrame *crfp,
312  sysinterval_t timeout) {
313 
314  osalDbgCheck((canp != NULL) && (crfp != NULL) &&
315  (mailbox <= (canmbx_t)CAN_RX_MAILBOXES));
316 
317  osalSysLock();
318  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
319  "invalid state");
320 
321  /*lint -save -e9007 [13.5] Right side is supposed to be pure.*/
322  while ((canp->state == CAN_SLEEP) || !can_lld_is_rx_nonempty(canp, mailbox)) {
323  /*lint -restore*/
324  msg_t msg = osalThreadEnqueueTimeoutS(&canp->rxqueue, timeout);
325  if (msg != MSG_OK) {
326  osalSysUnlock();
327  return msg;
328  }
329  }
330  can_lld_receive(canp, mailbox, crfp);
331  osalSysUnlock();
332  return MSG_OK;
333 }
334 
335 #if (CAN_USE_SLEEP_MODE == TRUE) || defined(__DOXYGEN__)
336 /**
337  * @brief Enters the sleep mode.
338  * @details This function puts the CAN driver in sleep mode and broadcasts
339  * the @p sleep_event event source.
340  * @pre In order to use this function the option @p CAN_USE_SLEEP_MODE must
341  * be enabled and the @p CAN_SUPPORTS_SLEEP mode must be supported
342  * by the low level driver.
343  *
344  * @param[in] canp pointer to the @p CANDriver object
345  *
346  * @api
347  */
348 void canSleep(CANDriver *canp) {
349 
350  osalDbgCheck(canp != NULL);
351 
352  osalSysLock();
353  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
354  "invalid state");
355  if (canp->state == CAN_READY) {
356  can_lld_sleep(canp);
357  canp->state = CAN_SLEEP;
358 #if CAN_ENFORCE_USE_CALLBACKS == FALSE
361 #endif
362  }
363  osalSysUnlock();
364 }
365 
366 /**
367  * @brief Enforces leaving the sleep mode.
368  * @note The sleep mode is supposed to be usually exited automatically by
369  * an hardware event.
370  *
371  * @param[in] canp pointer to the @p CANDriver object
372  */
373 void canWakeup(CANDriver *canp) {
374 
375  osalDbgCheck(canp != NULL);
376 
377  osalSysLock();
378  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
379  "invalid state");
380  if (canp->state == CAN_SLEEP) {
381  can_lld_wakeup(canp);
382  canp->state = CAN_READY;
383 #if CAN_ENFORCE_USE_CALLBACKS == FALSE
386 #endif
387  }
388  osalSysUnlock();
389 }
390 #endif /* CAN_USE_SLEEP_MODE == TRUE */
391 
392 #endif /* HAL_USE_CAN == TRUE */
393 
394 /** @} */
CANDriver::rxfull_event
event_source_t rxfull_event
One or more frames become available.
Definition: hal_can_lld.h:180
MSG_RESET
#define MSG_RESET
Wakeup caused by a reset condition.
Definition: chschd.h:42
osalThreadDequeueAllI
void osalThreadDequeueAllI(threads_queue_t *tqp, msg_t msg)
Dequeues and wakes up all threads from the queue.
Definition: osal.c:309
can_lld_abort
void can_lld_abort(CANDriver *canp, canmbx_t mailbox)
Tries to abort an ongoing transmission.
Definition: hal_can_lld.c:220
CANDriver::rxqueue
threads_queue_t rxqueue
Receive threads queue.
Definition: hal_can_lld.h:166
hal.h
HAL subsystem header.
CAN_SLEEP
@ CAN_SLEEP
Definition: hal_can.h:108
can_lld_is_rx_nonempty
bool can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox)
Determines whether a frame has been received.
Definition: hal_can_lld.c:176
osalDbgCheckClassI
#define osalDbgCheckClassI()
I-Class state check.
Definition: osal.h:298
eventflags_t
uint32_t eventflags_t
Definition: chearly.h:91
osalSysUnlock
static void osalSysUnlock(void)
Leaves a critical zone from thread context.
Definition: osal.h:611
can_lld_is_tx_empty
bool can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox)
Determines whether a frame can be transmitted.
Definition: hal_can_lld.c:127
osalEventObjectInit
static void osalEventObjectInit(event_source_t *esp)
Initializes an event source object.
Definition: osal.h:737
canTryReceiveI
bool canTryReceiveI(CANDriver *canp, canmbx_t mailbox, CANRxFrame *crfp)
Can frame receive attempt.
Definition: hal_can.c:201
msg_t
int32_t msg_t
Definition: chearly.h:88
canReceiveTimeout
msg_t canReceiveTimeout(CANDriver *canp, canmbx_t mailbox, CANRxFrame *crfp, sysinterval_t timeout)
Can frame receive.
Definition: hal_can.c:309
CAN_TX_MAILBOXES
#define CAN_TX_MAILBOXES
Number of transmit mailboxes.
Definition: hal_can_lld.h:37
can_lld_sleep
void can_lld_sleep(CANDriver *canp)
Enters the sleep mode.
Definition: hal_can_lld.c:235
CANDriver::config
const CANConfig * config
Current configuration data.
Definition: hal_can_lld.h:158
CANDriver::wakeup_event
event_source_t wakeup_event
Exiting sleep state event.
Definition: hal_can_lld.h:201
can_lld_start
void can_lld_start(CANDriver *canp)
Configures and activates the CAN peripheral.
Definition: hal_can_lld.c:80
osalOsRescheduleS
void osalOsRescheduleS(void)
Checks if a reschedule is required and performs it.
Definition: osal.c:119
canInit
void canInit(void)
CAN Driver initialization.
Definition: hal_can.c:56
canObjectInit
void canObjectInit(CANDriver *canp)
Initializes the standard part of a CANDriver structure.
Definition: hal_can.c:68
canWakeup
void canWakeup(CANDriver *canp)
Enforces leaving the sleep mode.
Definition: hal_can.c:373
CAN_STARTING
@ CAN_STARTING
Definition: hal_can.h:105
CANDriver
Structure representing an CAN driver.
Definition: hal_can_lld.h:150
CAN_RX_MAILBOXES
#define CAN_RX_MAILBOXES
Number of receive mailboxes.
Definition: hal_can_lld.h:42
CANDriver::txqueue
threads_queue_t txqueue
Transmission threads queue.
Definition: hal_can_lld.h:162
CANDriver::state
canstate_t state
Driver state.
Definition: hal_can_lld.h:154
canStop
void canStop(CANDriver *canp)
Deactivates the CAN peripheral.
Definition: hal_can.c:131
canStart
void canStart(CANDriver *canp, const CANConfig *config)
Configures and activates the CAN peripheral.
Definition: hal_can.c:104
osalEventBroadcastFlagsI
void osalEventBroadcastFlagsI(event_source_t *esp, eventflags_t flags)
Add flags to an event source object.
Definition: osal.c:323
canTryAbortX
void canTryAbortX(CANDriver *canp, canmbx_t mailbox)
Tries to abort an ongoing transmission.
Definition: hal_can.c:230
CANDriver::error_event
event_source_t error_event
A CAN bus error happened.
Definition: hal_can_lld.h:192
CAN_STOPPING
@ CAN_STOPPING
Definition: hal_can.h:106
can_lld_stop
void can_lld_stop(CANDriver *canp)
Deactivates the CAN peripheral.
Definition: hal_can_lld.c:101
can_lld_init
void can_lld_init(void)
Low level CAN driver initialization.
Definition: hal_can_lld.c:65
MSG_OK
#define MSG_OK
Normal wakeup message.
Definition: chschd.h:39
osalThreadEnqueueTimeoutS
msg_t osalThreadEnqueueTimeoutS(threads_queue_t *tqp, sysinterval_t timeout)
Enqueues the caller thread.
Definition: osal.c:277
osalDbgCheck
#define osalDbgCheck(c)
Function parameters check.
Definition: osal.h:284
CAN_ANY_MAILBOX
#define CAN_ANY_MAILBOX
Special mailbox identifier.
Definition: hal_can.h:63
can_lld_wakeup
void can_lld_wakeup(CANDriver *canp)
Enforces leaving the sleep mode.
Definition: hal_can_lld.c:248
CANTxFrame
CAN transmission frame.
Definition: hal_can_lld.h:96
canTryTransmitI
bool canTryTransmitI(CANDriver *canp, canmbx_t mailbox, const CANTxFrame *ctfp)
Can frame transmission attempt.
Definition: hal_can.c:167
sysinterval_t
uint64_t sysinterval_t
Type of time interval.
Definition: chtime.h:119
osalSysLock
static void osalSysLock(void)
Enters a critical zone from thread context.
Definition: osal.h:601
CANDriver::sleep_event
event_source_t sleep_event
Entering sleep state event.
Definition: hal_can_lld.h:197
CANConfig
Driver configuration structure.
Definition: hal_can_lld.h:142
CANRxFrame
CAN received frame.
Definition: hal_can_lld.h:119
CANDriver::txempty_event
event_source_t txempty_event
One or more transmission mailbox become available.
Definition: hal_can_lld.h:186
CAN_READY
@ CAN_READY
Definition: hal_can.h:107
osalDbgAssert
#define osalDbgAssert(c, remark)
Condition assertion.
Definition: osal.h:264
canSleep
void canSleep(CANDriver *canp)
Enters the sleep mode.
Definition: hal_can.c:348
canmbx_t
uint32_t canmbx_t
Type of a transmission mailbox index.
Definition: hal_can_lld.h:78
can_lld_transmit
void can_lld_transmit(CANDriver *canp, canmbx_t mailbox, const CANTxFrame *ctfp)
Inserts a frame into the transmit queue.
Definition: hal_can_lld.c:154
CAN_STOP
@ CAN_STOP
Definition: hal_can.h:104
canTransmitTimeout
msg_t canTransmitTimeout(CANDriver *canp, canmbx_t mailbox, const CANTxFrame *ctfp, sysinterval_t timeout)
Can frame transmission.
Definition: hal_can.c:261
osalThreadQueueObjectInit
static void osalThreadQueueObjectInit(threads_queue_t *tqp)
Initializes a threads queue object.
Definition: osal.h:725
can_lld_receive
void can_lld_receive(CANDriver *canp, canmbx_t mailbox, CANRxFrame *crfp)
Receives a frame from the input queue.
Definition: hal_can_lld.c:202