Statistics
| Branch: | Tag: | Revision:

amiro-os / periphery-lld / periphAL.h @ b49ed442

History | View | Annotate | Download (20.536 KB)

1
/*
2
AMiRo-OS is an operating system designed for the Autonomous Mini Robot (AMiRo) platform.
3
Copyright (C) 2016..2019  Thomas Schöpping et al.
4

5
This program is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9

10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
GNU General Public License for more details.
14

15
You should have received a copy of the GNU General Public License
16
along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18

    
19
#ifndef AMIROOS_PERIPHAL_H
20
#define AMIROOS_PERIPHAL_H
21

    
22
#include <amiro-lld.h>
23

    
24
/*============================================================================*/
25
/* VERSION                                                                    */
26
/*============================================================================*/
27

    
28
/**
29
 * @brief   The periphery abstraction layer interface major version.
30
 * @note    Changes of the major version imply incompatibilities.
31
 */
32
#define PERIPHAL_VERSION_MAJOR    1
33

    
34
/**
35
 * @brief   The periphery abstraction layer interface minor version.
36
 * @note    A higher minor version implies new functionalty, but all old interfaces are still available.
37
 */
38
#define PERIPHAL_VERSION_MINOR    1
39

    
40
/*============================================================================*/
41
/* DEPENDENCIES                                                               */
42
/*============================================================================*/
43

    
44
#include <aosconf.h>
45
#include <hal.h>
46

    
47
/*============================================================================*/
48
/* DEBUG                                                                      */
49
/*============================================================================*/
50

    
51
#if (AMIROOS_CFG_DBG == true) || defined(__DOXYGEN__)
52

    
53
#if defined(__cplusplus)
54
extern "C" {
55
#endif /* defined(__cplusplus) */
56
  void _apalDbgAssertMsg(const bool c, const char* fmt, ...);
57
  void apalDbgPrintf(const char* fmt, ...);
58
#if defined(__cplusplus)
59
}
60
#endif /* defined(__cplusplus) */
61

    
62
/**
63
 * @brief Assert function to check a given condition.
64
 *
65
 * @param[in] c     The condition to check.
66
 */
67
#define apalDbgAssert(c)                                                      \
68
  _apalDbgAssertMsg(c, "%s(%u): apalDbgAssert failed", __FILE__, __LINE__);
69

    
70
#else /* (AMIROOS_CFG_DBG != true) */
71

    
72
#define apalDbgAssert(constition)
73
#define apalDbgAssertMsg(condition, fmt, ...)
74
#define apalDbgPrintf(fmt, ...)
75

    
76
#endif /* (AMIROOS_CFG_DBG == true) */
77

    
78
/*============================================================================*/
79
/* GENERAL                                                                    */
80
/*============================================================================*/
81

    
82
/**
83
 * @brief Delay execution by a specific number of microseconds.
84
 *
85
 * @param[in]   us    Time to sleep until execution continues in microseconds.
86
 */
87
static inline void usleep(apalTime_t us)
88
{
89
  // check if the specified time can be represented by the system
90
  apalDbgAssert(us <= chTimeI2US(TIME_INFINITE));
91

    
92
  const sysinterval_t interval = chTimeUS2I(us);
93
  // TIME_IMMEDIATE makes no sense and would even cause system halt
94
  if (interval != TIME_IMMEDIATE) {
95
    chThdSleep(interval);
96
  }
97
  return;
98
}
99

    
100
/*============================================================================*/
101
/* GPIO                                                                       */
102
/*============================================================================*/
103

    
104
#if (HAL_USE_PAL == TRUE) || defined (__DOXYGEN__)
105

    
106
/**
107
 * @brief GPIO driver type.
108
 */
109
struct apalGpio_t {
110
  ioline_t line;
111
} PACKED_VAR;
112

    
113
/**
114
 * @brief Read the current value of a GPIO pin.
115
 *
116
 * @param[in]   gpio  GPIO to read.
117
 * @param[out]  val   Current value of the GPIO.
118
 *
119
 * @return The status indicates whether the function call was successful.
120
 */
121
static inline apalExitStatus_t apalGpioRead(apalGpio_t* gpio, apalGpioState_t* const val)
122
{
123
  apalDbgAssert(gpio != NULL);
124
  apalDbgAssert(val != NULL);
125

    
126
  *val = (palReadLine(gpio->line) == PAL_HIGH) ? APAL_GPIO_HIGH : APAL_GPIO_LOW;
127
  return APAL_STATUS_OK;
128
}
129

    
130
/**
131
 * @brief Set the value of a GPIO pin.
132
 *
133
 * @param[in] gpio  GPIO to write.
134
 * @param[in] val   Value to set for the GPIO.
135
 *
136
 * @return The status indicates whether the function call was successful.
137
 */
