Skip to content
Snippets Groups Projects
Commit ea4fb69f authored by Hauke Petersen's avatar Hauke Petersen
Browse files

Merge pull request #3779 from haukepetersen/add_lpc2387_timer

cpu/lpc2387: added periph timer implementation
parents 2d231611 84d61124
No related branches found
No related tags found
No related merge requests found
Showing with 337 additions and 262 deletions
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_MCU_GROUP = arm7
......@@ -56,6 +56,11 @@ extern "C" {
#define STDIO_RX_BUFSIZE (64U)
/** @} */
/**
* @brief Deprecated HW_TIMER definition (to be removed)
*/
#define HW_TIMER TIMER_DEV(0)
/**
* @brief Initialize the board's clock system
*/
......
......@@ -25,6 +25,22 @@
extern "C" {
#endif
/**
* @brief Clock configuration
* @{
*/
#define CLOCK_CORECLOCK (72000000U) /* this board runs with 72MHz */
#define CLOCK_PCLK (CLOCK_CORECLOCK)
/** @} */
/**
* @brief Timer configuration, select a number from 1 to 4
* @{
*/
#define TIMER_NUMOF (1U)
/** @} */
/**
* @brief PWM device and pinout configuration
*/
......
/*
* Copyright (C) 2014 INRIA
*
* 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 msba2
* @{
*/
/**
* @file
* @brief msba2 benchmark functions
*
* @author Heiko Will <heiko.will@fu-berlin.de>
* @author Zakaria Kasmi <zkasmi@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
*/
#include <stdint.h>
#include <stdio.h>
#include "lpc2387.h"
void benchmark_init(void)
{
PCLKSEL1 = (PCLKSEL1 & ~(BIT14|BIT15)) | (1 << 14); // CCLK to PCLK divider
PCONP |= PCTIM3;
T3TCR = 0; // disable timer
T3MCR = 0; // disable interrupt
T3CCR = 0; // capture is disabled.
T3EMR = 0; // no external match output.
T3PR = 0; // set prescaler
T3TC = 0; // reset counter
}
void benchmark_reset_start(void)
{
T3TCR = 0; // disable timer
T3TC = 0; // reset counter
T3TCR = BIT0;
}
unsigned int benchmark_read_stop(void)
{
T3TCR = 0; // disable timer
return T3TC;
}
......@@ -2,6 +2,7 @@ FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_PROVIDED += cpp
FEATURES_PROVIDED += config
FEATURES_MCU_GROUP = arm7
......@@ -53,6 +53,11 @@ extern "C" {
#define STDIO_RX_BUFSIZE (64U)
/** @} */
/**
* @brief Deprecated HW_TIMER definition (to be removed)
*/
#define HW_TIMER TIMER_DEV(0)
/**
* @brief initialize the board's clock system
*/
......
......@@ -25,6 +25,22 @@
extern "C" {
#endif
/**
* @brief Clock configuration
* @{
*/
#define CLOCK_CORECLOCK (72000000U) /* the msba2 runs with 72MHz */
#define CLOCK_PCLK (CLOCK_CORECLOCK)
/** @} */
/**
* @brief Timer configuration, select a number from 1 to 4
* @{
*/
#define TIMER_NUMOF (1U)
/** @} */
/**
* @brief PWM device and pinout configuration
*/
......
FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_spi
FEATURES_PROVIDED += periph_timer
FEATURES_MCU_GROUP = arm7
......@@ -59,6 +59,11 @@ void bl_init_clks(void);
#define STDIO_RX_BUFSIZE (64U)
/** @} */
/**
* @brief Deprecated HW_TIMER definition (to be removed)
*/
#define HW_TIMER TIMER_DEV(0)
/**
* @name dummy-defines for LEDs
* @{
......
......@@ -25,6 +25,22 @@
extern "C" {
#endif
/**
* @brief Clock configuration
* @{
*/
#define CLOCK_CORECLOCK (72000000U) /* the msba2 runs with 72MHz */
#define CLOCK_PCLK (CLOCK_CORECLOCK)
/** @} */
/**
* @brief Timer configuration, select a number from 1 to 4
* @{
*/
#define TIMER_NUMOF (1U)
/** @} */
/**
* @brief PWM device and pinout configuration
*/
......
......@@ -2,4 +2,4 @@ INCLUDES += -I$(RIOTCPU)/lpc2387/include
include $(RIOTCPU)/arm7_common/Makefile.include
USEMODULE += arm7_common periph periph_common bitfield newlib
USEMODULE += arm7_common periph periph_common bitfield newlib hwtimer_compat
/*
* Copyright (C) 2014 INRIA
*
* 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
* @file
* @internal
* @brief ARM kernel timer CPU dependent functions implementation
*
* @author Thomas Hillebrandt <hillebra@inf.fu-berlin.de>
* @author Heiko Will <hwill@inf.fu-berlin.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
*
*/
#include "cpu.h"
#include "bitarithm.h"
#include "hwtimer_cpu.h"
#include "arch/hwtimer_arch.h"
#include "irq.h"
#define VULP(x) ((volatile unsigned long*) (x))
/* High level interrupt handler */
static void (*int_handler)(int);
/* Timer 0-3 interrupt handler */
static void timer_irq(void) __attribute__((interrupt("IRQ")));
inline static unsigned long get_base_address(short timer)
{
return (volatile unsigned long)(TMR0_BASE_ADDR + (timer / 8) * 0x6C000 + (timer / 4 - (timer / 8) * 2) * 0x4000);
}
static void timer_irq(void)
{
short timer = 0;
if (T0IR) {
timer = 0;
}
else if (T1IR) {
timer = 4;
}
else if (T2IR) {
timer = 8;
}
volatile unsigned long base = get_base_address(timer);
if (*VULP(base + TXIR) & BIT0) {
*VULP(base + TXMCR) &= ~BIT0;
*VULP(base + TXIR) = BIT0;
int_handler(timer);
}
if (*VULP(base + TXIR) & BIT1) {
*VULP(base + TXMCR) &= ~BIT3;
*VULP(base + TXIR) = BIT1;
int_handler(timer + 1);
}
if (*VULP(base + TXIR) & BIT2) {
*VULP(base + TXMCR) &= ~BIT6;
*VULP(base + TXIR) = BIT2;
int_handler(timer + 2);
}
if (*VULP(base + TXIR) & BIT3) {
*VULP(base + TXMCR) &= ~BIT9;
*VULP(base + TXIR) = BIT3;
int_handler(timer + 3);
}
VICVectAddr = 0; /* acknowledge interrupt (if using VIC IRQ) */
}
static void timer0_init(uint32_t cpsr)
{
PCONP |= PCTIM0; /* power up timer */
T0TCR = 2; /* disable and reset timer */
T0MCR = 0; /* disable compare */
T0CCR = 0; /* capture is disabled */
T0EMR = 0; /* no external match output */
T0PR = cpsr; /* set prescaler */
install_irq(TIMER0_INT, &timer_irq, 1);
T0TCR = 1; /* reset counter */
}
static void timer1_init(uint32_t cpsr)
{
PCONP |= PCTIM1; /* power up timer */
T1TCR = 2; /* disable and reset timer */
T1MCR = 0; /* disable compare */
T1CCR = 0; /* capture is disabled */
T1EMR = 0; /* no external match output */
T1PR = cpsr; /* set prescaler */
install_irq(TIMER1_INT, &timer_irq, 1);
T1TCR = 1; /* reset counter */
}
static void timer2_init(uint32_t cpsr)
{
PCONP |= PCTIM2; /* power up timer */
T2TCR = 2; /* disable and reset timer */
T2MCR = 0; /* disable compare */
T2CCR = 0; /* capture is disabled */
T2EMR = 0; /* no external match output */
T2PR = cpsr; /* set prescaler */
install_irq(TIMER2_INT, &timer_irq, 1);
T2TCR = 1; /* reset counter */
}
void hwtimer_arch_init(void (*handler)(int), uint32_t fcpu)
{
uint32_t cpsr;
int_handler = handler;
cpu_clock_scale(fcpu, HWTIMER_SPEED, &cpsr);
timer0_init(cpsr);
timer1_init(cpsr);
timer2_init(cpsr);
}
/*---------------------------------------------------------------------------*/
void hwtimer_arch_enable_interrupt(void)
{
VICIntEnable = 1 << TIMER0_INT; /* Enable Interrupt */
VICIntEnable = 1 << TIMER1_INT; /* Enable Interrupt */
VICIntEnable = 1 << TIMER2_INT; /* Enable Interrupt */
}
/*---------------------------------------------------------------------------*/
void hwtimer_arch_disable_interrupt(void)
{
VICIntEnClr = 1 << TIMER0_INT; /* Disable Interrupt */
VICIntEnClr = 1 << TIMER1_INT; /* Disable Interrupt */
VICIntEnClr = 1 << TIMER2_INT; /* Disable Interrupt */
}
/*---------------------------------------------------------------------------*/
void hwtimer_arch_set(unsigned long offset, short timer)
{
/* Calculate base address of timer register */
/* Timer 0-3 are matched to TIMER0 */
/* Timer 4-7 are matched to TIMER1 */
/* Timer 8-11 are matched to TIMER2 */
volatile unsigned long base = get_base_address(timer);
/* Calculate match register address of corresponding timer */
timer %= 4;
unsigned long cpsr = disableIRQ();
volatile unsigned long *addr = VULP(base + TXMR0 + 4 * timer);
/* Calculate match register value */
unsigned long value = *VULP(base + TXTC) + offset;
*addr = value; /* set match register */
*VULP(base + TXIR) = 0x01 << timer; /* reset interrupt register value for corresponding match register */
*VULP(base + TXMCR) &= ~(7 << (3 * timer)); /* Clear all bits */
*VULP(base + TXMCR) |= (MR0I << (3 * timer)); /* enable interrupt for match register */
restoreIRQ(cpsr);
}
void hwtimer_arch_set_absolute(unsigned long value, short timer)
{
/* Calculate base address of timer register */
/* Timer 0-3 are matched to TIMER0 */
/* Timer 4-7 are matched to TIMER1 */
/* Timer 8-11 are matched to TIMER2 */
volatile unsigned long base = get_base_address(timer);
/* Calculate match register address of corresponding timer */
timer %= 4;
volatile unsigned long *addr = VULP(base + TXMR0 + 4 * timer);
/* Calculate match register value */
*addr = value; /* set match register */
*VULP(base + TXIR) = 0x01 << timer; /* reset interrupt register value for corresponding match register */
*VULP(base + TXMCR) &= ~(7 << (3 * timer)); /* Clear all bits */
*VULP(base + TXMCR) |= (MR0I << (3 * timer)); /* enable interrupt for match register */
}
/*---------------------------------------------------------------------------*/
void hwtimer_arch_unset(short timer)
{
volatile unsigned long base = get_base_address(timer);
timer %= 4;
*VULP(base + TXMCR) &= ~(MR0I << (3 * timer)); /* disable interrupt for match register */
*VULP(base + TXIR) = 0x01 << timer; /* reset interrupt register value for corresponding match register */
}
/*---------------------------------------------------------------------------*/
unsigned long hwtimer_arch_now(void)
{
return T0TC;
}
void hwtimer_arch_setcounter(unsigned int val)
{
T0TC = val;
}
......@@ -12,13 +12,20 @@
*
******************************************************************************/
#ifndef __LPC23xx_H
#define __LPC23xx_H
#ifndef LPC23XX_H
#define LPC23XX_H
#include <stdint.h>
#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 */
......@@ -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
......
/*
* 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 <hauke.petersen@fu-berlin.de>
* @}
*/
#include <stdint.h>
#include <string.h>
#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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment