|
|
|
#include "timer.h"
|
|
|
|
#include "interrupt.h"
|
|
|
|
#include "uart.h"
|
|
|
|
#include "event.h"
|
|
|
|
|
|
|
|
#define FP0XVAL (*((volatile unsigned int *) 0x3FFFC014))
|
|
|
|
|
|
|
|
#define TIMER0BASE 0xE0004000
|
|
|
|
#define TIMER1BASE 0xE0008000
|
|
|
|
#define TIMER2BASE 0xE0070000
|
|
|
|
#define TIMER3BASE 0xE0074000
|
|
|
|
|
|
|
|
#define IR 0x00
|
|
|
|
#define TCR 0x04
|
|
|
|
#define TC 0x08
|
|
|
|
#define PR 0x0c
|
|
|
|
#define PC 0x10
|
|
|
|
#define MCR 0x14
|
|
|
|
#define MR0 0x18
|
|
|
|
#define MR1 0x1C
|
|
|
|
#define MR2 0x20
|
|
|
|
#define MR3 0x24
|
|
|
|
#define CCR 0x28
|
|
|
|
#define CR0 0x2C
|
|
|
|
#define CR1 0x30
|
|
|
|
#define CR2 0x34
|
|
|
|
#define CR3 0x38
|
|
|
|
#define EMR 0x3C
|
|
|
|
#define CTCR 0x70
|
|
|
|
#define PWM 0x74
|
|
|
|
|
|
|
|
#define TREG(x) (((volatile unsigned char *)TIMER0BASE)[x])
|
|
|
|
#define TWREG(x) (((volatile unsigned int *)TIMER0BASE)[(x)/sizeof(unsigned int)])
|
|
|
|
|
|
|
|
#define T1REG(x) (((volatile unsigned char *)TIMER1BASE)[x])
|
|
|
|
#define T1WREG(x) (((volatile unsigned int *)TIMER1BASE)[(x)/sizeof(unsigned int)])
|
|
|
|
|
|
|
|
#define T2REG(x) (((volatile unsigned char *)TIMER2BASE)[x])
|
|
|
|
#define T2WREG(x) (((volatile unsigned int *)TIMER2BASE)[(x)/sizeof(unsigned int)])
|
|
|
|
|
|
|
|
#define T3REG(x) (((volatile unsigned char *)TIMER3BASE)[x])
|
|
|
|
#define T3WREG(x) (((volatile unsigned int *)TIMER3BASE)[(x)/sizeof(unsigned int)])
|
|
|
|
|
|
|
|
#define TCR_ENABLE (1<<0)
|
|
|
|
#define TCR_RESET (1<<1)
|
|
|
|
|
|
|
|
#define MR0I (1<<0)
|
|
|
|
#define MR0R (1<<1)
|
|
|
|
#define MR0S (1<<2)
|
|
|
|
#define MR1I (1<<3)
|
|
|
|
#define MR1R (1<<4)
|
|
|
|
#define MR1S (1<<5)
|
|
|
|
#define MR2I (1<<6)
|
|
|
|
#define MR2R (1<<7)
|
|
|
|
#define MR2S (1<<8)
|
|
|
|
#define MR3I (1<<9)
|
|
|
|
#define MR3R (1<<10)
|
|
|
|
#define MR3S (1<<11)
|
|
|
|
|
|
|
|
|
|
|
|
volatile unsigned int timer0_rising[4];
|
|
|
|
volatile unsigned int timer0_width[4];
|
|
|
|
|
|
|
|
#ifdef TIMER_CPPM
|
|
|
|
volatile unsigned int timer0_cppm[8];
|
|
|
|
volatile unsigned int timer0_cppm_chan = 0;
|
|
|
|
volatile unsigned int timer0_sync_timestamp;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
unsigned int timer_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
|
|
|
|
|
|
|
|
void __attribute__((interrupt("IRQ"))) timer_interrupt_handler(void);
|
|
|
|
void __attribute__((interrupt("IRQ"))) timer0_interrupt_handler(void);
|
|
|
|
|
|
|
|
void timer_event_handler(void);
|
|
|
|
|
|
|
|
/* Timer 0 : Capture */
|
|
|
|
/* Timer 1 : Output */
|
|
|
|
/* Timer 2 : Output */
|
|
|
|
/* Timer 3 : System */
|
|
|
|
|
|
|
|
void init_timer(void)
|
|
|
|
{
|
|
|
|
T3REG(TCR) = TCR_ENABLE | TCR_RESET;
|
|
|
|
|
|
|
|
T3REG(CTCR) = 0; /* Use PCLK */
|
|
|
|
T3WREG(TC) = 0;
|
|
|
|
T3WREG(PR) = TIMER_PRESCALE;
|
|
|
|
T3WREG(PC) = 0;
|
|
|
|
|
|
|
|
T3REG(TCR) = TCR_ENABLE;
|
|
|
|
|
|
|
|
interrupt_register(TIMER0, timer0_interrupt_handler);
|
|
|
|
|
|
|
|
TREG(TCR) = TCR_ENABLE | TCR_RESET;
|
|
|
|
|
|
|
|
TREG(CTCR) = 0; /* Use PCLK */
|
|
|
|
TWREG(TC) = 0;
|
|
|
|
TWREG(PR) = TIMER0_PRESCALE;
|
|
|
|
TWREG(PC) = 0;
|
|
|
|
|
|
|
|
TWREG(CCR) = 0x00000fff;
|
|
|
|
|
|
|
|
TREG(TCR) = TCR_ENABLE;
|
|
|
|
|
|
|
|
T2REG(TCR) = TCR_ENABLE | TCR_RESET;
|
|
|
|
T2REG(CTCR) = 0; /* Use PCLK */
|
|
|
|
T2WREG(PR) = 3; // Prescaling
|
|
|
|
T2WREG(PC) = 0; // Reset the prescale counter
|
|
|
|
T2WREG(TC) = 0; // Reset the counter
|
|
|
|
|
|
|
|
T2WREG(MCR) = 0x0400; // Reset on MR3 match
|
|
|
|
T2WREG(PWM) = 0x00000005; // Enable PWMs
|
|
|
|
|
|
|
|
T2WREG(MR3) = PWM_PERIOD; // Period duration
|
|
|
|
|
|
|
|
/* This is chosen to be an invalid output. */
|
|
|
|
T2WREG(MR2) = 1; // Pulse width
|
|
|
|
T2WREG(MR0) = 1; // Pulse width
|
|
|
|
|
|
|
|
T1REG(TCR) = TCR_ENABLE | TCR_RESET;
|
|
|
|
T1REG(CTCR) = 0; /* Use PCLK */
|
|
|
|
T1WREG(PR) = 3; // Prescaling
|
|
|
|
T1WREG(PC) = 0; // Reset the prescale counter
|
|
|
|
T1WREG(TC) = 0; // Reset the counter
|
|
|
|
|
|
|
|
T1WREG(MCR) = 0x0400; // Reset on MR3 match
|
|
|
|
T1WREG(PWM) = 0x00000003; // Enable PWMs
|
|
|
|
|
|
|
|
T1WREG(MR3) = PWM_PERIOD; // Period duration
|
|
|
|
|
|
|
|
/* This is chosen to be an invalid output. */
|
|
|
|
T1WREG(MR1) = 1; // Pulse width
|
|
|
|
T1WREG(MR0) = 1; // Pulse width
|
|
|
|
|
|
|
|
T2REG(TCR) = TCR_ENABLE;
|
|
|
|
T1REG(TCR) = TCR_ENABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int timer_read(void)
|
|
|
|
{
|
|
|
|
return TWREG(TC);
|
|
|
|
}
|
|
|
|
|
|
|
|
void timer_delay_clocks(unsigned int clocks)
|
|
|
|
{
|
|
|
|
signed int time = TWREG(TC) + clocks;
|
|
|
|
while (((signed int) (time-TWREG(TC))) > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void timer_set_period(unsigned int period)
|
|
|
|
{
|
|
|
|
interrupt_register(TIMER3, timer_interrupt_handler);
|
|
|
|
T3WREG(MR0) = period-1;
|
|
|
|
T3WREG(MCR) = MR0I | MR0R;
|
|
|
|
T3WREG(TC) = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void __attribute__((interrupt("IRQ"))) timer_interrupt_handler(void)
|
|
|
|
{
|
|
|
|
unsigned int ir;
|
|
|
|
ir = T3REG(IR);
|
|
|
|
T3REG(IR) = ir;
|
|
|
|
|
|
|
|
if (ir & (1<<0)) {
|
|
|
|
/* Match channel 0 */
|
|
|
|
event_set(EVENT_TIMER);
|
|
|
|
}
|
|
|
|
|
|
|
|
interrupt_clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void __attribute__((interrupt("IRQ"))) timer0_interrupt_handler(void)
|
|
|
|
{
|
|
|
|
unsigned int ir;
|
|
|
|
unsigned int gpio;
|
|
|
|
ir = TREG(IR);
|
|
|
|
TREG(IR) = ir;
|
|
|
|
|
|
|
|
gpio = FP0XVAL;
|
|
|
|
|
|
|
|
if (ir & (1<<5)) {
|
|
|
|
/* Capture channel 1 */
|
|
|
|
if (gpio & (1<<4)) {
|
|
|
|
timer0_rising[0] = TWREG(CR1);
|
|
|
|
} else {
|
|
|
|
timer0_width[0] = TWREG(CR1) - timer0_rising[0];
|
|
|
|
#ifdef TIMER_CPPM
|
|
|
|
if (timer0_width[0] > TIMER_CPPM_SYNC) {
|
|
|
|
timer0_cppm_chan = 0;
|
|
|
|
timer0_sync_timestamp = timer0_rising[0];
|
|
|
|
} else {
|
|
|
|
if (timer0_cppm_chan < 8) {
|
|
|
|
timer0_cppm[timer0_cppm_chan] =
|
|
|
|
timer0_width[0];
|
|
|
|
timer0_cppm_chan++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ir & (1<<6)) {
|
|
|
|
/* Capture channel 2 */
|
|
|
|
if (gpio & (1<<6)) {
|
|
|
|
timer0_rising[1] = TWREG(CR2);
|
|
|
|
} else {
|
|
|
|
timer0_width[1] = TWREG(CR2) - timer0_rising[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
interrupt_clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool timer_valid(int channel) {
|
|
|
|
channel = TIMER_CH(channel);
|
|
|
|
/* Be careful here to ensure that this can't be in the past */
|
|
|
|
unsigned int chtime = timer0_rising[channel]; /* Atomic */
|
|
|
|
unsigned int time = TWREG(TC); /* Atomic */
|
|
|
|
return (time - chtime) < TIMER_INPUT_TIMEOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TIMER_CPPM
|
|
|
|
bool timer_allvalid(void) {
|
|
|
|
/* Be careful here to ensure that this can't be in the past */
|
|
|
|
unsigned int chtime = timer0_sync_timestamp; /* Atomic */
|
|
|
|
unsigned int time = TWREG(TC); /* Atomic */
|
|
|
|
return (time - chtime) < TIMER_INPUT_TIMEOUT;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
bool timer_allvalid(void) {
|
|
|
|
unsigned int time;
|
|
|
|
unsigned int chtime[4];
|
|
|
|
int i;
|
|
|
|
/* Be careful here to ensure that this can't be in the past */
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
chtime[i] = timer0_rising[i];
|
|
|
|
time = TWREG(TC);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
if ((time - chtime[i]) >= TIMER_INPUT_TIMEOUT)
|
|
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void timer_set_pwm_value(int channel, int value)
|
|
|
|
{
|
|
|
|
value = PWM_PERIOD - (PWM_MAX + value);
|
|
|
|
switch (channel) {
|
|
|
|
case 0:
|
|
|
|
T2WREG(MR2) = value;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
T2WREG(MR0) = value;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
T1WREG(MR0) = value;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
T1WREG(MR1) = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void timer_set_pwm_invalid(int channel)
|
|
|
|
{
|
|
|
|
int value = 1;
|
|
|
|
switch (channel) {
|
|
|
|
case 0:
|
|
|
|
T2WREG(MR2) = value;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
T2WREG(MR0) = value;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
T1WREG(MR0) = value;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
T1WREG(MR1) = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|