Adding partial support for State Configurable Timers (SCT) Use as one 32 bits counter...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Sat, 18 Jun 2016 11:13:02 +0000 (13:13 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Fri, 10 Feb 2023 18:02:59 +0000 (19:02 +0100)
drivers/sctimers.c [new file with mode: 0644]
drivers/timers.c [new file with mode: 0644]
include/core/lpc_regs.h
include/drivers/sctimers.h [new file with mode: 0644]
include/drivers/timers.h [new file with mode: 0644]

diff --git a/drivers/sctimers.c b/drivers/sctimers.c
new file mode 100644 (file)
index 0000000..4009300
--- /dev/null
@@ -0,0 +1,357 @@
+/****************************************************************************
+ *  drivers/sctimers.c
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+
+/***************************************************************************** */
+/*              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 (file)
index 0000000..2c232fd
--- /dev/null
@@ -0,0 +1,175 @@
+/****************************************************************************
+ *  drivers/timers.c
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+
+/***************************************************************************** */
+/*                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);
+       }
+}
+
index 3665689..5861049 100644 (file)
@@ -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 (file)
index 0000000..16a3893
--- /dev/null
@@ -0,0 +1,231 @@
+/****************************************************************************
+ *  drivers/sctimers.h
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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 (file)
index 0000000..4a732a1
--- /dev/null
@@ -0,0 +1,148 @@
+/****************************************************************************
+ *  drivers/timers.h
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#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 */
+