Statistics
| Branch: | Tag: | Revision:

amiro-os / periphery-lld / periphAL.h @ 680d05e5

History | View | Annotate | Download (17.023 KB)

1 e545e620 Thomas Schöpping
/*
2
AMiRo-OS is an operating system designed for the Autonomous Mini Robot (AMiRo) platform.
3
Copyright (C) 2016..2018  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
/*============================================================================*/
23
/* VERSION                                                                    */
24
/*============================================================================*/
25
26
/**
27
 * @brief   The periphery abstraction layer interface major version.
28
 * @note    Changes of the major version imply incompatibilities.
29
 */
30
#define PERIPHAL_VERSION_MAJOR    1
31
32
/**
33
 * @brief   The periphery abstraction layer interface minor version.
34
 * @note    A higher minor version implies new functionalty, but all old interfaces are still available.
35
 */
36
#define PERIPHAL_VERSION_MINOR    0
37
38
/*============================================================================*/
39
/* DEPENDENCIES                                                               */
40
/*============================================================================*/
41
42
#include <periphALtypes.h>
43
44
#include <hal.h>
45
#include <hal_qei.h>
46
#include <ch.h>
47
#include <aos_debug.h>
48
49
/*============================================================================*/
50
/* GENERAL                                                                    */
51
/*============================================================================*/
52
53
/**
54
 * @brief Delay execution by a specific number of microseconds.
55
 *
56
 * @param[in]   us    Time to sleep until execution continues in microseconds.
57
 */
58
static inline void usleep(apalTime_t us)
59
{
60
  // check if the specified time can be represented by the system
61
  aosDbgCheck(us <= LL_ST2US(TIME_INFINITE));
62
63
  const systime_t st = LL_US2ST(us);
64
  // TIME_IMMEDIATE makes no sense and would even cause system halt
65
  if (st != TIME_IMMEDIATE) {
66
    chThdSleep(st);
67
  }
68
  return;
69
}
70
71
/*============================================================================*/
72
/* GPIO                                                                       */
73
/*============================================================================*/
74
75
#if HAL_USE_PAL || defined (__DOXYGEN__)
76
77
/**
78
 * @brief GPIO driver type.
79
 */
80
struct apalGpio_t {
81
  /*
82
   * Workaround, since GPIOv2 (STM32F4XX) uses a different type than GPIOv1 (STM32F1XX).
83
   */
84
  #if defined(STM32F4XX)
85
  stm32_gpio_t* port;
86
  #elif defined(STM32F1XX)
87
  GPIO_TypeDef* port;
88
  #else
89
  void* port;
90
  #endif
91
92
  uint8_t pad;
93
} PACKED_VAR;
94
95
/**
96
 * @brief Read the current value of a GPIO pin.
97
 *
98
 * @param[in]   gpio  GPIO to read.
99
 * @param[out]  val   Current value of the GPIO.
100
 *
101
 * @return The status indicates whether the function call was successful.
102
 */
103
static inline apalExitStatus_t apalGpioRead(apalGpio_t* gpio, apalGpioState_t* const val)
104
{
105
  aosDbgCheck(gpio != NULL);
106
  aosDbgCheck(val != NULL);
107
108
  *val = (palReadPad(gpio->port, gpio->pad) == PAL_HIGH) ? APAL_GPIO_HIGH : APAL_GPIO_LOW;
109
  return APAL_STATUS_OK;
110
}
111
112
/**
113
 * @brief Set the value of a GPIO pin.
114
 *
115
 * @param[in] gpio  GPIO to write.
116
 * @param[in] val   Value to set for the GPIO.
117
 *
118
 * @return The status indicates whether the function call was successful.
119
 */
120
static inline apalExitStatus_t apalGpioWrite(apalGpio_t* gpio, const apalGpioState_t val)
121
{
122
  aosDbgCheck(gpio != NULL);
123
124
  // palWritePad() is not guaranteed to be atomic, thus the scheduler is locked.
125
  syssts_t sysstatus = chSysGetStatusAndLockX();
126
  palWritePad(gpio->port, gpio->pad, (val == APAL_GPIO_HIGH) ? PAL_HIGH : PAL_LOW);
127
  chSysRestoreStatusX(sysstatus);
128
  return APAL_STATUS_OK;
129
}
130
131
/**
132
 * @brief Toggle the output of a GPIO.
133
 *
134
 * @param[in] gpio  GPIO to toggle.
135
 *
136
 * @return The status indicates whether the function call was successful.
137
 */