138
static inline apalExitStatus_t apalGpioWrite(apalGpio_t* gpio, const apalGpioState_t val)
139
{
140
  apalDbgAssert(gpio != NULL);
141

    
142
  // palWriteLine() is not guaranteed to be atomic, thus the scheduler is locked.
143
  syssts_t sysstatus = chSysGetStatusAndLockX();
144
  palWriteLine(gpio->line, (val == APAL_GPIO_HIGH) ? PAL_HIGH : PAL_LOW);
145
  chSysRestoreStatusX(sysstatus);
146
  return APAL_STATUS_OK;
147
}
148

    
149
/**
150
 * @brief Toggle the output of a GPIO.
151
 *
152
 * @param[in] gpio  GPIO to toggle.
153
 *
154
 * @return The status indicates whether the function call was successful.
155
 */
156
static inline apalExitStatus_t apalGpioToggle(apalGpio_t* gpio)
157
{
158
  apalDbgAssert(gpio != NULL);
159

    
160
  // palWriteLine() is not guaranteed to be atomic, thus the scheduler is locked.
161
  syssts_t sysstatus = chSysGetStatusAndLockX();
162
  palWriteLine(gpio->line, (palReadLine(gpio->line) == PAL_HIGH) ? PAL_LOW : PAL_HIGH);
163
  chSysRestoreStatusX(sysstatus);
164
  return APAL_STATUS_OK;
165
}
166

    
167
/**
168
 * @brief Get the current on/off state of a control GPIO.
169
 *
170
 * @param[in]   gpio  Control GPIO to read.
171
 * @param[out]  val   Current activation status of the control GPIO.
172
 *
173
 * @return The status indicates whether the function call was successful.
174
 */
175
static inline apalExitStatus_t apalControlGpioGet(const apalControlGpio_t* const cgpio, apalControlGpioState_t* const val)
176
{
177
  apalDbgAssert(cgpio != NULL);
178
  apalDbgAssert(cgpio->gpio != NULL);
179
  apalDbgAssert(val != NULL);
180

    
181
  *val = ((palReadLine(cgpio->gpio->line) == PAL_HIGH) ^ (cgpio->meta.active == APAL_GPIO_ACTIVE_HIGH)) ? APAL_GPIO_OFF : APAL_GPIO_ON;
182
  return APAL_STATUS_OK;
183
}
184

    
185
/**
186
 * @brief Turn a control GPIO 'on' or 'off' respectively.
187
 *
188
 * @param[in] gpio  Control GPIO to set.
189
 * @param[in] val   Activation value to set for the control GPIO.
190
 *
191
 * @return The status indicates whether the function call was successful.
192
 */
193
static inline apalExitStatus_t apalControlGpioSet(const apalControlGpio_t* const cgpio, const apalControlGpioState_t val)
194
{
195
  apalDbgAssert(cgpio != NULL);
196
  apalDbgAssert(cgpio->gpio != NULL);
197
  apalDbgAssert(cgpio->meta.direction == APAL_GPIO_DIRECTION_OUTPUT || cgpio->meta.direction == APAL_GPIO_DIRECTION_BIDIRECTIONAL);
198

    
199
  // palWriteLine() is not guaranteed to be atomic, thus the scheduler is locked.
200
  syssts_t sysstatus = chSysGetStatusAndLockX();
201
  palWriteLine(cgpio->gpio->line, ((cgpio->meta.active == APAL_GPIO_ACTIVE_HIGH) ^ (val == APAL_GPIO_ON)) ? PAL_LOW : PAL_HIGH);
202
  chSysRestoreStatusX(sysstatus);
203
  return APAL_STATUS_OK;
204
}
205

    
206
/**
207
 * @brief   Converts an apalGpioEdge_t to an ChibiOS PAL edge.
208
 */
209
#define APAL2CH_EDGE(edge)                                            \
210
  ((edge == APAL_GPIO_EDGE_RISING) ? PAL_EVENT_MODE_RISING_EDGE :     \
211
    (edge == APAL_GPIO_EDGE_FALLING) ? PAL_EVENT_MODE_FALLING_EDGE :  \
212
     (edge == APAL_GPIO_EDGE_BOTH) ? PAL_EVENT_MODE_BOTH_EDGES :      \
213
      PAL_EVENT_MODE_DISABLED)
