From 27bb535f4b3b912a83b3d2e63ad584d262d4fdc4 Mon Sep 17 00:00:00 2001 From: penfold42 Date: Mon, 2 Dec 2019 16:50:38 +1100 Subject: [PATCH] Swap TIM3 and TIM4 to free up the SPI1 DMA channel for future use. For code clarity, move all TIMx/SPIx enables to peripheral_init(). [Manually merged by keir.xen@gmail.com] --- inc/timer.h | 3 +++ src/amiga.c | 23 ++++++++++++++++++++++- src/main.c | 34 ++++++++++++++-------------------- src/stm32f10x.c | 13 ++++++++++--- src/timer.c | 20 +++++++++----------- 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/inc/timer.h b/inc/timer.h index c8f3013..658b34c 100644 --- a/inc/timer.h +++ b/inc/timer.h @@ -23,6 +23,9 @@ void timer_cancel(struct timer *timer); void timers_init(void); +/* Trigger this IRQ on TIM3->SR[UIF]. */ +#define IRQ_TIMER 18 + /* * Local variables: * mode: C diff --git a/src/amiga.c b/src/amiga.c index 8a023e2..bfbae43 100644 --- a/src/amiga.c +++ b/src/amiga.c @@ -18,8 +18,10 @@ #define pin_amikbd_dat 3 #define pin_amikbd_clk 4 +/* As the highest-priority target for TIM3 interrupts, we are responsible + * for demuxing to any lower-priority targets (specifically, timer.c). */ #define irq_tim3 29 -void IRQ_29(void) __attribute__((alias("IRQ_amikbd_clk"))); +void IRQ_29(void) __attribute__((alias("IRQ_TIM3_demux"))); static uint8_t keymap[0x68]; @@ -114,6 +116,25 @@ static void IRQ_amikbd_clk(void) handshake(); } +/* TIM3 is shared with timer.c: We demuux to the correct handler based on + * flags in TIM3->SR. */ +static void IRQ_TIM3_demux(void) +{ + uint16_t sr = tim3->sr; + + /* Clear the irq line. */ + tim3->sr = ~sr; + + if (sr & TIM_SR_CC1IF) { + IRQ_amikbd_clk(); + } + + if (sr & TIM_SR_UIF) { + /* Switch to timer-handling priority to handle timer.c work. */ + IRQx_set_pending(IRQ_TIMER); + } +} + bool_t amiga_key_pressed(uint8_t keycode) { uint8_t state; diff --git a/src/main.c b/src/main.c index 698e110..506e0ba 100644 --- a/src/main.c +++ b/src/main.c @@ -77,12 +77,12 @@ void IRQ_28(void) __attribute__((alias("IRQ_osd_pre_start"))); #define tim2_up_dma_ch 2 #define tim2_up_dma_tc_irq 12 -/* TIM3: Overflow triggers DMA to enable Display Output. +/* TIM4: Overflow triggers DMA to enable Display Output. * Counter starts on TIM1 UEV (itself triggered by TIM1 Ch.1 input pin). */ -#define tim3_irq 29 -#define tim3_up_dma (dma1->ch3) -#define tim3_up_dma_ch 3 -#define tim3_up_dma_tc_irq 13 +#define tim4_irq 30 +#define tim4_up_dma (dma1->ch7) +#define tim4_up_dma_ch 7 +#define tim4_up_dma_tc_irq 17 /* Display Output (B15): Pixels are generated by SPI. */ #define gpio_display gpiob @@ -200,9 +200,9 @@ static void slave_arr_update(void) { unsigned int hstart = config.h_off * 20; - /* Enable output pin first (TIM3) and then start SPI transfers (TIM2). */ + /* Enable output pin first (TIM4) and then start SPI transfers (TIM2). */ + tim4->arr = hstart-49; tim2->arr = hstart-1; - tim3->arr = hstart-49; /* Trigger TIM2 IRQ 1us before OSD box. */ tim2->ccr1 = hstart - sysclk_us(1); @@ -585,12 +585,6 @@ int main(void) gpio_configure_pin(gpioa, 4, GPO_opendrain(_2MHz, LOW)); gpio_configure_pin(gpioa, 5, GPO_opendrain(_2MHz, LOW)); - /* Turn on the clocks. */ - rcc->apb1enr |= (RCC_APB1ENR_SPI2EN - | RCC_APB1ENR_TIM2EN - | RCC_APB1ENR_TIM3EN); - rcc->apb2enr |= RCC_APB2ENR_TIM1EN; - config_init(); /* Set user pin output modes and initial logic levels. */ @@ -633,19 +627,19 @@ int main(void) DMA_CCR_EN); setup_slave_timer(tim2); - /* Timer 3 is triggered by Timer 1. On overflow it triggers DMA + /* Timer 4 is triggered by Timer 1. On overflow it triggers DMA * to enable the OSD box. */ setup_dispctl_mode(); - tim3_up_dma.cpar = dispctl_reg; - tim3_up_dma.cmar = (uint32_t)(unsigned long)&dispctl_on; - tim3_up_dma.cndtr = 1; - tim3_up_dma.ccr = (DMA_CCR_PL_V_HIGH | + tim4_up_dma.cpar = dispctl_reg; + tim4_up_dma.cmar = (uint32_t)(unsigned long)&dispctl_on; + tim4_up_dma.cndtr = 1; + tim4_up_dma.ccr = (DMA_CCR_PL_V_HIGH | DMA_CCR_MSIZE_32BIT | DMA_CCR_PSIZE_32BIT | DMA_CCR_CIRC | DMA_CCR_DIR_M2P | DMA_CCR_EN); - setup_slave_timer(tim3); + setup_slave_timer(tim4); /* Timer 2 interrupts us horizontally just before the OSD box, so that * we can pause I2C IRQ transfers. */ @@ -653,7 +647,7 @@ int main(void) tim2->ccer = TIM_CCER_CC1E; tim2->dier |= TIM_DIER_CC1IE; - /* CSYNC is on Timer 1 Channel 1. Use it to trigger Timer 2 and 3. */ + /* CSYNC is on Timer 1 Channel 1. Use it to trigger Timer 2 and 4. */ tim1->psc = 0; tim1->arr = 0xffff; tim1->ccmr1 = TIM_CCMR1_CC1S(TIM_CCS_INPUT_TI1); diff --git a/src/stm32f10x.c b/src/stm32f10x.c index 71f567e..e83cd75 100644 --- a/src/stm32f10x.c +++ b/src/stm32f10x.c @@ -120,12 +120,19 @@ static void gpio_init(GPIO gpio) static void peripheral_init(void) { - /* Enable basic GPIO and AFIO clocks, and DMA. */ - rcc->apb1enr = 0; + /* Enable basic GPIO and AFIO clocks, DMA, all timers, and all SPI. */ + rcc->apb1enr = (RCC_APB1ENR_TIM2EN | + RCC_APB1ENR_TIM3EN | + RCC_APB1ENR_TIM4EN | + RCC_APB1ENR_SPI2EN); + rcc->apb2enr = (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | - RCC_APB2ENR_AFIOEN); + RCC_APB2ENR_AFIOEN | + RCC_APB2ENR_TIM1EN | + RCC_APB2ENR_SPI1EN); + rcc->ahbenr = RCC_AHBENR_DMA1EN; /* Turn off serial-wire JTAG and reclaim the GPIOs. */ diff --git a/src/timer.c b/src/timer.c index b6e812d..13cd33d 100644 --- a/src/timer.c +++ b/src/timer.c @@ -9,10 +9,11 @@ * See the file COPYING for more details, or visit . */ -/* TIM4: IRQ 30. */ -void IRQ_30(void) __attribute__((alias("IRQ_timer"))); -#define TIMER_IRQ 30 -#define tim tim4 +/* This is a handy otherwise-unused IRQ vector at which the real TIM3 ISR + * can trigger us. */ +void IRQ_18(void) __attribute__((alias("IRQ_timers"))); + +#define tim tim3 /* IRQ only on counter overflow, one-time enable. */ #define TIM_CR1 (TIM_CR1_URS | TIM_CR1_OPM) @@ -107,20 +108,17 @@ void timer_cancel(struct timer *timer) void timers_init(void) { - rcc->apb1enr |= RCC_APB1ENR_TIM4EN; tim->cr2 = 0; - tim->dier = TIM_DIER_UIE; - IRQx_set_prio(TIMER_IRQ, TIMER_IRQ_PRI); - IRQx_enable(TIMER_IRQ); + tim->dier |= TIM_DIER_UIE; + IRQx_set_prio(IRQ_TIMER, TIMER_IRQ_PRI); + IRQx_enable(IRQ_TIMER); } -static void IRQ_timer(void) +static void IRQ_timers(void) { struct timer *t; int32_t delta; - tim->sr = 0; - while ((t = head) != NULL) { if ((delta = time_diff(time_now(), t->deadline)) > SLACK_TICKS) { reprogram_timer(delta);