From 8cc1870663e5d4a32ec58b1408f5e81f0fc96775 Mon Sep 17 00:00:00 2001 From: Antonio Lignan Date: Mon, 22 Aug 2016 12:36:12 +0200 Subject: [PATCH] Zoul: generic servo driver --- cpu/cc2538/dev/pwm.c | 18 +++- cpu/cc2538/dev/pwm.h | 4 +- examples/cc2538-common/test-pwm.c | 2 +- examples/zolertia/zoul/Makefile | 2 +- examples/zolertia/zoul/test-servo.c | 107 ++++++++++++++++++++++ platform/zoul/dev/servo.c | 132 +++++++++++++++++++++++++++ platform/zoul/dev/servo.h | 136 ++++++++++++++++++++++++++++ 7 files changed, 393 insertions(+), 8 deletions(-) create mode 100644 examples/zolertia/zoul/test-servo.c create mode 100644 platform/zoul/dev/servo.c create mode 100644 platform/zoul/dev/servo.h diff --git a/cpu/cc2538/dev/pwm.c b/cpu/cc2538/dev/pwm.c index d6b5b9286..73440013b 100644 --- a/cpu/cc2538/dev/pwm.c +++ b/cpu/cc2538/dev/pwm.c @@ -90,7 +90,8 @@ permit_pm1(void) } /*---------------------------------------------------------------------------*/ int8_t -pwm_enable(uint32_t freq, uint8_t duty, uint8_t timer, uint8_t ab) +pwm_enable(uint32_t freq, uint8_t duty, uint32_t count, uint8_t timer, + uint8_t ab) { uint8_t offset = 0; uint32_t interval_load, duty_count, copy; @@ -109,7 +110,7 @@ pwm_enable(uint32_t freq, uint8_t duty, uint8_t timer, uint8_t ab) return PWM_ERROR; } - PRINTF("PWM: F%08luHz: %u%% on GPT%u-%u\n", freq, duty, timer, ab); + PRINTF("PWM: F%08luHz: %u%%/%lu on GPT%u-%u\n", freq, duty, count, timer, ab); lpm_register_peripheral(permit_pm1); @@ -147,14 +148,21 @@ pwm_enable(uint32_t freq, uint8_t duty, uint8_t timer, uint8_t ab) /* If the duty cycle is zero, leave the GPTIMER configured as PWM to pass a next * configured check, but do nothing else */ - if(!duty) { + if((!duty) && (!count)) { REG(gpt_base + GPTIMER_CTL) |= (copy | gpt_dir); return PWM_SUCCESS; } - /* Get the peripheral clock and equivalent deassert count */ + /* Get the peripheral clock and equivalent deassert count, depending on the + * value given by the user, either use the count number of the duty cycle in + * percentage + */ interval_load = sys_ctrl_get_sys_clock() / freq; - duty_count = ((interval_load * duty) + 1) / 100; + if(duty) { + duty_count = ((interval_load * duty) + 1) / 100; + } else { + duty_count = count; + } PRINTF("PWM: sys %luHz: %lu %lu\n", sys_ctrl_get_sys_clock(), interval_load, duty_count); diff --git a/cpu/cc2538/dev/pwm.h b/cpu/cc2538/dev/pwm.h index 744fa4b6c..294ec3559 100644 --- a/cpu/cc2538/dev/pwm.h +++ b/cpu/cc2538/dev/pwm.h @@ -116,11 +116,13 @@ /** \brief Configures the general purpose timer in PWM mode * \param freq PWM frequency (in Hz) * \param duty PWM duty cycle (percentage in integers) + * \param count PWM duty cycle (count number) * \param timer General purpose timer to use [0-3] * \param ab Select which timer to use (Timer A or B) * \return \c PWM_SUCCESS if successful, else \c PWM_ERROR */ -int8_t pwm_enable(uint32_t freq, uint8_t duty, uint8_t timer, uint8_t ab); +int8_t pwm_enable(uint32_t freq, uint8_t duty, uint32_t count, uint8_t timer, + uint8_t ab); /*---------------------------------------------------------------------------*/ /** \brief Disables a previously PWM configured GPTn * \param timer General purpose timer to disable [0-3] diff --git a/examples/cc2538-common/test-pwm.c b/examples/cc2538-common/test-pwm.c index a1fa8c727..4aa5aede6 100644 --- a/examples/cc2538-common/test-pwm.c +++ b/examples/cc2538-common/test-pwm.c @@ -145,7 +145,7 @@ PROCESS_THREAD(cc2538_pwm_test, ev, data) PRINTF("\nStarting the test\n"); for(i = 0; i < MAX_PWM; i++) { - if(pwm_enable(pwm_num[i].freq, pwm_num[i].duty, + if(pwm_enable(pwm_num[i].freq, pwm_num[i].duty, 0, pwm_num[i].timer, pwm_num[i].ab) == PWM_SUCCESS) { pwm_en[i] = 1; PRINTF("%s (%u) configuration OK\n", gpt_name(pwm_num[i].timer), diff --git a/examples/zolertia/zoul/Makefile b/examples/zolertia/zoul/Makefile index d11279e44..66b530341 100644 --- a/examples/zolertia/zoul/Makefile +++ b/examples/zolertia/zoul/Makefile @@ -10,7 +10,7 @@ CONTIKI_PROJECT += test-zonik test-dht22.c CONTIKI_TARGET_SOURCEFILES += tsl2563.c sht25.c bmpx8x.c motion-sensor.c CONTIKI_TARGET_SOURCEFILES += adc-sensors.c weather-meter.c grove-gyro.c CONTIKI_TARGET_SOURCEFILES += rgb-bl-lcd.c pm10-sensor.c iaq.c zonik.c relay.c -CONTIKI_TARGET_SOURCEFILES += dht22.c +CONTIKI_TARGET_SOURCEFILES += dht22.c servo.c all: $(CONTIKI_PROJECT) diff --git a/examples/zolertia/zoul/test-servo.c b/examples/zolertia/zoul/test-servo.c new file mode 100644 index 000000000..9b65ae617 --- /dev/null +++ b/examples/zolertia/zoul/test-servo.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016, Zolertia - http://www.zolertia.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \addtogroup zoul-examples + * @{ + * + * \defgroup zoul-servo-test Test the EMAX ES08A II servo motor + * + * Demonstrates the use of the EMAX ES08A servo motor. This servo requires a + * +5V voltage, it can be powered from D+5.1 pin (of the ADC3 connector), but + * it requires either an external power supply other than the USB for programing + * or it can be powered by the USB 2.0 connector, which allows a higher current + * draw. + * + * This test uses the default servo values (freq 50Hz, traveling time 1.5-1.9ms) + * for 0-180º movement, tested with the EMAX ES08A. Depending on the servo used + * you might need to adjust these parameters in the servo.h header file. + * + * @{ + * + * \file + * A quick program for testing a servo motor + * \author + * Antonio Lignan + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "dev/leds.h" +#include "dev/servo.h" +#include +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG 1 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +static struct etimer et; +/*---------------------------------------------------------------------------*/ +PROCESS(servo_test_process, "Zolertia servo test process"); +AUTOSTART_PROCESSES(&servo_test_process); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(servo_test_process, ev, data) +{ + PROCESS_BEGIN(); + + static uint8_t deg = 0; + + PRINTF("\nStarting the test\n"); + + while(1) { + + servo_position(SERVO_CHANNEL_5, GPIO_A_NUM, 5, deg); + PRINTF("Current position --> %03uº\n", deg); + + etimer_set(&et, CLOCK_SECOND / 2); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + + /* Increase the position by 10º each iteration */ + deg += 10; + if(deg > SERVO_MAX_DEGREES) { + deg = 0; + servo_stop(SERVO_CHANNEL_5, GPIO_A_NUM, 5); + + /* Stop the servo and wait 2 seconds before resuming from start */ + etimer_set(&et, CLOCK_SECOND * 2); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/platform/zoul/dev/servo.c b/platform/zoul/dev/servo.c new file mode 100644 index 000000000..2b08599a1 --- /dev/null +++ b/platform/zoul/dev/servo.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016, Zolertia - http://www.zolertia.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup zoul-servo + * @{ + * + * \file + * Driver for a generic Servo driver + * + * \author + * Antonio Lignan + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "dev/pwm.h" +#include "dev/gpio.h" +#include "servo.h" +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +int +servo_position(uint16_t gptab, uint8_t port, uint8_t pin, uint16_t pos) +{ + uint8_t gpt_num; + uint8_t gpt_ab; + uint32_t count = 0; + + if((gptab < SERVO_CHANNEL_1) && (gptab > SERVO_CHANNEL_7)) { + PRINTF("Servo: invalid servo channel\n"); + return SERVO_ERROR; + } + + /* CC2538 has 4 ports (A-D) and up to 8 pins (0-7) */ + if((port > GPIO_D_NUM) || (pin > 7)) { + PRINTF("Servo: Invalid pin/port settings\n"); + return SERVO_ERROR; + } + + if(pos > SERVO_MAX_DEGREES) { + PRINTF("Servo: invalid position (max %u)\n", SERVO_MAX_DEGREES); + return SERVO_ERROR; + } + + count = (SERVO_MAX_VAL - SERVO_MIN_VAL) * pos; + count /= SERVO_MAX_DEGREES; + count += SERVO_MIN_VAL; + + gpt_num = (uint8_t)(gptab >> 8); + gpt_ab = (uint8_t)(gptab & 0x00FF); + + PRINTF("Servo: F%uHz GPTNUM %u GPTAB %u --> %uº (%lu)\n", SERVO_DEFAULT_FREQ, + gpt_num, gpt_ab, + pos, count); + /* Use count as argument instead of percentage */ + if(pwm_enable(SERVO_DEFAULT_FREQ, 0, count, gpt_num,gpt_ab) != PWM_SUCCESS) { + PRINTF("Servo: failed to configure the pwm channel\n"); + return SERVO_ERROR; + } + + /* Start the PWM as soon as possible, keep the pulses to lock the servo in the + * given position + */ + if(pwm_start(gpt_num, gpt_ab, port, pin) != PWM_SUCCESS) { + PRINTF("Servo: failed to initialize the pwm channel\n"); + return SERVO_ERROR; + } + + return SERVO_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +int +servo_stop(uint16_t gptab, uint8_t port, uint8_t pin) +{ + uint8_t gpt_num; + uint8_t gpt_ab; + + if((gptab < SERVO_CHANNEL_1) && (gptab > SERVO_CHANNEL_7)) { + PRINTF("Servo: invalid servo channel\n"); + return SERVO_ERROR; + } + + /* CC2538 has 4 ports (A-D) and up to 8 pins (0-7) */ + if((port > GPIO_D_NUM) || (pin > 7)) { + PRINTF("Servo: Invalid pin/port settings\n"); + return SERVO_ERROR; + } + + gpt_num = (uint8_t)((gptab & 0xFF00) >> 8); + gpt_ab = (uint8_t)(gptab & 0x00FF); + + if(pwm_disable(gpt_num, gpt_ab, port, pin) != PWM_SUCCESS) { + PRINTF("Servo: unable to disable the pwm channel\n"); + return SERVO_ERROR; + } + + return SERVO_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/platform/zoul/dev/servo.h b/platform/zoul/dev/servo.h new file mode 100644 index 000000000..6ec2fae5f --- /dev/null +++ b/platform/zoul/dev/servo.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2016, Zolertia - http://www.zolertia.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup zoul-sensors + * @{ + * + * \defgroup zoul-servo Generic servo driver + * + * Driver for a Generic Servo actuator + * + * @{ + * + * \file + * Header file for a Generic Servo driver + * + * \author + * Antonio Lignan + */ +/*---------------------------------------------------------------------------*/ +#ifndef SERVO_H_ +#define SERVO_H_ +#include +#include "dev/pwm.h" +/* -------------------------------------------------------------------------- */ +/** + * \name Servo default settings + * @{ + */ +/* -------------------------------------------------------------------------- */ +#ifndef SERVO_CONF_FREQ +#define SERVO_DEFAULT_FREQ 50 /**< 50 Hz */ +#else +#define SERVO_DEFAULT_FREQ SERVO_CONF_FREQ +#endif + +#ifndef SERVO_CONF_MAX_DEGREES +#define SERVO_MAX_DEGREES 180 +#else +#define SERVO_MAX_DEGREES SERVO_CONF_MAX_DEGREES +#endif + +#ifndef SERVO_CONF_MIN_VAL +#define SERVO_MIN_VAL 9600 /**> roughly equals to 3% duty cycle */ +#else +#define SERVO_MIN_VAL SERVO_CONF_MIN_VAL +#endif + +#ifndef SERVO_CONF_MAX_VAL +#define SERVO_MAX_VAL 38400 /**> roughly equals to 12% duty cycle */ +#else +#define SERVO_MAX_VAL SERVO_CONF_MAX_VAL +#endif +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name Servo general purpose timers mapping + * @{ + */ +#define SERVO_CHANNEL_1 0x001 /**< GPT0-B */ +#define SERVO_CHANNEL_2 0x100 /**< GPT1-A */ +#define SERVO_CHANNEL_3 0x101 /**< GPT1-B */ +#define SERVO_CHANNEL_4 0x200 /**< GPT2-A */ +#define SERVO_CHANNEL_5 0x201 /**< GPT2-B */ +#define SERVO_CHANNEL_6 0x300 /**< GPT3-A */ +#define SERVO_CHANNEL_7 0x301 /**< GPT3-B */ +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name Servo general constants + * @{ + */ +#define SERVO_SUCCESS 0 +#define SERVO_ERROR (-1) +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name Servo public funtions + * @{ + */ + +/** \brief Configures and positions a servo in a given position (by degrees) + * The servo will lock its position as long as it is not stopped + * \param gptab Servo channel (PWM GPT from 1-7) + * \param port Port number to use as PWM + * \param pin Pin number to use as PWM + * \param pos Position to map the servo to (0-360º, integer) + * \return \c SERVO_SUCCESS if successful, else \c SERVO_ERROR + */ +int servo_position(uint16_t gptab, uint8_t port, uint8_t pin, uint16_t pos); + +/** \brief Fully stop a servo and reconfigures back the pin/port as GPIO + * \param gptab Servo channel (PWM GPT from 1-7) + * \param port Port number to use as PWM + * \param pin Pin number to use as PWM + * \return \c SERVO_SUCCESS if successful, else \c SERVO_ERROR + */ +int servo_stop(uint16_t gptab, uint8_t port, uint8_t pin); +/** @} */ +/* -------------------------------------------------------------------------- */ +#endif +/* -------------------------------------------------------------------------- */ +/** + * @} + * @} + */ +