214

    
215
#endif /* (HAL_USE_PAL == TRUE) */
216

    
217
/*============================================================================*/
218
/* PWM                                                                        */
219
/*============================================================================*/
220

    
221
#if (HAL_USE_PWM == TRUE) || defined (__DOXYGEN__)
222

    
223
/**
224
 * @brief PWM driver type.
225
 */
226
typedef PWMDriver apalPWMDriver_t;
227

    
228
/**
229
 * @brief   Set the PWM with given parameters.
230
 *
231
 * @param[in] pwm       PWM driver to set.
232
 * @param[in] channel   Channel of the PWM driver to set.
233
 * @param[in] width     Width to set the channel to.
234
 *
235
 * @return  The status indicates whether the function call was successful.
236
 */
237
static inline apalExitStatus_t apalPWMSet(apalPWMDriver_t* pwm, const apalPWMchannel_t channel, const apalPWMwidth_t width)
238
{
239
  apalDbgAssert(pwm != NULL);
240

    
241
  pwmEnableChannel(pwm, (pwmchannel_t)channel, pwm->period * ((float)width / (float)APAL_PWM_WIDTH_MAX) + 0.5f);
242
  return APAL_STATUS_OK;
243
}
244

    
245
/**
246
 * @brief   Retrieve the current frequency of the PWM.
247
 *
248
 * @param[in]  pwm        PWM driver to read.
249
 * @param[out] frequency  The currently set frequency.
250
 *
251
 * @return  The status indicates whether the function call was successful.
252
 */
253
static inline apalExitStatus_t apalPWMGetFrequency(apalPWMDriver_t* pwm, apalPWMfrequency_t* const frequency)
254
{
255
  apalDbgAssert(pwm != NULL);
256
  apalDbgAssert(frequency != NULL);
257

    
258
  *frequency = pwm->config->frequency;
259
  return APAL_STATUS_OK;
260
}
261

    
262
/**
263
 * @brief   Retrieve the current period of the PWM.
264
 *
265
 * @param[in]   pwm     PWM driver to read.
266
 * @param[out]  period  The currently set period.
267
 *
268
 * @return  The status indicates whether the function call was successful.
269
 */
270
static inline apalExitStatus_t apalPWMGetPeriod(apalPWMDriver_t* pwm, apalPWMperiod_t* const period)
271
{
272
  apalDbgAssert(pwm != NULL);
273
  apalDbgAssert(period != NULL);
274

    
275
  *period = pwm->period;
276
  return APAL_STATUS_OK;
277
}
278

    
279
#endif /* (HAL_USE_PWM == TRUE) */
280

    
281
/*============================================================================*/
282
/* QEI                                                                        */
283
/*============================================================================*/
284

    
285
#if (HAL_USE_QEI == TRUE) || defined (__DOXYGEN__)
286

    
287
/**
288
 * @brief QEI driver type.
289
 */
290
typedef QEIDriver apalQEIDriver_t;
291

    
292
/**
293
 * @brief Gets the direction of the last transition.
294
 *
295
 * @param[in]   qei         The QEI driver to use.
296
 * @param[out]  direction   The direction of the last transition.
297
 *
298
 * @return The status indicates whether the function call was successful.
299
 */
300
static inline apalExitStatus_t apalQEIGetDirection(apalQEIDriver_t* qei, apalQEIDirection_t* const direction)
301
{
302
  apalDbgAssert(qei != NULL);
303
  apalDbgAssert(direction != NULL);
304

    
305
  *direction = (qei_lld_get_direction(qei)) ? APAL_QEI_DIRECTION_DOWN : APAL_QEI_DIRECTION_UP;
306

    
307
  return APAL_STATUS_OK;
308
}
309

    
310
/**
311
 * @brief Gets the current position of the ecnoder.
312
 *
313
 * @param[in]   qei       The QEI driver to use.
314
 * @param[out]  position  The current position of the encoder.
315
 *
316
 * @return The status indicates whether the function call was successful.
317
 */
318
static inline apalExitStatus_t apalQEIGetPosition(apalQEIDriver_t* qei, apalQEICount_t* const position)
319
{
320
  apalDbgAssert(qei != NULL);
321
  apalDbgAssert(position != NULL);
322

    
323
  *position = qei_lld_get_position(qei);
324

    
325
  return APAL_STATUS_OK;
326
}
327

    
328
/**
329
 * @brief Gets the value range of the encoder.
330
 *
331
 * @param[in]   qei     The QEI driver to use.
332
 * @param[out]  range   The value range of the encoder.
333
 *
334
 * @return The status indicates whether the function call was successful.
335
 */
