From 57ba2e8aad9cf729ad7e4432430998c04638175f Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Tue, 27 Sep 2016 23:08:21 +0200 Subject: [PATCH] Complete rewrite of timers API, now documented here : http://wiki.techno-innov.fr/index.php/Technique/Logiciel/API/Use/Drivers/Timers --- drivers/countertimers.c | 371 ++++++++++++++++++++++++++++++++ drivers/timers.c | 328 ++++++++++++---------------- include/drivers/countertimers.h | 116 ++++++++++ include/drivers/timers.h | 231 +++++++++++++++----- 4 files changed, 800 insertions(+), 246 deletions(-) create mode 100644 drivers/countertimers.c create mode 100644 include/drivers/countertimers.h diff --git a/drivers/countertimers.c b/drivers/countertimers.c new file mode 100644 index 0000000..eb678d6 --- /dev/null +++ b/drivers/countertimers.c @@ -0,0 +1,371 @@ +/**************************************************************************** + * drivers/countertimers.c + * + * Copyright 2012 Nathael Pajani + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + + + +/***************************************************************************** */ +/* Timers */ +/***************************************************************************** */ + +/* Timers driver for the integrated timers of the LPC122x. + * The LPC122x Has two 16bits timers and two 32bits timers. + * Refer to LPC122x documentation (UM10441.pdf) for more information. + */ + +#include "core/system.h" +#include "lib/errno.h" +#include "drivers/timers.h" +#include "drivers/countertimers.h" + + +/* These are local to our file */ +struct countertimer_device +{ + struct lpc_timer* regs; + uint32_t power_bit; + uint8_t irq; + uint8_t configured; + void (*callback)(uint32_t); /* Possible RX callback */ +}; + +static struct countertimer_device countertimers[NUM_COUNTERTIMERS] = { + { + .regs = LPC_TMR16B0, + .power_bit = LPC_SYS_ABH_CLK_CTRL_CT16B0, + .irq = TIMER0_IRQ, + .configured = 0, + .callback = NULL, + }, + { + .regs = LPC_TMR16B1, + .power_bit = LPC_SYS_ABH_CLK_CTRL_CT16B1, + .irq = TIMER1_IRQ, + .configured = 0, + .callback = NULL, + }, + { + .regs = LPC_TMR32B0, + .power_bit = LPC_SYS_ABH_CLK_CTRL_CT32B0, + .irq = TIMER2_IRQ, + .configured = 0, + .callback = NULL, + }, + { + .regs = LPC_TMR32B1, + .power_bit = LPC_SYS_ABH_CLK_CTRL_CT32B1, + .irq = TIMER3_IRQ, + .configured = 0, + .callback = NULL, + }, +}; + +/* Handlers */ +void TIMER_Handler(struct countertimer_device* timer) +{ + uint32_t intr_flags = timer->regs->int_reg; /* Backup the flags */ + + /* Clear the interrupt */ + timer->regs->int_reg = intr_flags; + /* And call the user routine if one has been registered */ + if (timer->callback != NULL) { + timer->callback(intr_flags); + } +} +void TIMER_0_Handler(void) +{ + TIMER_Handler(&countertimers[0]); +} +void TIMER_1_Handler(void) +{ + TIMER_Handler(&countertimers[1]); +} +void TIMER_2_Handler(void) +{ + TIMER_Handler(&countertimers[2]); +} +void TIMER_3_Handler(void) +{ + TIMER_Handler(&countertimers[3]); +} + + + +/* Start the timer : + * Remove the reset flag if present and set timer enable flag. + * Timer must be turned on and configured (no checks done here). + */ +void countertimer_start(uint8_t timer_num) +{ + /* Remove reset flag and set timer enable flag */ + countertimers[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE; +} + +/* Pause the timer counter, does not reset */ +void countertimer_pause(uint8_t timer_num) +{ + /* Remove timer enable flag */ + countertimers[timer_num].regs->timer_ctrl = 0; +} + +/* Stops and resets the timer counter */ +void countertimer_stop(uint8_t timer_num) +{ + /* Remove timer enable flag and request reset */ + countertimers[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET; + /* Remove reset flag */ + countertimers[timer_num].regs->timer_ctrl = 0; +} + +/* Resets the timer and lets it count again imediately */ +void countertimer_restart(uint8_t timer_num) +{ + /* Set timer reset flag */ + countertimers[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET; + /* Remove reset flag and start counter */ + countertimers[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE; +} + +int countertimer_get_counter_val(uint8_t timer_num, uint32_t* val) +{ + *val = countertimers[timer_num].regs->timer_counter; + return 0; +} + +int countertimer_get_capture_val(uint8_t timer_num, uint8_t channel, uint32_t* val) +{ + if (channel >= MAX_CHANNELS) { + return -EINVAL; + } + *val = countertimers[timer_num].regs->capture_reg[channel]; + return 0; +} + + +/* Change the match value of a single timer channel */ +int countertimer_set_match(uint8_t timer_num, uint8_t channel, uint32_t val) +{ + if (channel > NUM_COUNTERTIMERS_CHANS) + return -EINVAL; + + countertimers[timer_num].regs->match_reg[channel] = val; + return 0; +} + +struct common_operations countertimer_ops = { + .start = countertimer_start, + .cont = countertimer_start, + .pause = countertimer_pause, + .stop = countertimer_stop, + .restart = countertimer_restart, + .halt = countertimer_stop, + .get_counter = countertimer_get_counter_val, + .get_capture = countertimer_get_capture_val, + .set_match = countertimer_set_match, +}; + + + +/*******************************************************************************/ +/* Configuration operations */ + + +/* Timer Setup as PWM */ +/* Returns 0 on success + * Takes a timer number and a timer PWM config structure as arguments. + * Refer to timer PWM config structure for details. + */ +int countertimer_pwm_setup(uint8_t timer_num, const struct lpc_timer_pwm_config* conf) +{ + struct countertimer_device* timer = &(countertimers[timer_num]); + uint8_t max_chan = 0; + uint8_t active_chans = 0; + int i = 0; + + /* Make sure we have a PWM cycle length */ + if (conf->period == 0) { + return -EINVAL; + } + switch (timer_num) { + case LPC_TIMER_16B0: + case LPC_TIMER_16B1: + if (conf->nb_channels > NUM_COUNTERTIMERS_16B_PWM_CHANS) { + return -EINVAL; + } + max_chan = NUM_COUNTERTIMERS_16B_PWM_CHANS; + break; + case LPC_TIMER_32B0: + case LPC_TIMER_32B1: + if (conf->nb_channels > NUM_COUNTERTIMERS_32B_PWM_CHANS) { + return -EINVAL; + } + max_chan = NUM_COUNTERTIMERS_32B_PWM_CHANS; + break; + } + + if (conf->period_chan >= MAX_CHANNELS) { + return -EINVAL; + } + /* Setup selected PWM channels */ + for (i = 0; i < conf->nb_channels; i++) { + if (conf->outputs[i] >= max_chan) { + continue; + } + timer->regs->match_reg[ conf->outputs[i] ] = conf->match_values[i]; + /* Mark channel as active */ + active_chans |= LPC_PWM_CHANNEL_ENABLE(conf->outputs[i]); + } + + /* Setup period */ + timer->regs->match_reg[ conf->period_chan ] = conf->period; + /* Setup selected channel as PWM cycle length control */ + timer->regs->match_ctrl &= ~(LPC_TIMER_MATCH_ERASE(conf->period_chan)); + timer->regs->match_ctrl |= (LPC_TIMER_RESET_ON_MATCH << LPC_TIMER_MATCH_SHIFT(conf->period_chan)); + active_chans |= LPC_PWM_CHANNEL_ENABLE(conf->period_chan); + + /* Setup count mode as timer */ + timer->regs->count_ctrl = LPC_COUNTER_IS_TIMER; + + /* Activate selected PWM channels and period channel */ + timer->regs->pwm_ctrl = active_chans; + + return 0; /* Config OK */ +} + + +/* Timer Setup in timer or counter mode, with optionnal capture and match configuration + * Takes a timer number and a timer counter config structure as arguments. + * Returns 0 on success + */ +int countertimer_tc_setup(uint8_t timer_num, const struct lpc_tc_config* conf) +{ + struct countertimer_device* timer = &(countertimers[timer_num]); + int i = 0; + + if (conf->mode & LPC_TIMER_MODE_SPECIFIC) { + return -EINVAL; + } + /* Erase existing configuration */ + timer->regs->capture_ctrl = 0; + timer->regs->match_ctrl = 0; + timer->regs->external_match = 0; + + /* Override the prescaler value if requested */ + if (conf->prescale_val != 0) { + timer->regs->prescale = conf->prescale_val; + } + /* Select between timer (counts on PCLK) and counter mode (counts on CAP events) */ + if (conf->mode & LPC_TIMER_MODE_COUNTER) { + /* Configure the counter */ + timer->regs->count_ctrl = (conf->count_control & 0x0F); + timer->regs->count_ctrl |= LPC_COUNTER_INC_INPUT(conf->count_chan); + } else { + /* Timer mode */ + timer->regs->count_ctrl = LPC_COUNTER_IS_TIMER; + } + + /* Configure the reset on capture functionality when selected */ + if (conf->reset_on_cap & LPC_COUNTER_CLEAR_ON_EVENT_EN) { + timer->regs->count_ctrl = LPC_COUNTER_CLEAR_ON_EVENT_EN; + timer->regs->count_ctrl |= ((conf->reset_on_cap & 0x07) << LPC_COUNTER_CLEAR_ON_EVENT_SHIFT); + } + + if (conf->mode & LPC_TIMER_MODE_CAPTURE) { + for (i = 0; i < MAX_CHANNELS; i++) { + timer->regs->capture_ctrl |= ((conf->cap_control[i] & 0x07) << LPC_TIMER_CAPTURE_SHIFT(i)); + } + } + + if (conf->mode & LPC_TIMER_MODE_MATCH) { + for (i = 0; i < MAX_CHANNELS; i++) { + timer->regs->match_ctrl |= ((conf->match_control[i] & 0x07) << LPC_TIMER_MATCH_SHIFT(i)); + timer->regs->match_reg[i] = conf->match[i]; + timer->regs->external_match |= ((conf->ext_match_config[i] & 0x03) << LPC_TIMER_EXT_MATCH_SHIFT(i)); + } + } + + /* Ensure that counter input chan has not been configured to reset or stop the timer or as a + * capture channel (See remarks in user manual UM10441 page 268 section 14.7.11) when the ct is + * configured in counter mode. */ + if (conf->mode & LPC_TIMER_MODE_COUNTER) { + timer->regs->match_ctrl &= ~LPC_TIMER_MATCH_ERASE(conf->count_chan); + timer->regs->capture_ctrl &= ~LPC_TIMER_CAPTURE_ERASE(conf->count_chan); + timer->regs->external_match &= ~LPC_TIMER_EXT_MATCH_ERASE(conf->count_chan); + } + + return 0; /* Config OK */ +} + +struct config_operations countertimer_cfg_ops = { + .pwm_config = countertimer_pwm_setup, + .tc_config = countertimer_tc_setup, +}; + + + + +/*******************************************************************************/ +/* Init operations */ + +/* Power up a timer. + * clkrate is the desired timer clock rate. It will be used to divide the main clock + * to get the timer prescaler value. + * Set clkrate to 0 to disable the prescaler. + */ +int countertimer_on(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t)) +{ + struct countertimer_device* timer = NULL; + uint32_t prescale; /* The clock divider for the counter */ + + timer = &(countertimers[timer_num]); + + NVIC_DisableIRQ( timer->irq ); + /* Power up the timer */ + subsystem_power(timer->power_bit, 1); + /* Reset counter on next PCLK positive edge, and disable counter */ + timer->regs->timer_ctrl = LPC_TIMER_COUNTER_RESET; + + /* Store the callback, OK even if none given */ + timer->callback = callback; + + /* Set the prescaler value */ + if (clkrate == 0) { + prescale = 0; + } else { + prescale = (get_main_clock() / clkrate) - 1; + } + timer->regs->prescale = prescale; + + NVIC_EnableIRQ(countertimers[timer_num].irq); + return 0; +} + +/* Removes the main clock from the selected timer block */ +int countertimer_off(uint8_t timer_num) +{ + NVIC_DisableIRQ( countertimers[timer_num].irq ); + subsystem_power(countertimers[timer_num].power_bit, 0); + return 0; +} + +struct init_operations countertimer_init_ops = { + .timer_on = countertimer_on, + .timer_off = countertimer_off, +}; + diff --git a/drivers/timers.c b/drivers/timers.c index 4e1e451..994c9fc 100644 --- a/drivers/timers.c +++ b/drivers/timers.c @@ -24,9 +24,18 @@ /* Timers */ /***************************************************************************** */ -/* Timers driver for the integrated timers of the LPC1224. - * The LPC1224 Has two 16bits timers and 2 32bits timers. - * Refer to LPC1224 documentation (UM10441.pdf) for more information. +/* Driver for the different kinds of timers available in the LPC122x + * + * This inludes : + * - 32 bits Timers driver + * The LPC122x has two 32 bits Timer. + * - 16 bits Timers driver + * The LPC122x has two 16 bits Timer. + * + * Refer to LPC122x documentation (UM10441.pdf) for more information. + * + * All common functions are available using a common interface. Only specific + * functions have a specific interface, described in separate headers files. */ @@ -36,232 +45,171 @@ /* These are local to our file */ -struct timer_device -{ - struct lpc_timer* regs; - uint32_t power_bit; - uint32_t irq; - void (*callback)(uint32_t); /* Possible RX callback */ +struct timer_device { + uint8_t num; + struct common_operations* ops; + struct config_operations* cfg_ops; + struct init_operations* init_ops; }; -static struct timer_device timer_devices[NUM_TIMERS] = { - { LPC_TMR16B0, LPC_SYS_ABH_CLK_CTRL_CT16B0, TIMER0_IRQ }, - { LPC_TMR16B1, LPC_SYS_ABH_CLK_CTRL_CT16B1, TIMER1_IRQ }, - { LPC_TMR32B0, LPC_SYS_ABH_CLK_CTRL_CT32B0, TIMER2_IRQ }, - { LPC_TMR32B1, LPC_SYS_ABH_CLK_CTRL_CT32B1, TIMER3_IRQ }, -}; - -/* Handlers */ -void TIMER_Handler(struct timer_device* timer) -{ - uint32_t intr_flags = timer->regs->int_reg; /* Backup the flags */ +/* 32 bits and 16 bits Counter Timers */ +extern struct common_operations countertimer_ops; +extern struct config_operations countertimer_cfg_ops; +extern struct init_operations countertimer_init_ops; + + +struct timer_device timers[NUM_TIMERS] = { + { + .num = LPC_TIMER_16B0, + .ops = &countertimer_ops, + .cfg_ops = &countertimer_cfg_ops, + .init_ops = &countertimer_init_ops, + }, + { + .num = LPC_TIMER_16B1, + .ops = &countertimer_ops, + .cfg_ops = &countertimer_cfg_ops, + .init_ops = &countertimer_init_ops, + }, + { + .num = LPC_TIMER_32B0, + .ops = &countertimer_ops, + .cfg_ops = &countertimer_cfg_ops, + .init_ops = &countertimer_init_ops, + }, + { + .num = LPC_TIMER_32B1, + .ops = &countertimer_ops, + .cfg_ops = &countertimer_cfg_ops, + .init_ops = &countertimer_init_ops, + }, +}; - /* Clear the interrupt */ - timer->regs->int_reg = intr_flags; - /* And call the user routine if one has been registered */ - if (timer->callback != NULL) { - timer->callback(intr_flags); - } -} -void TIMER_0_Handler(void) -{ - TIMER_Handler(&timer_devices[0]); -} -void TIMER_1_Handler(void) -{ - TIMER_Handler(&timer_devices[1]); -} -void TIMER_2_Handler(void) -{ - TIMER_Handler(&timer_devices[2]); -} -void TIMER_3_Handler(void) -{ - TIMER_Handler(&timer_devices[3]); -} +/*******************************************************************************/ +/* Wrappers to common functions */ +#define GENERIC_TIMER_OPS(name) \ + void timer_ ## name(uint8_t timer_num) \ + { \ + if (timer_num >= NUM_TIMERS) \ + return; \ + if (timers[timer_num].ops && timers[timer_num].ops->name) { \ + timers[timer_num].ops->name(timer_num); \ + } \ + } -/* Start the timer : - * Remove the reset flag if present and set timer enable flag. - * Timer must be turned on and configured (no checks done here). +/* Start a timer */ +GENERIC_TIMER_OPS(start); +/* Pause timer operation */ +GENERIC_TIMER_OPS(pause); +/* Continue timer operation when the timer got paused */ +GENERIC_TIMER_OPS(cont); +/* Reset the timer and let it count again imediately */ +GENERIC_TIMER_OPS(restart); +/* Stop timer counting and reset timer counter to reload value / initial state */ +GENERIC_TIMER_OPS(stop); +/* Same as stop */ +GENERIC_TIMER_OPS(halt); + + +/* Get the current counter value + * Return 0 if the value is valid. */ -void timer_start(uint32_t timer_num) -{ - if (timer_num >= NUM_TIMERS) - return; - /* Remove reset flag and set timer enable flag */ - timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE; -} -void timer_continue(uint32_t timer_num) __attribute__ ((alias ("timer_start"))); -/* Pause the timer counter, does not reset */ -void timer_pause(uint32_t timer_num) -{ - if (timer_num >= NUM_TIMERS) - return; - /* Remove timer enable flag */ - timer_devices[timer_num].regs->timer_ctrl = 0; -} -/* Stops and resets the timer counter */ -void timer_stop(uint32_t timer_num) -{ - if (timer_num >= NUM_TIMERS) - return; - /* Remove timer enable flag and request reset */ - timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET; - /* Remove reset flag */ - timer_devices[timer_num].regs->timer_ctrl = 0; -} -/* Resets the timer and lets it count again imediately */ -void timer_restart(uint32_t timer_num) +int timer_get_counter_val(uint8_t timer_num, uint32_t* val) { if (timer_num >= NUM_TIMERS) - return; - /* Set timer reset flag */ - timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET; - /* Remove reset flag and start counter */ - timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE; + return -EINVAL; + if (timers[timer_num].ops && timers[timer_num].ops->get_counter) { + return timers[timer_num].ops->get_counter(timer_num, val); + } + return -ENODEV; } -uint32_t timer_get_capture_val(uint32_t timer_num, uint32_t channel) + +/* Get the value of the timer when the capture event last triggered + * Return 0 if the value is valid. + */ +int timer_get_capture_val(uint8_t timer_num, uint8_t channel, uint32_t* val) { if (timer_num >= NUM_TIMERS) - return 0; - /* FIXME */ - return 0; + return -EINVAL; + if (timers[timer_num].ops && timers[timer_num].ops->get_capture) { + return timers[timer_num].ops->get_capture(timer_num, channel, val); + } + return -ENODEV; } -uint32_t timer_get_counter_val(uint32_t timer_num) + + +/* Change the match value of a single timer channel */ +int timer_set_match(uint8_t timer_num, uint8_t channel, uint32_t val) { if (timer_num >= NUM_TIMERS) - return 0; - return timer_devices[timer_num].regs->timer_counter; + return -EINVAL; + if (timers[timer_num].ops && timers[timer_num].ops->set_match) { + return timers[timer_num].ops->set_match(timer_num, channel, val); + } + return -ENODEV; } -/* Change the match value of a single timer channel */ -void timer_set_match(uint32_t timer_num, uint32_t channel, uint32_t val) + +/*******************************************************************************/ +/* Configuration */ + +/* Configure the timer as PWM. Call to timer-specific function */ +int timer_pwm_config(uint8_t timer_num, const struct lpc_timer_pwm_config* conf) { if (timer_num >= NUM_TIMERS) - return; - if (channel > 3) - return; - - timer_devices[timer_num].regs->match_reg[channel] = val; + return -EINVAL; + if (timers[timer_num].cfg_ops && timers[timer_num].cfg_ops->pwm_config) { + return timers[timer_num].cfg_ops->pwm_config(timer_num, conf); + } + return -ENODEV; } -/***************************************************************************** */ -/* Timer Setup */ -/* Returns 0 on success - * Takes a timer number and a timer config structure as arguments. - * Refer to timer config structure for details. +/* Timer Setup in timer or counter mode, with optionnal capture and match configuration + * Takes a timer number and a timer counter config structure as arguments. + * Returns 0 on success */ -int timer_setup(uint32_t timer_num, const struct timer_config* conf) +int timer_counter_config(uint8_t timer_num, const struct lpc_tc_config* conf) { - struct timer_device* timer = NULL; - int i = 0; if (timer_num >= NUM_TIMERS) - return -ENODEV; - timer = &(timer_devices[timer_num]); - - /* Configure the reset on capture functionality */ - if (conf->reset_on_capture != 0x00) { - timer->regs->count_ctrl = LPC_COUNTER_CLEAR_ON_EVENT_EN; - timer->regs->count_ctrl |= ((conf->reset_on_capture & 0x07) << LPC_COUNTER_CLEAR_ON_EVENT_SHIFT); + return -EINVAL; + if (timers[timer_num].cfg_ops && timers[timer_num].cfg_ops->tc_config) { + return timers[timer_num].cfg_ops->tc_config(timer_num, conf); } - - switch (conf->mode) { - case LPC_TIMER_MODE_TIMER: - timer->regs->capture_ctrl = 0; /* Timer mode ! */ - timer->regs->count_ctrl = LPC_COUNTER_IS_TIMER; - break; - case LPC_TIMER_MODE_COUNTER: - if ((conf->config[0] & 0x03) == 0x00) { - return -EINVAL; - } - /* Must set capture chanel N config to 0b000 in capture control register, - * (see remarks in user manual UM10441 page 268 section 14.7.11) - * Use the LPC_COUNTER_INC_INPUT(x) set by the user to do so automatically - */ - timer->regs->capture_ctrl &= ~LPC_TIMER_CAPTURE_ERASE(((conf->config[0] >> LPC_COUNTER_INC_INPUT_SHIFT) & 0x03) * 3); - /* Configure the counter */ - timer->regs->count_ctrl |= (conf->config[0] & 0x0F); - break; - case LPC_TIMER_MODE_CAPTURE: - timer->regs->capture_ctrl = 0; - for (i = 0; i < NUM_CHANS; i++) { - timer->regs->capture_ctrl |= ((conf->config[i] & 0x07) << LPC_TIMER_CAPTURE_SHIFT(i)); - } - break; - case LPC_TIMER_MODE_MATCH: - timer->regs->match_ctrl = 0; - timer->regs->external_match = 0; - for (i = 0; i < NUM_CHANS; i++) { - timer->regs->match_ctrl |= ((conf->config[i] & 0x07) << LPC_TIMER_MATCH_SHIFT(i)); - timer->regs->match_reg[i] = conf->match[i]; - timer->regs->external_match |= ((conf->ext_match_config[i] & 0x03) << (LPC_TIMER_EXT_MATCH0_SHIFT + i*2)); - } - break; - case LPC_TIMER_MODE_PWM: - /* Make sure we have a PWM cycle length */ - if (conf->match[ conf->config[1] ] == 0) { - return -EINVAL; - } - /* Activate selected PWM channels 0 to 3 */ - timer->regs->pwm_ctrl = (conf->config[0] & 0x0F); - timer->regs->match_ctrl &= ~(LPC_TIMER_MATCH_ERASE(conf->config[1])); - timer->regs->match_ctrl |= (LPC_TIMER_RESET_ON_MATCH << LPC_TIMER_MATCH_SHIFT(conf->config[1])); - for (i = 0; i < NUM_CHANS; i++) { - timer->regs->match_reg[i] = conf->match[i]; - } - break; - case LPC_TIMER_MODE_PWD: - break; - } - return 0; /* Config OK */ + return -ENODEV; } +/*******************************************************************************/ +/* Init operations */ /* Power up a timer. - * Note that clkrate should be a divider of the main clock frequency chosed - * for your application as it will be used to divide the main clock to get - * the prescaler value. - * Set clkrate to 0 to disable the prescaler. + * clkrate is the desired timer clock rate. It will be used to divide the main clock + * to get the timer prescaler value. + * Set clkrate to 0 to disable the prescaler. + * callback is the interrupt callback for this timer. */ -void timer_on(uint32_t timer_num, uint32_t clkrate, void (*callback)(uint32_t)) +int timer_on(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t)) { - struct timer_device* timer = NULL; - uint32_t prescale; /* The clock divider for the counter */ - if (timer_num >= NUM_TIMERS) - return; - timer = &(timer_devices[timer_num]); - - NVIC_DisableIRQ( timer->irq ); - /* Power up the timer */ - subsystem_power(timer->power_bit, 1); - /* Reset counter on next PCLK positive edge, and disable counter */ - timer->regs->timer_ctrl = LPC_TIMER_COUNTER_RESET; - - /* Store the callback, OK even if none given */ - timer->callback = callback; - - /* Set the prescaler value */ - if (clkrate == 0) { - prescale = 0; - } else { - prescale = (get_main_clock() / clkrate) - 1; + return -EINVAL; + if (timers[timer_num].init_ops && timers[timer_num].init_ops->timer_on) { + timers[timer_num].init_ops->timer_on(timer_num, clkrate, callback); } - timer->regs->prescale = prescale; - - NVIC_EnableIRQ(timer_devices[timer_num].irq); + return -ENODEV; } /* Removes the main clock from the selected timer block */ -void timer_off(uint32_t timer_num) +int timer_off(uint8_t timer_num) { if (timer_num >= NUM_TIMERS) - return; - NVIC_DisableIRQ( timer_devices[timer_num].irq ); - subsystem_power(timer_devices[timer_num].power_bit, 0); + return -EINVAL; + if (timers[timer_num].init_ops && timers[timer_num].init_ops->timer_off) { + return timers[timer_num].init_ops->timer_off(timer_num); + } + return -ENODEV; } + diff --git a/include/drivers/countertimers.h b/include/drivers/countertimers.h new file mode 100644 index 0000000..fa758ba --- /dev/null +++ b/include/drivers/countertimers.h @@ -0,0 +1,116 @@ +/**************************************************************************** + * drivers/countertimers.h + * + * Copyright 2012-2016 Nathael Pajani + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + +#ifndef DRIVERS_COUNTER_TIMERS_H +#define DRIVERS_COUNTER_TIMERS_H + + +#include "lib/stdint.h" +#include "core/lpc_regs.h" + +/***************************************************************************** */ +/* Counter Timer */ +/***************************************************************************** */ +/* Timers driver for the integrated timers of the LPC122x. + * The LPC122x Has two 16bits timers and two 32bits timers. + * Refer to LPC122x documentation (UM10441.pdf) for more information. + */ +#define NUM_COUNTERTIMERS 4 +#define NUM_COUNTERTIMERS_CHANS 4 +#define NUM_COUNTERTIMERS_16B_PWM_CHANS 2 +#define NUM_COUNTERTIMERS_32B_PWM_CHANS 4 + +/* Timer (TMR) */ +struct lpc_timer +{ + volatile uint32_t int_reg; /* 0x000 : Interrupt Register (R/W) */ + volatile uint32_t timer_ctrl; /* 0x004 : Timer Control Register (R/W) */ + volatile uint32_t timer_counter; /* 0x008 : Timer Counter Register (R/W) */ + volatile uint32_t prescale; /* 0x00C : Prescale Register (R/W) */ + volatile uint32_t prescale_counter; /* 0x010 : Prescale Counter Register (R/W) */ + volatile uint32_t match_ctrl; /* 0x014 : Match Control Register (R/W) */ + volatile uint32_t match_reg[4]; /* 0x018 : Match Register 0 to 3 (R/W) */ + volatile uint32_t capture_ctrl; /* 0x028 : Capture Control Register (R/W) */ + volatile const uint32_t capture_reg[4]; /* 0x02C : Capture Register 0 to 3 (R/ ) */ + volatile uint32_t external_match; /* 0x03C : External Match Register (R/W) */ + uint32_t reserved_2[12]; + volatile uint32_t count_ctrl; /* 0x070 : Count Control Register (R/W) */ + volatile uint32_t pwm_ctrl; /* 0x074 : PWM Control Register (R/W) */ +}; +#define LPC_TMR16B0 ((struct lpc_timer *) LPC_TIMER0_BASE) +#define LPC_TMR16B1 ((struct lpc_timer *) LPC_TIMER1_BASE) +#define LPC_TMR32B0 ((struct lpc_timer *) LPC_TIMER2_BASE) +#define LPC_TMR32B1 ((struct lpc_timer *) LPC_TIMER3_BASE) +#define LPC_TIMER_REGS(x) ((struct lpc_timer *) (LPC_TIMER0_BASE + ((x) * 0x4000))) + + +#define LPC_TIMER_COUNTER_ENABLE (1 << 0) /* CEN */ +#define LPC_TIMER_COUNTER_RESET (1 << 1) /* CRST */ + + +/* Match internal configuration */ +#define LPC_TIMER_INTERRUPT_ON_MATCH 0x01 +#define LPC_TIMER_RESET_ON_MATCH 0x02 +#define LPC_TIMER_STOP_ON_MATCH 0x04 +#define LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH \ + (LPC_TIMER_INTERRUPT_ON_MATCH | LPC_TIMER_RESET_ON_MATCH | LPC_TIMER_STOP_ON_MATCH) +#define LPC_TIMER_MATCH_SHIFT(x) (((x) & 0x03) * 3) +#define LPC_TIMER_MATCH_ERASE(x) (0x07 << LPC_TIMER_MATCH_SHIFT(x)) + +/* Capture internal configuration */ +#define LPC_TIMER_CAP_ON_RISING_EDGE 0x01 +#define LPC_TIMER_CAP_ON_FALLING_EDGE 0x02 +#define LPC_TIMER_INTERRUPT_ON_CAPTURE 0x04 +#define LPC_TIMER_CAPTURE_SHIFT(x) (((x) & 0x03) * 3) +#define LPC_TIMER_CAPTURE_ERASE(x) (0x07 << LPC_TIMER_CAPTURE_SHIFT(x)) + +/* Match external configuration */ +#define LPC_TIMER_NOTHING_ON_MATCH 0x00 +#define LPC_TIMER_CLEAR_ON_MATCH 0x01 +#define LPC_TIMER_SET_ON_MATCH 0x02 +#define LPC_TIMER_TOGGLE_ON_MATCH 0x03 +#define LPC_TIMER_EXT_MATCH_SHIFT(x) (((x) * 2) + 4) +#define LPC_TIMER_EXT_MATCH_ERASE(x) (0x03 << LPC_TIMER_EXT_MATCH_SHIFT(x)) + +/* Counter */ +#define LPC_COUNTER_IS_TIMER 0x00 +#define LPC_COUNTER_INC_ON_RISING 0x01 +#define LPC_COUNTER_INC_ON_FALLING 0x02 +#define LPC_COUNTER_INC_ON_BOTH 0x03 +#define LPC_COUNTER_INC_INPUT_SHIFT 2 +#define LPC_COUNTER_INC_INPUT(x) (((x) & 0x03) << LPC_COUNTER_INC_INPUT_SHIFT) +#define LPC_COUNTER_CLEAR_ON_EVENT_EN (0x01 << 4) +#define LPC_COUNTER_CLEAR_ON_EVENT_SHIFT 5 +#define LPC_COUNTER_CLEAR_ON_CHAN0_RISE 0x00 +#define LPC_COUNTER_CLEAR_ON_CHAN0_FALL 0x01 +#define LPC_COUNTER_CLEAR_ON_CHAN1_RISE 0x02 +#define LPC_COUNTER_CLEAR_ON_CHAN1_FALL 0x03 +#define LPC_COUNTER_CLEAR_ON_CHAN2_RISE 0x04 +#define LPC_COUNTER_CLEAR_ON_CHAN2_FALL 0x05 +#define LPC_COUNTER_CLEAR_ON_CHAN3_RISE 0x06 +#define LPC_COUNTER_CLEAR_ON_CHAN3_FALL 0x07 + +/* PWM */ +#define LPC_PWM_CHANNEL_ENABLE(x) (0x01 << ((x) & 0x03)) + + +#endif /* DRIVERS_COUNTER_TIMERS_H */ + + diff --git a/include/drivers/timers.h b/include/drivers/timers.h index d5008a2..b735ddb 100644 --- a/include/drivers/timers.h +++ b/include/drivers/timers.h @@ -1,7 +1,7 @@ /**************************************************************************** * drivers/timers.h * - * Copyright 2012 Nathael Pajani + * Copyright 2012-2016 Nathael Pajani * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,100 +24,219 @@ /***************************************************************************** */ /* Timers */ /***************************************************************************** */ -/* Timers driver for the integrated timers of the LPC1224. - * Four timers are available, Two 16 bits and two 32 bits - * All timers have 4 channels though 32 bits timers have all 4 channels available - * on capture /match pins while 16bits ones have only two (channels 0 and 1). - * Refer to LPC1224 documentation (UM10441.pdf) for more information. + +/* Driver for the different kinds of timers available in the LPC122x + * + * This inludes : + * - 32 bits Timers driver + * The LPC122x has two 32 bits Timer. + * - 16 bits Timers driver + * The LPC122x has two 16 bits Timer. + * + * Refer to LPC122x documentation (UM10441.pdf) for more information. + * + * All common functions are available using a common interface. Only specific + * functions have a specific interface, described in separate headers files. + */ #include "lib/stdint.h" +#include "drivers/countertimers.h" + +/***************************************************************************** */ +/* All timers have 4 channels. 32 bits timers have all 4 channels available + * on capture / match pins while 16 bits timers have only two (channels 0 and 1). */ #define NUM_TIMERS 4 -#define NUM_CHANS 4 +#define MAX_CHANNELS 4 /* Timer numbers to be used for functions from this driver. */ -#define LPC_TIMER_16B0 0 -#define LPC_TIMER_16B1 1 -#define LPC_TIMER_32B0 2 -#define LPC_TIMER_32B1 3 - - -/* Timer modes for the "mode" timer config structure field */ -enum lpc_timer_mode { - LPC_TIMER_MODE_TIMER = 0, - LPC_TIMER_MODE_COUNTER, - LPC_TIMER_MODE_CAPTURE, - LPC_TIMER_MODE_MATCH, - LPC_TIMER_MODE_PWM, /* Pulse Width Modulation */ - LPC_TIMER_MODE_PWD, /* Pulse Width Demodulation */ +enum lpc_timers { + LPC_TIMER_16B0 = 0, /* 16 bits timer 0 */ + LPC_TIMER_16B1, /* 16 bits timer 1 */ + LPC_TIMER_32B0, /* 32 bits timer 0 */ + LPC_TIMER_32B1, /* 32 bits timer 1 */ }; -/* Structure used to pass parameters to configure a timer */ -/* Notes: - * In counter or PWM mode, the config is done using config[0] for enabled channels and config[1] holds - * the channel number used to control PWM cycle. - * Note that the manual recommends Channel 3 be used for PWM cycle length. - * The field "reset_on_capture" must be set to LPC_COUNTER_CLEAR_ON_EVENT_EN ored with one - * of the LPC_COUNTER_CLEAR_ON_CHAN*_* to activate the clear timer on event functionality +/* Available timer modes * + * Some mode may be combined, and some are exlusive : + * Values here are important : + * PWM and PWD and any further mode handled by a specific configure function will have + * bit 2 set */ -struct timer_config -{ - uint32_t mode; /* Counter, Timer Capture, Timer Match or PWM */ - uint8_t config[NUM_CHANS]; /* Configure the internal behavior when a capture or a match occurs */ - uint8_t ext_match_config[NUM_CHANS]; /* Configure the external behavior when a match occurs */ - uint32_t match[NUM_CHANS]; /* The match values if the timer is used in match mode */ - uint32_t reset_on_capture; +#define LPC_TIMER_MODE_TIMER (0x00 << 0) +#define LPC_TIMER_MODE_COUNTER (0x01 << 0) +#define LPC_TIMER_MODE_SPECIFIC (0x02 << 0) +#define LPC_TIMER_MODE_CAPTURE (0x01 << 6) +#define LPC_TIMER_MODE_MATCH (0x01 << 7) +/* Specific modes */ +#define LPC_TIMER_MODE_PWM (LPC_TIMER_MODE_SPECIFIC | (0x01 << 2)) /* Pulse Width Modulation */ +#define LPC_TIMER_MODE_PWD (LPC_TIMER_MODE_SPECIFIC | (0x02 << 2)) /* Pulse Width Demodulation */ + + +/*******************************************************************************/ +/* Configurations */ + +/* PWM + * This structure is used for the timer_pwm_config() function. + * nb_channels is the number of PWM channels used + * period_chan is the channel number used to generate the period. + * period is the PWM cycle period, in timer clock cycles. It is the value to wich the timer + * will count. + * outputs[] is the list of outputs / channels corresponding to each 'match_values' + * match_values[] control the PWM channels duty-cycle. They are the timer values at wich + * the corresponding outputs will toggle. They must be lower than or equal to the period + * value. + * outputs_initial_state defines whether the outputs start low or high at the beginning of + * the PWM cycle. Support for this depends on the target. + * The LPC122x enforces a low state initial output. + */ +struct lpc_timer_pwm_config { + uint8_t nb_channels; + uint8_t period_chan; + uint32_t period; + uint8_t outputs[MAX_CHANNELS]; + uint32_t match_values[MAX_CHANNELS]; + uint32_t outputs_initial_state; +}; + +/* This structure is used for the timer_counter_config() function. + * mode is a mask of LPC_TIMER_MODE_* values. Note that some can be combined, and some are + * exclusive : only one of these : LPC_TIMER_MODE_TIMER or LPC_TIMER_MODE_COUNTER and + * possibly one or both of these : LPC_TIMER_MODE_CAPTURE and LPC_TIMER_MODE_MATCH + * prescale_val: if not nul, override the prescaler value computed during timer_on() with this + * value. It is not clear in the documentation whether the prescaler is used in counter mode + * or not. + * count_control selects the capture channel used to increment the counter when in counter mode. + * count_control is one of LPC_COUNTER_INC_ON_RISING or LPC_COUNTER_INC_ON_FALLING or + * LPC_COUNTER_INC_ON_BOTH. + * count_chann selects the channel used to increment the counter (value between 0 and MAX_CHANS). + * reset_on_cap is activated by adding LPC_COUNTER_CLEAR_ON_EVENT_EN to a capture reset event (one + * of LPC_COUNTER_CLEAR_ON_*** whith *** Going from CHAN0_RISE, CHAN0_FALL, ... to CHAN3_FALL) + * match_control[] is a combination of LPC_TIMER_INTERRUPT_ON_MATCH, LPC_TIMER_RESET_ON_MATCH and + * LPC_TIMER_STOP_ON_MATCH for each match channel. + * This controls the internal behaviour on each match event. + * match[] holds the match values. + * ext_match_config[] is one of LPC_TIMER_NOTHING_ON_MATCH, LPC_TIMER_CLEAR_ON_MATCH, + * LPC_TIMER_SET_ON_MATCH or LPC_TIMER_TOGGLE_ON_MATCH for each match channel. + * This controls the behavior of the output associated to the corresponding match channel. The + * corresponding pin must be configured as match output by the user. + * cap_control[] is a combination of LPC_TIMER_CAP_ON_RISING_EDGE, LPC_TIMER_CAP_ON_FALLING_EDGE + * and LPC_TIMER_INTERRUPT_ON_CAPTURE. + * This controls when the TC current value is loaded to the corresponding match value register, + * and whether this triggers an interrupt or not. + */ +struct lpc_tc_config { + uint16_t mode; + uint32_t prescale_val; + uint8_t count_control; + uint8_t count_chan; + uint8_t reset_on_cap; + uint8_t match_control[MAX_CHANNELS]; + uint32_t match[MAX_CHANNELS]; + uint8_t ext_match_config[MAX_CHANNELS]; + uint8_t cap_control[MAX_CHANNELS]; }; +/*******************************************************************************/ +/* Operation structures, not for use by user programms */ +struct common_operations { + /* Running control */ + void (*start)(uint8_t); + void (*stop)(uint8_t); + void (*pause)(uint8_t); + void (*cont)(uint8_t); + void (*restart)(uint8_t); + void (*halt)(uint8_t); + /* Counter */ + int (*get_counter)(uint8_t, uint32_t*); + int (*get_capture)(uint8_t, uint8_t, uint32_t*); + int (*set_match)(uint8_t, uint8_t, uint32_t); +}; +struct config_operations { + int (*pwm_config)(uint8_t, const struct lpc_timer_pwm_config*); + int (*tc_config)(uint8_t, const struct lpc_tc_config*); +}; +struct init_operations { + int (*timer_on)(uint8_t, uint32_t, void (*)(uint32_t)); + int (*timer_off)(uint8_t); +}; + +/*******************************************************************************/ +/* Common operations */ + /* Start the timer : * Remove the reset flag if present and set timer enable flag. * Timer must be turned on and configured (no checks done here). */ -void timer_start(uint32_t timer_num); -void timer_continue(uint32_t timer_num); +void timer_start(uint8_t timer_num); +void timer_continue(uint8_t timer_num); /* Pause the timer counter, does not reset */ -void timer_pause(uint32_t timer_num); +void timer_pause(uint8_t timer_num); /* Stop and reset the timer counter */ -void timer_stop(uint32_t timer_num); +void timer_stop(uint8_t timer_num); +void timer_halt(uint8_t timer_num); /* Resets the timer and lets it count again imediately */ -void timer_restart(uint32_t timer_num); +void timer_restart(uint8_t timer_num); -uint32_t timer_get_capture_val(uint32_t timer_num, uint32_t channel); -uint32_t timer_get_counter_val(uint32_t timer_num); +/* Get the current counter value + * Return 0 if the value is valid. + */ +int timer_get_counter_val(uint8_t timer_num, uint32_t* val); + + +/* Get the value of the timer when the capture event last triggered + * Return 0 if the value is valid. + */ +int timer_get_capture_val(uint8_t timer_num, uint8_t channel, uint32_t* val); /* Change the match value of a single timer channel */ -void timer_set_match(uint32_t timer_num, uint32_t channel, uint32_t val); +int timer_set_match(uint8_t timer_num, uint8_t channel, uint32_t val); -/***************************************************************************** */ -/* Timer Setup */ -/* Returns 0 on success - * Takes a timer number and a timer config structure as arguments. - * Refer to timer config structure for details. - * Note: use of channel 3 for PWM cycle length is enforced. + +/*******************************************************************************/ +/* Configuration */ + +/* PWM configuration in order to use the timer as a PWM controller. + * For the pwm_conf structure, refer to it's definition above.. + * The Timer must be "on" (call timer_on() for this timer before the call to + * timer_pwm_config(), with the disired clock rate, which will define the + * length of a timer clock cycle, which is the base for the timer period + * definition). + * The timer will not be started. User code must call timer_start() in order + * to start the PWM. + */ +int timer_pwm_config(uint8_t timer_num, const struct lpc_timer_pwm_config* pwm_conf); + + +/* Timer Setup in timer or counter mode, with optionnal capture and match configuration + * Takes a timer number and a timer counter config structure as arguments. + * Returns 0 on success */ -int timer_setup(uint32_t timer_num, const struct timer_config* conf); +int timer_counter_config(uint8_t timer_num, const struct lpc_tc_config* conf); +/*******************************************************************************/ +/* Init */ /* Power up a timer. - * Note that clkrate should be a divider of the main clock frequency chosed - * for your application as it will be used to divide the main clock to get - * the prescaler value. - * Set clkrate to 0 to disable the prescaler. + * clkrate is the desired timer clock rate. It will be used to divide the main clock + * to get the timer prescaler value. + * Set clkrate to 0 to disable the prescaler. * callback is used for all the possible timer interrupts (activated using the * config field in timer_config struct upon timer setup) * The interrupt flags are passed to the interrupt routine as argument. */ -void timer_on(uint32_t timer_num, uint32_t clkrate, void (*callback)(uint32_t)); +int timer_on(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t)); /* Removes the main clock from the selected timer block */ -void timer_off(uint32_t timer_num); +int timer_off(uint8_t timer_num); + #endif /* DRIVERS_TIMERS_H */ -- 2.43.0