138
static inline apalExitStatus_t apalGpioToggle(apalGpio_t* gpio)
139
{
140
  aosDbgCheck(gpio != NULL);
141
142
  // palWritePad() is not guaranteed to be atomic, thus the scheduler is locked.
143
  syssts_t sysstatus = chSysGetStatusAndLockX();
144
  palWritePad(gpio->port, gpio->pad, (palReadPad(gpio->port, gpio->pad) == PAL_HIGH) ? PAL_LOW : PAL_HIGH);
145
  chSysRestoreStatusX(sysstatus);
146
  return APAL_STATUS_OK;
147
}
148
149
/**
150
 * @brief Get the current on/off state of a control GPIO.
151
 *
152
 * @param[in]   gpio  Control GPIO to read.
153
 * @param[out]  val   Current activation status of the control GPIO.
154
 *
155
 * @return The status indicates whether the function call was successful.
156
 */
157
static inline apalExitStatus_t apalControlGpioGet(const apalControlGpio_t* const cgpio, apalControlGpioState_t* const val)
158
{
159
  aosDbgCheck(cgpio != NULL);
160
  aosDbgCheck(cgpio->gpio != NULL);
161
  aosDbgCheck(val != NULL);
162
163
  *val = ((palReadPad(cgpio->gpio->port, cgpio->gpio->pad) == PAL_HIGH) ^ (cgpio->meta.active == APAL_GPIO_ACTIVE_HIGH)) ? APAL_GPIO_OFF : APAL_GPIO_ON;
164
  return APAL_STATUS_OK;
165
}
166
167
/**
168
 * @brief Turn a control GPIO 'on' or 'off' respectively.
169
 *
170
 * @param[in] gpio  Control GPIO to set.
171
 * @param[in] val   Activation value to set for the control GPIO.
172
 *
173
 * @return The status indicates whether the function call was successful.
174
 */
175
static inline apalExitStatus_t apalControlGpioSet(const apalControlGpio_t* const cgpio, const apalControlGpioState_t val)
176
{
177
  aosDbgCheck(cgpio != NULL);
178
  aosDbgCheck(cgpio->gpio != NULL);
179
  aosDbgCheck(cgpio->meta.direction == APAL_GPIO_DIRECTION_OUTPUT || cgpio->meta.direction == APAL_GPIO_DIRECTION_BIDIRECTIONAL);
180
181
  // palWritePad() is not guaranteed to be atomic, thus the scheduler is locked.
182
  syssts_t sysstatus = chSysGetStatusAndLockX();
183
  palWritePad(cgpio->gpio->port, cgpio->gpio->pad, ((cgpio->meta.active == APAL_GPIO_ACTIVE_HIGH) ^ (val == APAL_GPIO_ON)) ? PAL_LOW : PAL_HIGH);
184
  chSysRestoreStatusX(sysstatus);
185
  return APAL_STATUS_OK;
186
}
187
188
#endif
189
190
#if HAL_USE_EXT || defined(__DOXYGEN__)
191
192
/**
193
 * @brief   Converts an apalGpioEdge_t to an ChibiOS EXT edge.
194
 */
195
#define APAL2CH_EDGE(edge)                                        \
196
  ((edge == APAL_GPIO_EDGE_RISING) ? EXT_CH_MODE_RISING_EDGE :    \
197
    (edge == APAL_GPIO_EDGE_FALLING) ? EXT_CH_MODE_FALLING_EDGE : \
198
     (edge == APAL_GPIO_EDGE_BOTH) ? EXT_CH_MODE_BOTH_EDGES : 0)
199
200
#endif
201
202
/*============================================================================*/
203
/* PWM                                                                        */
204
/*============================================================================*/
205
206
#if HAL_USE_PWM || defined (__DOXYGEN__)
207
208
/**
209
 * @brief PWM driver type.
210
 */
211
typedef PWMDriver apalPWMDriver_t;
212
213
/**
214
 * @brief   Set the PWM with given parameters.
215
 *
216
 * @param[in] pwm       PWM driver to set.
217
 * @param[in] channel   Channel of the PWM driver to set.
218
 * @param[in] width     Width to set the channel to.
219
 *
220
 * @return  The status indicates whether the function call was successful.
221
 */