336
static inline apalExitStatus_t apalQEIGetRange(apalQEIDriver_t* qei, apalQEICount_t* const range)
337
{
338
  apalDbgAssert(qei != NULL);
339
  apalDbgAssert(range != NULL);
340

    
341
  *range = qei_lld_get_range(qei);
342

    
343
  return APAL_STATUS_OK;
344
}
345

    
346
#endif /* (HAL_USE_QEI == TRUE) */
347

    
348
/*============================================================================*/
349
/* I2C                                                                        */
350
/*============================================================================*/
351

    
352
#if (HAL_USE_I2C == TRUE) || defined(__DOXYGEN__)
353

    
354
/**
355
 * @brief I2C driver type.
356
 */
357
typedef I2CDriver apalI2CDriver_t;
358

    
359
/**
360
 * @brief Transmit data and receive a response.
361
 *
362
 * @param[in]   i2cd      The I2C driver to use.
363
 * @param[in]   addr      Address to write to.
364
 * @param[in]   txbuf     Buffer containing data to send.
365
 * @param[in]   txbytes   Number of bytes to send.
366
 * @param[out]  rxbuf     Buffer to store a response to.
367
 * @param[in]   rxbytes   Number of bytes to receive.
368
 * @param[in]   timeout   Timeout for the function to return (in microseconds).
369
 *
370
 * @return The status indicates whether the function call was succesful or a timeout occurred.
371
 */
372
static inline apalExitStatus_t apalI2CMasterTransmit(apalI2CDriver_t* i2cd, const apalI2Caddr_t addr, const uint8_t* const txbuf, const size_t txbytes, uint8_t* const rxbuf, const size_t rxbytes, const apalTime_t timeout)
373
{
374
  apalDbgAssert(i2cd != NULL);
375

    
376
#if (I2C_USE_MUTUAL_EXCLUSION == TRUE)
377
  // check whether the I2C driver was locked externally
378
  const bool i2cd_locked_external = i2cd->mutex.owner == currp;
379
  if (!i2cd_locked_external) {
380
    i2cAcquireBus(i2cd);
381
  }
382
#endif /* (I2C_USE_MUTUAL_EXCLUSION == TRUE) */
383

    
384
#pragma GCC diagnostic push
385
#pragma GCC diagnostic ignored "-Wtype-limits"
386
#if defined(STM32F1XX_I2C)
387
  // Due to a hardware limitation, for STM32F1 platform the minimum number of bytes that can be received is two.
388
  msg_t status = MSG_OK;
389
  if (rxbytes == 1) {
390
    uint8_t buffer[2];
391
    status = i2cMasterTransmitTimeout(i2cd, addr, txbuf, txbytes, buffer, 2, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : TIME_US2I(timeout)) );
392
    rxbuf[0] = buffer[0];
393
  } else {
394
    status = i2cMasterTransmitTimeout(i2cd, addr, txbuf, txbytes, rxbuf, rxbytes, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : TIME_US2I(timeout)) );
395
  }
396
#else /* defined(STM32F1XX_I2C) */
397
  const msg_t status = i2cMasterTransmitTimeout(i2cd, addr, txbuf, txbytes, rxbuf, rxbytes, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : TIME_US2I(timeout)) );
398
#endif /* defined(STM32F1XX_I2C) */
399
#pragma GCC diagnostic pop
400

    
401
#if (I2C_USE_MUTUAL_EXCLUSION == TRUE)
402
  if (!i2cd_locked_external) {
403
    i2cReleaseBus(i2cd);
404
  }
405
#endif /* (I2C_USE_MUTUAL_EXCLUSION == TRUE) */
406

    
407
  switch (status)
408
  {
409
    case MSG_OK:
410
#if defined(STM32F1XX_I2C)
411
      return (rxbytes != 1) ? APAL_STATUS_OK : APAL_STATUS_WARNING;
412
#else /* defined(STM32F1XX_I2C) */
413
      return APAL_STATUS_OK;
414
#endif /* defined(STM32F1XX_I2C) */
415
    case MSG_TIMEOUT:
416
      return APAL_STATUS_TIMEOUT;
417
    case MSG_RESET:
418
    default:
419
      return APAL_STATUS_ERROR;
420
  }
