From: Martin Mares Date: Thu, 13 Jul 2023 13:26:34 +0000 (+0200) Subject: test-sinclair: Testing... X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=098472c29f39ce1ffe5e76148fbe3dfb4358929a;p=home-hw.git test-sinclair: Testing... --- diff --git a/test-sinclair/README b/test-sinclair/README index a62505b..39eb505 100644 --- a/test-sinclair/README +++ b/test-sinclair/README @@ -1,19 +1,19 @@ Assignment of peripherals and pins ================================== -I2C1 display +SPI2 emulated TM1618 LED driver USART1 debugging Blue Pill pinout +--------------------+ | VBATT 3.3V | -BluePill LED | PC13 GND | display power - | PC14 5V | display power (white side of connector) +BluePill LED | PC13 GND | + | PC14 5V | | PC15 PB9 | - | PA0 PB8 | SFH5110 output (white side of connector) - | PA1 PB7 | SDA1 display (white side of connector) - | PA2 PB6 | SCL1 display + | PA0 PB8 | + | PA1 PB7 | + | PA2 PB6 | | PA3 PB5 | | PA4 PB4 | | PA5 PB3 | @@ -23,8 +23,8 @@ BluePill LED | PC13 GND | display power | PB1 PA10 | RXD1 - debugging console | PB10 PA9 | TXD1 - debugging console | PB11 PA8 | - | RESET PB15 | - | 3.3 V PB14 | - | GND PB13 | - | GND PB12 | + | RESET PB15 | MOSI2 - LED driver data input + | 3.3 V PB14 | MISO2 - unused + | GND PB13 | SCK2 - LED driver clock + | GND PB12 | SS2 - LED driver non-inverted select +--------------------+ diff --git a/test-sinclair/main.c b/test-sinclair/main.c index 7e8659b..8de64a7 100644 --- a/test-sinclair/main.c +++ b/test-sinclair/main.c @@ -1,7 +1,7 @@ /* - * Workshop Clock + * Testing Communication with Sinclair Air Conditioner * - * (c) 2020 Martin Mareš + * (c) 2023 Martin Mareš */ #include "util.h" @@ -14,7 +14,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -24,21 +26,25 @@ static void clock_init(void) { - rcc_clock_setup_in_hse_8mhz_out_72mhz(); + rcc_clock_setup_pll(&rcc_hse_configs[RCC_CLOCK_HSE8_72MHZ]); rcc_periph_clock_enable(RCC_GPIOA); rcc_periph_clock_enable(RCC_GPIOB); rcc_periph_clock_enable(RCC_GPIOC); - rcc_periph_clock_enable(RCC_I2C1); + 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); rcc_periph_reset_pulse(RST_GPIOB); rcc_periph_reset_pulse(RST_GPIOC); - rcc_periph_reset_pulse(RST_I2C1); + 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); } static void gpio_init(void) @@ -51,6 +57,13 @@ static void gpio_init(void) // PC13 = BluePill LED gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); gpio_clear(GPIOC, GPIO13); + + // PB12 = SS2 (but used as GP input) + // PB13 = SCK2 + // PB15 = MOSI2 + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO12); + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO13); + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO15); } static void usart_init(void) @@ -88,6 +101,156 @@ static void delay_ms(uint ms) ; } +/*** Emulated TM1618 LED Driver ***/ + +static void tm_init(void) +{ + // Configure SPI2 to receive + spi_set_receive_only_mode(SPI2); + spi_enable_software_slave_management(SPI2); + spi_set_nss_low(SPI2); + spi_send_lsb_first(SPI2); + spi_set_clock_polarity_0(SPI2); + spi_set_clock_phase_1(SPI2); + spi_enable_rx_buffer_not_empty_interrupt(SPI2); + 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 + 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); + timer_disable_preload(TIM3); + 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]; +static volatile byte tm_overrun; + +static volatile byte tm_buffer[256]; +static volatile uint tm_len; + +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) { + byte x = SPI_DR(SPI2) ^ 0xff; +#if 0 + if (tm_len < ARRAY_SIZE(tm_buffer)) + tm_buffer[tm_len++] = x; +#endif + static byte tm_address; + if (tm_address) { + tm_data[tm_address & 7] = x; + tm_address = 0; + } else if ((x & 0xc0) == 0xc0) { + tm_address = x; + } + timer_set_period(TIM3, 999); + timer_generate_event(TIM3, TIM_EGR_UG); + timer_enable_counter(TIM3); + } +} + +void tim3_isr(void) +{ + if (TIM_SR(TIM3) & TIM_SR_UIF) { + TIM_SR(TIM3) &= ~TIM_SR_UIF; + tm_timeouts++; + spi_set_nss_high(SPI2); + spi_set_nss_low(SPI2); + } +} + +#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= 1000) { debug_led_toggle(); last_blink = ms_ticks; + tm_show(); } if (usb_event_pending) {