From: Nathael Pajani Date: Sat, 18 Jun 2016 11:13:02 +0000 (+0200) Subject: Adding partial support for State Configurable Timers (SCT) Use as one 32 bits counter... X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=f50b2004d4f3a0871df2f75f651eecbc503177d2;p=soft%2Flpc82x%2Fcore Adding partial support for State Configurable Timers (SCT) Use as one 32 bits counter for PWM implemented and tested --- diff --git a/drivers/sctimers.c b/drivers/sctimers.c new file mode 100644 index 0000000..4009300 --- /dev/null +++ b/drivers/sctimers.c @@ -0,0 +1,357 @@ +/**************************************************************************** + * drivers/sctimers.c + * + * Copyright 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 . + * + *************************************************************************** */ + + + +/***************************************************************************** */ +/* State Configurable Timer */ +/***************************************************************************** */ + +/* State Configurable Timers driver for the integrated SCT of the LPC82x + * The LPC82x has one State Configurable Timer. + * Refer to LPC82x documentation (UM10800.pdf) for more information. + */ + +#include "lib/stdint.h" +#include "core/system.h" +#include "drivers/timers.h" +#include "drivers/sctimers.h" + + +/* These are local to our file */ +enum sct_config_states { + SCT_UNCONFIGURED = 0, + SCT_CONFIGURED_AS_32, + SCT_CONFIGURED_AS_16, +}; +struct sctimer_device { + struct lpc_sct_timer* regs; + uint32_t power_bit; + uint32_t reset; + uint8_t mode; + uint8_t irq; + uint8_t configured; + void (*callback)(uint32_t); /* Possible Interrupt callback */ +}; +struct sctimer_device sctimer = { + .regs = LPC_SCTIMER, + .power_bit = LPC_SYS_ABH_CLK_CTRL_SCT, + .reset = LPC_SCT_RESET_N, + .irq = SCT_IRQ, + .configured = 0, + .callback = NULL, +}; + +/* Handlers */ +void SCT_Handler(void) +{ + struct lpc_sct_timer* sct = LPC_SCTIMER; + uint32_t event_flags = sct->event_flag; /* Backup the flags */ + + /* Clear the interrupt */ + sct->event_flag = event_flags; + /* And call the user routine if one has been registered */ + if (sctimer.callback != NULL) { + sctimer.callback(event_flags); + } +} + + + +/*******************************************************************************/ +/* Common operations - Unified 32bits timer */ + +/* Start a timer : + * Remove the reset and stop flag if present. + * Timer must be turned on and configured (no checks done here). + */ +void sctimer_start(uint8_t timer_num) +{ + struct lpc_sct_timer* sct = LPC_SCTIMER; + if (timer_num != LPC_SCT) + return; + sct->control &= ~(LPC_SCT_STOP | LPC_SCT_HALT); +} + +/* Pause the SCT counter, does not reset */ +void sctimer_pause(uint8_t timer_num) +{ + struct lpc_sct_timer* sct = LPC_SCTIMER; + if (timer_num != LPC_SCT) + return; + /* Set the STOP Flag */ + sct->control |= LPC_SCT_STOP; +} + +/* Stops and resets the timer counter */ +void sctimer_stop(uint8_t timer_num) +{ + struct lpc_sct_timer* sct = LPC_SCTIMER; + if (timer_num != LPC_SCT) + return; + sct->control |= (LPC_SCT_STOP | LPC_SCT_HALT | LPC_SCT_CLEAR); +} + +/* Resets the timer and lets it count again imediately */ +void sctimer_restart(uint8_t timer_num) +{ + struct lpc_sct_timer* sct = LPC_SCTIMER; + if (timer_num != LPC_SCT) + return; + sct->control |= LPC_SCT_CLEAR; + /* If it was stoped, let it start */ + sct->control &= ~(LPC_SCT_STOP | LPC_SCT_HALT); +} + +uint32_t sctimer_get_counter_val(uint8_t timer_num) +{ + struct lpc_sct_timer* sct = LPC_SCTIMER; + if (timer_num != LPC_SCT) + return 0; + return sct->count; +} + +struct common_operations sct_ops = { + .start = sctimer_start, + .cont = sctimer_start, + .pause = sctimer_pause, + .stop = sctimer_stop, + .restart = sctimer_restart, + .halt = sctimer_stop, + .get_counter = sctimer_get_counter_val, +}; + + +/*******************************************************************************/ +/* Common operations - 16bits timers */ + +void sctimer16_start(uint8_t timer_num) +{ + struct lpc_sct_timer_16* sct = LPC_SCTIMER_16; + int num = 0; + + if (timer_num == LPC_SCT16_T1) { + num = 1; + } else if (timer_num != LPC_SCT16_T0) { + return; + } + sct->control[num] = 0; +} + +struct common_operations sct16_ops = { + .start = sctimer16_start, + .cont = sctimer16_start, +}; + + +/*******************************************************************************/ +/* Configuration operations - Unified 32bits timer */ + +/* Change the match value of a single timer channel */ +void sctimer_set_match(uint8_t timer_num, uint8_t channel, uint32_t val) +{ +} + +void sctimer_pwm_config(uint8_t timer_num, const struct lpc_timer_pwm_config* pwm) +{ + struct lpc_sct_timer* sct = LPC_SCTIMER; + int i = 0; + + if (timer_num != LPC_SCT) + return; + + /* Some configs require the timer to be halted (setting the initial output values */ + sctimer_stop(timer_num); + + /* Use a single state */ + sct->state_var = 0; + /* No need to reload, match registers will keep their values */ + sct->config |= LPC_SCT_NORELOAD; + /* All match/capture regs act as match registers */ + sct->mode = 0; + + /* Set all match values for each PWM channels and the limit channel */ + for (i = 0; ((i < pwm->nb_channels) && (i < NUM_SCT_CHANS)); i++) { + uint32_t evt_ctrl = 0; + + sct->match[ i + 1 ] = pwm->match_values[i]; + /* Enable one event for state 0 for this match */ + sct->event[ i + 1 ].state = LPC_SCT_EVT_EN_STATE(0); + /* Setup the event to happen on given match channel and control the requested output */ + evt_ctrl = (LPC_SCT_MATCHSEL(i + 1) | LPC_SCT_COMB_MODE_MATCH); + evt_ctrl |= (LPC_SCT_OUTSEL | LPC_SCT_IOSEL(pwm->outputs[i])); + sct->event[ i + 1 ].control = evt_ctrl; + /* Setup the event to toggle the requested output by configuring both set and clear and + * using the toggle solution to conflict resolution */ + sct->out_evt_sel[ pwm->outputs[i] ].set = LPC_SCT_OUTPUT_EVT_SET(i + 1); + sct->out_evt_sel[ pwm->outputs[i] ].clear = LPC_SCT_OUTPUT_EVT_CLR(i + 1); + sct->conflict_res |= LPC_SCT_CONFLICT_TOGGLE( pwm->outputs[i] ); + /* And configure the limit event (channel 0) to toggle this output too */ + sct->out_evt_sel[ pwm->outputs[i] ].set |= LPC_SCT_OUTPUT_EVT_SET(0); + sct->out_evt_sel[ pwm->outputs[i] ].clear |= LPC_SCT_OUTPUT_EVT_CLR(0); + } + + /* Select the channel 0 as the timer limit value to generate the PWM period. + * Use autolimit, but configure an event anyway as we want to toggle the outputs as well */ + sct->match[0] = pwm->period; + sct->config |= LPC_SCT_AUTOLIMIT; + sct->event[0].state = LPC_SCT_EVT_EN_STATE(0); + sct->event[0].control = (LPC_SCT_MATCHSEL(0) | LPC_SCT_COMB_MODE_MATCH); + + /* Set all outputs initial states */ + sct->output = LPC_SCT_OUT_SET_ALL(pwm->outputs_initial_state); +} + +struct config_operations sct_cfg_ops = { + .set_match = sctimer_set_match, + .pwm_config = sctimer_pwm_config, +}; + + + + + +/*******************************************************************************/ +/* Init operations */ + +/* Power up SCT. + * clkrate is a divider of the main clock frequency chosen 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. + */ +void sctimer_on(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t)) +{ + struct lpc_sct_timer* sct = LPC_SCTIMER; + uint32_t prescale; /* The clock divider for the counter */ + + if (timer_num != LPC_SCT) { + return; + } + + NVIC_DisableIRQ(sctimer.irq); + /* Power up the timer and perform subsystem reset */ + subsystem_power(sctimer.power_bit, 1); + subsystem_reset(sctimer.reset); + + /* Compute the prescaler value */ + if (clkrate == 0) { + prescale = 0; + } else { + prescale = (get_main_clock() / clkrate) - 1; + } + + /* Set main configurations */ + if (timer_num == LPC_SCT) { + sct->config = (LPC_SCT_UNIFIED_TIMER | LPC_SCT_CLK_SYSCLK); + /* Reset and disable counter */ + sct->control = (LPC_SCT_CLEAR | LPC_SCT_HALT | LPC_SCT_PRESCALE(prescale)); + } else { + sct->config = LPC_SCT_CLK_SYSCLK; + if (timer_num == LPC_SCT16_T0) { + sct->control = (LPC_SCT_CLEAR | LPC_SCT_HALT | LPC_SCT_PRESCALE(prescale)); + } else { + sct->control = ((LPC_SCT_CLEAR | LPC_SCT_HALT | LPC_SCT_PRESCALE(prescale)) << 16); + } + } + + /* Store the callback, OK even if none given */ + sctimer.callback = callback; + sctimer.configured = SCT_CONFIGURED_AS_32; + + /* Disable all interrupt events */ + sct->event_int_en = 0; + + NVIC_EnableIRQ(sctimer.irq); +} + + +/* Removes the main clock from the selected sctimer block */ +void sctimer_off(uint8_t timer_num) +{ + if ((timer_num != LPC_SCT) && (timer_num != LPC_SCT16_T0) && (timer_num != LPC_SCT16_T1)) + return; + + NVIC_DisableIRQ(sctimer.irq); + /* Power down the timer */ + subsystem_power(sctimer.power_bit, 0); + sctimer.callback = NULL; + sctimer.configured = SCT_UNCONFIGURED; +} + + +struct init_operations sct_init_ops = { + .timer_on = sctimer_on, + .timer_off = sctimer_off, +}; + + +void sctimer16_on(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t)) +{ + struct lpc_sct_timer_16* sct = LPC_SCTIMER_16; + + uint32_t prescale; /* The clock divider for the counter */ + + if ((timer_num != LPC_SCT16_T0) && (timer_num != LPC_SCT16_T1)) { + return; + } + if (sctimer.configured == SCT_CONFIGURED_AS_32) { + return; + } + + if (sctimer.configured == SCT_UNCONFIGURED) { + NVIC_DisableIRQ(sctimer.irq); + /* Power up the timer and perform subsystem reset */ + subsystem_power(sctimer.power_bit, 1); + subsystem_reset(sctimer.reset); + + /* Set main configurations */ + sct->config = LPC_SCT_CLK_SYSCLK; + } + + /* Compute the prescaler value */ + if (clkrate == 0) { + prescale = 0; + } else { + prescale = (get_main_clock() / clkrate) - 1; + } + + if (timer_num == LPC_SCT16_T0) { + sct->control[0] |= (LPC_SCT_CLEAR | LPC_SCT_HALT | LPC_SCT_PRESCALE(prescale)); + } else { + sct->control[1] |= (LPC_SCT_CLEAR | LPC_SCT_HALT | LPC_SCT_PRESCALE(prescale)); + } + + if (sctimer.configured == SCT_UNCONFIGURED) { + /* Store the callback, OK even if none given */ + sctimer.callback = callback; + sctimer.configured = SCT_CONFIGURED_AS_16; + + /* Disable all interrupt events */ + sct->event_int_en = 0; + + NVIC_EnableIRQ(sctimer.irq); + } +} + +struct init_operations sct16_init_ops = { + .timer_on = sctimer16_on, + .timer_off = sctimer_off, +}; + diff --git a/drivers/timers.c b/drivers/timers.c new file mode 100644 index 0000000..2c232fd --- /dev/null +++ b/drivers/timers.c @@ -0,0 +1,175 @@ +/**************************************************************************** + * drivers/timers.c + * + * Copyright 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 . + * + *************************************************************************** */ + + + +/***************************************************************************** */ +/* Timers */ +/***************************************************************************** */ + +/* Driver for the different kinds of timers available in the LPC82x + * + * This inludes : + * - State Configurable Timers driver for the integrated SCT of the LPC82x + * The LPC82x has one State Configurable Timer. + * + * Refer to LPC82x documentation (UM10800.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/timers.h" + + +/* These are local to our file */ +struct timer_device { + uint8_t num; + struct common_operations* ops; + struct config_operations* cfg_ops; + struct init_operations* init_ops; +}; + + +/* 32 bits State Configurable Timer */ +extern struct common_operations sct_ops; +extern struct config_operations sct_cfg_ops; +extern struct init_operations sct_init_ops; +/* 16 bits State Configurable Timer */ +extern struct common_operations sct16_ops; +extern struct config_operations sct16_cfg_ops; +extern struct init_operations sct16_init_ops; +/* Multi Rate Timer */ + + +struct timer_device timers[NUM_TIMERS] = { + { + .num = LPC_SCT, + .ops = &sct_ops, + .cfg_ops = &sct_cfg_ops, + .init_ops = &sct_init_ops, + }, + { + .num = LPC_SCT16_T0, + .ops = &sct16_ops, + .init_ops = &sct16_init_ops, + }, + { + .num = LPC_SCT16_T1, + .ops = &sct16_ops, + .init_ops = &sct16_init_ops, + }, +}; + + +/*******************************************************************************/ +/* 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); \ + } \ + } + +/* void timer_start(uint32_t timer_num) + * 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); + + +/* Return the current counter value + * Note that for 32 bits timers there is not much possibilities to differentiate + * between an error and a real value + */ +uint32_t timer_get_counter_val(uint8_t timer_num) +{ + if (timer_num >= NUM_TIMERS) + return 0; + if (timers[timer_num].ops && timers[timer_num].ops->get_counter) { + return timers[timer_num].ops->get_counter(timer_num); + } + return 0; +} + + +/*******************************************************************************/ +/* Configuration */ + +/* Change the match value of a single timer channel */ +void timer_set_match(uint8_t timer_num, uint8_t channel, uint32_t val) +{ + if (timer_num >= NUM_TIMERS) + return; + if (timers[timer_num].cfg_ops && timers[timer_num].cfg_ops->set_match) { + timers[timer_num].cfg_ops->set_match(timer_num, channel, val); + } +} + +void timer_pwm_config(uint8_t timer_num, const struct lpc_timer_pwm_config* conf) +{ + if (timer_num >= NUM_TIMERS) + return; + if (timers[timer_num].cfg_ops && timers[timer_num].cfg_ops->pwm_config) { + timers[timer_num].cfg_ops->pwm_config(timer_num, conf); + } +} + + + + +/* Power up a timer. + * clkrate is a divider of the main clock frequency chosen 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. + * callback is the interrupt callback for this timer. + */ +void timer_on(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t)) +{ + if (timer_num >= NUM_TIMERS) + return; + if (timers[timer_num].init_ops && timers[timer_num].init_ops->timer_on) { + timers[timer_num].init_ops->timer_on(timer_num, clkrate, callback); + } +} + +/* Removes the main clock from the selected timer block */ +void timer_off(uint8_t timer_num) +{ + if (timer_num >= NUM_TIMERS) + return; + if (timers[timer_num].init_ops && timers[timer_num].init_ops->timer_off) { + timers[timer_num].init_ops->timer_off(timer_num); + } +} + diff --git a/include/core/lpc_regs.h b/include/core/lpc_regs.h index 3665689..5861049 100644 --- a/include/core/lpc_regs.h +++ b/include/core/lpc_regs.h @@ -36,7 +36,7 @@ #define LPC_RAM_BASE (0x10000000UL) #define LPC_APB_BASE (0x40000000UL) #define LPC_CRC_BASE (0x50000000UL) -#define LPC_SCT_PWM_BASE (0x50004000UL) +#define LPC_SCT_BASE (0x50004000UL) #define LPC_DMA_BASE (0x50008000UL) #define LPC_GPIO_BASE (0xA0000000UL) #define LPC_GPIO_INTR_BASE (0xA0004000UL) @@ -74,7 +74,6 @@ - /***************************************************************************** */ /* Power Management Unit */ /***************************************************************************** */ @@ -150,80 +149,4 @@ struct lpc_ssp #define LPC_SSP_TX_DMA_EN (0x01 << 1) - -/***************************************************************************** */ -/* Timer */ -/***************************************************************************** */ -/* 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_MATCH_ERASE(x) (0x07 << (((x) & 0x03) * 3)) -#define LPC_TIMER_MATCH_SHIFT(x) (((x) & 0x03) * 3) -/* 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_ERASE(x) (0x07 << (((x) & 0x03) * 3)) -#define LPC_TIMER_CAPTURE_SHIFT(x) (((x) & 0x03) * 3) -/* 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_MATCH0_SHIFT 4 -#define LPC_TIMER_EXT_MATCH1_SHIFT 6 -#define LPC_TIMER_EXT_MATCH2_SHIFT 8 -#define LPC_TIMER_EXT_MATCH3_SHIFT 10 -/* 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 /* LPC_REGS_H */ diff --git a/include/drivers/sctimers.h b/include/drivers/sctimers.h new file mode 100644 index 0000000..16a3893 --- /dev/null +++ b/include/drivers/sctimers.h @@ -0,0 +1,231 @@ +/**************************************************************************** + * drivers/sctimers.h + * + * Copyright 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_SCTIMERS_H +#define DRIVERS_SCTIMERS_H + +#include "lib/stdint.h" + +/***************************************************************************** */ +/* State Configurable Timers */ +/***************************************************************************** */ +/* State Configurable Timers driver for the integrated SCT of the LPC82x + * The LPC82x has one State Configurable Timer. + * Refer to LPC82x documentation (UM10800.pdf) for more information. + */ +#define NUM_SCTIMERS 1 +#define NUM_SCT_CHANS 8 +#define NUM_SCT_OUTPUTS 6 + + +/* Many functions are accessible through the common timer interface and are not + * included here. + * Use the common interface to access timer_on(), timer_off(), and all the + * start, stop, pause, continue, ... functions. + */ + + + + +/***************************************************************************** */ +/* State Configurable Timer */ +/***************************************************************************** */ +/* State Configurable Timer (SCT) */ +struct lpc_sct_event { + volatile uint32_t state; + volatile uint32_t control; +}; +struct lpc_sct_out { + volatile uint32_t set; + volatile uint32_t clear; +}; +struct lpc_sct_timer +{ + volatile uint32_t config; /* 0x000 : SCT configuration register (R/W) */ + volatile uint32_t control; /* 0x004 : SCT control register (R/W) */ + volatile uint32_t limit_evt_sel; /* 0x008 : SCT limit event select register (R/W) */ + volatile uint32_t halt_evt_sel; /* 0x00C : SCT halt event select register (R/W) */ + volatile uint32_t stop_evt_sel; /* 0x010 : SCT stop event select register (R/W) */ + volatile uint32_t start_evt_sel; /* 0x014 : SCT start event select register (R/W) */ + uint32_t reserved_0[10]; + volatile uint32_t count; /* 0x040 : SCT counter register (R/W) */ + volatile uint32_t state_var; /* 0x044 : SCT state variable register (R/W) */ + volatile uint32_t input; /* 0x048 : SCT input register (R/-) */ + volatile uint32_t mode; /* 0x04C : SCT match/capture mode register (R/W) */ + volatile uint32_t output; /* 0x050 : SCT output register (R/W) */ + volatile uint32_t out_dir_ctrl; /* 0x054 : SCT output counter direction control register (R/W) */ + volatile uint32_t conflict_res; /* 0x058 : SCT conflict resolution register (R/W) */ + volatile uint32_t dma_req[2]; /* 0x05C - 0x060 : SCT DMA request 0 and 1 registers (R/W) */ + uint32_t reserved_1[35]; + volatile uint32_t event_int_en; /* 0x0F0 : SCT event interrupt enable register (R/W) */ + volatile uint32_t event_flag; /* 0x0F4 : SCT event flag register (R/W) */ + volatile uint32_t conflict_int_en; /* 0x0F8 : SCT conflict interrupt enable register (R/W) */ + volatile uint32_t conflict_flag; /* 0x0FC : SCT conflict flag register (R/W) */ + union { + volatile uint32_t match[8]; /* 0x100 - 0x11C : SCT match value for channels 0 to 7 (R/W) */ + volatile uint32_t capture[8]; /* 0x100 - 0x11C : SCT capture value for channels 0 to 7 (R/W) */ + }; + uint32_t reserved_2[56]; + union { + volatile uint32_t match_reload[8]; /* 0x200 - 0x21C : SCT match reload value register 0 to 7 (R/W) */ + volatile uint32_t capture_ctrl[8]; /* 0x200 - 0x21C : SCT capture control register 0 to 7 (R/W) */ + }; + uint32_t reserved_3[56]; + struct lpc_sct_event event[8]; /* 0x300 - 0x33C : SCT event state and control register 0 to 7 (R/W) */ + uint32_t reserved_4[112]; + struct lpc_sct_out out_evt_sel[6]; /* 0x500 - 0x52C : SCT event state and control register 0 to 7 (R/W) */ +}; +#define LPC_SCTIMER ((struct lpc_sct_timer *) LPC_SCT_BASE) + + +/* Configuration register */ +#define LPC_SCT_UNIFIED_TIMER (0x01 << 0) +/* Clock */ +#define LPC_SCT_CLK_SYSCLK (0x00 << 1) +#define LPC_SCT_CLK_SAMPLE_SYSCLK (0x01 << 1) +#define LPC_SCT_CLK_SCT_INPUT (0x02 << 1) +#define LPC_SCT_CLK_SCT_ASYNC (0x03 << 1) +#define LPC_SCT_CLK_RISING_IN(x) (((x) * 2 ) << 3) +#define LPC_SCT_CLK_FALLING_IN(x) ((((x) * 2 ) + 1) << 3) +/* Other */ +#define LPC_SCT_NORELOAD (0x01 << 7) +#define LPC_SCT_IN_EVT_SYNC(x) ((0x01 << ((x) & 0x03)) << 9) +#define LPC_SCT_AUTOLIMIT (0x01 << 17) + +/* Control register */ +#define LPC_SCT_DOWN (0x01 << 0) +#define LPC_SCT_STOP (0x01 << 1) +#define LPC_SCT_HALT (0x01 << 2) +#define LPC_SCT_CLEAR (0x01 << 3) +#define LPC_SCT_BIDIR (0x01 << 4) +#define LPC_SCT_PRESCALE(x) (((x) & 0xFF) << 5) + +/* Limit event */ +#define LPC_SCT_LIMIT_BY_EVT(x) (0x01 << ((x) & 0x07)) +#define LPC_SCT_HALT_BY_EVT(x) (0x01 << ((x) & 0x07)) +#define LPC_SCT_STOP_BY_EVT(x) (0x01 << ((x) & 0x07)) +#define LPC_SCT_START_BY_EVT(x) (0x01 << ((x) & 0x07)) + +/* State variable */ +#define LPC_SCT_STATE_VAR(x) (((x) & 0x1F) << 0) + +/* Input states register */ +#define LPC_SCT_ASYNC_IN(reg, num) (((reg) >> ((num) & 0x03)) & 0x01) +#define LPC_SCT_SYNC_IN(reg, num) (((reg) >> (((num) & 0x03) + 16)) & 0x01) + +/* Mode register */ +#define LPC_SCT_MATCH(x) (0) +#define LPC_SCT_MATCH_CLR(x) (0x01 << ((x) & 0x07)) +#define LPC_SCT_CAPTURE(x) (0x01 << ((x) & 0x07)) + +/* Output register */ +#define LPC_SCT_OUT_SET(x) (0x01 << ((x) & 0x07)) +#define LPC_SCT_OUT_SET_ALL(all) ((all) & 0x3F) +#define LPC_SCT_OUT_VAL(reg, num) (((reg) >> ((num) & 0x07)) & 0x01) + +/* Bi-directionnal Output control register */ +#define LPC_SCT_BIDIR_INDEP (0) +#define LPC_SCT_BIDIR_REV(x) (0x01 << (((x) & 0x07) * 2)) + +/* Conflict resolution */ +#define LPC_SCT_CONFLICT_SET(x) (0x01 << (((x) & 0x07) * 2)) +#define LPC_SCT_CONFLICT_CLR(x) (0x02 << (((x) & 0x07) * 2)) +#define LPC_SCT_CONFLICT_TOGGLE(x) (0x03 << (((x) & 0x07) * 2)) + +/* Event interrupt enable and flag registers */ +#define LPC_SCT_EVT_INT(x) (0x01 << ((x) & 0x07)) + +/* Conflict interrupt enable and flag register */ +#define LPC_SCT_CONFLICT_INT(x) (0x01 << ((x) & 0x07)) + +/* Event enable */ +#define LPC_SCT_EVT_EN_STATE(x) (0x01 << ((x) & 0x07)) + +/* Event control register */ +#define LPC_SCT_MATCHSEL(x) ((x) & 0x0F) +#define LPC_SCT_OUTSEL (0x01 << 5) +#define LPC_SCT_IOSEL(x) (((x) & 0x0F) << 6) +#define LPC_SCT_IOCOND_LOW (0x0 << 10) +#define LPC_SCT_IOCOND_RISE (0x1 << 10) +#define LPC_SCT_IOCOND_FALL (0x2 << 10) +#define LPC_SCT_IOCOND_HIGH (0x3 << 10) +#define LPC_SCT_COMB_MODE_OR (0x0 << 12) +#define LPC_SCT_COMB_MODE_MATCH (0x1 << 12) +#define LPC_SCT_COMB_MODE_IO (0x2 << 12) +#define LPC_SCT_COMB_MODE_AND (0x3 << 12) +#define LPC_SCT_STATE_ADD (0x0 << 14) +#define LPC_SCT_STATE_LOAD (0x1 << 14) +#define LPC_SCT_STATE_VAL(x) (((x) & 0x1F) << 15) +#define LPC_SCT_MATCH_EQUAL (0x0 << 20) +#define LPC_SCT_MATCH_LT_GT (0x1 << 20) +#define LPC_SCT_CNTDIR_INDEP (0x0 << 21) +#define LPC_SCT_CNTDIR_UP (0x1 << 21) +#define LPC_SCT_CNTDIR_DOWN (0x2 << 21) + +/* Event output set */ +#define LPC_SCT_OUTPUT_EVT_SET(x) (0x01 << ((x) & 0x07)) + +/* Event output clear */ +#define LPC_SCT_OUTPUT_EVT_CLR(x) (0x01 << ((x) & 0x07)) + + + + + +struct lpc_sct_timer_16 +{ + volatile uint32_t config; /* 0x000 : SCT configuration register (R/W) */ + volatile uint16_t control[2]; /* 0x004 : SCT control register (R/W) */ + volatile uint16_t limit[2]; /* 0x008 : SCT limit event select register (R/W) */ + volatile uint16_t halt[2]; /* 0x00C : SCT halt event select register (R/W) */ + volatile uint16_t stop[2]; /* 0x010 : SCT stop event select register (R/W) */ + volatile uint16_t start[2]; /* 0x014 : SCT start event select register (R/W) */ + uint32_t reserved_0[10]; + volatile uint16_t count[2]; /* 0x040 : SCT counter register (R/W) */ + volatile uint16_t state[2]; /* 0x044 : SCT state register (R/W) */ + volatile uint32_t input; /* 0x048 : SCT input register (R/-) */ + volatile uint16_t mode[2]; /* 0x04C : SCT match/capture mode register (R/W) */ + volatile uint32_t output; /* 0x050 : SCT output register (R/W) */ + volatile uint32_t out_dir_ctrl; /* 0x054 : SCT output counter direction control register (R/W) */ + volatile uint32_t conflict_res; /* 0x058 : SCT conflict resolution register (R/W) */ + volatile uint32_t dma_req[2]; /* 0x05C - 0x060 : SCT DMA request 0 and 1 registers (R/W) */ + uint32_t reserved_1[35]; + volatile uint32_t event_int_en; /* 0x0F0 : SCT event interrupt enable register (R/W) */ + volatile uint32_t event_flag; /* 0x0F4 : SCT event flag register (R/W) */ + volatile uint32_t conflict_int_en; /* 0x0F8 : SCT conflict interrupt enable register (R/W) */ + volatile uint32_t conflict_flag; /* 0x0FC : SCT conflict flag register (R/W) */ + union { + volatile uint16_t match[8][2]; /* 0x100 - 0x11C : SCT match value for channels 0 to 7 (R/W) */ + volatile uint16_t capture[8][2]; /* 0x100 - 0x11C : SCT capture value for channels 0 to 7 (R/W) */ + }; + uint32_t reserved_2[56]; + union { + volatile uint16_t match_reload[8][2]; /* 0x200 - 0x21C : SCT match reload value register 0 to 7 (R/W) */ + volatile uint16_t capture_ctrl[8][2]; /* 0x200 - 0x21C : SCT capture control register 0 to 7 (R/W) */ + }; + uint32_t reserved_3[56]; + struct lpc_sct_event event[8]; /* 0x300 - 0x33C : SCT event state and control register 0 to 7 (R/W) */ + uint32_t reserved_4[112]; + struct lpc_sct_out out_evt_sel[6]; /* 0x500 - 0x52C : SCT event state and control register 0 to 7 (R/W) */ +}; +#define LPC_SCTIMER_16 ((struct lpc_sct_timer_16 *) LPC_SCT_BASE) + +#endif /* DRIVERS_SCTIMERS_H */ + diff --git a/include/drivers/timers.h b/include/drivers/timers.h new file mode 100644 index 0000000..4a732a1 --- /dev/null +++ b/include/drivers/timers.h @@ -0,0 +1,148 @@ +/**************************************************************************** + * drivers/timers.h + * + * Copyright 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_TIMERS_H +#define DRIVERS_TIMERS_H + +/***************************************************************************** */ +/* Timers */ +/***************************************************************************** */ + +/* Driver for the different kinds of timers available in the LPC82x + * + * This inludes : + * - State Configurable Timers driver for the integrated SCT of the LPC82x + * The LPC82x has one State Configurable Timer. + * + * Refer to LPC82x documentation (UM10800.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" + +#define NUM_TIMERS 4 +#define MAX_CHANNELS 8 +enum lpc_timers { + LPC_SCT = 0, /* Unified 32 bits SCT timer */ + LPC_SCT16_T0, /* _L 16 bits SCT timer */ + LPC_SCT16_T1, /* _H 16 bits SCT timer */ + LPC_MRT, +}; + +/* Available timer modes */ +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 */ +}; + + +/* Configurations */ +struct lpc_timer_pwm_config { + uint8_t nb_channels; + uint8_t outputs_initial_state; + uint32_t period; + uint32_t match_values[MAX_CHANNELS]; + uint8_t outputs[MAX_CHANNELS]; +}; + + + +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 */ + uint32_t (*get_counter)(uint8_t); +}; +struct config_operations { + void (*set_match)(uint8_t, uint8_t, uint32_t); + void (*pwm_config)(uint8_t, const struct lpc_timer_pwm_config*); +}; +struct init_operations { + void (*timer_on)(uint8_t, uint32_t, void (*)(uint32_t)); + void (*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(uint8_t timer_num); +void timer_continue(uint8_t timer_num); + +/* Pause the timer counter, does not reset */ +void timer_pause(uint8_t timer_num); + +/* Stop and reset the timer counter */ +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(uint8_t timer_num); + + +/* Return the current counter value + * Note that for 32 bits timers there is not much possibilities to differentiate + * between an error and a real value + */ +uint32_t timer_get_counter_val(uint8_t timer_num); + + +/*******************************************************************************/ +/* Configuration */ + +/* Change the match value of a single timer channel */ +void timer_set_match(uint8_t timer_num, uint8_t channel, uint32_t val); + +void timer_pwm_config(uint8_t timer_num, const struct lpc_timer_pwm_config* pwm_conf); + + + +/*******************************************************************************/ +/* Init */ + +/* Power up a timer. + * clkrate is a divider of the main clock frequency chosen 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. + * 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(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t)); + +/* Removes the main clock from the selected timer block */ +void timer_off(uint8_t timer_num); + +#endif /* DRIVERS_TIMERS_H */ +