421
}
422

    
423
/**
424
 * @brief Read data from a specific address.
425
 *
426
 * @param[in]   i2cd      The I2C driver to use.
427
 * @param[in]   addr      Address to read.
428
 * @param[out]  rxbuf     Buffer to store the response to.
429
 * @param[in]   rxbytes   Number of bytes to receive.
430
 * @param[in]   timeout   Timeout for the function to return (in microseconds).
431
 *
432
 * @return The status indicates whether the function call was succesful or a timeout occurred.
433
 */
434
static inline apalExitStatus_t apalI2CMasterReceive(apalI2CDriver_t* i2cd, const apalI2Caddr_t addr, uint8_t* const rxbuf, const size_t rxbytes, const apalTime_t timeout)
435
{
436
  apalDbgAssert(i2cd != NULL);
437

    
438
#if (I2C_USE_MUTUAL_EXCLUSION == TRUE)
439
  // check whether the I2C driver was locked externally
440
  const bool i2cd_locked_external = i2cd->mutex.owner == currp;
441
  if (!i2cd_locked_external) {
442
    i2cAcquireBus(i2cd);
443
  }
444
#endif /* (I2C_USE_MUTUAL_EXCLUSION == TRUE) */
445

    
446
#pragma GCC diagnostic push
447
#pragma GCC diagnostic ignored "-Wtype-limits"
448
#if defined(STM32F1XX_I2C)
449
  // Due to a hardware limitation, for STM32F1 platform the minimum number of bytes that can be received is two.
450
  msg_t status = MSG_OK;
451
  if (rxbytes == 1) {
452
    uint8_t buffer[2];
453
    status = i2cMasterReceiveTimeout(i2cd, addr, buffer, 2, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : TIME_US2I(timeout)) );
454
    rxbuf[0] = buffer[0];
455
  } else {
456
    status = i2cMasterReceiveTimeout(i2cd, addr, rxbuf, rxbytes, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : TIME_US2I(timeout)) );
457
  }
458
#else /* defined(STM32F1XX_I2C) */
459
  const msg_t status = i2cMasterReceiveTimeout(i2cd, addr, rxbuf, rxbytes, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : TIME_US2I(timeout)) );
460
#endif /* defined(STM32F1XX_I2C) */
461
#pragma GCC diagnostic pop
462

    
463
#if (I2C_USE_MUTUAL_EXCLUSION == TRUE)
464
  if (!i2cd_locked_external) {
465
    i2cReleaseBus(i2cd);
466
  }
467
#endif /* (I2C_USE_MUTUAL_EXCLUSION == TRUE) */
468

    
469
  switch (status)
470
  {
471
    case MSG_OK:
472
#if defined(STM32F1XX_I2C)
473
      return (rxbytes != 1) ? APAL_STATUS_OK : APAL_STATUS_WARNING;
474
#else /* defined(STM32F1XX_I2C) */
475
      return APAL_STATUS_OK;
476
#endif /* defined(STM32F1XX_I2C) */
477
    case MSG_TIMEOUT:
478
      return APAL_STATUS_TIMEOUT;
479
    case MSG_RESET:
480
    default:
481
      return APAL_STATUS_ERROR;
482
  }
483
}
484

    
485
#endif /* (HAL_USE_I2C == TRUE) */
486

    
487
/*============================================================================*/
488
/* SPI                                                                        */
489
/*============================================================================*/
490

    
491
#if (HAL_USE_SPI == TRUE) || defined(__DOXYGEN__)
492

    
493
/**
494
 * @brief SPI driver type.
495
 */
496
typedef SPIDriver apalSPIDriver_t;
497

    
498
/**
499
 * @brief Transmit and receive data from SPI
500
 *
501
 * @param[in]   spid      The SPI driver to use.
502
 * @param[in]   txData    Buffer containing data to send.
503
 * @param[out]  rxData    Buffer to store.
504
 * @param[in]   length    Number of bytes to send.
505
 *
506
 * @return The status indicates whether the function call was succesful.
507
 */
