diff options
-rw-r--r-- | examples/test-timers.cpp | 174 | ||||
-rw-r--r-- | examples/vga.cpp | 262 | ||||
-rw-r--r-- | libmaple/timers.c | 415 | ||||
-rw-r--r-- | libmaple/timers.h | 21 | ||||
-rw-r--r-- | libmaple/util.c | 2 | ||||
-rw-r--r-- | notes/timers.txt | 77 | ||||
-rw-r--r-- | notes/vga.txt | 32 | ||||
-rw-r--r-- | wirish/comm/HardwareSPI.cpp | 4 | ||||
-rw-r--r-- | wirish/comm/HardwareSerial.cpp | 4 | ||||
-rw-r--r-- | wirish/wirish.c | 6 | ||||
-rw-r--r-- | wirish/wirish.h | 2 |
11 files changed, 957 insertions, 42 deletions
diff --git a/examples/test-timers.cpp b/examples/test-timers.cpp new file mode 100644 index 0000000..c3e3cb9 --- /dev/null +++ b/examples/test-timers.cpp @@ -0,0 +1,174 @@ +// Sample main.cpp file. Blinks an LED, sends a message out USART2 +// and turns on PWM on pin 2 + +#include "wirish.h" +#include "timers.h" + +#define LED_PIN 13 +#define PWM_PIN 2 + +void handler1(void); +void handler2(void); +void handler3(void); +void handler4(void); + +void setup_test_timer(void); + +int toggle = 0; +int timer = 1; +int state = 3; +int last_but = 0; + +int count1 = 0; +int count2 = 0; +int count3 = 0; +int count4 = 0; +uint16 rate1 = 1000; +uint16 rate2 = 2000; +uint16 rate3 = 3000; +uint16 rate4 = 4000; +uint16 val1 = 10000; +uint16 val2 = 10000; +uint16 val3 = 10000; +uint16 val4 = 10000; + +void setup() +{ + /* Set up the LED to blink */ + pinMode(LED_PIN, OUTPUT); + + pinMode(38, INPUT); + + /* Send a message out USART2 */ + //SerialUSB.begin(9600); + SerialUSB.println("Begining timer test..."); + + /* Send a message out the usb virtual serial port */ + //SerialUSB.println("Hello!"); + + timer = 1; + setup_test_timer(); + +} + + +void loop() { + toggle ^= 1; + digitalWrite(LED_PIN, toggle); + delay(800); + + + if(digitalRead(38) && !last_but) { + state++; + switch(state){ + case 1: + SerialUSB.println("Testing Timer1 ---------------------------"); + timer = 1; + setup_test_timer(); + break; + case 2: + SerialUSB.println("Testing Timer2 ---------------------------"); + timer_set_mode(timer,1,TIMER_DISABLED); + timer_set_mode(timer,2,TIMER_DISABLED); + timer_set_mode(timer,3,TIMER_DISABLED); + timer_set_mode(timer,4,TIMER_DISABLED); + timer_set_count(1,0); + timer_set_count(2,0); + timer_set_count(3,0); + timer_set_count(4,0); + timer = 2; + setup_test_timer(); + break; + case 3: + SerialUSB.println("Testing Timer3 ---------------------------"); + timer_set_mode(timer,1,TIMER_DISABLED); + timer_set_mode(timer,2,TIMER_DISABLED); + timer_set_mode(timer,3,TIMER_DISABLED); + timer_set_mode(timer,4,TIMER_DISABLED); + timer = 3; + setup_test_timer(); + break; + case 4: + SerialUSB.println("Testing Timer4 ---------------------------"); + timer_set_mode(timer,1,TIMER_DISABLED); + timer_set_mode(timer,2,TIMER_DISABLED); + timer_set_mode(timer,3,TIMER_DISABLED); + timer_set_mode(timer,4,TIMER_DISABLED); + timer = 4; + setup_test_timer(); + break; + default: + state = 0; + timer_set_mode(timer,1,TIMER_DISABLED); + timer_set_mode(timer,2,TIMER_DISABLED); + timer_set_mode(timer,3,TIMER_DISABLED); + timer_set_mode(timer,4,TIMER_DISABLED); + timer = 0; + SerialUSB.println("Restarting -------------------------------"); + } + } + + SerialUSB.print("Doing ------------------ "); SerialUSB.println(timer,DEC); + if(timer!=0) { SerialUSB.print("CNT: "); SerialUSB.println(timer_get_count(timer),DEC); } + SerialUSB.print("Count1 : "); SerialUSB.println(count1,DEC); + SerialUSB.print("Count2 : "); SerialUSB.println(count2,DEC); + SerialUSB.print("Count3 : "); SerialUSB.println(count3,DEC); + SerialUSB.print("Count4 : "); SerialUSB.println(count4,DEC); + SerialUSB.println(); + /* + SerialUSB.print("Status : "); SerialUSB.println(get_sr(),HEX); + */ + last_but = digitalRead(38); +} + +void setup_test_timer(void) { + timer_set_prescaler(timer,10000); + timer_set_mode(timer,1,TIMER_OUTPUTCOMPARE); + timer_set_mode(timer,2,TIMER_OUTPUTCOMPARE); + timer_set_mode(timer,3,TIMER_OUTPUTCOMPARE); + timer_set_mode(timer,4,TIMER_OUTPUTCOMPARE); + val1 = val2 = val3 = val4 = 10000; + timer_set_compare_value(timer,1,val1); + timer_set_compare_value(timer,2,val2); + timer_set_compare_value(timer,3,val3); + timer_set_compare_value(timer,4,val4); + timer_attach_interrupt(timer,1,handler1); + timer_attach_interrupt(timer,2,handler2); + timer_attach_interrupt(timer,3,handler3); + timer_attach_interrupt(timer,4,handler4); + count1 = count2 = count3 = count4 = 0; +} + +void handler1(void) { + val1 += rate1; + timer_set_compare_value(timer,1,val1); + count1++; + //SerialUSB.print("CC3 Inter: "); SerialUSB.print(get_sr(),HEX); + // SerialUSB.print(", "); SerialUSB.println(get_sr_buff(),HEX); +} +void handler2(void) { + val2 += rate2; + timer_set_compare_value(timer,2,val2); + count2++; +} +void handler3(void) { + val3 += rate3; + timer_set_compare_value(timer,3,val3); + count3++; +} +void handler4(void) { + val4 += rate4; + timer_set_compare_value(timer,4,val4); + count4++; +} + + +int main(void) { + init(); + setup(); + + while (1) { + loop(); + } + return 0; +} diff --git a/examples/vga.cpp b/examples/vga.cpp new file mode 100644 index 0000000..c2e61ae --- /dev/null +++ b/examples/vga.cpp @@ -0,0 +1,262 @@ + +#include "wirish.h" + +/* +D5 PB6 - TIM4_CH1 I2C1_SCL - - Y +D6 PA8 - TIM1_CH1 - USART1_CK - Y +D7 PA9 - TIM1_CH2 - USART1_TX - Y +D8 PA10 - TIM1_CH3 - USART1_RX - Y +D9 PB7 - TIM4_CH2 I2C1_SDA - - Y +*/ + +//gpio_write_bit(GPIOB_BASE, 6, 1); // VGA_R +//gpio_write_bit(GPIOB_BASE, 6, 0); + +//(GPIOA_BASE)->BSRR = BIT(8); +//asm volatile("nop"); +//(GPIOA_BASE)->BRR = BIT(8); +/* + gpio_write_bit(GPIOB_BASE, 6, 1); // VGA_R + gpio_write_bit(GPIOB_BASE, 6, 0); + gpio_write_bit(GPIOA_BASE, 8, 1); // VGA_G + gpio_write_bit(GPIOA_BASE, 8, 0); + gpio_write_bit(GPIOA_BASE, 9, 1); // VGA_B + gpio_write_bit(GPIOA_BASE, 9, 0); + gpio_write_bit(GPIOA_BASE, 10, 1); // VGA_V + gpio_write_bit(GPIOA_BASE, 10, 0); + gpio_write_bit(GPIOB_BASE, 7, 1); // VGA_H + gpio_write_bit(GPIOB_BASE, 7, 0); +*/ + +#define LED_PIN 13 +#define VGA_R 5 // B6 +#define VGA_G 6 // A8 +#define VGA_B 7 // A9 +#define VGA_V 11 // A6 +#define VGA_H 12 // A7 +#define VGA_R_HIGH (GPIOB_BASE)->BSRR = BIT(6) +#define VGA_R_LOW (GPIOB_BASE)->BRR = BIT(6) +#define VGA_G_HIGH (GPIOA_BASE)->BSRR = BIT(8) +#define VGA_G_LOW (GPIOA_BASE)->BRR = BIT(8) +#define VGA_B_HIGH (GPIOA_BASE)->BSRR = BIT(9) +#define VGA_B_LOW (GPIOA_BASE)->BRR = BIT(9) +#define VGA_V_HIGH (GPIOA_BASE)->BSRR = BIT(6) +#define VGA_V_LOW (GPIOA_BASE)->BRR = BIT(6) +#define VGA_H_HIGH (GPIOA_BASE)->BSRR = BIT(7) +#define VGA_H_LOW (GPIOA_BASE)->BRR = BIT(7) + +void isr_porch(void); +void isr_start(void); +void isr_stop(void); +void isr_update(void); + +void setup() +{ + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, 1); + pinMode(VGA_R, OUTPUT); + pinMode(VGA_G, OUTPUT); + pinMode(VGA_B, OUTPUT); + pinMode(VGA_V, OUTPUT); + pinMode(VGA_H, OUTPUT); + + /* Send a message out USART2 */ + Serial2.begin(9600); + Serial2.println("Video time..."); + + + digitalWrite(VGA_R, 0); + digitalWrite(VGA_G, 0); + digitalWrite(VGA_B, 0); + digitalWrite(VGA_H,1); + digitalWrite(VGA_V,1); + + timer_set_prescaler(4,0); + timer_set_mode(4, 1, TIMER_OUTPUTCOMPARE); + timer_set_mode(4, 2, TIMER_OUTPUTCOMPARE); + timer_set_mode(4, 3, TIMER_OUTPUTCOMPARE); + timer_set_mode(4, 4, TIMER_OUTPUTCOMPARE); + timer_set_reload(4, 2287); + timer_set_compare_value(4,1,200); + timer_set_compare_value(4,2,300); + timer_set_compare_value(4,3,2170); // 2219 max... + timer_set_compare_value(4,4,1); + timer_attach_interrupt(4,1,isr_porch); + timer_attach_interrupt(4,2,isr_start); + timer_attach_interrupt(4,3,isr_stop); + timer_attach_interrupt(4,4,isr_update); + + timer_set_count(4,0); +} + +int toggle = 0; +uint16 x = 0; +uint16 y = 0; +uint8 v_active = 1; +GPIO_Port *portb = GPIOB_BASE; + +void isr_porch(void) { + VGA_H_HIGH; + y++; + if(y>=523) { + y=1; + v_active = 1; + return; + } + if(y>=492) { + VGA_V_HIGH; + return; + } + if(y>=490) { + VGA_V_LOW; + return; + } + if(y>=479) { // 479 + v_active = 0; + return; + } +} + +uint8 logo[18][16] = { + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, + {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, + {0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,}, + {0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,}, + {0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,}, + {0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,}, + {0,0,1,0,0,1,0,1,0,1,0,0,1,0,0,0,}, + {0,1,0,0,0,0,1,1,1,0,0,0,0,1,0,0,}, + {0,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,}, + {1,0,0,0,1,0,0,1,0,0,1,0,0,0,1,0,}, + {1,0,0,0,0,1,0,1,0,1,0,0,0,0,1,0,}, + {1,0,0,0,0,0,1,1,1,0,0,0,0,0,1,0,}, + {0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,}, + {0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,}, + {0,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,}, + {0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,}, + {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, }; + +void isr_start(void) { + if(!v_active) { return; } + VGA_R_HIGH; + //delayMicroseconds(2); + //gpio_write_bit(GPIOA_BASE, 8, 1); // VGA_G + for(x=0; x<32; x++) { + if(logo[y/28][x/2]) { + VGA_G_HIGH; + VGA_B_HIGH; + } else { + VGA_G_LOW; + VGA_B_LOW; + } + } + +} +void isr_stop(void) { + if(!v_active) { return; } + VGA_R_LOW; + VGA_G_LOW; + VGA_B_LOW; +} +void isr_update(void) { + VGA_H_LOW; +} + +void loop() { + /* + toggle ^= 1; + digitalWrite(LED_PIN, toggle); + delay(100); + Serial2.println("HIHIHI!"); + */ + //for(y=0; y<480; y++) { + /* + for(y=0; y<160; y++) { + VGA_R_LOW; + VGA_H_LOW; + delayMicroseconds(3); + VGA_H_HIGH; + VGA_R_HIGH; + delayMicroseconds(8); + VGA_G_HIGH; + delayMicroseconds(10); + VGA_B_HIGH; + delayMicroseconds(10); + VGA_R_LOW; + VGA_B_LOW; + VGA_G_LOW; + //VGA_G_HIGH; + } + for(y=0; y<160; y++) { + VGA_R_LOW; + VGA_H_LOW; + delayMicroseconds(3); + VGA_H_HIGH; + VGA_G_HIGH; + delayMicroseconds(8); + VGA_R_HIGH; + delayMicroseconds(10); + VGA_B_HIGH; + delayMicroseconds(10); + VGA_R_LOW; + VGA_B_LOW; + VGA_G_LOW; + //VGA_G_HIGH; + } + for(y=0; y<160; y++) { + VGA_R_LOW; + VGA_H_LOW; + delayMicroseconds(3); + VGA_H_HIGH; + VGA_B_HIGH; + delayMicroseconds(8); + VGA_G_HIGH; + delayMicroseconds(10); + VGA_R_HIGH; + delayMicroseconds(10); + VGA_R_LOW; + VGA_B_LOW; + VGA_G_LOW; + //VGA_G_HIGH; + } + for(y=0; y<11; y++) { + VGA_R_LOW; + VGA_H_LOW; + delayMicroseconds(3); + VGA_R_LOW; + VGA_H_HIGH; + delayMicroseconds(28); + } + VGA_V_LOW; + for(y=0; y<2; y++) { + VGA_R_LOW; + VGA_H_LOW; + delayMicroseconds(3); + VGA_R_LOW; + VGA_H_HIGH; + delayMicroseconds(28); + } + VGA_V_HIGH; + for(y=0; y<30; y++) { + VGA_R_LOW; + VGA_H_LOW; + delayMicroseconds(3); + VGA_R_LOW; + VGA_H_HIGH; + delayMicroseconds(28); + } + */ + +} + + +int main(void) { + init(); + setup(); + + while (1) { + loop(); + } + return 0; +} diff --git a/libmaple/timers.c b/libmaple/timers.c index b13cc3a..a772cf9 100644 --- a/libmaple/timers.c +++ b/libmaple/timers.c @@ -75,9 +75,17 @@ typedef struct { uint16 RESERVED19; } Timer; + +volatile static voidFuncPtr timer1_handlers[4]; +volatile static voidFuncPtr timer2_handlers[4]; +volatile static voidFuncPtr timer3_handlers[4]; +volatile static voidFuncPtr timer4_handlers[4]; + void timer_init(uint8 timer_num, uint16 prescale) { - Timer *timer; - uint32 is_advanced = 0; + // This initialization is very PWM-specific. That's a good default but it + // should probably call down to a set_mode function + + Timer *timer; uint8 is_advanced = 0; ASSERT(timer_num > 0 && timer_num <= 4); @@ -118,7 +126,7 @@ void timer_init(uint8 timer_num, uint16 prescale) { * we'll worry about that later. */ timer->CCR1 = 0x8FFF; // PWM start value timer->CCMR1 |= 0x68; // PWM mode 1, enable preload register. - timer->CCER |= 0x001; // enable ch + timer->CCER |= 0x002; // enable ch timer->CCR2 = 0x8FFF; // PWM start value timer->CCMR1 |= (0x68 << 8);// PWM mode 1, enable preload register. @@ -137,12 +145,13 @@ void timer_init(uint8 timer_num, uint16 prescale) { timer->BDTR = 0x8000; // moe enable } + timer->SR = 0; // clear it timer->DIER = 0; // disable update interrupt timer->EGR = 1; // Initialize update event and shadow registers timer->CR1 |= 1; // Enable timer } -void timers_set_prescaler(uint32 timer_num, uint16 prescale) { +void timer_set_count(uint8 timer_num, uint16 value) { Timer *timer; ASSERT(timer_num > 0 && timer_num <= 4); @@ -160,10 +169,10 @@ void timers_set_prescaler(uint32 timer_num, uint16 prescale) { timer = (Timer*)TIMER4_BASE; break; } - timer->PSC = prescale; + timer->CNT = value; } -void timers_set_reload(uint32 timer_num, uint16 max_reload) { +uint16 timer_get_count(uint8 timer_num) { Timer *timer; ASSERT(timer_num > 0 && timer_num <= 4); @@ -181,28 +190,61 @@ void timers_set_reload(uint32 timer_num, uint16 max_reload) { timer = (Timer*)TIMER4_BASE; break; } - timer->ARR = max_reload; + return timer->CNT; } +void timer_set_prescaler(uint8 timer_num, uint16 prescale) { + Timer *timer; + ASSERT(timer_num > 0 && timer_num <= 4); -void timers_disable(void) { + switch(timer_num) { + case 1: + timer = (Timer*)TIMER1_BASE; + break; + case 2: + timer = (Timer*)TIMER2_BASE; + break; + case 3: + timer = (Timer*)TIMER3_BASE; + break; + case 4: + timer = (Timer*)TIMER4_BASE; + break; + } + timer->PSC = prescale; +} + +void timer_set_reload(uint8 timer_num, uint16 max_reload) { Timer *timer; - int i; - Timer *timers[4] = { - (Timer*)TIMER1_BASE, - (Timer*)TIMER2_BASE, - (Timer*)TIMER3_BASE, - (Timer*)TIMER4_BASE, - }; - - for (i = 0; i < 4; i++) { - timer = timers[i]; - timer->CR1 = 0; - timer->CCER = 0; + ASSERT(timer_num > 0 && timer_num <= 4); + + switch(timer_num) { + case 1: + timer = (Timer*)TIMER1_BASE; + break; + case 2: + timer = (Timer*)TIMER2_BASE; + break; + case 3: + timer = (Timer*)TIMER3_BASE; + break; + case 4: + timer = (Timer*)TIMER4_BASE; + break; } + timer->ARR = max_reload; } -void timers_disable_channel(uint8 timer_num, uint8 channel) { + +void timer_disable_all(void) { + // Note: this must be very robust because it gets called from, eg, ASSERT + // TODO: rewrite? + Timer *timer; Timer *timers[4] = { (Timer*)TIMER1_BASE, + (Timer*)TIMER2_BASE, (Timer*)TIMER3_BASE, (Timer*)TIMER4_BASE, }; int i; + for (i = 0; i < 4; i++) { timer = timers[i]; timer->CR1 = 0; timer->CCER = + 0; } } + +void timer_set_mode(uint8 timer_num, uint8 channel, uint8 mode) { Timer *timer; switch (timer_num) { case 1: @@ -220,21 +262,338 @@ void timers_disable_channel(uint8 timer_num, uint8 channel) { default: ASSERT(0); } + switch(mode) { + case TIMER_DISABLED: + // Disable the channel + // Disable any interrupt + // Clear interrupt SR? (TODO) + switch (channel) { + case 1: + timer->DIER &= ~(1 << channel); // 1-indexed compare nums + timer->CCER &= ~(0x1); + break; + case 2: + timer->DIER &= ~(1 << channel); // 1-indexed compare nums + timer->CCER &= ~(0x10); + break; + case 3: + timer->DIER &= ~(1 << channel); // 1-indexed compare nums + timer->CCER &= ~(0x100); + break; + case 4: + timer->DIER &= ~(1 << channel); // 1-indexed compare nums + timer->CCER &= ~(0x1000); + break; + default: + ASSERT(0); + } + break; + case TIMER_PWM: + // Set CCMR mode + // Keep existing reload value + // Disable any interrupt + // Clear interrupt SR? (TODO) + // Enable channel + switch (channel) { + case 1: + timer->DIER &= ~(1 << channel); // 1-indexed compare nums + timer->CCMR1 &= ~(0xFF); + timer->CCMR1 |= 0x68; // PWM mode 1, enable preload register. + timer->CCER |= 0x0001; // enable ch + break; + case 2: + timer->DIER &= ~(1 << channel); // 1-indexed compare nums + timer->CCMR1 &= ~(0xFF00); + timer->CCMR1 |= (0x68 << 8);// PWM mode 1, enable preload register. + timer->CCER |= 0x0010; // enable ch + break; + case 3: + timer->DIER &= ~(1 << channel); // 1-indexed compare nums + timer->CCMR2 &= ~(0xFF); + timer->CCMR2 |= 0x68; // PWM mode 1, enable preload register. + timer->CCER |= 0x0100; // enable ch + break; + case 4: + timer->DIER &= ~(1 << channel); // 1-indexed compare nums + timer->CCMR2 &= ~(0xFF00); + timer->CCMR2 |= (0x68 << 8);// PWM mode 1, enable preload register. + timer->CCER |= 0x1000; // enable ch + break; + default: + ASSERT(0); + } + case TIMER_OUTPUTCOMPARE: + // Set CCMR mode + // Keep existing reload value + // Don't modify interrupt (needs to be attached to enable) + // Clear interrupt SR? (TODO) + // Enable channel + switch (channel) { + case 1: + timer->CCMR1 &= ~(0xFF); + timer->CCMR1 |= 0x0010; // PWM mode 1, enable preload register. + timer->CCER |= 0x001; // enable ch + break; + case 2: + timer->CCMR1 &= ~(0xFF00); + timer->CCMR1 |= 0x1000; // PWM mode 1, enable preload register. + timer->CCER |= 0x0010; // enable ch + break; + case 3: + timer->CCMR2 &= ~(0xFF); + timer->CCMR2 |= 0x0010; // PWM mode 1, enable preload register. + timer->CCER |= 0x0100; // enable ch + break; + case 4: + timer->CCMR2 &= ~(0xFF00); + timer->CCMR2 |= 0x1000; // PWM mode 1, enable preload register. + timer->CCER |= 0x1000; // enable ch + break; + default: + ASSERT(0); + } + break; + // TODO: others + default: + ASSERT(0); + } +} - switch (channel) { +void timer_set_compare_value(uint8 timer_num, uint8 compare_num, uint16 value) { + // The faster version of this function is the inline timer_pwm_write_ccr + + Timer *timer; TimerCCR *timer_ccr; ASSERT(timer_num > 0 && timer_num <= 4 + && compare_num > 0 && compare_num <= 4); + + switch(timer_num) { case 1: - timer->CCER &= ~(0x1); + timer = (Timer*)TIMER1_BASE; break; case 2: - timer->CCER &= ~(0x10); + timer = (Timer*)TIMER2_BASE; break; case 3: - timer->CCER &= ~(0x100); + timer = (Timer*)TIMER3_BASE; break; case 4: - timer->CCER &= ~(0x1000); + timer = (Timer*)TIMER4_BASE; break; - default: - ASSERT(0); + } + switch(compare_num) { + case 1: + timer->CCR1 = value; + break; + case 2: + timer->CCR2 = value; + break; + case 3: + timer->CCR3 = value; + break; + case 4: + timer->CCR4 = value; + break; + } +} + +void timer_attach_interrupt(uint8 timer_num, uint8 compare_num, voidFuncPtr handler) { + Timer *timer; + ASSERT(timer_num > 0 && timer_num <= 4 && compare_num > 0 && compare_num <= 4); + + switch(timer_num) { + case 1: + timer = (Timer*)TIMER1_BASE; + timer1_handlers[compare_num-1] = handler; + nvic_enable_interrupt(27); + timer->DIER |= (1 << compare_num); // 1-indexed compare nums + break; + case 2: + timer = (Timer*)TIMER2_BASE; + timer2_handlers[compare_num-1] = handler; + nvic_enable_interrupt(28); + timer->DIER |= (1 << compare_num); // 1-indexed compare nums + break; + case 3: + timer = (Timer*)TIMER3_BASE; + timer3_handlers[compare_num-1] = handler; + nvic_enable_interrupt(29); + timer->DIER |= (1 << compare_num); // 1-indexed compare nums + break; + case 4: + timer = (Timer*)TIMER4_BASE; + timer4_handlers[compare_num-1] = handler; + nvic_enable_interrupt(30); + timer->DIER |= (1 << compare_num); // 1-indexed compare nums + break; + } +} + +void timer_detach_interrupt(uint8 timer_num, uint8 compare_num) { + Timer *timer; + ASSERT(timer_num > 0 && timer_num <= 4 && compare_num > 0 && compare_num <= 4); + + switch(timer_num) { + case 1: + timer = (Timer*)TIMER1_BASE; + timer1_handlers[compare_num-1] = 0; + timer->DIER &= ~(1 << compare_num); // 1-indexed compare nums + break; + case 2: + timer = (Timer*)TIMER2_BASE; + timer2_handlers[compare_num-1] = 0; + timer->DIER &= ~(1 << compare_num); // 1-indexed compare nums + break; + case 3: + timer = (Timer*)TIMER3_BASE; + timer3_handlers[compare_num-1] = 0; + timer->DIER &= ~(1 << compare_num); // 1-indexed compare nums + break; + case 4: + timer = (Timer*)TIMER4_BASE; + timer4_handlers[compare_num-1] = 0; + timer->DIER &= ~(1 << compare_num); // 1-indexed compare nums + break; + } +} + +void TIM1_CC_IRQHandler(void) { + // This is a rather long implementation... + Timer *timer = (Timer*)TIMER1_BASE; + uint16 sr_buffer; + sr_buffer = timer->SR; + + if(sr_buffer & 0x10){ // CC4 flag + timer->SR &= ~(0x10); + if(timer1_handlers[3]) { + timer1_handlers[3](); + } + } + if(sr_buffer & 0x8){ // CC3 flag + timer->SR &= ~(0x8); + if(timer1_handlers[2]) { + timer1_handlers[2](); + } + } + if(sr_buffer & 0x4){ // CC2 flag + timer->SR &= ~(0x4); + if(timer1_handlers[1]) { + timer1_handlers[1](); + } + } + if(sr_buffer & 0x2){ // CC1 flag + timer->SR &= ~(0x2); + if(timer1_handlers[0]) { + timer1_handlers[0](); + } + } + if(sr_buffer & 0x1){ // Update flag + timer->SR &= ~(0x1); + //timer->EGR = 1; + } +} +void TIM2_IRQHandler(void) { + // This is a rather long implementation... + Timer *timer = (Timer*)TIMER2_BASE; + uint16 sr_buffer; + sr_buffer = timer->SR; + + if(sr_buffer & 0x10){ // CC4 flag + timer->SR &= ~(0x10); + if(timer2_handlers[3]) { + timer2_handlers[3](); + } + } + if(sr_buffer & 0x8){ // CC3 flag + timer->SR &= ~(0x8); + if(timer2_handlers[2]) { + timer2_handlers[2](); + } + } + if(sr_buffer & 0x4){ // CC2 flag + timer->SR &= ~(0x4); + if(timer2_handlers[1]) { + timer2_handlers[1](); + } + } + if(sr_buffer & 0x2){ // CC1 flag + timer->SR &= ~(0x2); + if(timer2_handlers[0]) { + timer2_handlers[0](); + } + } + if(sr_buffer & 0x1){ // Update flag + timer->SR &= ~(0x1); + //timer->EGR = 1; } } +void TIM3_IRQHandler(void) { + // This is a rather long implementation... + Timer *timer = (Timer*)TIMER3_BASE; + uint16 sr_buffer; + sr_buffer = timer->SR; + + if(sr_buffer & 0x10){ // CC4 flag + timer->SR &= ~(0x10); + if(timer3_handlers[3]) { + timer3_handlers[3](); + } + } + if(sr_buffer & 0x8){ // CC3 flag + timer->SR &= ~(0x8); + if(timer3_handlers[2]) { + timer3_handlers[2](); + } + } + if(sr_buffer & 0x4){ // CC2 flag + timer->SR &= ~(0x4); + if(timer3_handlers[1]) { + timer3_handlers[1](); + } + } + if(sr_buffer & 0x2){ // CC1 flag + timer->SR &= ~(0x2); + if(timer3_handlers[0]) { + timer3_handlers[0](); + } + } + if(sr_buffer & 0x1){ // Update flag + timer->SR &= ~(0x1); + //timer->EGR = 1; + } +} + +void TIM4_IRQHandler(void) { + // This is a rather long implementation... + Timer *timer = (Timer*)TIMER4_BASE; + uint16 sr_buffer; + sr_buffer = timer->SR; + + if(sr_buffer & 0x10){ // CC4 flag + timer->SR &= ~(0x10); + if(timer4_handlers[3]) { + timer4_handlers[3](); + } + } + if(sr_buffer & 0x8){ // CC3 flag + timer->SR &= ~(0x8); + if(timer4_handlers[2]) { + timer4_handlers[2](); + } + } + if(sr_buffer & 0x4){ // CC2 flag + timer->SR &= ~(0x4); + if(timer4_handlers[1]) { + timer4_handlers[1](); + } + } + if(sr_buffer & 0x2){ // CC1 flag + timer->SR &= ~(0x2); + if(timer4_handlers[0]) { + timer4_handlers[0](); + } + } + if(sr_buffer & 0x1){ // Update flag + timer->SR &= ~(0x1); + //timer->EGR = 1; + } +} + diff --git a/libmaple/timers.h b/libmaple/timers.h index 1ee89a0..bcbd751 100644 --- a/libmaple/timers.h +++ b/libmaple/timers.h @@ -93,6 +93,9 @@ typedef volatile uint32* TimerCCR; #define ARPE BIT(7) // Auto-reload preload enable #define NOT_A_TIMER 0 +// just threw this in here cause I can, aw yeah +#define TIMER_CCR(NUM,CHAN) TIMER ## NUM ## _CH ## CHAN ## _CRR + #define TIMER1_CH1_CCR (TimerCCR)(TIMER1_BASE + 0x34) #define TIMER1_CH2_CCR (TimerCCR)(TIMER1_BASE + 0x38) #define TIMER1_CH3_CCR (TimerCCR)(TIMER1_BASE + 0x3C) @@ -114,16 +117,25 @@ typedef volatile uint32* TimerCCR; #define TIMER4_CH4_CCR (TimerCCR)(TIMER4_BASE + 0x40) +#define TIMER_DISABLED 0 +#define TIMER_PWM 1 +#define TIMER_OUTPUTCOMPARE 2 + /* Turn on timer with prescale as the divisor * void timer_init(uint32 timer, uint16 prescale) * timer -> {1-4} * prescale -> {1-65535} * */ void timer_init(uint8, uint16); -void timers_disable(void); -void timers_disable_channel(uint8, uint8); -void timers_set_prescaler(uint32 timer_num, uint16 prescale); -void timers_set_reload(uint32 timer_num, uint16 max_reload); +void timer_disable_all(void); +uint16 timer_get_count(uint8); +void timer_set_count(uint8,uint16); +void timer_set_prescaler(uint8 timer_num, uint16 prescale); +void timer_set_reload(uint8 timer_num, uint16 max_reload); +void timer_set_mode(uint8 timer_num, uint8 compare_num, uint8 mode); +void timer_set_compare_value(uint8 timer_num, uint8 compare_num, uint16 value); +void timer_attach_interrupt(uint8 timer_num, uint8 compare_num, voidFuncPtr handler); +void timer_detach_interrupt(uint8 timer_num, uint8 compare_num); /* Turn on PWM with duty_cycle on the specified channel in timer. * This function takes in a pointer to the corresponding CCR @@ -142,7 +154,6 @@ static inline void timer_pwm_write_ccr(TimerCCR CCR, uint16 duty_cycle) { *CCR = duty_cycle; } - #ifdef __cplusplus } // extern "C" #endif diff --git a/libmaple/util.c b/libmaple/util.c index a7eb831..36173ee 100644 --- a/libmaple/util.c +++ b/libmaple/util.c @@ -54,7 +54,7 @@ void _fail(const char* file, int line, const char* exp) { nvic_disable_interrupts(); /* Turn off timers */ - timers_disable(); + timer_disable_all(); /* Turn off ADC */ adc_disable(); diff --git a/notes/timers.txt b/notes/timers.txt new file mode 100644 index 0000000..3e6bb9c --- /dev/null +++ b/notes/timers.txt @@ -0,0 +1,77 @@ + +Each timer (1-4) has 4 capture/compare channels (1-4). These are directly used +by PWM but have a ton of other possible functionality. The STM32 implementation +is particularly complicated with, eg, the ability to chain together timers + +Timer1 is an "advanced timer" with many more features. I think if we use just +the "Capture and compare interrupt", and enable MOE during initialization +everything will be ok. There are seperate Break, Update, and Trigger interrupts +as well that we will ignore for now. + +Timer2,Ch 3+4 are D0 and D1, which conflict with Serial2. USART should work +fine as long as pins aren't in output mode? and timers should work fine if +Serial2 isn't in use? + +Misc Notes +------------------------------------------------------------------------------ +implementation with case/switch in the interrupt handlers doesn't work; a lot +of the time multiple interrupt flags are active at the same time (or become +active?) + +TODO +------------------------------------------------------------------------------ +- function to read out CCR registers +- allow comparison output to the pin (a la PWM) +- additional modes and configuration (up, down, up/down, etc) +- Wirish (C++) higher level implementation +- implement the update interrupt as a "5th channel" +- track down and handle all pin conflicts +- document + +Possible Wirish implementation +------------------------------------------------------------------------------ +This recent implementation seems pretty clean: +http://arduino.cc/pipermail/developers_arduino.cc/2010-June/002845.html + + class Timer1Class { + public: + static void (*isrCompareA)(void); + static void (*isrCompareB)(void); + + static const uint8_t PRESCALE1 = 1; + static const uint8_t PRESCALE8 = 2; + static const uint8_t PRESCALE64 = 3; + static const uint8_t PRESCALE256 = 4; + static const uint8_t PRESCALE1024 = 5; + + const static uint8_t NORMAL = 0; + const static uint8_t CTC = 4; + + void setPrescaleFactor(uint8_t factor); + void setMode(uint8_t mode); + uint16_t read(); + void write(uint16_t val); + void writeCompareA(uint16_t val); + void writeCompareB(uint16_t val); + void attachCompareAInterrupt(void (*f)(void)); + void attachCompareBInterrupt(void (*f)(void)); + void detachCompareAInterrupt(); + void detachCompareBInterrupt(); + }; + + extern Timer1Class Timer1; + +Here's one of the more standard libaries out there: +http://www.arduino.cc/playground/Code/Timer1 + + void initialize(long microseconds=1000000); + void start(); + void stop(); + void restart(); + void setPeriod(long microseconds); + void pwm(char pin, int duty, long microseconds=-1); + void setPwmDuty(char pin, int duty); + void disablePwm(char pin); + void attachInterrupt(void (*isr)(), long microseconds=-1); + void detachInterrupt(); + diff --git a/notes/vga.txt b/notes/vga.txt new file mode 100644 index 0000000..2230f57 --- /dev/null +++ b/notes/vga.txt @@ -0,0 +1,32 @@ + +classic digitalWrite() gives ~500ns pulse time (2MHz) + +gpio_write_bit() is about 360ns (2.78MHz) + +writing to GPIO?_BASE is about 60ns (16.6MHz -> 18MHz) + +pwm write 0x0001 is about 30ns (33MHz) +pwm write 0x0001 is about 14ns (72MHz) with prescaler as 0 (!) + +1/25.125MHz = 39.72ns + +crude 640x480 directions: + www.epanorama.net/documents/pc/vga_timing.html + 480 lines + 31.77 us horizontal line length -> 2287.44 clock cycles -> 2287 + 3.77 us sync period -> 271 clocks -> 271 + 1.89 us front porch? -> 136 clocks -> 136 + 25.17 us video -> 1812.24 clocks -> 1812 + + so... + 2287 reload + 271 1: Hsync high + 407 2: Video on + 2219 3: Video off + 2287 4: Hsync low + + vertically, it's + 480 lines active video + 11 lines front porch + 2 lines Vsync (low) + 31 lines back porch diff --git a/wirish/comm/HardwareSPI.cpp b/wirish/comm/HardwareSPI.cpp index 3dfe10a..eadcdd7 100644 --- a/wirish/comm/HardwareSPI.cpp +++ b/wirish/comm/HardwareSPI.cpp @@ -95,8 +95,8 @@ void HardwareSPI::begin(SPIFrequency freq, uint32 endianness, uint32 mode) { } /* Turn off PWM on shared pins */ - timers_disable_channel(3, 2); - timers_disable_channel(3, 1); + timer_set_mode(3, 2, TIMER_DISABLED); + timer_set_mode(3, 1, TIMER_DISABLED); } endianness = (endianness == LSBFIRST) ? SPI_LSBFIRST : SPI_MSBFIRST; diff --git a/wirish/comm/HardwareSerial.cpp b/wirish/comm/HardwareSerial.cpp index fc0d01e..7157e74 100644 --- a/wirish/comm/HardwareSerial.cpp +++ b/wirish/comm/HardwareSerial.cpp @@ -78,13 +78,13 @@ void HardwareSerial::begin(uint32 baud) { gpio_set_mode(USART1_TX_PORT, USART1_TX_PIN, GPIO_MODE_AF_OUTPUT_PP); gpio_set_mode(USART1_RX_PORT, USART1_RX_PIN, GPIO_MODE_INPUT_FLOATING); /* Turn off any pwm */ - timers_disable_channel(1, 2); + timer_set_mode(1, 2, TIMER_DISABLED); break; case 2: gpio_set_mode(USART2_TX_PORT, USART2_TX_PIN, GPIO_MODE_AF_OUTPUT_PP); gpio_set_mode(USART2_RX_PORT, USART2_RX_PIN, GPIO_MODE_INPUT_FLOATING); /* Turn off any pwm */ - timers_disable_channel(2, 3); + timer_set_mode(2, 3, TIMER_DISABLED); break; case 3: gpio_set_mode(USART3_TX_PORT, USART3_TX_PIN, GPIO_MODE_AF_OUTPUT_PP); diff --git a/wirish/wirish.c b/wirish/wirish.c index e21f792..520079c 100644 --- a/wirish/wirish.c +++ b/wirish/wirish.c @@ -31,17 +31,17 @@ #include "systick.h" #include "gpio.h" #include "nvic.h" -#include "usb.h" +//#include "usb.h" void init(void) { rcc_init(); nvic_init(); - systick_init(); +// systick_init(); gpio_init(); adc_init(); timer_init(1, 1); timer_init(2, 1); timer_init(3, 1); timer_init(4, 1); - setupUSB(); + //setupUSB(); } diff --git a/wirish/wirish.h b/wirish/wirish.h index 2541e5e..34464a2 100644 --- a/wirish/wirish.h +++ b/wirish/wirish.h @@ -43,7 +43,7 @@ #ifdef __cplusplus #include "HardwareSPI.h" #include "HardwareSerial.h" -#include "usb_serial.h" +//#include "usb_serial.h" #endif #ifdef __cplusplus |