From c567f9879e77f1d3197ed11177d3dc3c90d7bf47 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Thu, 13 Jul 2023 15:41:11 +0200 Subject: [PATCH] test-sinclair: Clean up and document --- test-sinclair/main.c | 97 ++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 67 deletions(-) diff --git a/test-sinclair/main.c b/test-sinclair/main.c index 8de64a7..aa2441f 100644 --- a/test-sinclair/main.c +++ b/test-sinclair/main.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -34,7 +33,6 @@ static void clock_init(void) rcc_periph_clock_enable(RCC_SPI2); rcc_periph_clock_enable(RCC_USART1); rcc_periph_clock_enable(RCC_USB); - rcc_periph_clock_enable(RCC_AFIO); rcc_periph_clock_enable(RCC_TIM3); rcc_periph_reset_pulse(RST_GPIOA); @@ -43,7 +41,6 @@ static void clock_init(void) rcc_periph_reset_pulse(RST_SPI2); rcc_periph_reset_pulse(RST_USART1); rcc_periph_reset_pulse(RST_USB); - rcc_periph_reset_pulse(RST_AFIO); rcc_periph_reset_pulse(RST_TIM3); } @@ -103,6 +100,35 @@ static void delay_ms(uint ms) /*** Emulated TM1618 LED Driver ***/ +/* + * Theory of operation: + * + * TM1618 communicates using a bi-directional SPI-like protocol. + * The AC unit is sending a stream of commands like this once per ca. 4 ms: + * + * 00 - set mode: 4 grids, 8 segments + * 44 - will write to display memory, no auto-increment + * Cx - set memory address to x + * yy - data to write, two most-significant bits are always zero + * 8B - display ON, duty cycle 10/16 + * + * No read commands are issued, so we can simulate TM1618 using a pure SPI slave. + * + * Commands are delimited using the STB* (strobe) pin, but since our opto-couplers + * are negating, we cannot route this pin to SS (slave select) of our SPI. + * We tried triggering an external interrupt by this pin, but it turned out + * that the latency is too high. + * + * Instead, we ignore STB* completely and implement a self-synchronizing receiver: + * + * - The only byte which can have top 2 bits both set is the Cx command, + * so we can use this to find memory addresses and data in the stream. + * We can ignore all other commands. + * + * - Whenever 1 ms passes since the last byte was received, we reset the SPI. + * This allows us to recover from misaligned bytes. + */ + static void tm_init(void) { // Configure SPI2 to receive @@ -116,18 +142,7 @@ static void tm_init(void) nvic_enable_irq(NVIC_SPI2_IRQ); spi_enable(SPI2); -#if 0 - // Since our optocouplers are negating, we cannot let STM32 to handle slave - // select in hardware. Instead, we let SS trigger an interrupt, which changes - // SPI state accordingly. - nvic_set_priority(NVIC_EXTI15_10_IRQ, 0); - nvic_enable_irq(NVIC_EXTI15_10_IRQ); - exti_set_trigger(EXTI12, EXTI_TRIGGER_BOTH); - exti_select_source(EXTI12, GPIOB); - exti_enable_request(EXTI12); -#endif - -#if 1 + // TIM3 will handle receive timeout timer_set_prescaler(TIM3, CPU_CLOCK_MHZ-1); // 1 tick = 1 μs timer_set_mode(TIM3, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_DOWN); timer_update_on_overflow(TIM3); @@ -135,24 +150,6 @@ static void tm_init(void) timer_one_shot_mode(TIM3); timer_enable_irq(TIM3, TIM_DIER_UIE); nvic_enable_irq(NVIC_TIM3_IRQ); - //timer_set_period(TIM3, 9999); - //timer_generate_event(TIM3, TIM_EGR_UG); - //timer_enable_counter(TIM3); -#endif -} - -void exti15_10_isr(void) -{ - // We require low latency here, so interaction with peripherals is open-coded. -#if 0 - if (GPIO_IDR(GPIOB) & (1 << 12)) - SPI_CR1(SPI2) |= SPI_CR1_SSI; - else - SPI_CR1(SPI2) &= ~SPI_CR1_SSI; -#else - SPI_CR1(SPI2) &= ~SPI_CR1_SSI; -#endif - EXTI_PR = EXTI12; } static volatile byte tm_data[8]; @@ -165,18 +162,6 @@ static volatile uint tm_timeouts; void spi2_isr(void) { - /* - * The AC unit is sending a stream of commands like this: - * - * 00 - set mode: 4 grids, 8 segments - * 44 - will write to display memory, no auto-increment - * Cx - set memory address to x - * yy - data to write, two most-significant bits are always zero - * 8B - display ON, duty cycle 10/16 - * - * So the only byte which can have top 2 bits both set is the Cx command. - * We make use of this to synchronize the stream. - */ if (SPI_SR(SPI2) & SPI_SR_OVR) tm_overrun = 1; if (SPI_SR(SPI2) & SPI_SR_RXNE) { @@ -208,28 +193,6 @@ void tim3_isr(void) } } -#if 0 -static void tm_test(void) -{ - u32 start_ticks = ms_ticks; - static byte tmbuf[256]; - uint len = 0; - - while (ms_ticks - start_ticks < 1000 && len < ARRAY_SIZE(tmbuf)) { - if (SPI_SR(SPI2) & SPI_SR_RXNE) { - tmbuf[len++] = SPI_DR(SPI2) ^ 0xff; - } - } - - for (uint i=0; i