UART magic seems to be working now

This commit is contained in:
jaseg 2017-09-01 20:26:05 +02:00
parent 6b40626a1b
commit e6437f975b
4 changed files with 216 additions and 121 deletions

View file

@ -47,8 +47,14 @@ cmsis_exports.c: $(CMSIS_DEV_PATH)/Include/stm32f030x6.h $(CMSIS_PATH)/Include/c
%.dot: %.elf
r2 -a arm -qc 'aa;agC' $< 2>/dev/null >$@
sources.tar.xz: main.c transpose.c transpose.h
tar -caf $@ $^
main.elf: main.o startup_stm32f030x6.o system_stm32f0xx.o $(HAL_PATH)/Src/stm32f0xx_ll_utils.o base.o cmsis_exports.o transpose.o
sources.c: sources.tar.xz
xxd -i $< > $@
main.elf: main.o startup_stm32f030x6.o system_stm32f0xx.o $(HAL_PATH)/Src/stm32f0xx_ll_utils.o base.o cmsis_exports.o transpose.o bus_addr.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
$(OBJCOPY) -O ihex $@ $(@:.elf=.hex)
$(OBJCOPY) -O binary $@ $(@:.elf=.bin)

303
fw/main.c
View file

@ -43,47 +43,41 @@ void strobe_leds(void) {
GPIOA->BSRR = GPIO_BSRR_BR_9;
}
#define FIRMWARE_VERSION 1
#define HARDWARE_VERSION 1
volatile uint16_t adc_vcc_mv = 0;
volatile uint16_t adc_temp_tenth_celsius = 0;
volatile unsigned int sys_time = 0;
volatile unsigned int sys_time_seconds = 0;
volatile struct framebuf fb[2] = {0};
volatile struct framebuf *read_fb=fb+0, *write_fb=fb+1;
volatile int led_state = 0;
volatile enum { FB_WRITE, FB_FORMAT, FB_UPDATE } fb_op;
volatile uint8_t rx_buf[sizeof(struct framebuf) + 4 /* crc */];
volatile union {
struct {
struct __attribute__((packed)) { struct framebuf fb; } set_fb_rq;
struct __attribute__((packed)) { uint8_t nbits; } set_nbits_rq;
uint8_t byte_data[0];
} rx_buf;
volatile union {
struct { uint32_t magic; } ping_reply;
struct __attribute__((packed)) {
uint8_t firmware_version,
hardware_version,
digit_rows,
digit_cols;
uint32_t uptime;
uint32_t millifps;
} fps_reply;
uint16_t vcc_mv,
temp_tenth_celsius;
uint8_t nbits;
} desc_reply;
} tx_buf;
volatile uint8_t this_addr = 0x05; /* FIXME */
union {
struct {
uint8_t device_id __attribute__((aligned(2)));
};
uint16_t flash_data[0];
} flash_data __attribute__((section ("configflash")));
void write_config(void *target, uint16_t value) {
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;
FLASH->CR = FLASH_CR_PG;
*(uint16_t *)target = value;
while (FLASH->SR & FLASH_SR_BSY)
;
FLASH->CR = FLASH_CR_LOCK;
}
void erase_config() {
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;
FLASH->CR = FLASH_CR_PER;
FLASH->AR = (uint32_t)&flash_data;
FLASH->CR = FLASH_CR_STRT;
while (FLASH->SR & FLASH_SR_BSY)
;
FLASH->CR = FLASH_CR_LOCK;
}
extern uint8_t bus_addr;
#define LED_COMM 0x0001
#define LED_ERROR 0x0002
@ -157,6 +151,7 @@ void SPI1_IRQHandler() {
uint8_t segment_map[8] = {5, 7, 6, 4, 1, 3, 0, 2};
static volatile int frame_duration_us;
volatile int nbits = MAX_BITS;
/* returns new bit time in cycles */
int shift_data() {
static int active_segment = 0;
@ -165,11 +160,11 @@ int shift_data() {
/* Note: On boot, multiplexing will start with bit 1 due to the next few lines. This is perfectly ok. */
active_bit++;
if (active_bit == nbits) {
if (active_bit >= nbits) {
active_bit = 0;
active_segment++;
if (active_segment == nsegments) {
if (active_segment == NSEGMENTS) {
active_segment = 0;
int time = stk_microseconds();
@ -183,13 +178,13 @@ int shift_data() {
}
}
spi_word = read_fb->data[active_bit*frame_size_words + active_segment];
spi_word = read_fb->data[active_bit*FRAME_SIZE_WORDS + active_segment];
spi_state = SPI_AUX;
SPI1->DR = (read_fb->brightness ? SR_ILED_HIGH : SR_ILED_LOW)
| (led_state<<1)
| (0xff00 ^ (0x100<<segment_map[active_segment]));
} else {
spi_word = read_fb->data[active_bit*frame_size_words + active_segment];
spi_word = read_fb->data[active_bit*FRAME_SIZE_WORDS + active_segment];
spi_state = SPI_WORD0;
SPI1->DR = spi_word>>16;
}
@ -233,81 +228,184 @@ void TIM3_IRQHandler() {
enum Command {
CMD_PING,
CMD_SET_ADDR,
CMD_SET_FB,
CMD_SET_NBITS,
CMD_GET_DESC,
CMD_GET_FRAMERATE,
CMD_ERASE_CONFIG,
N_CMDS
};
enum {
PROT_ADDR,
PROT_CMD,
PROT_
PROT_CRC,
PROT_COMPLETE,
PROT_INVALID,
N_PROT_STATES
} protocol_state = PROT_ADDR;
int gaddr;
void crc_reset(void) {
CRC->CR |= CRC_CR_RESET;
while (CRC->CR&CRC_CR_RESET)
;
}
void crc_feed(uint8_t data) {
*(uint8_t *)&CRC->DR = data;
}
void kickoff_uart_rx_dma(volatile void *target, size_t len) {
DMA1_Channel3->CMAR = (unsigned int)target;
DMA1_Channel3->CNDTR = len;
/* Disable RX interrupt for duration of DMA transfer */
USART1->CR1 &= ~USART_CR1_RXNEIE_Msk;
/* Enable DMA transfer to write buffer */
DMA1->IFCR |= DMA_IFCR_CGIF3;
DMA1_Channel3->CCR |= DMA_CCR_EN;
USART1->CR3 |= USART_CR3_DMAR;
}
void kickoff_uart_tx_dma(size_t len) {
DMA1_Channel4->CNDTR = len;
USART1->ICR |= USART_ICR_TCCF; /* FIXME: (1) is this necessary? (2) where should this be done? */
DMA1_Channel4->CCR |= DMA_CCR_EN;
USART1->RQR |= USART_RQR_MMRQ;
}
static size_t cmd_payload_len[N_CMDS] = {
[CMD_PING] = 0,
[CMD_SET_FB] = sizeof(rx_buf.set_fb_rq),
[CMD_SET_NBITS] = sizeof(rx_buf.set_nbits_rq),
[CMD_GET_DESC] = 0,
};
int crc_error_count = 0;
static volatile uint32_t rx_crc;
static volatile enum Command rx_cmd;
void USART1_IRQHandler() {
gaddr = USART1->RDR;
uint8_t data = USART1->RDR;
int isr = USART1->ISR;
USART1->RQR |= USART_RQR_RXFRQ;
int addr = gaddr; /* FIXME DEBUG */
int cmd = addr>>5;
addr &= 0x1F;
/* Overrun detected? */
if (USART1->ISR & USART_ISR_ORE) {
if (isr & USART_ISR_ORE) {
USART1->ICR |= USART_ICR_ORECF;
USART1->RQR |= USART_RQR_MMRQ;
goto errout;
}
/* Are we addressed? */
if (addr != this_addr) {
asm("bkpt");
/* We are not. Mute USART until next idle condition */
USART1->RQR |= USART_RQR_MMRQ;
} else {
/* We are. Switch by command. */
switch (cmd) {
case CMD_PING:
USART1->TDR = 42;
USART1->RQR |= USART_RQR_MMRQ;
break;
case CMD_SET_FB:
/* Are we ready to process new frame data? */
if (fb_op != FB_WRITE) {
//asm("bkpt"); /* FIXME DEBUG */
goto errout; /* Error: Not yet ready to receive new packet */
}
/* Disable this RX interrupt for duration of DMA transfer */
USART1->CR1 &= ~USART_CR1_RXNEIE_Msk;
/* Enable DMA transfer to write buffer */
DMA1->IFCR |= DMA_IFCR_CGIF3;
DMA1_Channel3->CCR |= DMA_CCR_EN;
USART1->CR3 |= USART_CR3_DMAR;
break;
case CMD_GET_FRAMERATE:
tx_buf.fps_reply.millifps = frame_duration_us > 0 ? 1000000000 / frame_duration_us : 0;
DMA1_Channel4->CNDTR = sizeof(tx_buf.fps_reply);
USART1->ICR |= USART_ICR_TCCF; /* FIXME: (1) is this necessary? (2) where should this be done? */
DMA1_Channel4->CCR |= DMA_CCR_EN;
USART1->RQR |= USART_RQR_MMRQ;
break;
if (isr & USART_ISR_IDLE) {
USART1->ICR |= USART_ICR_IDLECF;
USART1->CR3 &= ~USART_CR3_DMAR_Msk;
DMA1_Channel3->CCR &= ~DMA_CCR_EN_Msk;
protocol_state = PROT_ADDR;
USART1->RQR |= USART_RQR_RXFRQ;
USART1->CR1 |= USART_CR1_RXNEIE;
return;
}
switch(protocol_state) {
case PROT_ADDR:
if (data == bus_addr) /* Are we addressed? */
protocol_state = PROT_CMD;
else /* We are not. Mute USART until next idle condition */
goto errout;
break;
case PROT_CMD:
if (data > N_CMDS)
goto errout;
rx_cmd = data;
crc_reset();
crc_feed(data);
size_t payload_len = cmd_payload_len[data];
if (payload_len) {
/* Is rx_buf currently occupied by the main loop formatting frame data? */
if (fb_op != FB_WRITE)
goto errout;
kickoff_uart_rx_dma(&rx_buf, payload_len);
DMA1_Channel5->CNDTR = payload_len;
protocol_state = PROT_CRC;
} else {
kickoff_uart_rx_dma(&rx_crc, sizeof(rx_crc));
protocol_state = PROT_COMPLETE;
}
break;
default:
/* can/must not happen */
asm("bkpt");
}
return;
errout:
/* FIXME */
return;
protocol_state = PROT_ADDR;
USART1->RQR |= USART_RQR_MMRQ;
}
void DMA1_Channel2_3_IRQHandler() {
/* DMA Transfer complete */
/* ...and disable this DMA channel */
/* Disable this DMA channel */
USART1->CR3 &= ~USART_CR3_DMAR_Msk;
DMA1_Channel3->CCR &= ~DMA_CCR_EN_Msk;
/* Kick off formatting code in main loop outside interrupt context */
fb_op = FB_FORMAT;
DMA1->IFCR |= DMA_IFCR_CGIF3;
/* re-enable receive interrupt */
USART1->RQR |= USART_RQR_RXFRQ;
USART1->CR1 |= USART_CR1_RXNEIE;
switch(protocol_state) {
case PROT_CRC:
kickoff_uart_rx_dma(&rx_crc, sizeof(rx_crc));
protocol_state = PROT_COMPLETE;
DMA1_Channel5->CCR |= DMA_CCR_EN;
break;
case PROT_COMPLETE:
DMA1_Channel5->CCR &= ~DMA_CCR_EN_Msk;
DMA1->IFCR |= DMA_IFCR_CGIF5;
if (rx_crc != CRC->DR) {
crc_error_count++;
protocol_state = PROT_ADDR;
/* re-enable receive interrupt */
USART1->RQR |= USART_RQR_RXFRQ;
USART1->RQR |= USART_RQR_MMRQ;
USART1->CR1 |= USART_CR1_RXNEIE;
break;
}
protocol_state = PROT_ADDR;
switch(rx_cmd) {
case CMD_PING:
tx_buf.ping_reply.magic = 0x39404142;
kickoff_uart_tx_dma(sizeof(tx_buf.ping_reply));
break;;
case CMD_SET_FB:
fb_op = FB_UPDATE;
break;
case CMD_SET_NBITS:
nbits = rx_buf.set_nbits_rq.nbits;
break;
case CMD_GET_DESC:
tx_buf.desc_reply.firmware_version = FIRMWARE_VERSION;
tx_buf.desc_reply.hardware_version = HARDWARE_VERSION;
tx_buf.desc_reply.digit_rows = NROWS;
tx_buf.desc_reply.digit_cols = NCOLS;
tx_buf.desc_reply.uptime = sys_time_seconds;
tx_buf.desc_reply.vcc_mv = adc_vcc_mv;
tx_buf.desc_reply.temp_tenth_celsius = adc_temp_tenth_celsius;
tx_buf.desc_reply.nbits = nbits;
tx_buf.desc_reply.millifps = frame_duration_us > 0 ? 1000000000 / frame_duration_us : 0;
kickoff_uart_tx_dma(sizeof(tx_buf.desc_reply));
break;
default:
/* can/must not happen */
asm("bkpt");
}
/* re-enable receive interrupt */
USART1->RQR |= USART_RQR_RXFRQ;
USART1->CR1 |= USART_CR1_RXNEIE;
break;
default:
/* can/must not happen */
asm("bkpt");
}
}
void DMA1_Channel4_5_IRQHandler() {
@ -326,6 +424,7 @@ void uart_config(void) {
/* WAKE clear */
/* PCE, PS clear */
| USART_CR1_RXNEIE
| USART_CR1_IDLEIE
/* other interrupts clear */
| USART_CR1_TE
| USART_CR1_RE;
@ -338,8 +437,6 @@ void uart_config(void) {
/* Configure DMA 1 / Channel 3 for USART frame data reception: USART1->RDR -> rx_buf */
DMA1_Channel3->CPAR = (unsigned int)&USART1->RDR;
DMA1_Channel3->CMAR = (unsigned int)rx_buf;
DMA1_Channel3->CNDTR = sizeof(rx_buf);
DMA1_Channel3->CCR = (1<<DMA_CCR_PL_Pos);
DMA1_Channel3->CCR |=
(0<<DMA_CCR_MSIZE_Pos) /* 8 bit */
@ -350,7 +447,7 @@ void uart_config(void) {
/* Configure DMA 1 / Channel 5 for USART frame data CRC check: rx_buf -> CRC->DR */
DMA1_Channel5->CPAR = (unsigned int)&CRC->DR;
DMA1_Channel5->CMAR = (unsigned int)rx_buf;
DMA1_Channel5->CMAR = (unsigned int)&rx_buf;
DMA1_Channel5->CCR = (0<<DMA_CCR_PL_Pos);
DMA1_Channel5->CCR |=
DMA_CCR_MEM2MEM /* Software trigger (precludes CIRC) */
@ -380,7 +477,6 @@ void uart_config(void) {
NVIC_SetPriority(DMA1_Channel4_5_IRQn, 5);
}
int errcnt = 0;
int main(void) {
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR&RCC_CR_HSERDY));
@ -393,7 +489,7 @@ int main(void) {
RCC->CFGR |= (2<<RCC_CFGR_SW_Pos);
SystemCoreClockUpdate();
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_DMAEN | RCC_AHBENR_CRCEN;
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_DMAEN | RCC_AHBENR_CRCEN | RCC_AHBENR_FLITFEN;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_SYSCFGEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
@ -445,25 +541,11 @@ int main(void) {
while (42) {
if (fb_op == FB_FORMAT) {
CRC->CR |= CRC_CR_RESET;
DMA1_Channel5->CNDTR = sizeof(struct framebuf);
DMA1_Channel5->CCR |= DMA_CCR_EN;
transpose_data(rx_buf.byte_data, write_fb);
transpose_data(rx_buf, write_fb);
while (!(DMA1->ISR & DMA_ISR_TCIF4))
fb_op = FB_UPDATE;
while (fb_op == FB_UPDATE)
;
DMA1->IFCR |= DMA_IFCR_CGIF4;
DMA1_Channel5->CCR &= ~DMA_CCR_EN_Msk;
if (CRC->DR != *(uint32_t *)(rx_buf+sizeof(struct framebuf))) {
fb_op = FB_WRITE;
errcnt++;
} else {
fb_op = FB_UPDATE;
while (fb_op == FB_UPDATE)
;
}
}
}
}
@ -484,6 +566,11 @@ void PendSV_Handler(void) {
}
void SysTick_Handler(void) {
static int n = 0;
sys_time++;
if (n++ == 1000) {
n = 0;
sys_time_seconds++;
}
}

View file

@ -2,8 +2,8 @@
ENTRY(Reset_Handler)
MEMORY {
FLASH (rx): ORIGIN = 0x08000000, LENGTH = 14K
CONFIGFLASH (rw): ORIGIN = 0x08003800, LENGTH = 2K
FLASH (rx): ORIGIN = 0x08000000, LENGTH = 0x3C00
CONFIGFLASH (rw): ORIGIN = 0x08003C00, LENGTH = 0x400
RAM (xrw): ORIGIN = 0x20000000, LENGTH = 4K
}
@ -39,11 +39,13 @@ SECTIONS {
_sidata = _etext;
} >FLASH
/*
.configflash : {
. = ALIGN(2048);
. = ALIGN(0x400);
*(.configdata)
_econfig = .;
} >CONFIGFLASH
} >FLASH
*/
/* This is the initialized data section
The program executes knowing that the data is in the RAM

View file

@ -3,25 +3,25 @@
#include <stdint.h>
enum Segment { SegA, SegB, SegC, SegD, SegE, SegF, SegG, SegDP, nsegments };
enum Segment { SegA, SegB, SegC, SegD, SegE, SegF, SegG, SegDP, NSEGMENTS };
enum {
nrows = 4,
ncols = 8,
nbits = 10,
NROWS = 4,
NCOLS = 8,
MAX_BITS = 10,
};
enum {
frame_size_words = nrows*ncols*nsegments/32,
FRAME_SIZE_WORDS = NROWS*NCOLS*NSEGMENTS/32,
};
struct framebuf {
/* Multiplexing order: first Digits, then Time/bits, last Segments */
union {
uint32_t data[nbits*frame_size_words];
uint32_t data[MAX_BITS*FRAME_SIZE_WORDS];
struct {
struct {
uint32_t data[frame_size_words];
} frame[nbits];
uint32_t data[FRAME_SIZE_WORDS];
} frame[MAX_BITS];
};
};
uint8_t brightness; /* 0 or 1; controls global brighntess control */