ChibiOS/HAL  6.1.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  can_lld_stop(canp);
141  canp->config = NULL;
142  canp->state = CAN_STOP;
143 
144  /* Threads waiting on CAN APIs are notified that the driver has been
145  stopped in order to not have stuck threads.*/
146  osalThreadDequeueAllI(&canp->rxqueue, MSG_RESET);
147  osalThreadDequeueAllI(&canp->txqueue, MSG_RESET);
149  osalSysUnlock();
150 }
151 
152 /**
153  * @brief Can frame transmission attempt.
154  * @details The specified frame is queued for transmission, if the hardware
155  * queue is full then the function fails.
156  *
157  * @param[in] canp pointer to the @p CANDriver object
158  * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
159  * @param[in] ctfp pointer to the CAN frame to be transmitted
160  * @return The operation result.
161  * @retval false Frame transmitted.
162  * @retval true Mailbox full.
163  *
164  * @iclass
165  */
167  canmbx_t mailbox,
168  const CANTxFrame *ctfp) {
169 
171  osalDbgCheck((canp != NULL) && (ctfp != NULL) &&
172  (mailbox <= (canmbx_t)CAN_TX_MAILBOXES));
173  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
174  "invalid state");
175 
176  /* If the RX mailbox is full then the function fails.*/
177  if (!can_lld_is_tx_empty(canp, mailbox)) {
178  return true;
179  }
180 
181  /* Transmitting frame.*/
182  can_lld_transmit(canp, mailbox, ctfp);
183 
184  return false;
185 }
186 
187 /**
188  * @brief Can frame receive attempt.
189  * @details The function tries to fetch a frame from a mailbox.
190  *
191  * @param[in] canp pointer to the @p CANDriver object
192  * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
193  * @param[out] crfp pointer to the buffer where the CAN frame is copied
194  * @return The operation result.
195  * @retval false Frame fetched.
196  * @retval true Mailbox empty.
197  *
198  * @iclass
199  */
201  canmbx_t mailbox,
202  CANRxFrame *crfp) {
203 
205  osalDbgCheck((canp != NULL) && (crfp != NULL) &&
206  (mailbox <= (canmbx_t)CAN_RX_MAILBOXES));
207  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
208  "invalid state");
209 
210  /* If the RX mailbox is empty then the function fails.*/
211  if (!can_lld_is_rx_nonempty(canp, mailbox)) {
212  return true;
213  }
214 
215  /* Fetching the frame.*/
216  can_lld_receive(canp, mailbox, crfp);
217 
218  return false;
219 }
220 
221 /**
222  * @brief Can frame transmission.
223  * @details The specified frame is queued for transmission, if the hardware
224  * queue is full then the invoking thread is queued.
225  * @note Trying to transmit while in sleep mode simply enqueues the thread.
226  *
227  * @param[in] canp pointer to the @p CANDriver object
228  * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
229  * @param[in] ctfp pointer to the CAN frame to be transmitted
230  * @param[in] timeout the number of ticks before the operation timeouts,
231  * the following special values are allowed:
232  * - @a TIME_IMMEDIATE immediate timeout.
233  * - @a TIME_INFINITE no timeout.
234  * .
235  * @return The operation result.
236  * @retval MSG_OK the frame has been queued for transmission.
237  * @retval MSG_TIMEOUT The operation has timed out.
238  * @retval MSG_RESET The driver has been stopped while waiting.
239  *
240  * @api
241  */
243  canmbx_t mailbox,
244  const CANTxFrame *ctfp,
245  sysinterval_t timeout) {
246 
247  osalDbgCheck((canp != NULL) && (ctfp != NULL) &&
248  (mailbox <= (canmbx_t)CAN_TX_MAILBOXES));
249 
250  osalSysLock();
251  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
252  "invalid state");
253 
254  /*lint -save -e9007 [13.5] Right side is supposed to be pure.*/
255  while ((canp->state == CAN_SLEEP) || !can_lld_is_tx_empty(canp, mailbox)) {
256  /*lint -restore*/
257  msg_t msg = osalThreadEnqueueTimeoutS(&canp->txqueue, timeout);
258  if (msg != MSG_OK) {
259  osalSysUnlock();
260  return msg;
261  }
262  }
263  can_lld_transmit(canp, mailbox, ctfp);
264  osalSysUnlock();
265  return MSG_OK;
266 }
267 
268 /**
269  * @brief Can frame receive.
270  * @details The function waits until a frame is received.
271  * @note Trying to receive while in sleep mode simply enqueues the thread.
272  *
273  * @param[in] canp pointer to the @p CANDriver object
274  * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
275  * @param[out] crfp pointer to the buffer where the CAN frame is copied
276  * @param[in] timeout the number of ticks before the operation timeouts,
277  * the following special values are allowed:
278  * - @a TIME_IMMEDIATE immediate timeout (useful in an
279  * event driven scenario where a thread never blocks
280  * for I/O).
281  * - @a TIME_INFINITE no timeout.
282  * .
283  * @return The operation result.
284  * @retval MSG_OK a frame has been received and placed in the buffer.
285  * @retval MSG_TIMEOUT The operation has timed out.
286  * @retval MSG_RESET The driver has been stopped while waiting.
287  *
288  * @api
289  */
291  canmbx_t mailbox,
292  CANRxFrame *crfp,
293  sysinterval_t timeout) {
294 
295  osalDbgCheck((canp != NULL) && (crfp != NULL) &&
296  (mailbox <= (canmbx_t)CAN_RX_MAILBOXES));
297 
298  osalSysLock();
299  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
300  "invalid state");
301 
302  /*lint -save -e9007 [13.5] Right side is supposed to be pure.*/
303  while ((canp->state == CAN_SLEEP) || !can_lld_is_rx_nonempty(canp, mailbox)) {
304  /*lint -restore*/
305  msg_t msg = osalThreadEnqueueTimeoutS(&canp->rxqueue, timeout);
306  if (msg != MSG_OK) {
307  osalSysUnlock();
308  return msg;
309  }
310  }
311  can_lld_receive(canp, mailbox, crfp);
312  osalSysUnlock();
313  return MSG_OK;
314 }
315 
316 #if (CAN_USE_SLEEP_MODE == TRUE) || defined(__DOXYGEN__)
317 /**
318  * @brief Enters the sleep mode.
319  * @details This function puts the CAN driver in sleep mode and broadcasts
320  * the @p sleep_event event source.
321  * @pre In order to use this function the option @p CAN_USE_SLEEP_MODE must
322  * be enabled and the @p CAN_SUPPORTS_SLEEP mode must be supported
323  * by the low level driver.
324  *
325  * @param[in] canp pointer to the @p CANDriver object
326  *
327  * @api
328  */
329 void canSleep(CANDriver *canp) {
330 
331  osalDbgCheck(canp != NULL);
332 
333  osalSysLock();
334  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
335  "invalid state");
336  if (canp->state == CAN_READY) {
337  can_lld_sleep(canp);
338  canp->state = CAN_SLEEP;
339 #if CAN_ENFORCE_USE_CALLBACKS == FALSE
342 #endif
343  }
344  osalSysUnlock();
345 }
346 
347 /**
348  * @brief Enforces leaving the sleep mode.
349  * @note The sleep mode is supposed to be usually exited automatically by
350  * an hardware event.
351  *
352  * @param[in] canp pointer to the @p CANDriver object
353  */
354 void canWakeup(CANDriver *canp) {
355 
356  osalDbgCheck(canp != NULL);
357 
358  osalSysLock();
359  osalDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
360  "invalid state");
361  if (canp->state == CAN_SLEEP) {
362  can_lld_wakeup(canp);
363  canp->state = CAN_READY;
364 #if CAN_ENFORCE_USE_CALLBACKS == FALSE
367 #endif
368  }
369  osalSysUnlock();
370 }
371 #endif /* CAN_USE_SLEEP_MODE == TRUE */
372 
373 #endif /* HAL_USE_CAN == TRUE */
374 
375 /** @} */
event_source_t wakeup_event
Exiting sleep state event.
Definition: hal_can_lld.h:201
uint32_t canmbx_t
Type of a transmission mailbox index.
Definition: hal_can_lld.h:78
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
uint32_t eventflags_t
Type of an event flags mask.
Definition: osal.h:185
void osalThreadDequeueAllI(threads_queue_t *tqp, msg_t msg)
Dequeues and wakes up all threads from the queue.
Definition: osal.c:309
CAN transmission frame.
Definition: hal_can_lld.h:96
void canStart(CANDriver *canp, const CANConfig *config)
Configures and activates the CAN peripheral.
Definition: hal_can.c:104
event_source_t txempty_event
One or more transmission mailbox become available.
Definition: hal_can_lld.h:186
#define CAN_TX_MAILBOXES
Number of transmit mailboxes.
Definition: hal_can_lld.h:37
CAN received frame.
Definition: hal_can_lld.h:119
HAL subsystem header.
event_source_t error_event
A CAN bus error happened.
Definition: hal_can_lld.h:192
#define osalDbgCheckClassI()
I-Class state check.
Definition: osal.h:292
void can_lld_stop(CANDriver *canp)
Deactivates the CAN peripheral.
Definition: hal_can_lld.c:101
static void osalSysUnlock(void)
Leaves a critical zone from thread context.
Definition: osal.h:540
canstate_t state
Driver state.
Definition: hal_can_lld.h:154
Structure representing an CAN driver.
Definition: hal_can_lld.h:150
void osalOsRescheduleS(void)
Checks if a reschedule is required and performs it.
Definition: osal.c:119
static void osalEventObjectInit(event_source_t *esp)
Initializes an event source object.
Definition: osal.h:665
void can_lld_wakeup(CANDriver *canp)
Enforces leaving the sleep mode.
Definition: hal_can_lld.c:233
msg_t canTransmitTimeout(CANDriver *canp, canmbx_t mailbox, const CANTxFrame *ctfp, sysinterval_t timeout)
Can frame transmission.
Definition: hal_can.c:242
int32_t msg_t
Type of a message.
Definition: osal.h:160
void osalEventBroadcastFlagsI(event_source_t *esp, eventflags_t flags)
Add flags to an event source object.
Definition: osal.c:323
void can_lld_receive(CANDriver *canp, canmbx_t mailbox, CANRxFrame *crfp)
Receives a frame from the input queue.
Definition: hal_can_lld.c:202
const CANConfig * config
Current configuration data.
Definition: hal_can_lld.h:158
void canObjectInit(CANDriver *canp)
Initializes the standard part of a CANDriver structure.
Definition: hal_can.c:68
bool canTryReceiveI(CANDriver *canp, canmbx_t mailbox, CANRxFrame *crfp)
Can frame receive attempt.
Definition: hal_can.c:200
#define osalDbgCheck(c)
Function parameters check.
Definition: osal.h:278
void can_lld_sleep(CANDriver *canp)
Enters the sleep mode.
Definition: hal_can_lld.c:220
void canSleep(CANDriver *canp)
Enters the sleep mode.
Definition: hal_can.c:329
void canInit(void)
CAN Driver initialization.
Definition: hal_can.c:56
void canWakeup(CANDriver *canp)
Enforces leaving the sleep mode.
Definition: hal_can.c:354
uint32_t sysinterval_t
Type of system time interval.
Definition: osal.h:170
bool can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox)
Determines whether a frame has been received.
Definition: hal_can_lld.c:176
void can_lld_start(CANDriver *canp)
Configures and activates the CAN peripheral.
Definition: hal_can_lld.c:80
msg_t osalThreadEnqueueTimeoutS(threads_queue_t *tqp, sysinterval_t timeout)
Enqueues the caller thread.
Definition: osal.c:277
void canStop(CANDriver *canp)
Deactivates the CAN peripheral.
Definition: hal_can.c:131
bool can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox)
Determines whether a frame can be transmitted.
Definition: hal_can_lld.c:127
void can_lld_init(void)
Low level CAN driver initialization.
Definition: hal_can_lld.c:65
static void osalSysLock(void)
Enters a critical zone from thread context.
Definition: osal.h:530
Driver configuration structure.
Definition: hal_can_lld.h:142
threads_queue_t rxqueue
Receive threads queue.
Definition: hal_can_lld.h:166
#define osalDbgAssert(c, remark)
Condition assertion.
Definition: osal.h:258
msg_t canReceiveTimeout(CANDriver *canp, canmbx_t mailbox, CANRxFrame *crfp, sysinterval_t timeout)
Can frame receive.
Definition: hal_can.c:290
bool canTryTransmitI(CANDriver *canp, canmbx_t mailbox, const CANTxFrame *ctfp)
Can frame transmission attempt.
Definition: hal_can.c:166
#define CAN_RX_MAILBOXES
Number of receive mailboxes.
Definition: hal_can_lld.h:42
static void osalThreadQueueObjectInit(threads_queue_t *tqp)
Initializes a threads queue object.
Definition: osal.h:653
event_source_t rxfull_event
One or more frames become available.
Definition: hal_can_lld.h:180
threads_queue_t txqueue
Transmission threads queue.
Definition: hal_can_lld.h:162
event_source_t sleep_event
Entering sleep state event.
Definition: hal_can_lld.h:197