222
static inline apalExitStatus_t apalPWMSet(apalPWMDriver_t* pwm, const apalPWMchannel_t channel, const apalPWMwidth_t width)
223
{
224
  aosDbgCheck(pwm != NULL);
225
226
  pwmEnableChannel(pwm, (pwmchannel_t)channel, pwm->period * ((float)width / (float)APAL_PWM_WIDTH_MAX) + 0.5f);
227
  return APAL_STATUS_OK;
228
}
229
230
/**
231
 * @brief   Retrieve the current frequency of the PWM.
232
 *
233
 * @param[in]  pwm        PWM driver to read.
234
 * @param[out] frequency  The currently set frequency.
235
 *
236
 * @return  The status indicates whether the function call was successful.
237
 */
238
static inline apalExitStatus_t apalPWMGetFrequency(apalPWMDriver_t* pwm, apalPWMfrequency_t* const frequency)
239
{
240
  aosDbgCheck(pwm != NULL);
241
  aosDbgCheck(frequency != NULL);
242
243
  *frequency = pwm->config->frequency;
244
  return APAL_STATUS_OK;
245
}
246
247
/**
248
 * @brief   Retrieve the current period of the PWM.
249
 *
250
 * @param[in]   pwm     PWM driver to read.
251
 * @param[out]  period  The currently set period.
252
 *
253
 * @return  The status indicates whether the function call was successful.
254
 */
255
static inline apalExitStatus_t apalPWMGetPeriod(apalPWMDriver_t* pwm, apalPWMperiod_t* const period)
256
{
257
  aosDbgCheck(pwm != NULL);
258
  aosDbgCheck(period != NULL);
259
260
  *period = pwm->period;
261
  return APAL_STATUS_OK;
262
}
263
264
#endif
265
266
/*============================================================================*/
267
/* QEI                                                                        */
268
/*============================================================================*/
269
270
#if HAL_USE_QEI || defined (__DOXYGEN__)
271
272
/**
273
 * @brief QEI driver type.
274
 */
275
typedef QEIDriver apalQEIDriver_t;
276
277
/**
278
 * @brief Gets the direction of the last transition.
279
 *
280
 * @param[in]   qei         The QEI driver to use.
281
 * @param[out]  direction   The direction of the last transition.
282
 *
283
 * @return The status indicates whether the function call was successful.
284
 */
285
static inline apalExitStatus_t apalQEIGetDirection(apalQEIDriver_t* qei, apalQEIDirection_t* const direction)
286
{
287
  aosDbgCheck(qei != NULL);
288
  aosDbgCheck(direction != NULL);
289
290
  *direction = (qei_lld_get_direction(qei)) ? APAL_QEI_DIRECTION_DOWN : APAL_QEI_DIRECTION_UP;
291
292
  return APAL_STATUS_OK;
293
}
294
295
/**
296
 * @brief Gets the current position of the ecnoder.
297
 *
298
 * @param[in]   qei       The QEI driver to use.
299
 * @param[out]  position  The current position of the encoder.
300
 *
301
 * @return The status indicates whether the function call was successful.
302
 */
303
static inline apalExitStatus_t apalQEIGetPosition(apalQEIDriver_t* qei, apalQEICount_t* const position)
304
{
305
  aosDbgCheck(qei != NULL);
306
  aosDbgCheck(position != NULL);
307
308
  *position = qei_lld_get_position(qei);
309
310
  return APAL_STATUS_OK;
311
}
312
313
/**
314
 * @brief Gets the value range of the encoder.
315
 *
316
 * @param[in]   qei     The QEI driver to use.
317
 * @param[out]  range   The value range of the encoder.
318
 *
319
 * @return The status indicates whether the function call was successful.
320
 */
321
static inline apalExitStatus_t apalQEIGetRange(apalQEIDriver_t* qei, apalQEICount_t* const range)
322
{
323
  aosDbgCheck(qei != NULL);
324
  aosDbgCheck(range != NULL);
325
326
  *range = qei_lld_get_range(qei);
327
328
  return APAL_STATUS_OK;
329
}
330
331
#endif
332
333
/*============================================================================*/
334
/* I2C                                                                        */
335
/*============================================================================*/
336
337
#if HAL_USE_I2C || defined(__DOXYGEN__)
338
339
/**
340
 * @brief I2C driver type.
341
 */
