]> mj.ucw.cz Git - home-hw.git/commitdiff
test-sinclair: Clean up and document
authorMartin Mares <mj@ucw.cz>
Thu, 13 Jul 2023 13:41:11 +0000 (15:41 +0200)
committerMartin Mares <mj@ucw.cz>
Thu, 13 Jul 2023 13:41:11 +0000 (15:41 +0200)
test-sinclair/main.c

index 8de64a79ffc54fa7d509e9050e4b84f0d12eef99..aa2441fa33e8f5bc0846f3fd8735ebe5538bac5c 100644 (file)
@@ -15,7 +15,6 @@
 #include <libopencm3/stm32/gpio.h>
 #include <libopencm3/stm32/usart.h>
 #include <libopencm3/stm32/spi.h>
-#include <libopencm3/stm32/exti.h>
 #include <libopencm3/stm32/timer.h>
 #include <libopencm3/usb/dfu.h>
 #include <libopencm3/usb/usbd.h>
@@ -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<len; i++) {
-               debug_printf("%02x ", tmbuf[i]);
-               if ((i % 5) == 4)
-                       debug_putc('\n');
-       }
-       debug_putc('\n');
-}
-#endif
-
 static void tm_show(void)
 {
        debug_printf("TM:");