From 342d29288977de7c88751291fbd6937a3961a4ba Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Thu, 3 Sep 2015 23:47:59 +0200 Subject: [PATCH] cpu/lpc2387: added low-level timer implementation --- cpu/lpc2387/include/lpc23xx.h | 35 ++++- cpu/lpc2387/include/periph_cpu.h | 7 +- cpu/lpc2387/periph/timer.c | 232 +++++++++++++++++++++++++++++++ 3 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 cpu/lpc2387/periph/timer.c diff --git a/cpu/lpc2387/include/lpc23xx.h b/cpu/lpc2387/include/lpc23xx.h index b6de1a990f..dac0070437 100644 --- a/cpu/lpc2387/include/lpc23xx.h +++ b/cpu/lpc2387/include/lpc23xx.h @@ -12,13 +12,20 @@ * ******************************************************************************/ -#ifndef __LPC23xx_H -#define __LPC23xx_H +#ifndef LPC23XX_H +#define LPC23XX_H + +#include #ifdef __cplusplus extern "C" { #endif +/** + * @brief Type for 32-bit registers + */ +#define REG32 volatile uint32_t + /* Vectored Interrupt Controller (VIC) */ #define VIC_BASE_ADDR 0xFFFFF000 #define VICIRQStatus (*(volatile unsigned long *)(VIC_BASE_ADDR + 0x000)) @@ -496,8 +503,27 @@ are for LPC24xx only. */ #define EMC_STA_EXT_WAIT (*(volatile unsigned long *)(EMC_BASE_ADDR + 0x880)) +/** + * @brief Generic timer register map + */ +typedef struct { + REG32 IR; /**< interrupt register */ + REG32 TCR; /**< timer control register */ + REG32 TC; /**< timer counter */ + REG32 PR; /**< prescale register */ + REG32 PC; /**< prescale counter */ + REG32 MCR; /**< match control register */ + REG32 MR[4]; /**< match registers 1-4 */ + REG32 CCR; /**< capture control register */ + REG32 CR[4]; /**< capture register 1-4 */ + REG32 EMR; /**< external match register */ + REG32 reserved[12]; /**< reserved */ + REG32 CTCR; /**< count control register */ +} lpc23xx_timer_t; + /* Timer 0 */ #define TMR0_BASE_ADDR 0xE0004000 +#define TMR0 ((lpc23xx_timer_t *)TMR0_BASE_ADDR) #define T0IR (*(volatile unsigned long *)(TMR0_BASE_ADDR + 0x00)) #define T0TCR (*(volatile unsigned long *)(TMR0_BASE_ADDR + 0x04)) #define T0TC (*(volatile unsigned long *)(TMR0_BASE_ADDR + 0x08)) @@ -518,6 +544,7 @@ are for LPC24xx only. */ /* Timer 1 */ #define TMR1_BASE_ADDR 0xE0008000 +#define TMR1 ((lpc23xx_timer_t *)TMR1_BASE_ADDR) #define T1IR (*(volatile unsigned long *)(TMR1_BASE_ADDR + 0x00)) #define T1TCR (*(volatile unsigned long *)(TMR1_BASE_ADDR + 0x04)) #define T1TC (*(volatile unsigned long *)(TMR1_BASE_ADDR + 0x08)) @@ -538,6 +565,7 @@ are for LPC24xx only. */ /* Timer 2 */ #define TMR2_BASE_ADDR 0xE0070000 +#define TMR2 ((lpc23xx_timer_t *)TMR2_BASE_ADDR) #define T2IR (*(volatile unsigned long *)(TMR2_BASE_ADDR + 0x00)) #define T2TCR (*(volatile unsigned long *)(TMR2_BASE_ADDR + 0x04)) #define T2TC (*(volatile unsigned long *)(TMR2_BASE_ADDR + 0x08)) @@ -558,6 +586,7 @@ are for LPC24xx only. */ /* Timer 3 */ #define TMR3_BASE_ADDR 0xE0074000 +#define TMR3 ((lpc23xx_timer_t *)TMR3_BASE_ADDR) #define T3IR (*(volatile unsigned long *)(TMR3_BASE_ADDR + 0x00)) #define T3TCR (*(volatile unsigned long *)(TMR3_BASE_ADDR + 0x04)) #define T3TC (*(volatile unsigned long *)(TMR3_BASE_ADDR + 0x08)) @@ -1134,4 +1163,4 @@ with the spec. update in USB Device Section. */ } #endif -#endif // __LPC23xx_H +#endif /* LPC23XX_H */ diff --git a/cpu/lpc2387/include/periph_cpu.h b/cpu/lpc2387/include/periph_cpu.h index 47a07255fd..8fccc27687 100644 --- a/cpu/lpc2387/include/periph_cpu.h +++ b/cpu/lpc2387/include/periph_cpu.h @@ -71,7 +71,12 @@ typedef enum { } gpio_flank_t; /** - * @brief declare needed generic SPI functions + * @brief Number of available timer channels + */ +#define TIMER_CHAN_NUMOF (4U) + +/** + * @brief Declare needed generic SPI functions * @{ */ #define PERIPH_SPI_NEEDS_TRANSFER_BYTES diff --git a/cpu/lpc2387/periph/timer.c b/cpu/lpc2387/periph/timer.c new file mode 100644 index 0000000000..6b1e783cb9 --- /dev/null +++ b/cpu/lpc2387/periph/timer.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup cpu_lpc2387 + * @{ + * + * @file + * @brief Implementation of the low-level timer driver for the LPC2387 + * + * @author Hauke Petersen + * @} + */ + +#include +#include + +#include "periph_conf.h" +#include "periph_cpu.h" +#include "periph/timer.h" + +/** + * @brief Check the board config to make sure we do not exceed max number of + * timers + */ +#if TIMER_NUMOF > 3 +#error "ERROR in timer configuration: too many timers defined" +#endif + +/** + * @brief Interrupt context information for configured timers + */ +static timer_isr_ctx_t isr_ctx[TIMER_NUMOF]; + +/** + * @brief Forward declarations for interrupt functions + * @{ + */ +void tim_isr_0(void); +void tim_isr_1(void); +void tim_isr_2(void); +void tim_isr_3(void); +/** @} */ + +/** + * @brief Get the base pointer of a timer + */ +static inline lpc23xx_timer_t *get_dev(tim_t tim) +{ + switch (tim) { + case 0: + return TMR0; +#if TIMER_NUMOF > 1 + case 1: + return TMR1; +#endif +#if TIMER_NUMOF > 2 + case 2: + return TMR2; +#endif +#if TIMER_NUMOF > 3 + case 3: + return TMR3; +#endif + default: + return NULL; + } +} + +static inline void pwr_clk_and_isr(tim_t tim) +{ + switch (tim) { + case 0: + PCONP |= (1 << 1); + PCLKSEL0 &= ~(0x03 << 2); + PCLKSEL0 |= (0x01 << 2); + install_irq(TIMER0_INT, &tim_isr_0, 1); + break; +#if TIMER_NUMOF > 1 + case 1: + PCONP |= (1 << 2); + PCLKSEL0 &= ~(0x03 << 4); + PCLKSEL0 |= (0x01 << 4); + install_irq(TIMER1_INT, &tim_isr_1, 1); + break; +#endif +#if TIMER_NUMOF > 2 + case 2: + PCONP |= (1 << 22); + PCLKSEL1 &= ~(0x03 << 12); + PCLKSEL1 |= (0x01 << 12); + install_irq(TIMER2_INT, &tim_isr_2, 1); + break; +#endif +#if TIMER_NUMOF > 3 + case 3: + PCONP |= (1 << 23); + PCLKSEL1 &= ~(0x03 << 14); + PCLKSEL1 |= (0x01 << 14); + install_irq(TIMER3_INT, &tim_isr_3, 1); + break; +#endif + } +} + +int timer_init(tim_t tim, unsigned int us_per_tick, void (*callback)(int)) +{ + /* get the timers base register */ + lpc23xx_timer_t *dev = get_dev(tim); + + /* make sure the timer device is valid */ + if (dev == NULL) { + return -1; + } + + /* save the callback */ + isr_ctx[tim].cb = callback; + /* enable power, config periph clock and install ISR vector */ + pwr_clk_and_isr(tim); + /* reset timer configuration (sets the timer to timer mode) */ + dev->TCR = 0; + dev->CTCR = 0; + /* configure the prescaler */ + dev->PR = (us_per_tick * ((CLOCK_PCLK / 1000000) - 1)); + /* enable timer */ + dev->TCR = 1; + return 0; +} + +int timer_set(tim_t tim, int channel, unsigned int timeout) +{ + unsigned int now = timer_read(tim); + return timer_set_absolute(tim, channel, (timeout + now)); +} + +int timer_set_absolute(tim_t tim, int channel, unsigned int value) +{ + if (tim >= TIMER_NUMOF || channel >= TIMER_CHAN_NUMOF) { + return -1; + } + + lpc23xx_timer_t *dev = get_dev(tim); + dev->MR[channel] = value; + dev->MCR |= (1 << (channel * 3)); + return 0; +} + +int timer_clear(tim_t tim, int channel) +{ + if (tim >= TIMER_NUMOF || channel >= TIMER_CHAN_NUMOF) { + return -1; + } + get_dev(tim)->MCR &= ~(1 << (channel * 3)); + return 0; +} + +unsigned int timer_read(tim_t tim) +{ + return (unsigned int)(get_dev(tim)->TC); +} + +void timer_start(tim_t tim) +{ + get_dev(tim)->TCR = 1; +} + +void timer_stop(tim_t tim) +{ + get_dev(tim)->TCR = 0; +} + +void timer_reset(tim_t tim) +{ + lpc23xx_timer_t *dev = get_dev(tim); + dev->TCR |= 2; + asm("nop"); + dev->TCR &= ~(2); +} + +void timer_irq_enable(tim_t tim) +{ + /* TODO */ +} + +void timer_irq_disable(tim_t tim) +{ + /* TODO */ +} + +static inline void isr_handler(lpc23xx_timer_t *dev, int tim_num) +{ + for (int i = 0; i < TIMER_CHAN_NUMOF; i++) { + if (dev->IR & (1 << i)) { + dev->IR |= (1 << i); + dev->MCR &= ~(1 << (i * 3)); + isr_ctx[tim_num].cb(i); + } + } + /* we must not forget to acknowledge the handling of the interrupt */ + VICVectAddr = 0; +} + +void __attribute__((interrupt("IRQ"))) tim_isr_0(void) +{ + isr_handler(TMR0, 0); +} + +#if TIMER_NUMOF > 1 +void __attribute__((interrupt("IRQ"))) tim_isr_1(void) +{ + isr_handler(TMR1, 1); +} +#endif + +#if TIMER_NUMOF > 2 +void __attribute__((interrupt("IRQ"))) tim_isr_2(void) +{ + isr_handler(TMR2, 2); +} +#endif + +#if TIMER_NUMOF > 3 +void __attribute__((interrupt("IRQ"))) tim_isr_3(void) +{ + isr_handler(TMR3, 3); +} +#endif