342
typedef I2CDriver apalI2CDriver_t;
343
344
/**
345
 * @brief Transmit data and receive a response.
346
 *
347
 * @param[in]   i2cd      The I2C driver to use.
348
 * @param[in]   addr      Address to write to.
349
 * @param[in]   txbuf     Buffer containing data to send.
350
 * @param[in]   txbytes   Number of bytes to send.
351
 * @param[out]  rxbuf     Buffer to store a response to.
352
 * @param[in]   rxbytes   Number of bytes to receive.
353
 * @param[in]   timeout   Timeout for the function to return (in microseconds).
354
 *
355
 * @return The status indicates whether the function call was succesful or a timeout occurred.
356
 */
357
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)
358
{
359
  aosDbgCheck(i2cd != NULL);
360
361
#if (I2C_USE_MUTUAL_EXCLUSION == TRUE)
362
  i2cAcquireBus(i2cd);
363
#endif
364
365
#if defined(STM32F1XX_I2C)
366
  // Due to a hardware limitation, for STM32F1 platform the minimum number of bytes that can be received is two.
367
  msg_t status = MSG_OK;
368
  if (rxbytes == 1) {
369
    uint8_t buffer[2];
370
    status = i2cMasterTransmitTimeout(i2cd, addr, txbuf, txbytes, buffer, 2, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : LL_US2ST(timeout)) );
371
    rxbuf[0] = buffer[0];
372
  } else {
373
    status = i2cMasterTransmitTimeout(i2cd, addr, txbuf, txbytes, rxbuf, rxbytes, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : LL_US2ST(timeout)) );
374
  }
375
#else
376
  const msg_t status = i2cMasterTransmitTimeout(i2cd, addr, txbuf, txbytes, rxbuf, rxbytes, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : LL_US2ST(timeout)) );
377
#endif
378
379
#if (I2C_USE_MUTUAL_EXCLUSION == TRUE)
380
  i2cReleaseBus(i2cd);
381
#endif
382
383
  switch (status)
384
  {
385
    case MSG_OK:
386
#if defined(STM32F1XX_I2C)
387
      return (rxbytes != 1) ? APAL_STATUS_OK : APAL_STATUS_WARNING;
388
#else
389
      return APAL_STATUS_OK;
390
#endif
391
    case MSG_TIMEOUT:
392
      return APAL_STATUS_TIMEOUT;
393
    case MSG_RESET:
394
    default:
395
      return APAL_STATUS_ERROR;
396
  }
397
}
398
399
/**
400
 * @brief Read data from a specific address.
401
 *
402
 * @param[in]   i2cd      The I2C driver to use.
403
 * @param[in]   addr      Address to read.
404
 * @param[out]  rxbuf     Buffer to store the response to.
405
 * @param[in]   rxbytes   Number of bytes to receive.
406
 * @param[in]   timeout   Timeout for the function to return (in microseconds).
407
 *
408
 * @return The status indicates whether the function call was succesful or a timeout occurred.
409
 */
410
static inline apalExitStatus_t apalI2CMasterReceive(apalI2CDriver_t* i2cd, const apalI2Caddr_t addr, uint8_t* const rxbuf, const size_t rxbytes, const apalTime_t timeout)
411
{
412
  aosDbgCheck(i2cd != NULL);
413
414
#if (I2C_USE_MUTUAL_EXCLUSION == TRUE)
415
  i2cAcquireBus(i2cd);
416
#endif
417
418
#if defined(STM32F1XX_I2C)
419
  // Due to a hardware limitation, for STM32F1 platform the minimum number of bytes that can be received is two.
420
  msg_t status = MSG_OK;
421
  if (rxbytes == 1) {
422
    uint8_t buffer[2];
423
    status = i2cMasterReceiveTimeout(i2cd, addr, buffer, 2, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : LL_US2ST(timeout)) );
424
    rxbuf[0] = buffer[0];
425
  } else {
426
    status = i2cMasterReceiveTimeout(i2cd, addr, rxbuf, rxbytes, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : LL_US2ST(timeout)) );
427
  }
428
#else
429
  const msg_t status = i2cMasterReceiveTimeout(i2cd, addr, rxbuf, rxbytes, ((timeout >= TIME_INFINITE) ? TIME_INFINITE : LL_US2ST(timeout)) );
