Skip to content

Commit

Permalink
Swap TIM3 and TIM4 to free up the SPI1 DMA channel for future use.
Browse files Browse the repository at this point in the history
For code clarity, move all TIMx/SPIx enables to peripheral_init().

[Manually merged by [email protected]]
  • Loading branch information
penfold42 authored and keirf committed Dec 2, 2019
1 parent f1e9b51 commit 27bb535
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 35 deletions.
3 changes: 3 additions & 0 deletions inc/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 22 additions & 1 deletion src/amiga.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];

Expand Down Expand Up @@ -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;
Expand Down
34 changes: 14 additions & 20 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -633,27 +627,27 @@ 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. */
tim2->ccmr1 = TIM_CCMR1_CC1S(TIM_CCS_OUTPUT);
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);
Expand Down
13 changes: 10 additions & 3 deletions src/stm32f10x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
20 changes: 9 additions & 11 deletions src/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/

/* 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)
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 27bb535

Please sign in to comment.