Added PWM driver for the Zolertia Zoul module and CC2538 platforms

This commit is contained in:
Antonio Lignan 2015-11-25 23:44:54 +01:00
parent ddb6e4b3d7
commit 37fcb927be
7 changed files with 726 additions and 2 deletions

View file

@ -60,7 +60,7 @@ CONTIKI_CPU_SOURCEFILES += ecc-curve.c
CONTIKI_CPU_SOURCEFILES += dbg.c ieee-addr.c
CONTIKI_CPU_SOURCEFILES += slip-arch.c slip.c
CONTIKI_CPU_SOURCEFILES += i2c.c cc2538-temp-sensor.c vdd3-sensor.c
CONTIKI_CPU_SOURCEFILES += cfs-coffee.c cfs-coffee-arch.c
CONTIKI_CPU_SOURCEFILES += cfs-coffee.c cfs-coffee-arch.c pwm.c
DEBUG_IO_SOURCEFILES += dbg-printf.c dbg-snprintf.c dbg-sprintf.c strformat.c

343
cpu/cc2538/dev/pwm.c Normal file
View file

@ -0,0 +1,343 @@
/*
* Copyright (c) 2015, 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 cc2538-pwm-driver
* @{
*
* \file
* Driver for the CC2538 PWM
*
* \author
* Javier Sanchez <jsanchez@zolertia.com>
* Antonio Lignan <alinan@zolertia.com>
*/
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "dev/ioc.h"
#include "dev/gpio.h"
#include "dev/sys-ctrl.h"
#include "dev/pwm.h"
#include <stdio.h>
#include <stdlib.h>
/*---------------------------------------------------------------------------*/
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/*---------------------------------------------------------------------------*/
#define PWM_GPTIMER_NUM_TO_BASE(x) ((GPT_0_BASE) + ((x) << 12))
/*---------------------------------------------------------------------------*/
static uint8_t
pwm_configured(uint8_t timer, uint8_t ab)
{
uint8_t offset;
uint32_t gpt_base;
gpt_base = PWM_GPTIMER_NUM_TO_BASE(timer);
offset = (ab) ? 4 : 0;
if((REG(gpt_base + GPTIMER_TAMR + offset) & GPTIMER_TAMR_TAAMS) &&
(REG(gpt_base + GPTIMER_TAMR + offset) & GPTIMER_TAMR_TAMR_PERIODIC)) {
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
int8_t
pwm_enable(uint32_t freq, uint8_t duty, uint8_t timer, uint8_t ab)
{
uint8_t offset = 0;
uint32_t interval_load, duty_count, copy;
uint32_t gpt_base, gpt_en, gpt_dir;
if((freq < PWM_FREQ_MIN) || (freq > PWM_FREQ_MAX) ||
(duty < PWM_DUTY_MIN) || (duty > PWM_DUTY_MAX) ||
(timer > PWM_TIMER_MAX) || (timer < PWM_TIMER_MIN)) {
PRINTF("PWM: Invalid PWM settings\n");
return PWM_ERROR;
}
/* GPT0 timer A is used for clock_delay_usec() in clock.c */
if((ab == PWM_TIMER_A) && (timer == PWM_TIMER_0)) {
PRINTF("PWM: GPT0 (timer A) is reserved for clock_delay_usec()\n");
return PWM_ERROR;
}
PRINTF("PWM: F%08luHz: %u%% on GPT%u-%u\n", freq, duty, timer, ab);
gpt_base = PWM_GPTIMER_NUM_TO_BASE(timer);
gpt_en = GPTIMER_CTL_TAEN;
gpt_dir = GPTIMER_CTL_TAPWML;
if(ab == PWM_TIMER_B) {
offset = 4;
gpt_en = GPTIMER_CTL_TBEN;
gpt_dir = GPTIMER_CTL_TBPWML;
}
PRINTF("PWM: GPT_x_BASE 0x%08lX (%u)\n", gpt_base, offset);
/* Restore later, ensure GPTIMER_CTL_TxEN and GPTIMER_CTL_TxPWML are clear */
copy = REG(gpt_base + GPTIMER_CTL);
copy &= ~(gpt_en | gpt_dir);
/* Enable module clock for the GPTx in Active mode */
REG(SYS_CTRL_RCGCGPT) |= (SYS_CTRL_RCGCGPT_GPT0 << timer);
/* Enable module clock for the GPTx in Sleep mode */
REG(SYS_CTRL_SCGCGPT) |= (SYS_CTRL_SCGCGPT_GPT0 << timer);
/* Enable module clock for the GPTx in PM0, in PM1 and below this doesn't matter */
REG(SYS_CTRL_DCGCGPT) |= (SYS_CTRL_DCGCGPT_GPT0 << timer);
/* Stop the timer */
REG(gpt_base + GPTIMER_CTL) = 0;
/* Use 16-bit timer */
REG(gpt_base + GPTIMER_CFG) = PWM_GPTIMER_CFG_SPLIT_MODE;
/* Configure PWM mode */
REG(gpt_base + GPTIMER_TAMR + offset) = 0;
REG(gpt_base + GPTIMER_TAMR + offset) |= GPTIMER_TAMR_TAAMS;
REG(gpt_base + GPTIMER_TAMR + offset) |= GPTIMER_TAMR_TAMR_PERIODIC;
/* If the duty cycle is zero, leave the GPTIMER configured as PWM to pass a next
* configured check, but do nothing else */
if(!duty) {
REG(gpt_base + GPTIMER_CTL) |= (copy | gpt_dir);
return PWM_SUCCESS;
}
/* Get the peripheral clock and equivalent deassert count */
interval_load = sys_ctrl_get_io_clock() / freq;
duty_count = ((interval_load * duty) + 1) / 100;
PRINTF("PWM: IO %luHz: %lu %lu\n", sys_ctrl_get_io_clock(),
interval_load, duty_count);
/* Set the start value (period), count down */
REG(gpt_base + GPTIMER_TAILR + offset) = ((uint16_t *)&interval_load)[0] - 1;
/* Set the deassert period */
REG(gpt_base + GPTIMER_TAMATCHR + offset) = ((uint16_t *)&duty_count)[0] - 1;
/* Set the prescaler if required */
REG(gpt_base + GPTIMER_TAPR + offset) = ((uint8_t *)&interval_load)[2];
/* Set the prescaler match if required */
REG(gpt_base + GPTIMER_TAPMR + offset) = ((uint8_t *)&duty_count)[2];
/* Restore the register content */
REG(gpt_base + GPTIMER_CTL) |= (copy | gpt_dir);
PRINTF("PWM: TnILR %lu ", REG(gpt_base + (GPTIMER_TAILR + offset)));
PRINTF("TnMATCHR %lu ", REG(gpt_base + (GPTIMER_TAMATCHR + offset)));
PRINTF("TnPR %lu ", REG(gpt_base + (GPTIMER_TAPR + offset)));
PRINTF("TnPMR %lu\n", REG(gpt_base + (GPTIMER_TAPMR + offset)));
return PWM_SUCCESS;
}
/*---------------------------------------------------------------------------*/
int8_t
pwm_stop(uint8_t timer, uint8_t ab, uint8_t port, uint8_t pin, uint8_t state)
{
uint32_t gpt_base, gpt_dis;
if((ab > PWM_TIMER_B) || (timer < PWM_TIMER_MIN) ||
(timer > PWM_TIMER_MAX)) {
PRINTF("PWM: Invalid PWM values\n");
return PWM_ERROR;
}
if(!pwm_configured(timer, ab)) {
PRINTF("PWM: GPTn not configured as PWM\n");
return PWM_ERROR;
}
/* CC2538 has 4 ports (A-D) and up to 8 pins (0-7) */
if((port > GPIO_D_NUM) || (pin > 7)) {
PRINTF("PWM: Invalid pin/port settings\n");
return PWM_ERROR;
}
/* CC2538 has 4 ports (A-D) and up to 8 pins (0-7) */
if((state != PWM_OFF_WHEN_STOP) && (state != PWM_ON_WHEN_STOP)) {
PRINTF("PWM: Invalid pin state when PWM is halt\n");
return PWM_ERROR;
}
gpt_base = PWM_GPTIMER_NUM_TO_BASE(timer);
gpt_dis = (ab == PWM_TIMER_B) ? GPTIMER_CTL_TBEN : GPTIMER_CTL_TAEN;
REG(gpt_base + GPTIMER_CTL) &= ~gpt_dis;
/* Configure the port/pin as GPIO, input */
ioc_set_over(port, pin, IOC_OVERRIDE_DIS);
GPIO_SOFTWARE_CONTROL(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
GPIO_SET_OUTPUT(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
if(state) {
GPIO_SET_PIN(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
} else {
GPIO_CLR_PIN(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
}
PRINTF("PWM: OFF -> Timer %u (%u)\n", timer, ab);
return PWM_SUCCESS;
}
/*---------------------------------------------------------------------------*/
int8_t
pwm_start(uint8_t timer, uint8_t ab, uint8_t port, uint8_t pin)
{
uint32_t gpt_base, gpt_en, gpt_sel;
if((ab > PWM_TIMER_B) || (timer < PWM_TIMER_MIN) ||
(timer > PWM_TIMER_MAX)) {
PRINTF("PWM: Invalid PWM values\n");
return PWM_ERROR;
}
if(!pwm_configured(timer, ab)) {
PRINTF("PWM: GPTn not configured as PWM\n");
return PWM_ERROR;
}
/* CC2538 has 4 ports (A-D) and up to 8 pins (0-7) */
if((port > GPIO_D_NUM) || (pin > 7)) {
PRINTF("PWM: Invalid pin/port settings\n");
return PWM_ERROR;
}
/* Map to given port/pin */
gpt_sel = IOC_PXX_SEL_GPT0_ICP1 + (timer * 2);
if(ab == PWM_TIMER_B) {
gpt_sel++;
}
ioc_set_sel(port, pin, gpt_sel);
ioc_set_over(port, pin, IOC_OVERRIDE_OE);
GPIO_PERIPHERAL_CONTROL(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
gpt_base = PWM_GPTIMER_NUM_TO_BASE(timer);
gpt_en = (ab == PWM_TIMER_B) ? GPTIMER_CTL_TBEN : GPTIMER_CTL_TAEN;
REG(gpt_base + GPTIMER_CTL) |= gpt_en;
PRINTF("PWM: ON -> Timer %u (%u) IOC_PXX_SEL_GPTx_IPCx 0x%08lX\n", timer, ab,
gpt_sel);
return PWM_SUCCESS;
}
/*---------------------------------------------------------------------------*/
int8_t
pwm_set_direction(uint8_t timer, uint8_t ab, uint8_t dir)
{
uint32_t gpt_base, gpt_dir;
if((ab > PWM_TIMER_B) || (timer < PWM_TIMER_MIN) ||
(timer > PWM_TIMER_MAX) || (dir > PWM_SIGNAL_INVERTED)) {
PRINTF("PWM: Invalid PWM values\n");
return PWM_ERROR;
}
if(!pwm_configured(timer, ab)) {
PRINTF("PWM: GPTn not configured as PWM\n");
return PWM_ERROR;
}
gpt_base = PWM_GPTIMER_NUM_TO_BASE(timer);
gpt_dir = (ab == PWM_TIMER_B) ? GPTIMER_CTL_TBPWML : GPTIMER_CTL_TAPWML;
if(dir) {
REG(gpt_base + GPTIMER_CTL) |= gpt_dir;
} else {
REG(gpt_base + GPTIMER_CTL) &= ~gpt_dir;
}
PRINTF("PWM: Signal direction (%u) -> Timer %u (%u)\n", dir, timer, ab);
return PWM_SUCCESS;
}
/*---------------------------------------------------------------------------*/
int8_t
pwm_toggle_direction(uint8_t timer, uint8_t ab)
{
uint32_t gpt_base, gpt_dir;
if((ab > PWM_TIMER_B) || (timer < PWM_TIMER_MIN) ||
(timer > PWM_TIMER_MAX)) {
PRINTF("PWM: Invalid PWM values\n");
return PWM_ERROR;
}
if(!pwm_configured(timer, ab)) {
PRINTF("PWM: GPTn not configured as PWM\n");
return PWM_ERROR;
}
gpt_base = PWM_GPTIMER_NUM_TO_BASE(timer);
gpt_dir = (ab == PWM_TIMER_B) ? GPTIMER_CTL_TBPWML : GPTIMER_CTL_TAPWML;
if(REG(gpt_base + GPTIMER_CTL) & gpt_dir) {
REG(gpt_base + GPTIMER_CTL) &= ~gpt_dir;
} else {
REG(gpt_base + GPTIMER_CTL) |= gpt_dir;
}
PRINTF("PWM: direction toggled -> Timer %u (%u)\n", timer, ab);
return PWM_SUCCESS;
}
/*---------------------------------------------------------------------------*/
int8_t
pwm_disable(uint8_t timer, uint8_t ab, uint8_t port, uint8_t pin)
{
uint32_t gpt_base;
uint8_t offset = (ab == PWM_TIMER_B) ? 4 : 0;
gpt_base = PWM_GPTIMER_NUM_TO_BASE(timer);
if((ab > PWM_TIMER_B) || (timer < PWM_TIMER_MIN) ||
(timer > PWM_TIMER_MAX)) {
PRINTF("PWM: Invalid PWM values\n");
return PWM_ERROR;
}
/* CC2538 has 4 ports (A-D) and up to 8 pins (0-7) */
if((port > GPIO_D_NUM) || (pin > 7)) {
PRINTF("PWM: Invalid pin/port settings\n");
return PWM_ERROR;
}
if(!pwm_configured(timer, ab)) {
PRINTF("PWM: GPTn not configured as PWM\n");
return PWM_ERROR;
}
/* Stop the PWM */
pwm_stop(timer, ab, port, pin, PWM_OFF_WHEN_STOP);
/* Disable the PWM mode */
REG(gpt_base + (GPTIMER_TAMR + offset)) = 0;
/* Restart the interval load and deassert values */
REG(gpt_base + (GPTIMER_TAILR + offset)) = 0;
REG(gpt_base + (GPTIMER_TAMATCHR + offset)) = 0;
/* Configure the port/pin as GPIO, input */
ioc_set_over(port, pin, IOC_OVERRIDE_DIS);
GPIO_SOFTWARE_CONTROL(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
GPIO_SET_INPUT(GPIO_PORT_TO_BASE(port), GPIO_PIN_MASK(pin));
return PWM_SUCCESS;
}
/*---------------------------------------------------------------------------*/
/** @} */

177
cpu/cc2538/dev/pwm.h Normal file
View file

@ -0,0 +1,177 @@
/*
* Copyright (c) 2015, 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 cc2538
* @{
*
* \defgroup cc2538-pwm-driver CC2538 PWM driver
*
* Driver for the CC2538 PWM on GPTIMER
*
* The driver uses the timers A and B of the general purpose timers to create
* a PWM signal, allowing to set a duty cycle value from 1-100%. This
* implementation relies on having a peripheral clock of 16MHz, but it can be
* easily changed (see PWM_FREQ_MIN and PWM_FREQ_MAX values). The reason it is
* fixed to these frequencies is to have a consistent duty cycle
* implementation.
* Depending on the specific needs these limits can be changed to meet a given
* duty cycle and lower frequencies by using the prescaler (GPTIMER_TnPR).
*
* @{
*
* \file
* Header file for the CC2538 PWM driver
*
* \author
* Javier Sanchez <jsanchez@zolertia.com>
* Antonio Lignan <alinan@zolertia.com>
*/
/*---------------------------------------------------------------------------*/
#ifndef PWM_H_
#define PWM_H_
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "dev/ioc.h"
#include "dev/gpio.h"
#include "dev/sys-ctrl.h"
/*---------------------------------------------------------------------------*/
/** \name PWM return values
* @{
*/
#define PWM_SUCCESS 0
#define PWM_ERROR (-1)
/** @} */
/*---------------------------------------------------------------------------*/
/** \name PWM recommended values respect to peripheral clock frequency
* @{
*/
/* Roughly 244 Hz with a 16 MHz IO clock, no prescaler */
#define PWM_SYS_IO_16MHZ_NO_PRES_MIN 0xFFFF
#define PWM_SYS_IO_16MHZ_NO_PRES_MIN_FREQ 244
/* Roughly 1 Hz with a 16 MHz IO clock, to keep frequency parameter in Hz */
#define PWM_SYS_IO_16MHZ_PRES_MIN 0x00F42400
#define PWM_SYS_IO_16MHZ_PRES_MIN_FREQ 1
/* Yields 160 KHz at 16 MHz and allows down to 1% (integer) duty cycles */
#define PWM_SYS_IO_16MHZ_NO_PRES_MAX 100
#define PWM_SYS_IO_16MHZ_NO_PRES_MAX_FREQ 160000
/** @} */
/*---------------------------------------------------------------------------*/
/** \name PWM driver definitions and configuration values
* @{
*/
#define PWM_TIMER_A 0
#define PWM_TIMER_B 1
#define PWM_TIMER_0 0
#define PWM_TIMER_1 1
#define PWM_TIMER_2 2
#define PWM_TIMER_3 3
#define PWM_TIMER_MIN PWM_TIMER_0
#define PWM_TIMER_MAX PWM_TIMER_3
#define PWM_SIGNAL_STRAIGHT 1
#define PWM_SIGNAL_INVERTED 0
#define PWM_OFF_WHEN_STOP 0
#define PWM_ON_WHEN_STOP 1
#define PWM_GPTIMER_CFG_SPLIT_MODE 0x04
#define PWM_DUTY_MAX 100
#define PWM_DUTY_MIN 0
#define PWM_FREQ_MIN PWM_SYS_IO_16MHZ_PRES_MIN_FREQ
#define PWM_FREQ_MAX PWM_SYS_IO_16MHZ_NO_PRES_MAX_FREQ
/** @} */
/*---------------------------------------------------------------------------*/
/** \name PWM functions
* @{
*/
/** \brief Configures the general purpose timer in PWM mode
* \param freq PWM frequency (in Hz)
* \param duty PWM duty cycle (percentage in integers)
* \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);
/*---------------------------------------------------------------------------*/
/** \brief Disables a previously PWM configured GPTn
* \param timer General purpose timer to disable [0-3]
* \param ab Select which timer to disable (Timer A or B)
* \param port Port number used as PWM to disable (set as input GPIO)
* \param pin Pin number used as PWM to disable (set as input GPIO)
* \return \c PWM_SUCCESS if successful, else \c PWM_ERROR
*
* This function disables a specific timer (A or B) and reset related registers
* to default values. The user must explicitely pass the port/pin number of
* the pin to disable as PWM and to be configured as input GPIO.
* The module clock is not disabled with this function
*/
int8_t pwm_disable(uint8_t timer, uint8_t ab, uint8_t port, uint8_t pin);
/*---------------------------------------------------------------------------*/
/** \brief Once configured, starts the PWM
* \param timer General purpose timer to start [0-3]
* \param ab Select which timer to start (Timer A or B)
* \param port Port number to use as PWM
* \param pin Pin number to use as PWM
* \return \c PWM_SUCCESS if successful, else \c PWM_ERROR
*/
int8_t pwm_start(uint8_t timer, uint8_t ab, uint8_t port, uint8_t pin);
/*---------------------------------------------------------------------------*/
/** \brief Halts the PWM in a given GPT/timer
* \param timer General purpose timer to stop [0-3]
* \param ab Select which timer to stop (Timer A or B)
* \param port Port of the gpio port mapped to the PWM to stop
* \param pin Pin of the gpio port mapped to the PWM to stop
* \param state State to leave the pin once stopped, on (1) or off (0)
* \return \c PWM_SUCCESS if successful, else \c PWM_ERROR
*/
int8_t pwm_stop(uint8_t timer, uint8_t ab, uint8_t port, uint8_t pin, uint8_t state);
/*---------------------------------------------------------------------------*/
/** \brief Sets the PWM duty cycle signal direction (high/low)
* \param timer General purpose timer [0-3]
* \param ab Select which timer to use (Timer A or B)
* \param dir Direction of the PWM signal, \c PWM_SIGNAL_INVERTED or
* \c PWM_SIGNAL_STRAIGHT
* \return \c PWM_SUCCESS if successful, else \c PWM_ERROR
*/
int8_t pwm_set_direction(uint8_t timer, uint8_t ab, uint8_t dir);
/*---------------------------------------------------------------------------*/
/** \brief Toggle the PWM signal direction (inverts the current duty cycle)
* \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_toggle_direction(uint8_t timer, uint8_t ab);
/*---------------------------------------------------------------------------*/
/** @} */
#endif /* PWM_H_ */
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/

View file

@ -1,5 +1,5 @@
DEFINES+=PROJECT_CONF_H=\"project-conf.h\"
CONTIKI_PROJECT = zoul-demo test-tsl2563 test-sht25
CONTIKI_PROJECT = zoul-demo test-tsl2563 test-sht25 test-pwm
CONTIKI_TARGET_SOURCEFILES += tsl2563.c sht25.c
all: $(CONTIKI_PROJECT)

View file

@ -0,0 +1,202 @@
/*
* Copyright (c) 2015, 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 remote-examples
* @{
*
* \defgroup remote-test-pwm Test the CC2538 PWM driver
*
* Demonstrates the use of the CC2538 PWM driver for the Zolertia's Zoul boards
* @{
*
* \file
* A quick program for testing the CC2538 PWM driver
* \author
* Javier Sanchez <jsanchez@zolertia.com>
* Antonio Lignan <alinan@zolertia.com>
*/
#include "contiki.h"
#include "cpu.h"
#include "dev/leds.h"
#include "dev/watchdog.h"
#include "dev/sys-ctrl.h"
#include "pwm.h"
#include "systick.h"
#include "lpm.h"
#include "dev/ioc.h"
#include <stdio.h>
#include <stdint.h>
/*---------------------------------------------------------------------------*/
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/*---------------------------------------------------------------------------*/
typedef struct {
uint8_t timer;
uint8_t ab;
uint8_t port;
uint8_t pin;
uint8_t duty;
uint8_t off_state;
uint32_t freq;
} pwm_config_t;
/*---------------------------------------------------------------------------*/
#define MAX_PWM 4
static const pwm_config_t pwm_num[MAX_PWM] = {
{
.timer = PWM_TIMER_1,
.ab = PWM_TIMER_A,
.port = GPIO_D_NUM,
.pin = 5,
.duty = 15,
.freq = 1,
.off_state = PWM_OFF_WHEN_STOP,
}, {
.timer = PWM_TIMER_1,
.ab = PWM_TIMER_B,
.port = GPIO_D_NUM,
.pin = 4,
.duty = 35,
.freq = 100,
.off_state = PWM_ON_WHEN_STOP,
}, {
.timer = PWM_TIMER_2,
.ab = PWM_TIMER_A,
.port = GPIO_D_NUM,
.pin = 3,
.duty = 50,
.freq = 1000,
.off_state = PWM_OFF_WHEN_STOP,
}, {
.timer = PWM_TIMER_2,
.ab = PWM_TIMER_B,
.port = GPIO_D_NUM,
.pin = 2,
.duty = 85,
.freq = 160000,
.off_state = PWM_ON_WHEN_STOP,
}
};
static uint8_t pwm_en[MAX_PWM];
/*---------------------------------------------------------------------------*/
#if DEBUG
static const char *
gpt_name(uint8_t timer)
{
switch(timer) {
case PWM_TIMER_0:
return "PWM TIMER 0";
case PWM_TIMER_1:
return "PWM TIMER 1";
case PWM_TIMER_2:
return "PWM TIMER 2";
case PWM_TIMER_3:
return "PWM TIMER 3";
default:
return "Unknown";
}
}
#endif
/*---------------------------------------------------------------------------*/
static struct etimer et;
/*---------------------------------------------------------------------------*/
PROCESS(cc2538_pwm_test, "cc2538 pwm test");
AUTOSTART_PROCESSES(&cc2538_pwm_test);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(cc2538_pwm_test, ev, data)
{
PROCESS_BEGIN();
uint8_t i;
memset(pwm_en, 0, MAX_PWM);
PRINTF("\nStarting the test\n");
for(i = 0; i < MAX_PWM; i++) {
if(pwm_enable(pwm_num[i].freq, pwm_num[i].duty,
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),
pwm_num[i].ab);
}
}
while(1) {
for(i = 0; i < MAX_PWM; i++) {
if((pwm_en[i]) &&
(pwm_start(pwm_num[i].timer, pwm_num[i].ab,
pwm_num[i].port, pwm_num[i].pin) != PWM_SUCCESS)) {
pwm_en[i] = 0;
PRINTF("%s (%u) failed to start \n", gpt_name(pwm_num[i].timer),
pwm_num[i].ab);
}
}
etimer_set(&et, CLOCK_SECOND * 2);
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
for(i = 0; i < MAX_PWM; i++) {
if((pwm_en[i]) &&
(pwm_toggle_direction(pwm_num[i].timer,
pwm_num[i].ab) != PWM_SUCCESS)) {
PRINTF("%s (%u) invert failed \n", gpt_name(pwm_num[i].timer),
pwm_num[i].ab);
}
}
etimer_set(&et, CLOCK_SECOND * 2);
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
for(i = 0; i < MAX_PWM; i++) {
if((pwm_en[i]) &&
(pwm_stop(pwm_num[i].timer, pwm_num[i].ab,
pwm_num[i].port, pwm_num[i].pin,
pwm_num[i].off_state) != PWM_SUCCESS)) {
pwm_en[i] = 0;
PRINTF("%s (%u) failed to stop\n", gpt_name(pwm_num[i].timer),
pwm_num[i].ab);
}
}
etimer_set(&et, CLOCK_SECOND * 2);
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/

View file

@ -29,6 +29,7 @@ In terms of hardware support, the following drivers have been implemented:
* Low Power Modes
* General-Purpose Timers. NB: GPT0 is in use by the platform code, the remaining GPTs are available for application development.
* ADC
* PWM
* Cryptoprocessor (AES-CCM-256, SHA-256)
* Public Key Accelerator (ECDH, ECDSA)
* Flash-based port of Coffee

View file

@ -54,6 +54,7 @@ In terms of hardware support, the following drivers have been implemented for th
* Cryptoprocessor (AES-CCM-256, SHA-256)
* Public Key Accelerator (ECDH, ECDSA)
* Flash-based port of Coffee
* PWM
* LEDs
* Buttons
* Built-in core temperature and battery sensor.