430
#endif
431
432
#if (I2C_USE_MUTUAL_EXCLUSION == TRUE)
433
  i2cReleaseBus(i2cd);
434
#endif
435
436
  switch (status)
437
  {
438
    case MSG_OK:
439
#if defined(STM32F1XX_I2C)
440
      return (rxbytes != 1) ? APAL_STATUS_OK : APAL_STATUS_WARNING;
441
#else
442
      return APAL_STATUS_OK;
443
#endif
444
    case MSG_TIMEOUT:
445
      return APAL_STATUS_TIMEOUT;
446
    case MSG_RESET:
447
    default:
448
      return APAL_STATUS_ERROR;
449
  }
450
}
451
452
#endif
453
454
/*============================================================================*/
455
/* SPI                                                                        */
456
/*============================================================================*/
457
458
#if HAL_USE_SPI || defined(__DOXYGEN__)
459
460
/**
461
 * @brief SPI driver type.
462
 */
463
typedef SPIDriver apalSPIDriver_t;
464
465
/**
466
 * @brief Transmit and receive data from SPI
467
 *
468
 * @param[in]   spid      The SPI driver to use.
469
 * @param[in]   txData    Buffer containing data to send.
470
 * @param[out]  rxData    Buffer to store.
471
 * @param[in]   length    Number of bytes to send.
472
 *
473
 * @return The status indicates whether the function call was succesful.
474
 */
475
static inline apalExitStatus_t apalSPIExchange(apalSPIDriver_t* spid, const uint8_t* const txData , uint8_t* const rxData, const size_t length)
476
{
477
  aosDbgCheck(spid != NULL);
478
479
#if (SPI_USE_MUTUAL_EXCLUSION)
480
  spiAcquireBus(spid);
481
#endif
482
  spiSelect(spid);
483
  spiExchange(spid, length, txData, rxData);
484
  spiUnselect(spid);
485
#if (SPI_USE_MUTUAL_EXCLUSION)
486
  spiReleaseBus(spid);
487
#endif
488
489
  return APAL_STATUS_OK;
490
}
491
492
/**
493
 * @brief Receive data from SPI
494
 *
495
 * @param[in]   spid      The SPI driver to use.
496
 * @param[out]  data      Buffer to store.
497
 * @param[in]   length    Number of bytes to send.
498
 *
499
 * @return The status indicates whether the function call was succesful.
500
 */
501
static inline apalExitStatus_t apalSPIReceive(apalSPIDriver_t* spid, uint8_t* const data, const size_t length)
502
{
503
  aosDbgCheck(spid != NULL);
504
505
#if (SPI_USE_MUTUAL_EXCLUSION)
506
  spiAcquireBus(spid);
507
#endif
508
  spiSelect(spid);
509
  spiReceive(spid, length, data);
510
  spiUnselect(spid);
511
#if (SPI_USE_MUTUAL_EXCLUSION)
512
  spiReleaseBus(spid);
513
#endif
514
515
  return APAL_STATUS_OK;
516
}
517
518
/**
519
 * @brief Transmit data to SPI
520
 *
521
 * @param[in]   spid      The SPI driver to use.
522
 * @param[in]   data      Buffer containing data to send.
523
 * @param[in]   length    Number of bytes to send.
524
 *
525
 * @return The status indicates whether the function call was succesful.
526
 */
527
static inline apalExitStatus_t apalSPITransmit(apalSPIDriver_t* spid, const uint8_t* const data, const size_t length)
528
{
529
  aosDbgCheck(spid != NULL);
530
531
#if (SPI_USE_MUTUAL_EXCLUSION)
532
  spiAcquireBus(spid);
533
#endif
534
  spiSelect(spid);
535
  spiSend(spid, length, data);
536
  spiUnselect(spid);
537
#if (SPI_USE_MUTUAL_EXCLUSION)
538
  spiReleaseBus(spid);
539
#endif
540
541
  return APAL_STATUS_OK;
542
}
543
544
#endif
545
546
/*============================================================================*/
547
/* DEBUG                                                                      */
548
/*============================================================================*/
549
550
/**
551
 * @brief Assert function to check a given condition.
552
 *
553
 * @param[in] c   The condition to check.
554
 */
555
#define apalDbgAssert(c)   aosDbgAssert(c)
556
557
#endif /* _AMIROOS_PERIPHAL_H_ */