Fix new packet-based comms protocol

This commit is contained in:
jaseg 2018-04-21 16:21:48 +02:00
parent ddbb807e54
commit 0c5b35fa3e
7 changed files with 223 additions and 220 deletions

View file

@ -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)

View file

@ -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)

View file

@ -1,3 +0,0 @@
#include "mac.h"
uint32_t device_mac = MAC_ADDR;

View file

@ -1,22 +0,0 @@
#ifndef __MAC_H__
#define __MAC_H__
#include <unistd.h>
/* 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__ */

View file

@ -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<<RCC_CFGR_PLLMUL_Pos) | RCC_CFGR_PLLSRC_HSE_PREDIV;
// PREDIV:
// HSE / PREDIV = PLL SRC
RCC->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<<RCC_CFGR_SW_Pos);
SystemCoreClockUpdate();
SysTick_Config(SystemCoreClock/1000); /* 1ms interval */
/* Enable all the periphery we need */
RCC->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<<GPIO_MODER_MODER0_Pos) /* PA0 - Current measurement analog input */
| (1<<GPIO_MODER_MODER1_Pos) /* PA1 - RS485 TX enable */
| (2<<GPIO_MODER_MODER2_Pos) /* PA2 - RS485 TX */
| (2<<GPIO_MODER_MODER3_Pos) /* PA3 - RS485 RX */
/* PA4 reserved because */
| (2<<GPIO_MODER_MODER5_Pos) /* PA5 - Shift register clk/SCLK */
| (1<<GPIO_MODER_MODER6_Pos) /* PA6 - LED2 open-drain output */
| (2<<GPIO_MODER_MODER7_Pos) /* PA7 - Shift register data/MOSI */
| (2<<GPIO_MODER_MODER9_Pos) /* FIXME PA9 - Shift register clear (TIM1_CH2) */
| (2<<GPIO_MODER_MODER10_Pos);/* PA10 - Shift register strobe (TIM1_CH3) */
GPIOB->MODER |=
(2<<GPIO_MODER_MODER1_Pos); /* PB1 - Shift register clear (TIM1_CH3N) */
GPIOA->OTYPER |= GPIO_OTYPER_OT_6; /* LED outputs -> open drain */
/* Set shift register IO GPIO output speed */
GPIOA->OSPEEDR |=
(3<<GPIO_OSPEEDR_OSPEEDR5_Pos) /* SCLK */
| (3<<GPIO_OSPEEDR_OSPEEDR6_Pos) /* LED1 */
| (3<<GPIO_OSPEEDR_OSPEEDR7_Pos) /* MOSI */
| (3<<GPIO_OSPEEDR_OSPEEDR10_Pos);/* Strobe */
GPIOB->OSPEEDR |=
(3<<GPIO_OSPEEDR_OSPEEDR1_Pos); /* Clear */
/* Alternate function settings */
GPIOA->AFR[0] |=
(1<<GPIO_AFRL_AFRL2_Pos) /* USART1_TX */
| (1<<GPIO_AFRL_AFRL3_Pos) /* USART1_RX */
| (0<<GPIO_AFRL_AFRL5_Pos) /* SPI1_SCK */
| (0<<GPIO_AFRL_AFRL7_Pos); /* SPI1_MOSI */
GPIOA->AFR[1] |=
(2<<GPIO_AFRH_AFRH2_Pos); /* TIM1_CH3 */
GPIOB->AFR[0] |=
(2<<GPIO_AFRL_AFRL1_Pos); /* TIM1_CH3N */
/* Configure SPI controller */
/* CPOL=0, CPHA=0, prescaler=2 -> 16MBd */
SPI1->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE | (0<<SPI_CR1_BR_Pos) | SPI_CR1_MSTR;
SPI1->CR2 = (0xf<<SPI_CR2_DS_Pos);
/* Configure TIM1 for display strobe generation */
TIM1->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<<TIM_CCMR2_OC3M_Pos) | TIM_CCMR2_OC3PE;
TIM1->CCER |= TIM_CCER_CC3E | TIM_CCER_CC3NE | TIM_CCER_CC3P | TIM_CCER_CC3NP;
TIM1->BDTR = TIM_BDTR_MOE | (1<<TIM_BDTR_DTG_Pos); /* really short dead time */
TIM1->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; i<NBITS; i++) {
uint32_t mask = 1<<i<<(MAX_BITS-NBITS); /* Bit mask for this bit value. */
uint32_t bv = 0; /* accumulator thing */
for (uint32_t j=0; j<32; j++) {
if (rx_buf.set_fb_rq.framebuf[j] & mask)
bv |= 1<<j;
}
brightness_by_bit[i] = bv;
}
}
/* This value sets how long a batch of ADC conversions used for temperature measurement is started before the end of the
* longest cycle. Here too the above caveats apply.
*
* This value is in TIM1/TIM3 timer counts. */
#define ADC_PRETRIGGER 150 /* trigger with about 12us margin to TIM1 CC IRQ */
/* Bit timing base value. This is the lowes bit interval used */
#define PERIOD_BASE 4
@ -266,6 +99,173 @@ static uint16_t timer_period_lookup[NBITS] = {
#undef B
#undef C
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<<RCC_CFGR_PLLMUL_Pos) | RCC_CFGR_PLLSRC_HSE_PREDIV;
// PREDIV:
// HSE / PREDIV = PLL SRC
RCC->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<<RCC_CFGR_SW_Pos);
SystemCoreClockUpdate();
SysTick_Config(SystemCoreClock/1000); /* 1ms interval */
/* Enable all the periphery we need */
RCC->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<<GPIO_MODER_MODER0_Pos) /* PA0 - Current measurement analog input */
| (2<<GPIO_MODER_MODER1_Pos) /* PA1 - RS485 TX enable */
| (2<<GPIO_MODER_MODER2_Pos) /* PA2 - RS485 TX */
| (2<<GPIO_MODER_MODER3_Pos) /* PA3 - RS485 RX */
/* PA4 reserved because */
| (2<<GPIO_MODER_MODER5_Pos) /* PA5 - Shift register clk/SCLK */
| (1<<GPIO_MODER_MODER6_Pos) /* PA6 - LED2 open-drain output */
| (2<<GPIO_MODER_MODER7_Pos) /* PA7 - Shift register data/MOSI */
| (2<<GPIO_MODER_MODER9_Pos) /* FIXME PA9 - Shift register clear (TIM1_CH2) */
| (2<<GPIO_MODER_MODER10_Pos);/* PA10 - Shift register strobe (TIM1_CH3) */
GPIOB->MODER |=
(2<<GPIO_MODER_MODER1_Pos); /* PB1 - Shift register clear (TIM1_CH3N) */
GPIOA->OTYPER |= GPIO_OTYPER_OT_6; /* LED outputs -> open drain */
/* Set shift register IO GPIO output speed */
GPIOA->OSPEEDR |=
(3<<GPIO_OSPEEDR_OSPEEDR5_Pos) /* SCLK */
| (3<<GPIO_OSPEEDR_OSPEEDR6_Pos) /* LED1 */
| (3<<GPIO_OSPEEDR_OSPEEDR7_Pos) /* MOSI */
| (3<<GPIO_OSPEEDR_OSPEEDR10_Pos);/* Strobe */
GPIOB->OSPEEDR |=
(3<<GPIO_OSPEEDR_OSPEEDR1_Pos); /* Clear */
/* Alternate function settings */
GPIOA->AFR[0] |=
(1<<GPIO_AFRL_AFRL1_Pos) /* USART1_RTS (RS485 DE) */
| (1<<GPIO_AFRL_AFRL2_Pos) /* USART1_TX */
| (1<<GPIO_AFRL_AFRL3_Pos) /* USART1_RX */
| (0<<GPIO_AFRL_AFRL5_Pos) /* SPI1_SCK */
| (0<<GPIO_AFRL_AFRL7_Pos); /* SPI1_MOSI */
GPIOA->AFR[1] |=
(2<<GPIO_AFRH_AFRH2_Pos); /* TIM1_CH3 */
GPIOB->AFR[0] |=
(2<<GPIO_AFRL_AFRL1_Pos); /* TIM1_CH3N */
/* Configure SPI controller */
/* CPOL=0, CPHA=0, prescaler=2 -> 16MBd */
SPI1->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE | (0<<SPI_CR1_BR_Pos) | SPI_CR1_MSTR;
SPI1->CR2 = (0xf<<SPI_CR2_DS_Pos);
/* Configure TIM1 for display strobe generation */
TIM1->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<<TIM_CCMR2_OC3M_Pos) | TIM_CCMR2_OC3PE | (6<<TIM_CCMR2_OC4M_Pos);
TIM1->CCER |= TIM_CCER_CC3E | TIM_CCER_CC3NE | TIM_CCER_CC3P | TIM_CCER_CC3NP | TIM_CCER_CC4E;
TIM1->BDTR = TIM_BDTR_MOE | (1<<TIM_BDTR_DTG_Pos); /* really short dead time */
TIM1->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; i<NBITS; i++) {
uint32_t mask = 1<<i<<(MAX_BITS-NBITS); /* Bit mask for this bit value. */
uint32_t bv = 0; /* accumulator thing */
for (uint32_t j=0; j<32; j++)
if (rx_buf.set_fb_rq.framebuf[j] & mask)
bv |= 1<<j;
brightness_by_bit[i] = bv;
}
}
/* Timer 1 main IRQ handler. This is used only for overflow ("update" or UP event in ST's terminology). */
void TIM1_BRK_UP_TRG_COM_IRQHandler(void) {
/* The index of the currently active bit. On entry of this function, this is the bit index of the upcoming period.

View file

@ -23,7 +23,6 @@
#include "serial.h"
#include "adc.h"
#include "mac.h"
unsigned int uart_overruns = 0;
unsigned int frame_overruns = 0;
@ -47,13 +46,17 @@ void send_frame_formatted(uint8_t *buf, int len) {
while (*p && p!=end)
tx_char(*p++);
p++, q++;
} while (p < end);
} while (p <= end);
tx_char('\0');
}
void send_status_reply(void) {
tx_buf.desc_reply.firmware_version = FIRMWARE_VERSION;
tx_buf.desc_reply.hardware_version = HARDWARE_VERSION;
tx_buf.desc_reply.nbits = NBITS;
tx_buf.desc_reply.channel_spec = CHANNEL_SPEC;
tx_buf.desc_reply.nchannels = NCHANNELS;
tx_buf.desc_reply.color_spec = COLOR_SPEC;
tx_buf.desc_reply.uptime_s = sys_time_seconds;
tx_buf.desc_reply.vcc_mv = adc_vcc_mv;
tx_buf.desc_reply.temp_celsius = adc_temp_celsius;
@ -66,16 +69,18 @@ void send_status_reply(void) {
/* This is the higher-level protocol handler for the serial protocol. It gets passed the number of data bytes in this
* frame (which may be zero) and returns a pointer to the buffer where the next frame should be stored.
*/
volatile uint8_t *packet_received(int len) {
static volatile inline void packet_received(int len) {
static enum {
PROT_ADDRESSED = 0,
PROT_EXPECT_FRAME_SECOND_HALF = 1,
PROT_IGNORE = 2,
} protocol_state = PROT_IGNORE;
/* Use mac frames as delimiters to synchronize this protocol layer */
if (len == 0) { /* Discovery packet */
if (len == 1 && rx_buf.byte_data[0] == 0x00) { /* Discovery packet */
if (sys_time < 100 && sys_time_seconds == 0) { /* Only respond during the first 100ms after boot */
send_frame_formatted((uint8_t*)&device_mac, sizeof(device_mac));
tx_buf.device_type_reply.mac = MAC_ADDR;
tx_buf.device_type_reply.device_type = DEVICE_TYPE;
send_frame_formatted(tx_buf.byte_data, sizeof(tx_buf.device_type_reply));
}
} else if (len == 1) { /* Command packet */
@ -91,13 +96,13 @@ volatile uint8_t *packet_received(int len) {
protocol_state = PROT_IGNORE;
} else if (len == 4) { /* Address packet */
if (rx_buf.mac_data == device_mac) { /* we are addressed */
if (rx_buf.mac_data == MAC_ADDR) { /* we are addressed */
protocol_state = PROT_ADDRESSED; /* start listening for frame buffer data */
} else { /* we are not addressed */
protocol_state = PROT_IGNORE; /* ignore packet */
}
} else if (len == sizeof(rx_buf.set_fb_rq.framebuf)) {
} else if (len == sizeof(rx_buf.set_fb_rq)) {
if (protocol_state == PROT_ADDRESSED) { /* First of two half-framebuffer data frames */
/* Kick off buffer transfer. This triggers the main loop to copy data out of the receive buffer and paste it
* properly formatted into the frame buffer. */
@ -117,11 +122,6 @@ volatile uint8_t *packet_received(int len) {
invalid_frames++;
protocol_state = PROT_IGNORE; /* go into "hang mode" until next zero-length packet */
}
/* By default, return rx_buf.byte_data . This means if an invalid protocol state is reached ("hang mode"), the next
* frame is still written to rx_buf. This is not a problem since whatever garbage is written at that point will be
* overwritten before the next buffer transfer. */
return rx_buf.byte_data;
}
void USART1_IRQHandler(void) {
@ -157,9 +157,6 @@ void USART1_IRQHandler(void) {
* http://www.stuartcheshire.org/papers/COBSforToN.pdf
*/
/* This pointer stores where we write data. The higher-level protocol logic decides on a frame-by-frame-basis where
* the next frame's data will be stored. */
static volatile uint8_t *writep = rx_buf.byte_data;
/* Index inside the current frame payload */
static int rxpos = 0;
/* COBS state machine. This implementation might be a little too complicated, but it works well enough and I find it
@ -184,8 +181,9 @@ void USART1_IRQHandler(void) {
uint8_t data = USART1->RDR; /* 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;
}
}
}

View file

@ -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;
};