508
static inline apalExitStatus_t apalSPIExchange(apalSPIDriver_t* spid, const uint8_t* const txData , uint8_t* const rxData, const size_t length)
509
{
510
  apalDbgAssert(spid != NULL);
511

    
512
#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)
513
  // check whether the SPI driver was locked externally
514
  const bool spid_locked_external = spid->mutex.owner == currp;
515
  if (!spid_locked_external) {
516
    spiAcquireBus(spid);
517
  }
518
#endif /* (SPI_USE_MUTUAL_EXCLUSION == TRUE) */
519

    
520
  spiSelect(spid);
521
  spiExchange(spid, length, txData, rxData);
522
  spiUnselect(spid);
523

    
524
#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)
525
  if (!spid_locked_external) {
526
    spiReleaseBus(spid);
527
  }
528
#endif /* (SPI_USE_MUTUAL_EXCLUSION == TRUE) */
529

    
530
  return APAL_STATUS_OK;
531
}
532

    
533
/**
534
 * @brief Receive data from SPI
535
 *
536
 * @param[in]   spid      The SPI driver to use.
537
 * @param[out]  data      Buffer to store.
538
 * @param[in]   length    Number of bytes to send.
539
 *
540
 * @return The status indicates whether the function call was succesful.
541
 */
542
static inline apalExitStatus_t apalSPIReceive(apalSPIDriver_t* spid, uint8_t* const data, const size_t length)
543
{
544
  apalDbgAssert(spid != NULL);
545

    
546
#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)
547
  // check whether the SPI driver was locked externally
548
  const bool spid_locked_external = spid->mutex.owner == currp;
549
  if (!spid_locked_external) {
550
    spiAcquireBus(spid);
551
  }
552
#endif /* (SPI_USE_MUTUAL_EXCLUSION == TRUE) */
553

    
554
  spiSelect(spid);
555
  spiReceive(spid, length, data);
556
  spiUnselect(spid);
557

    
558
#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)
559
  if (!spid_locked_external) {
560
    spiReleaseBus(spid);
561
  }
562
#endif /* (SPI_USE_MUTUAL_EXCLUSION == TRUE) */
563

    
564
  return APAL_STATUS_OK;
565
}
566

    
567
/**
568
 * @brief Transmit data to SPI
569
 *
570
 * @param[in]   spid      The SPI driver to use.
571
 * @param[in]   data      Buffer containing data to send.
572
 * @param[in]   length    Number of bytes to send.
573
 *
574
 * @return The status indicates whether the function call was succesful.
575
 */
576
static inline apalExitStatus_t apalSPITransmit(apalSPIDriver_t* spid, const uint8_t* const data, const size_t length)
577
{
578
  apalDbgAssert(spid != NULL);
579

    
580
#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)
581
  // check whether the SPI driver was locked externally
582
  const bool spid_locked_external = spid->mutex.owner == currp;
583
  if (!spid_locked_external) {
584
    spiAcquireBus(spid);
585
  }
586
#endif /* (SPI_USE_MUTUAL_EXCLUSION == TRUE) */
587

    
588
  spiSelect(spid);
589
  spiSend(spid, length, data);
590
  spiUnselect(spid);
591

    
592
#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)
593
  if (!spid_locked_external) {
594
    spiReleaseBus(spid);
595
  }
596
#endif /* (SPI_USE_MUTUAL_EXCLUSION == TRUE) */
597

    
598
  return APAL_STATUS_OK;
599
}
600

    
601
/**
602
 * @brief Transmit data to SPI and receive data afterwards without releasing the bus in between.
603
 *
604
 * @param   spid        The SPI driver to use.
605
 * @param   txData      Transmit data buffer.
606
 * @param   rxData      Receive data buffer.
607
 * @param   txLength    Number of bytes to send.
608
 * @param   rxLength    Number of bytes to receive.
609
 *
610
 * @return The status indicates whether the function call was succesful.
611
 */
612
static inline apalExitStatus_t apalSPITransmitAndReceive(apalSPIDriver_t* spid, const uint8_t* const txData , uint8_t* const rxData, const size_t txLength, const size_t rxLength)
613
{
614
  apalDbgAssert(spid != NULL);
615

    
616
#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)
617
  // check whether the SPI driver was locked externally
618
  const bool spid_locked_external = spid->mutex.owner == currp;
619
  if (!spid_locked_external) {
620
    spiAcquireBus(spid);
621
  }
622
#endif /* (SPI_USE_MUTUAL_EXCLUSION == TRUE) */
623

    
624
  spiSelect(spid);
625
  spiSend(spid, txLength, txData);
626
  spiReceive(spid, rxLength, rxData);
627
  spiUnselect(spid);
628

    
629
#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)
630
  if (!spid_locked_external) {
631
    spiReleaseBus(spid);
632
  }
633
#endif /* (SPI_USE_MUTUAL_EXCLUSION == TRUE) */
634

    
635
  return APAL_STATUS_OK;
636
}
637

    
638
#endif /* (HAL_USE_SPI == TRUE) */
639

    
640
#endif /* AMIROOS_PERIPHAL_H */