1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/include/servo.h

237 lines
6.9 KiB
C

/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2015 Eistec AB
* Copyright (C) 2022 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @defgroup drivers_servo Servo Motor Driver
* @ingroup drivers_actuators
* @brief High-level driver for servo motors
*
* Usage
* =====
*
* Select a flavor of the driver, e.g. `USEMODULE += servo_pwm` for
* @ref drivers_servo_pwm or `USEMODULE += servo_timer` for
* @ref drivers_servo_timer to use. Typically, the PWM implementation is the
* preferred one, but some MCU (e.g. nRF52xxx) cannot configure the PWM
* peripheral to run anywhere close to the 50 Hz to 100 Hz required.
*
* In addition, you many need to extend or adapt @ref servo_params and,
* depending on the selected implementation, @ref servo_pwm_params or
* @ref servo_timer_params to match your hardware configuration.
*
* The test application in `tests/drivers/servo` can serve as starting point for
* users.
*
* @{
*
* @file
* @brief High-level driver for easy handling of servo motors
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
*/
#ifndef SERVO_H
#define SERVO_H
#include <stddef.h>
#include <stdint.h>
#include "periph/pwm.h"
#include "periph/timer.h"
#include "saul.h"
#include "saul_reg.h"
#include "time_units.h"
#ifndef SERVO_TIMER_MAX_CHAN
/**
* @brief In case the `servo_timer` backend is used to driver the servo,
* this is the highest channel number usable by the driver
*
* @note To drive *n* servos, *n* + 1 timer channels are required. Hence,
* this must be at least 2
*
* Trimming this down safes a small bit of RAM: Storage for one pointer is
* wasted on every servo that could be controlled by a timer but is not
* actually used.
*/
#define SERVO_TIMER_MAX_CHAN 4
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The SAUL adaption driver for servos
*/
extern const saul_driver_t servo_saul_driver;
/**
* @brief PWM configuration parameters for a servos
*
* Only used with
*/
typedef struct {
uint16_t res; /**< PWM resolution to use */
uint16_t freq; /**< PWM frequency to use */
pwm_t pwm; /**< PWM dev the servo is connected to */
} servo_pwm_params_t;
/**
* @brief Servo device state
*/
typedef struct servo servo_t;
/**
* @brief Memory needed for book keeping when using @ref drivers_servo_timer
*/
typedef struct {
/**
* @brief Look up table to get from channel
*
* @note Since timer channel 0 is used to set all servo pins, we use
* `chan - 1` as idx rather than `chan` to not waste one entry.
*/
servo_t *servo_map[SERVO_TIMER_MAX_CHAN];
} servo_timer_ctx_t;
/**
* @brief Timer configuration parameters for a servos
*/
typedef struct {
tim_t timer; /**< Timer to use */
uint32_t timer_freq; /**< Timer frequency to use */
uint16_t servo_freq; /**< Servo frequency (typically 50 Hz or 100 Hz) */
servo_timer_ctx_t *ctx; /**< Per-timer state needed for book keeping */
} servo_timer_params_t;
/**
* @brief Configuration parameters for a servo
*/
typedef struct {
#if defined(MODULE_SERVO_PWM) || defined(DOXYGEN)
/**
* @brief Specification of the PWM device the servo is connected to
*
* @note Only available when @ref drivers_servo_pwm is used
*/
const servo_pwm_params_t *pwm;
#endif
#if defined(MODULE_SERVO_TIMER) || defined(DOXYGEN)
/**
* @brief Specification of the timer to use
*
* @note Only available when @ref drivers_servo_timer is used
*/
const servo_timer_params_t *timer;
/**
* @brief GPIO pin the servo is connected to
*
* @note Only available when @ref drivers_servo_timer is used
*/
gpio_t servo_pin;
#endif
uint16_t min_us; /**< Duration of high phase (in µs) for min extension */
uint16_t max_us; /**< Duration of high phase (in µs) for max extension */
#ifdef MODULE_SERVO_PWM
/**
* @brief PWM channel to use
*
* @note Only available when @ref drivers_servo_pwm is used
*/
uint8_t pwm_chan;
#endif
#ifdef MODULE_SERVO_TIMER
/**
* @brief Timer channel to use
*
* @pre `(timer_chan > 0) && (timer_chan <= SERVO_TIMER_MAX_CHAN)`
*
* @note Only available when @ref drivers_servo_timer is used
*
* The timer channel 0 is used to set the GPIO pin of all servos
* driver by the timer, the other channels are used to clean the GPIO pin
* of the corresponding servo according to the current duty cycle.
*/
uint8_t timer_chan;
#endif
} servo_params_t;
/**
* @brief Servo device state
*/
struct servo {
const servo_params_t *params; /**< Parameters of this servo */
/**
* @brief Minimum PWM duty cycle / timer target matching
* @ref servo_params_t::min_us
*
* Note that the actual PWM frequency can be significantly different from
* the requested one, depending on what the hardware can generate using the
* clock source and clock dividers available.
*/
uint16_t min;
/**
* @brief Maximum PWM duty cycle / timer target matching
* @ref servo_params_t::min_us
*
* Note that the actual PWM frequency can be significantly different from
* the requested one, depending on what the hardware can generate using the
* clock source and clock dividers available.
*/
uint16_t max;
#ifdef MODULE_SERVO_TIMER
uint16_t current; /**< Current timer target */
#endif
};
#if defined(MODULE_SERVO_TIMER) || DOXYGEN
/**
* @brief Default timer context
*/
extern servo_timer_ctx_t servo_timer_default_ctx;
#endif
/**
* @brief Initialize servo
*
* @param[out] dev Device handle to initialize
* @param[in] params Parameters defining the PWM configuration
*
* @retval 0 Success
* @retval <0 Failure (as negative errno code to indicate cause)
*/
int servo_init(servo_t *dev, const servo_params_t *params);
/**
* @brief Set the servo motor to a specified position
*
* The position of the servo is specified in the fraction of maximum extension,
* with 0 being the lowest extension (e.g. on an 180° servo it would be at -90°)
* and `UINT8_MAX` being the highest extension (e.g. +90° on that 180° servo).
*
* @param[in] dev the servo to set
* @param[in] pos the extension to set
*
* Note: 8 bit of resolution may seem low, but is indeed more than high enough
* for any practical PWM based servo. For higher precision, stepper motors would
* be required.
*/
void servo_set(servo_t *dev, uint8_t pos);
#ifdef __cplusplus
}
#endif
#endif /* SERVO_H */
/** @} */