From 0c5b35fa3eecbe8c0035fdc5ca608a5c510df165 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 21 Apr 2018 16:21:48 +0200 Subject: [PATCH] Fix new packet-based comms protocol --- firmware/Makefile | 7 +- firmware/global.h | 22 +++ firmware/mac.c | 3 - firmware/mac.h | 22 --- firmware/main.c | 344 +++++++++++++++++++++++----------------------- firmware/serial.c | 34 +++-- firmware/serial.h | 11 +- 7 files changed, 223 insertions(+), 220 deletions(-) delete mode 100644 firmware/mac.c delete mode 100644 firmware/mac.h diff --git a/firmware/Makefile b/firmware/Makefile index 1e668e1..be1709b 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -10,9 +10,10 @@ OBJCOPY := arm-none-eabi-objcopy OBJDUMP := arm-none-eabi-objdump SIZE := arm-none-eabi-size -CFLAGS = -Wall -Wpedantic -Wstrict-aliasing -g -std=gnu11 -Os +#CFLAGS = -Wall -Wpedantic -Wstrict-aliasing -g -std=gnu11 -Os +CFLAGS = -Wall -Wpedantic -Wstrict-aliasing -g -std=gnu11 -O1 CFLAGS += -mlittle-endian -mcpu=cortex-m0 -march=armv6-m -mthumb -#CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections +CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections CFLAGS += -Wl,-Map=main.map # Technically we're using an STM32F030F4, but apart from the TSSOP20 package that one is largely identical to the @@ -43,7 +44,7 @@ sources.tar.xz.zip: sources.tar.xz sources.c: sources.tar.xz.zip xxd -i $< | head -n -1 | sed 's/=/__attribute__((section(".source_tarball"))) =/' > $@ -main.elf: main.c mac.c adc.c serial.c startup_stm32f030x6.s system_stm32f0xx.c $(HAL_PATH)/Src/stm32f0xx_ll_utils.c cmsis_exports.c +main.elf: main.c adc.c serial.c startup_stm32f030x6.s system_stm32f0xx.c $(HAL_PATH)/Src/stm32f0xx_ll_utils.c cmsis_exports.c sources.c $(CC) $(CFLAGS) -o $@ $^ $(OBJCOPY) -O ihex $@ $(@:.elf=.hex) $(OBJCOPY) -O binary $@ $(@:.elf=.bin) diff --git a/firmware/global.h b/firmware/global.h index 6116adc..3e585f6 100644 --- a/firmware/global.h +++ b/firmware/global.h @@ -1,9 +1,31 @@ #ifndef __GLOBAL_H__ #define __GLOBAL_H__ +#define COLOR_SPEC_WHITE 0x00 +#define COLOR_SPEC_SINGLE_COLOR 0x01 +#define COLOR_SPEC_RGB 0x02 +#define COLOR_SPEC_RGBW 0x03 +#define COLOR_SPEC_COLD_WARM_WHITE 0x04 +#define COLOR_SPEC_WWA 0x05 /* cold white/warm white/amber */ + +#define OLSNDOT_V1 0x01 + #define FIRMWARE_VERSION 2 #define HARDWARE_VERSION 2 +/* Maximum bit count supported by serial command protocol. The brightness data is assumed to be of this bit width, but + * only the uppermost NBITS bits are used. */ +#define MAX_BITS 16 + +/* Bit count of this device. Note that to change this you will also have to adapt the per-bit timer period lookup table + * in main.c. */ +#define NBITS 14 + +#define NCHANNELS 32 +#define CHANNEL_SPEC 'H' +#define COLOR_SPEC COLOR_SPEC_RGBW +#define DEVICE_TYPE OLSNDOT_V1 + #define TS_CAL1 (*(uint16_t *)0x1FFFF7B8) #define VREFINT_CAL (*(uint16_t *)0x1FFFF7BA) diff --git a/firmware/mac.c b/firmware/mac.c deleted file mode 100644 index b2fb48a..0000000 --- a/firmware/mac.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "mac.h" - -uint32_t device_mac = MAC_ADDR; diff --git a/firmware/mac.h b/firmware/mac.h deleted file mode 100644 index 26aaff6..0000000 --- a/firmware/mac.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __MAC_H__ -#define __MAC_H__ - -#include - -/* Device MAC address. - * - * 32 bits might seem a little short for a device MAC, but at 20 bus nodes the probablility of a collision is about 1 in - * 10 million. Check for yourself using the python code below. - * - * #!/usr/bin/env python3 - * from operator import mul - * from functools import reduce - * m = 32 - * n = 20 - * print(reduce(mul, [2**m-i for i in range(n)]) / ((2**m)**n)) - * # -> 0.9999999557621786 - */ - -extern uint32_t device_mac; - -#endif /* __MAC_H__ */ diff --git a/firmware/main.c b/firmware/main.c index 7fc78c3..ec1e576 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -38,15 +38,6 @@ #include "serial.h" #include "adc.h" -/* Bit count of this device. Note that to change this you will also have to adapt the per-bit timer period lookup table - * below. - */ -#define NBITS 14 - -/* Maximum bit count supported by serial command protocol. The brightness data is assumed to be of this bit width, but - * only the uppermost NBITS bits are used. */ -#define MAX_BITS 16 - void do_transpose(void); /* Bit-golfed modulation data generated from the above values by the main loop, ready to be sent out to the shift @@ -58,169 +49,11 @@ volatile uint32_t brightness_by_bit[NBITS] = { 0 }; uint32_t sys_time = 0; uint32_t sys_time_seconds = 0; -int main(void) { - /* Get all the good clocks and PLLs on this thing up and running. We're running from an external 25MHz crystal, - * which we're first dividing down by 5 to get 5 MHz, then PLL'ing up by 6 to get 30 MHz as our main system clock. - * - * The busses are all run directly from these 30 MHz because why not. - * - * Be careful in mucking around with this code since you can kind of semi-brick the chip if you do it wrong. - */ - RCC->CR |= RCC_CR_HSEON; - while (!(RCC->CR&RCC_CR_HSERDY)); - - // HSE ready, let's configure the PLL - RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk & ~RCC_CFGR_SW_Msk & ~RCC_CFGR_PPRE_Msk & ~RCC_CFGR_HPRE_Msk; - - // PLLMUL: 6x (0b0100) - RCC->CFGR |= (0b0100<CFGR2 &= ~RCC_CFGR2_PREDIV_Msk; - RCC->CFGR2 |= RCC_CFGR2_PREDIV_DIV5; /* prediv :10 -> 5 MHz */ - - RCC->CR |= RCC_CR_PLLON; - while (!(RCC->CR&RCC_CR_PLLRDY)); - - RCC->CFGR |= (2<AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; - RCC->APB2ENR |= RCC_APB2ENR_SPI1EN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_ADCEN; - RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; - - /* Configure all the GPIOs */ - GPIOA->MODER |= - (3<MODER |= - (2<OTYPER |= GPIO_OTYPER_OT_6; /* LED outputs -> open drain */ - - /* Set shift register IO GPIO output speed */ - GPIOA->OSPEEDR |= - (3<OSPEEDR |= - (3<AFR[0] |= - (1<AFR[1] |= - (2<AFR[0] |= - (2< 16MBd */ - SPI1->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE | (0<CR2 = (0xf<CR1 = TIM_CR1_ARPE; - - TIM1->PSC = 1; /* Prescale by 2, resulting in a 16MHz timer frequency and 62.5ns timer step size. */ - /* CH2 - clear/!MR, CH3 - strobe/STCP */ - TIM1->CCMR2 = (6<CCER |= TIM_CCER_CC3E | TIM_CCER_CC3NE | TIM_CCER_CC3P | TIM_CCER_CC3NP; - TIM1->BDTR = TIM_BDTR_MOE | (1<DIER = TIM_DIER_UIE; /* Enable update (overrun) interrupt */ - TIM1->ARR = 1; - TIM1->CR1 |= TIM_CR1_CEN; - - /* Configure Timer 1 update (overrun) interrupt on NVIC. Used only for update (overrun) for strobe timing. */ - NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); - NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 1); - - /* Pre-load initial values, kick of first interrupt */ - TIM1->EGR |= TIM_EGR_UG; - - /* Configure TIM3 for USART timeout handing */ - TIM3->CR1 = TIM_CR1_OPM; - TIM3->DIER = TIM_DIER_UIE; - TIM3->PSC = 30; - TIM3->ARR = 1000; - - /* Configure Timer 3 update (overrun) interrupt on NVIC. Used only for update (overrun) for USART timeout handling. */ - NVIC_EnableIRQ(TIM3_IRQn); - NVIC_SetPriority(TIM3_IRQn, 2); - - /* Pre-load initial values */ - TIM3->EGR |= TIM_EGR_UG; - - /* Configure UART for RS485 comm */ - /* 8N1, 1MBd */ - USART1->CR1 = /* 8-bit -> M1, M0 clear */ - /* RTOIE clear */ - (8 << USART_CR1_DEAT_Pos) /* 8 sample cycles/1 bit DE assertion time */ - | (8 << USART_CR1_DEDT_Pos) /* 8 sample cycles/1 bit DE assertion time */ - /* CMIF clear */ - /* WAKE clear */ - /* PCE, PS clear */ - | USART_CR1_RXNEIE - /* other interrupts clear */ - | USART_CR1_TE - | USART_CR1_RE; - USART1->CR3 = USART_CR3_DEM; /* RS485 DE enable (output on RTS) */ - // USART1->BRR = 30; - USART1->BRR = 40; // 750000 - USART1->CR1 |= USART_CR1_UE; - - /* Configure USART1 interrupt on NVIC. Used only for RX. */ - NVIC_EnableIRQ(USART1_IRQn); - NVIC_SetPriority(USART1_IRQn, 2); - - adc_init(); - - /* Idly loop around, occassionally disfiguring some integers. */ - while (42) { - /* Debug output on LED. */ - GPIOA->ODR ^= GPIO_ODR_6; - - if (framebuf_out_of_sync != 0) { - /* This logic is very slightly racy, but that should not matter since we're updating the frame buffer often - * enough so you don't notice one miss every billion frames. */ - framebuf_out_of_sync = 0; - /* Bit-mangle the integer framebuf data to produce raw modulation data */ - do_transpose(); - } - } -} - -/* Modulation data bit golfing routine */ -void do_transpose(void) { - /* For each bit value */ - for (uint32_t i=0; iCR |= RCC_CR_HSEON; + while (!(RCC->CR&RCC_CR_HSERDY)); + + // HSE ready, let's configure the PLL + RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk & ~RCC_CFGR_SW_Msk & ~RCC_CFGR_PPRE_Msk & ~RCC_CFGR_HPRE_Msk; + + // PLLMUL: 6x (0b0100) + RCC->CFGR |= (0b0100<CFGR2 &= ~RCC_CFGR2_PREDIV_Msk; + RCC->CFGR2 |= RCC_CFGR2_PREDIV_DIV5; /* prediv :10 -> 5 MHz */ + + RCC->CR |= RCC_CR_PLLON; + while (!(RCC->CR&RCC_CR_PLLRDY)); + + RCC->CFGR |= (2<AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_DMAEN; + RCC->APB2ENR |= RCC_APB2ENR_SPI1EN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_ADCEN; + RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; + + /* Configure all the GPIOs */ + GPIOA->MODER |= + (3<MODER |= + (2<OTYPER |= GPIO_OTYPER_OT_6; /* LED outputs -> open drain */ + + /* Set shift register IO GPIO output speed */ + GPIOA->OSPEEDR |= + (3<OSPEEDR |= + (3<AFR[0] |= + (1<AFR[1] |= + (2<AFR[0] |= + (2< 16MBd */ + SPI1->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE | (0<CR2 = (0xf<CR1 = TIM_CR1_ARPE; + + TIM1->PSC = 1; /* Prescale by 2, resulting in a 16MHz timer frequency and 62.5ns timer step size. */ + /* CH2 - clear/!MR, CH3 - strobe/STCP */ + TIM1->CCMR2 = (6<CCER |= TIM_CCER_CC3E | TIM_CCER_CC3NE | TIM_CCER_CC3P | TIM_CCER_CC3NP | TIM_CCER_CC4E; + TIM1->BDTR = TIM_BDTR_MOE | (1<DIER = TIM_DIER_UIE; /* Enable update (overrun) interrupt */ + TIM1->ARR = 1; + TIM1->CR1 |= TIM_CR1_CEN; + /* Trigger at the end of the longest bit cycle. This means this does not trigger in shorter bit cycles. */ + TIM1->CCR4 = timer_period_lookup[NBITS-1] - ADC_PRETRIGGER; + + /* Configure Timer 1 update (overrun) interrupt on NVIC. Used only for update (overrun) for strobe timing. */ + NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); + NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 1); + + /* Pre-load initial values, kick of first interrupt */ + TIM1->EGR |= TIM_EGR_UG; + + /* Configure TIM3 for USART timeout handing */ + TIM3->CR1 = TIM_CR1_OPM; + TIM3->DIER = TIM_DIER_UIE; + TIM3->PSC = 30; + TIM3->ARR = 1000; + + /* Configure Timer 3 update (overrun) interrupt on NVIC. Used only for update (overrun) for USART timeout handling. */ + NVIC_EnableIRQ(TIM3_IRQn); + NVIC_SetPriority(TIM3_IRQn, 2); + + /* Pre-load initial values */ + TIM3->EGR |= TIM_EGR_UG; + + /* Configure UART for RS485 comm */ + /* 8N1, 1MBd */ + USART1->CR1 = /* 8-bit -> M1, M0 clear */ + /* RTOIE clear */ + (8 << USART_CR1_DEAT_Pos) /* 8 sample cycles/1 bit DE assertion time */ + | (8 << USART_CR1_DEDT_Pos) /* 8 sample cycles/1 bit DE assertion time */ + /* CMIF clear */ + /* WAKE clear */ + /* PCE, PS clear */ + | USART_CR1_RXNEIE + /* other interrupts clear */ + | USART_CR1_TE + | USART_CR1_RE; + USART1->CR3 = USART_CR3_DEM; /* RS485 DE enable (output on RTS) */ + // USART1->BRR = 30; + //USART1->BRR = 40; // 750000 + USART1->BRR = 60; // 500000 + USART1->CR1 |= USART_CR1_UE; + + /* Configure USART1 interrupt on NVIC. Used only for RX. */ + NVIC_EnableIRQ(USART1_IRQn); + NVIC_SetPriority(USART1_IRQn, 2); + + adc_init(); + + /* Idly loop around, occassionally disfiguring some integers. */ + while (42) { + /* Debug output on LED. */ + GPIOA->ODR ^= GPIO_ODR_6; + + if (framebuf_out_of_sync != 0) { + /* This logic is very slightly racy, but that should not matter since we're updating the frame buffer often + * enough so you don't notice one miss every billion frames. */ + framebuf_out_of_sync = 0; + /* Bit-mangle the integer framebuf data to produce raw modulation data */ + do_transpose(); + } + } +} + +/* Modulation data bit golfing routine */ +void do_transpose(void) { + /* For each bit value */ + for (uint32_t i=0; iRDR; /* This automatically acknowledges the IRQ */ if (data == 0x00) { /* End-of-packet */ - /* Process higher protocol layers on this packet. */ - writep = packet_received(rxpos); + if (cobs_state != COBS_WAIT_SYNC) /* Has a packet been received? */ + /* Process higher protocol layers on this packet. */ + packet_received(rxpos); /* Reset for next packet. */ cobs_state = COBS_WAIT_START; @@ -206,7 +204,7 @@ void USART1_IRQHandler(void) { } /* Write processed payload byte to current receive buffer */ - writep[rxpos++] = data; + rx_buf.byte_data[rxpos++] = data; } } } diff --git a/firmware/serial.h b/firmware/serial.h index 5066c43..94cfd57 100644 --- a/firmware/serial.h +++ b/firmware/serial.h @@ -27,7 +27,6 @@ void send_status_reply(void); /* Internal low-level stuff */ void tx_char(uint8_t c); void send_frame_formatted(uint8_t *buf, int len); -volatile uint8_t *packet_received(int len); /* Error counters for debugging */ extern unsigned int uart_overruns; @@ -38,6 +37,10 @@ union tx_buf_union { struct __attribute__((packed)) { uint8_t firmware_version, hardware_version; + uint8_t nbits; + uint8_t channel_spec; + uint8_t color_spec; + uint16_t nchannels; uint32_t uptime_s, uart_overruns, frame_overruns, @@ -45,6 +48,10 @@ union tx_buf_union { int16_t vcc_mv, temp_celsius; } desc_reply; + struct __attribute__((packed)) { + uint32_t mac; + uint16_t device_type; + } device_type_reply; uint8_t byte_data[0]; }; @@ -57,7 +64,7 @@ union rx_buf_union { * |<----------------NBITS---------------->| |<>|--ignored * | (MSB) brightness data (LSB) | |<>|--ignored */ - struct __attribute__((packed)) { uint32_t framebuf[32]; uint8_t end[0]; } set_fb_rq; + struct __attribute__((packed)) { uint16_t framebuf[32]; uint8_t end[0]; } set_fb_rq; uint8_t byte_data[0]; uint32_t mac_data; };