From ebb9c72384daf2e4fd56c832a6311b10b95ba2eb Mon Sep 17 00:00:00 2001 From: jaseg Date: Mon, 8 May 2023 23:23:30 +0200 Subject: [PATCH] serial WIP --- Makefile | 4 +- include/adc.h | 1 + include/cobs.h | 23 +++++ include/global.h | 7 ++ include/lcd.h | 2 + include/serial.h | 80 ++++++++++++++ include/usb_if.h | 21 ++++ include/usb_proto.h | 23 +++++ src/adc.c | 17 +-- src/cobs.c | 212 ++++++++++++++++++++++++++++++++++++++ src/lcd.c | 7 +- src/main.c | 115 ++++++++++++++++++--- src/serial.c | 246 ++++++++++++++++++++++++++++++++++++++++++++ src/usb_if.c | 100 +++++++++++------- 14 files changed, 783 insertions(+), 75 deletions(-) create mode 100644 include/cobs.h create mode 100644 include/serial.h create mode 100644 include/usb_proto.h create mode 100644 src/cobs.c create mode 100644 src/serial.c diff --git a/Makefile b/Makefile index ff1d99d..65bbe9b 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,8 @@ C_SOURCES := src/main.c \ src/adc.c \ src/lcd.c \ src/led.c \ + src/cobs.c \ + src/serial.c \ src/usb_if.c \ upstream/libusb_stm32/src/usbd_stm32l052_devfs.c \ upstream/libusb_stm32/src/usbd_core.c @@ -95,7 +97,7 @@ CFLAGS += -I$(CMSIS_DEVICE_DIR_ABS)/Include CFLAGS += $(ARCH_FLAGS) $(SYSTEM_FLAGS) CFLAGS += -fno-common -ffunction-sections -fdata-sections -COMMON_CFLAGS += -O$(OPT) -std=gnu11 -g +COMMON_CFLAGS += -O$(OPT) -std=gnu2x -g COMMON_CFLAGS += $(DEVICE_DEFINES) COMMON_CFLAGS += -DDEBUG=$(DEBUG) diff --git a/include/adc.h b/include/adc.h index 4d1b227..64d7119 100644 --- a/include/adc.h +++ b/include/adc.h @@ -15,5 +15,6 @@ struct adc_state { extern struct adc_state st_adc; void adc_init(void); +void adc_dma_interrupt(uint32_t channel, uint32_t flags); #endif /* __ADC_H__ */ diff --git a/include/cobs.h b/include/cobs.h new file mode 100644 index 0000000..8c84ca4 --- /dev/null +++ b/include/cobs.h @@ -0,0 +1,23 @@ +#ifndef __COBS_H__ +#define __COBS_H__ + +#include +#include +#include + + +struct cobs_decode_state { + size_t p; + size_t c; +}; + + +ssize_t cobs_encode(char *dst, size_t dstlen, char *src, size_t srclen); +ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen); + +int cobs_encode_usart(int (*output)(char), char *src, size_t srclen); + +void cobs_decode_incremental_initialize(struct cobs_decode_state *state); +int cobs_decode_incremental(struct cobs_decode_state *state, char *dst, size_t dstlen, char src); + +#endif//__COBS_H__ diff --git a/include/global.h b/include/global.h index badc2ed..05e1c01 100644 --- a/include/global.h +++ b/include/global.h @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -13,6 +14,10 @@ #include #include +#define DMA1_Channel DMA1_Channel1 +#define DMA_ISR_FLAGS_Pos(channel) (4 * ((channel) - 1)) +#define DMA_ISR_FLAGS_CH(channel) (0xf << DMA_ISR_FLAGS_Pos(channel)) + #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) #define APB1_PRESC (1<<(APBPrescTable[(RCC->CFGR & RCC_CFGR_PPRE1_Msk) >> RCC_CFGR_PPRE1_Pos])) @@ -24,6 +29,8 @@ enum ErrorCode { ERR_PHYSICAL_LAYER, ERR_PROTOCOL, ERR_DMA, + ERR_BUSY, + ERR_BUFFER_OVERFLOW, }; typedef enum ErrorCode ErrorCode; diff --git a/include/lcd.h b/include/lcd.h index f9895f0..b489219 100644 --- a/include/lcd.h +++ b/include/lcd.h @@ -18,4 +18,6 @@ struct lcd_state { extern struct lcd_state st_lcd; +void lcd_dma_interrupt(uint32_t channel, uint32_t flags); + #endif /* __LCD_H__ */ diff --git a/include/serial.h b/include/serial.h new file mode 100644 index 0000000..492f5f5 --- /dev/null +++ b/include/serial.h @@ -0,0 +1,80 @@ +#ifndef __SERIAL_H__ +#define __SERIAL_H__ + +#include +#include + +struct dma_tx_buf { + /* The following fields are accessed only from DMA ISR */ + ssize_t xfr_start; /* Start index of running DMA transfer */ + ssize_t xfr_end; /* End index of running DMA transfer plus one */ + bool wraparound; + ssize_t xfr_next; + bool ack; + + + /* The following fields are written only from non-interrupt code */ + ssize_t wr_pos; /* Next index to be written */ + ssize_t wr_idx; + ssize_t packet_end[8]; + + /* The following may be accessed by anything */ + uint8_t data[512]; +}; + +struct uart_dma_state { + struct dma_tx_buf tx_buf; + + uint32_t tx_overruns, rx_overruns; + uint32_t rx_framing_errors, rx_protocol_errors; + uint32_t retransmissions, packet_count; + ErrorCode error; + + struct cobs_decode_state cobs_state; + + size_t tx_dma_ch; + USART_TypeDef *usart; + + uint8_t rx_buf[32]; + uint8_t addr; +}; + +struct __attribute__((__packed__)) ll_pkt { + uint32_t crc32; + /* CRC computed over entire packet starting here */ + uint8_t src; + uint8_t dst; + uint8_t pid; + uint8_t type; + uint8_t data[0]; +}; + +enum ctrl_pkt_type { + CTRL_PKT_RESET = 1, + CTRL_PKT_ACK = 2, + CTRL_PKT_NACK = 3, + CTRL_PKT_PING = 4, + CTRL_PKT_PONG = 5, + _USER_PKT_START = 16, +}; + +enum user_pkt_type { + USER_PKT_ADC_DATA = _USER_PKT_START+0, + USER_PKT_SET_DISPLAY = _USER_PKT_START+1, +}; + +struct __attribute__((__packed__)) user_pkt_adc_data { + struct ll_pkt hdr; + uint8_t adc_data_packed[3*2*16]; +}; + +extern volatile struct uart_dma_state uart_st; + +void uart_dma_init(USART_TypeDef *usart, int tx_dma_ch, int addr); +int uart_send_packet_nonblocking(struct ll_pkt *pkt, size_t pkt_len); +int uart_ack_packet(uint8_t idx); +void uart_dma_interrupt(uint32_t channel, uint32_t flags); +void uart_interrupt(uint32_t isr); +extern void uart_handle_user_packet(struct ll_pkt *pkt, size_t len); + +#endif // __SERIAL_H__ diff --git a/include/usb_if.h b/include/usb_if.h index 9f4c680..3d8394e 100644 --- a/include/usb_if.h +++ b/include/usb_if.h @@ -2,7 +2,28 @@ #define __USB_IF_H__ #include +#include +#include +#include + +#define USB_CDC_DATA_SIZE 0x40 + +struct usb_state { + struct usb_cdc_line_coding line; + usbd_device usb_dev; + uint32_t usb_buf[0x20]; + uint8_t usb_pkbuf[USB_CDC_DATA_SIZE]; + struct cobs_decode_state cobs_state; + bool cobs_synchronized; + ErrorCode error; + uint8_t txbuf[USB_CDC_DATA_SIZE]; + size_t txpos; +}; + +extern struct usb_state st_usb; void usb_init(void); +extern void handle_usb_packet(void *packet, size_t len); +ErrorCode usb_write_response(void *data, size_t len); #endif /* __USB_IF_H__ */ diff --git a/include/usb_proto.h b/include/usb_proto.h new file mode 100644 index 0000000..667b2f8 --- /dev/null +++ b/include/usb_proto.h @@ -0,0 +1,23 @@ +#ifndef __USB_PROTO_H__ +#define __USB_PROTO_H__ + +#include + +enum usbp_pkt_type { + USBP_GET_STATUS = 0, + USBP_GET_MEASUREMENTS = 1, +}; + +struct __attribute__((__packed__)) usbp_status_pkt { + uint8_t packet_type; + uint8_t has_lcd; + uint8_t has_adc; +}; + +struct __attribute__((__packed__)) usbp_measurement_pkt { + uint8_t packet_type; + uint8_t num_channels; + uint32_t measurements[32]; +}; + +#endif /* __USB_PROTO_H__ */ diff --git a/src/adc.c b/src/adc.c index 3ff77c9..bf80f74 100644 --- a/src/adc.c +++ b/src/adc.c @@ -142,22 +142,11 @@ void adc_init() { TIM2->SMCR = TIM_SMCR_ETP | (7<ISR; - if (flags & (DMA_ISR_TEIF1)) { - DMA1->IFCR = DMA_IFCR_CTEIF1; - st_adc.error = ERR_DMA; - } -} - -void DMA1_Channel2_3_IRQHandler() { - int flags = DMA1->ISR; - if (flags & (DMA_ISR_TEIF2 | DMA_ISR_TEIF3)) { - DMA1->IFCR = DMA_IFCR_CTEIF2 | DMA_IFCR_CTEIF3; +void adc_dma_interrupt(uint32_t channel, uint32_t flags) { + if (flags & DMA_ISR_TEIF1) { st_adc.error = ERR_DMA; - } else if (flags & DMA_ISR_TCIF2) { - DMA1->IFCR = DMA_IFCR_CTCIF2; + } else if (flags & DMA_ISR_TCIF1 && channel == 3) { DMA1_Channel3->CCR &= ~DMA_CCR_EN; if ((st_adc.rxbuf[0] & 0x3) != 0x1) { diff --git a/src/cobs.c b/src/cobs.c new file mode 100644 index 0000000..6e917a1 --- /dev/null +++ b/src/cobs.c @@ -0,0 +1,212 @@ + +#include "serial.h" +#include "cobs.h" + +int cobs_encode_usart(int (*output)(char), char *src, size_t srclen) { + if (srclen > 254) + return ERR_BUFFER_OVERFLOW; + + size_t p = 0; + while (p <= srclen) { + + char val; + if (p != 0 && src[p-1] != 0) { + val = src[p-1]; + + } else { + size_t q = p; + while (q < srclen && src[q] != 0) + q++; + val = (char)q-p+1; + } + + int rv = output(val); + if (rv) + return rv; + p++; + } + + int rv = output(0); + if (rv) + return rv; + + return 0; +} + +/*@ requires \valid(dst + (0..dstlen-1)); + @ requires \valid_read(src + (0..srclen-1)); + @ requires \separated(dst + (0..dstlen-1), src + (0..srclen-1)); + @ + @ behavior maybe_valid_frame: + @ assumes 1 <= srclen <= dstlen <= 65535; + @ assumes \exists integer j; j > 0 && \forall integer i; 0 <= i < j ==> src[i] != 0; + @ assumes \exists integer i; 0 <= i < srclen && src[i] == 0; + @ assigns dst[0..dstlen-1]; + @ ensures \result >= 0 || \result == -3; + @ ensures \result >= 0 ==> src[\result+1] == 0; + @ ensures \result >= 0 ==> (\forall integer i; 0 <= i < \result ==> src[i] != 0); + @ + @ behavior invalid_frame: + @ assumes 1 <= srclen <= dstlen <= 65535; + @ assumes src[0] == 0 || \forall integer i; 0 <= i < srclen ==> src[i] != 0; + @ assigns dst[0..dstlen-1]; + @ ensures \result == -2; + @ + @ behavior invalid_buffers: + @ assumes dstlen < 0 || dstlen > 65535 + @ || srclen < 1 || srclen > 65535 + @ || dstlen < srclen; + @ assigns \nothing; + @ ensures \result == -1; + @ + @ complete behaviors; + @ disjoint behaviors; + @*/ +ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen) { + if (dstlen > 65535 || srclen > 65535) + return -1; + + if (srclen < 1) + return -1; + + if (dstlen < srclen) + return -1; + + size_t p = 1; + size_t c = (unsigned char)src[0]; + //@ assert 0 <= c < 256; + //@ assert 0 <= c; + //@ assert c < 256; + if (c == 0) + return -2; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */ + //@ assert c >= 0; + //@ assert c != 0; + //@ assert c <= 257; + //@ assert c > 0; + //@ assert c >= 0 && c != 0 ==> c > 0; + + /*@ //loop invariant \forall integer i; 0 <= i <= p ==> (i == srclen || src[i] != 0); + @ loop invariant \forall integer i; 1 <= i < p ==> src[i] != 0; + @ loop invariant c > 0; + @ loop invariant 1 <= p <= srclen <= dstlen <= 65535; + @ loop invariant \separated(dst + (0..dstlen-1), src + (0..srclen-1)); + @ loop invariant \valid_read(src + (0..srclen-1)); + @ loop invariant \forall integer i; 1 <= i <= srclen ==> \valid(dst + i - 1); + @ loop assigns dst[0..dstlen-1], p, c; + @ loop variant srclen-p; + @*/ + while (p < srclen && src[p]) { + char val; + c--; + + //@ assert src[p] != 0; + if (c == 0) { + c = (unsigned char)src[p]; + val = 0; + } else { + val = src[p]; + } + + //@ assert 0 <= p-1 <= dstlen-1; + dst[p-1] = val; + p++; + } + + if (p == srclen) + return -2; /* Invalid framing. The terminating null byte should always be present in the input buffer. */ + + if (c != 1) + return -3; /* Invalid framing. The skip counter does not hit the end of the frame. */ + + //@ assert 0 < p <= srclen <= 65535; + //@ assert src[p] == 0; + //@ assert \forall integer i; 1 <= i < p ==> src[i] != 0; + return p-1; +} + +void cobs_decode_incremental_initialize(struct cobs_decode_state *state) { + state->p = 0; + state->c = 0; +} + +int cobs_decode_incremental(struct cobs_decode_state *state, char *dst, size_t dstlen, char src) { + if (state->p == 0) { + if (src == 0) + goto empty_errout; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */ + state->c = (unsigned char)src; + state->p++; + return 0; + } + + if (!src) { + if (state->c != 1) + goto errout; /* Invalid framing. The skip counter does not hit the end of the frame. */ + int rv = state->p-1; + cobs_decode_incremental_initialize(state); + return rv; + } + + char val; + state->c--; + + if (state->c == 0) { + state->c = (unsigned char)src; + val = 0; + } else { + val = src; + } + + size_t pos = state->p-1; + if (pos >= dstlen) + return -2; /* output buffer too small */ + dst[pos] = val; + state->p++; + return 0; + +errout: + cobs_decode_incremental_initialize(state); + return -1; + +empty_errout: + cobs_decode_incremental_initialize(state); + return -3; +} + +#ifdef VALIDATION +/*@ + @ requires 0 <= d < 256; + @ assigns \nothing; + @*/ +size_t test(char foo, unsigned int d) { + unsigned int c = (unsigned char)foo; + if (c != 0) { + //@ assert c < 256; + //@ assert c >= 0; + //@ assert c != 0; + //@ assert c > 0; + } + if (d != 0) { + //@ assert d >= 0; + //@ assert d != 0; + //@ assert d > 0; + } + return c + d; +} + +#include <__fc_builtin.h> + +void main(void) { + char inbuf[254]; + char cobsbuf[256]; + char outbuf[256]; + + size_t range = Frama_C_interval(0, sizeof(inbuf)); + Frama_C_make_unknown((char *)inbuf, range); + + cobs_encode(cobsbuf, sizeof(cobsbuf), inbuf, sizeof(inbuf)); + cobs_decode(outbuf, sizeof(outbuf), cobsbuf, sizeof(cobsbuf)); + + //@ assert \forall integer i; 0 <= i < sizeof(inbuf) ==> outbuf[i] == inbuf[i]; +} +#endif//VALIDATION + diff --git a/src/lcd.c b/src/lcd.c index 15819e8..72231b6 100644 --- a/src/lcd.c +++ b/src/lcd.c @@ -195,15 +195,12 @@ void lcd_init() { st_lcd.led = true; } -void DMA1_Channel4_5_6_7_IRQHandler() { - int flags = DMA1->ISR; - if (flags & (DMA_ISR_TEIF5 | DMA_ISR_TEIF6 | DMA_ISR_TEIF7)) { - DMA1->IFCR = DMA_IFCR_CTEIF5 | DMA_IFCR_CTEIF6 | DMA_IFCR_CTEIF7; +void lcd_dma_interrupt(uint32_t channel, uint32_t flags) { + if (flags & DMA_ISR_TEIF1) { st_lcd.error = ERR_DMA; } } - void I2C1_IRQHandler() { int flags = I2C1->ISR; if (flags & (I2C_ISR_ARLO | I2C_ISR_BERR)) { diff --git a/src/main.c b/src/main.c index ded7a51..ef59610 100644 --- a/src/main.c +++ b/src/main.c @@ -2,9 +2,11 @@ #include #include +#include #include #include #include +#include #define AFRL(pin, val) ((val) << ((pin)*4)) #define AFRH(pin, val) ((val) << (((pin)-8)*4)) @@ -213,7 +215,7 @@ int main(void) { ; /* Enable peripheral clocks */ - RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_DMAEN; + RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_DMAEN | RCC_AHBENR_CRCEN; RCC->APB2ENR |= RCC_APB2ENR_TIM16EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_SPI1EN | RCC_APB2ENR_ADCEN | RCC_APB2ENR_TIM15EN | RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_TIM17EN; RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | RCC_APB1ENR_SPI2EN | RCC_APB1ENR_USART3EN | RCC_APB1ENR_I2C1EN | RCC_APB1ENR_USBEN | RCC_APB1ENR_CRSEN; RCC->CFGR3 |= RCC_CFGR3_I2C1SW; @@ -323,6 +325,7 @@ int main(void) { lcd_init(); if (st_lcd.has_lcd) { + uart_dma_init(USART3, 2, 2); const char *lines[4] = {"LINE 1 LINE 1 LINE 1", "LINE 2 LINE 2 LINE 2", "LINE 3 LINE 3 LINE 3", @@ -334,6 +337,7 @@ int main(void) { board_config = BCFG_DISPLAY; board_addr = 1; } else { + uart_dma_init(USART3, 7, 1); int bt = bt_inputs(); if (bt&0) { board_config = BCFG_MOTOR; @@ -351,22 +355,14 @@ int main(void) { SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; NVIC_EnableIRQ(SysTick_IRQn); -// int apb2_clock = SystemCoreClock / APB2_PRESC; -// -// TIM15->PSC = apb2_clock / 1000000 * 100 - 1; /* 100us ticks */ -// TIM15->ARR = 1000 - 1; /* 100ms overflow interrupt interval */ -// TIM15->DIER = TIM_DIER_UIE; -// TIM15->CR1 = TIM_CR1_CEN; -// NVIC_EnableIRQ(TIM1_BRK_TIM15_IRQn); -// -// int baudrate = 115200; -// -// USART1->CR1 = USART_CR1_TE | USART_CR1_RE; -// USART1->BRR = (apb2_clock + baudrate/2) / baudrate; -// USART1->CR2 |= USART_CR2_RXINV; //| USART_CR2_TXINV; -// USART1->CR1 |= USART_CR1_UE; -// -// USART1->TDR = 0; /* Kick off transmission */ + /* Either for UART or ADC */ + NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); + NVIC_SetPriority(DMA1_Channel2_3_IRQn, 2<<5); + + /* UART receive interrupt */ + NVIC_EnableIRQ(USART3_4_IRQn); + NVIC_SetPriority(USART3_4_IRQn, 1<<5); + for (size_t i=0; i<10; i++) { st_led.led[i].r = i*10; st_led.led[i].g = i*20; @@ -402,6 +398,83 @@ void SysTick_Handler() { decimal_7seg(1, sys_time_s, 0); } +void handle_usb_packet(void *packet, size_t len) { + switch (*(uint8_t *)packet) { + case USBP_GET_STATUS: { + struct usbp_status_pkt response = { + .packet_type = USBP_GET_STATUS, + .has_adc = st_adc.has_adc, + .has_lcd = st_lcd.has_lcd, + }; + usb_write_response(&response, sizeof(response)); + break; + } + + case USBP_GET_MEASUREMENTS: { + struct usbp_measurement_pkt response = { + .packet_type = USBP_GET_MEASUREMENTS, + }; + usb_write_response(&response, sizeof(response)); + break; + } + } +} + +void uart_handle_user_packet(struct ll_pkt *pkt, size_t len) { +} + +void DMA1_Channel2_3_IRQHandler() { + int flags = DMA1->ISR; + DMA1->IFCR = DMA_IFCR_CGIF2 | DMA_IFCR_CGIF3; + + if (flags & DMA_ISR_FLAGS_CH(2)) { + if (st_adc.has_adc) { + adc_dma_interrupt(2, flags>>DMA_ISR_FLAGS_Pos(2)); + } else { + uart_dma_interrupt(2, flags>>DMA_ISR_FLAGS_Pos(2)); + } + + } + + if (flags & DMA_ISR_FLAGS_CH(3)) { + if (st_adc.has_adc) { + adc_dma_interrupt(3, flags>>DMA_ISR_FLAGS_Pos(3)); + } + } +} + +void DMA1_Channel4_5_6_7_IRQHandler() { + int flags = DMA1->ISR; + DMA1->IFCR = DMA_IFCR_CGIF4 | DMA_IFCR_CGIF5 | DMA_IFCR_CGIF6 | DMA_IFCR_CGIF7; + + if (flags & DMA_ISR_FLAGS_CH(6)) { + if (st_lcd.has_lcd) { + lcd_dma_interrupt(6, flags>>DMA_ISR_FLAGS_Pos(6)); + } + } + + if (flags & DMA_ISR_FLAGS_CH(7)) { + if (st_lcd.has_lcd) { + lcd_dma_interrupt(6, flags>>DMA_ISR_FLAGS_Pos(7)); + } else { + uart_dma_interrupt(7, flags>>DMA_ISR_FLAGS_Pos(7)); + } + } +} + +void DMA1_Channel1_IRQHandler() { + int flags = DMA1->ISR; + if (flags & DMA_ISR_TEIF1) { + adc_dma_interrupt(1, flags>DMA_ISR_FLAGS_Pos(1)); + } +} + +void USART3_4_IRQHandler() { + uint32_t isr = USART3->ISR; + USART3->ICR = isr; + uart_interrupt(isr); +} + void HardFault_Handler() { asm volatile ("bkpt"); } @@ -425,7 +498,15 @@ void *memcpy(void *restrict dest, const void *restrict src, size_t n) void *memmove(void *dest, const void *src, size_t n) { +<<<<<<< HEAD return memcpy(dest, src, n); +======= + unsigned char *d = dest; + const unsigned char *s = src; + + for (; n; n--) *d++ = *s++; + return dest; +>>>>>>> e6841f8 (serial WIP) } void *memset(void *dest, int c, size_t n) diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..b3766ba --- /dev/null +++ b/src/serial.c @@ -0,0 +1,246 @@ +#include +#include +#include + +#include +#include +#include + +volatile struct uart_dma_state uart_st; + +static void uart_schedule_dma(bool retransmit); +static int uart_putc_nonblocking(uint8_t c); +static void uart_dma_reset(void); + + +void uart_dma_reset() { + memset((void *)&uart_st, 0, sizeof(uart_st)); + uart_st.tx_buf.xfr_start = -1; + + for (size_t i=0; iTDR); + DMA1_Channel[uart_st.tx_dma_ch].CCR = (0<CR1 = /* 8-bit -> M1, M0 clear */ + /* OVER8 clear. Use default 16x oversampling */ + /* CMIF clear */ + USART_CR1_MME + /* WAKE clear */ + /* PCE, PS clear */ + | USART_CR1_RXNEIE /* Enable receive interrupt */ + /* other interrupts clear */ + | USART_CR1_TE + | USART_CR1_RE; + /* Set baudrate */ + usart->BRR = 192; /* 250kBd */ + usart->CR3 |= USART_CR3_DMAT; /* TX DMA enable */ + + /* And... go! */ + usart->CR1 |= USART_CR1_UE; +} + +void uart_interrupt(uint32_t isr) { + if (isr & USART_ISR_ORE) { + uart_st.rx_overruns++; + return; + } + + if (isr & USART_ISR_RXNE) { + uint8_t c = uart_st.usart->RDR; + + int rc = cobs_decode_incremental((struct cobs_decode_state *)&uart_st.cobs_state, (char *)&uart_st.rx_buf, sizeof(uart_st.rx_buf), c); + if (rc == 0) /* packet still incomplete */ + return; + + if (rc < 0) { + uart_st.rx_framing_errors++; + return; + } + + /* A complete frame received */ + if (rc < (int)sizeof(struct ll_pkt)) { + uart_st.rx_protocol_errors++; + return; + } + + volatile struct ll_pkt *pkt = (volatile struct ll_pkt *)&uart_st.rx_buf; + + if (pkt->dst != uart_st.addr && pkt->dst != 0xff) { + /* we are not addressed */ + return; + } + + switch (pkt->type) { + case CTRL_PKT_RESET: + uart_dma_reset(); + break; + + case CTRL_PKT_ACK: + if (!(DMA1_Channel[uart_st.tx_dma_ch].CCR & DMA_CCR_EN)) + uart_schedule_dma(false); + break; + + case CTRL_PKT_NACK: + if (!(DMA1_Channel[uart_st.tx_dma_ch].CCR & DMA_CCR_EN)) + uart_schedule_dma(true); + break; + + case CTRL_PKT_PING: { + struct ll_pkt reply = { + .src = uart_st.addr, + .dst = pkt->src, + .pid = pkt->pid, + .type = CTRL_PKT_PONG, + }; + + rc = uart_send_packet_nonblocking(&reply, sizeof(reply)); + if (rc) { + uart_st.error = rc; + } + break; + } + + default: + if (pkt->type < _USER_PKT_START) { + uart_st.rx_protocol_errors++; + } else { + uart_handle_user_packet((struct ll_pkt *)pkt, rc); + } + break; + } + } +} + + +void uart_schedule_dma(bool retransmit) { + volatile struct dma_tx_buf *buf = &uart_st.tx_buf; + + ssize_t xfr_start, xfr_end, xfr_len; + if (buf->wraparound) { + buf->wraparound = false; + xfr_start = 0; + xfr_len = buf->xfr_end; + xfr_end = buf->xfr_end; + + } else if (!retransmit) { + if (buf->packet_end[buf->xfr_next] == -1) + return; /* Nothing to transmit */ + + xfr_start = buf->xfr_end; + xfr_end = buf->packet_end[buf->xfr_next]; + buf->packet_end[buf->xfr_next] = -1; + buf->xfr_next = (buf->xfr_next + 1) % COUNT_OF(buf->packet_end); + + if (xfr_end > xfr_start) { /* no wraparound */ + xfr_len = xfr_end - xfr_start; + + } else { /* wraparound */ + if (xfr_end != 0) + buf->wraparound = true; + xfr_len = sizeof(buf->data) - xfr_start; + } + + } else { + /* retransmit */ + /* First, send a zero to delimit any garbage from the following good packet */ + uart_st.usart->TDR = 0x00; + + xfr_start = buf->xfr_start; + xfr_end = buf->xfr_end; + + if (xfr_end > xfr_start) { /* no wraparound */ + xfr_len = xfr_end - xfr_start; + + } else { /* wraparound */ + if (xfr_end != 0) + buf->wraparound = true; + xfr_len = sizeof(buf->data) - xfr_start; + } + + uart_st.retransmissions++; + } + + buf->xfr_start = xfr_start; + buf->xfr_end = xfr_end; + + /* initiate transmission of new buffer */ + DMA1_Channel[uart_st.tx_dma_ch].CMAR = (uint32_t)(buf->data + xfr_start); + DMA1_Channel[uart_st.tx_dma_ch].CNDTR = xfr_len; + DMA1_Channel[uart_st.tx_dma_ch].CCR |= DMA_CCR_EN; +} + +int uart_putc_nonblocking(uint8_t c) { + volatile struct dma_tx_buf *buf = &uart_st.tx_buf; + + if (buf->wr_pos == buf->xfr_start) + return ERR_BUSY; + + buf->data[buf->wr_pos] = c; + buf->wr_pos = (buf->wr_pos + 1) % sizeof(buf->data); + return 0; +} + + +void uart_dma_interrupt(uint32_t channel, uint32_t flags) { + DMA1_Channel[uart_st.tx_dma_ch].CCR &= ~DMA_CCR_EN; + + if (flags & DMA_ISR_TEIF1) { + uart_st.error = ERR_DMA; + + } else if (flags & DMA_ISR_TCIF1 && channel == uart_st.tx_dma_ch) { + /* Wait for channel to turn off */ + while (DMA1_Channel[uart_st.tx_dma_ch].CCR & DMA_CCR_EN) { + /* do nothing */ + } + + if (uart_st.tx_buf.wraparound) { + uart_schedule_dma(false); + } + } +} + +/* len is the packet length including headers */ +int uart_send_packet_nonblocking(struct ll_pkt *pkt, size_t pkt_len) { + if (uart_st.tx_buf.packet_end[uart_st.tx_buf.wr_idx] != -1) { + /* Find a free slot for this packet */ + uart_st.tx_overruns++; + return ERR_BUSY; + } + + /* make the value this wonky-ass CRC implementation produces match zlib etc. */ + CRC->CR = CRC_CR_REV_OUT | (1<DR = ((uint8_t *)pkt)[i]; + pkt->crc32 = ~CRC->DR; + + int rc = cobs_encode_usart((int (*)(char))uart_putc_nonblocking, (char *)pkt, pkt_len); + if (rc) + return rc; + + uart_st.tx_buf.packet_end[uart_st.tx_buf.wr_idx] = uart_st.tx_buf.wr_pos; + uart_st.tx_buf.wr_idx = (uart_st.tx_buf.wr_idx + 1) % COUNT_OF(uart_st.tx_buf.packet_end); + + uart_st.packet_count++; + + if (!(DMA1_Channel[uart_st.tx_dma_ch].CCR & DMA_CCR_EN)) + uart_schedule_dma(false); + return 0; +} + diff --git a/src/usb_if.c b/src/usb_if.c index 8f78a51..3ee5c9b 100644 --- a/src/usb_if.c +++ b/src/usb_if.c @@ -1,14 +1,11 @@ #include #include -#include -#include #define USB_EP0_SIZE 8 #define USB_CDC_RX_EP 0x01 #define USB_CDC_TX_EP 0x81 -#define USB_CDC_DATA_SIZE 0x40 #define USB_CDC_NTF_EP 0x82 #define USB_CDC_NTF_SIZE 0x08 @@ -145,17 +142,7 @@ static const struct usb_string_descriptor lang_desc = USB_ARRAY_DESC(USB_LAN static const struct usb_string_descriptor manuf_desc_en = USB_STRING_DESC("TU Darmstadt, KOM / emergenCITY"); static const struct usb_string_descriptor prod_desc_en = USB_STRING_DESC("IHSM rotor tester"); -static struct usb_cdc_line_coding cdc_line = { - .dwDTERate = 115200, - .bCharFormat = USB_CDC_1_STOP_BITS, - .bParityType = USB_CDC_NO_PARITY, - .bDataBits = 8, -}; - -usbd_device usb_dev; -uint32_t usb_buf[0x20]; -uint8_t fifo[0x200]; -uint32_t fpos = 0; +struct usb_state st_usb; static usbd_respond usb_getdesc (usbd_ctlreq *req, void **address, uint16_t *length) { const uint8_t dtype = req->wValue >> 8; @@ -182,13 +169,16 @@ static usbd_respond usb_control(usbd_device *dev, usbd_ctlreq *req, usbd_rqc_cal switch (req->bRequest) { case USB_CDC_SET_CONTROL_LINE_STATE: return usbd_ack; + case USB_CDC_SET_LINE_CODING: - memcpy(&cdc_line, req->data, sizeof(cdc_line)); + memcpy(&st_usb.line, req->data, sizeof(st_usb.line)); return usbd_ack; + case USB_CDC_GET_LINE_CODING: - dev->status.data_ptr = &cdc_line; - dev->status.data_count = sizeof(cdc_line); + dev->status.data_ptr = &st_usb.line; + dev->status.data_count = sizeof(st_usb.line); return usbd_ack; + default: return usbd_fail; } @@ -197,21 +187,42 @@ static usbd_respond usb_control(usbd_device *dev, usbd_ctlreq *req, usbd_rqc_cal return usbd_fail; } -static void usb_loopback(usbd_device *dev, uint8_t event, uint8_t ep) { - int _t; - if (fpos <= (sizeof(fifo) - USB_CDC_DATA_SIZE)) { - _t = usbd_ep_read(dev, USB_CDC_RX_EP, &fifo[fpos], USB_CDC_DATA_SIZE); - if (_t > 0) { - fpos += _t; +static void usb_cobs_rx(usbd_device *dev, uint8_t event, uint8_t ep) { + uint8_t buf[USB_CDC_DATA_SIZE]; + int nread = usbd_ep_read(dev, USB_CDC_RX_EP, buf, USB_CDC_DATA_SIZE); + for (int i=0; i 0 && st_usb.cobs_synchronized) { /* Packet complete */ + handle_usb_packet(st_usb.usb_pkbuf, rc); + + } /* else rc == 0 -> packet in progress */ + + if (buf[i] == 0) { + st_usb.cobs_synchronized = true; } } - if (fpos > 0) { - _t = usbd_ep_write(dev, USB_CDC_TX_EP, &fifo[0], (fpos < USB_CDC_DATA_SIZE) ? fpos : USB_CDC_DATA_SIZE); - if (_t > 0) { - memmove(&fifo[0], &fifo[_t], fpos - _t); - fpos -= _t; - } +} + +static int usb_cobs_output(char c) { + if (st_usb.txpos == sizeof(st_usb.txbuf)) { + return ERR_BUFFER_OVERFLOW; } + + st_usb.txbuf[st_usb.txpos++] = c; + return ERR_SUCCESS; +} + +ErrorCode usb_write_response(void *data, size_t len) { + st_usb.txpos = 0; + return cobs_encode_usart(usb_cobs_output, data, len); +} + +static void usb_cobs_tx(usbd_device *dev, uint8_t event, uint8_t ep) { + /* do nothing, we enqueue packets manually in usb_write_response. */ } static usbd_respond usb_setconf (usbd_device *dev, uint8_t cfg) { @@ -224,32 +235,45 @@ static usbd_respond usb_setconf (usbd_device *dev, uint8_t cfg) { usbd_reg_endpoint(dev, USB_CDC_RX_EP, 0); usbd_reg_endpoint(dev, USB_CDC_TX_EP, 0); return usbd_ack; + case 1: /* configuring device */ usbd_ep_config(dev, USB_CDC_RX_EP, USB_EPTYPE_BULK /*| USB_EPTYPE_DBLBUF*/, USB_CDC_DATA_SIZE); usbd_ep_config(dev, USB_CDC_TX_EP, USB_EPTYPE_BULK /*| USB_EPTYPE_DBLBUF*/, USB_CDC_DATA_SIZE); usbd_ep_config(dev, USB_CDC_NTF_EP, USB_EPTYPE_INTERRUPT, USB_CDC_NTF_SIZE); - usbd_reg_endpoint(dev, USB_CDC_RX_EP, usb_loopback); - usbd_reg_endpoint(dev, USB_CDC_TX_EP, usb_loopback); + usbd_reg_endpoint(dev, USB_CDC_RX_EP, usb_cobs_tx); + usbd_reg_endpoint(dev, USB_CDC_TX_EP, usb_cobs_rx); usbd_ep_write(dev, USB_CDC_TX_EP, 0, 0); + + cobs_decode_incremental_initialize(&st_usb.cobs_state); + st_usb.cobs_synchronized = false; + + st_usb.line.dwDTERate = 115200; + st_usb.line.bCharFormat = USB_CDC_1_STOP_BITS; + st_usb.line.bParityType = USB_CDC_NO_PARITY; + st_usb.line.bDataBits = 8; + + st_usb.error = ERR_SUCCESS; + return usbd_ack; + default: return usbd_fail; } } void usb_init() { - usbd_init(&usb_dev, &usbd_hw, USB_EP0_SIZE, usb_buf, sizeof(usb_buf)); - usbd_reg_config(&usb_dev, usb_setconf); - usbd_reg_control(&usb_dev, usb_control); - usbd_reg_descr(&usb_dev, usb_getdesc); - usbd_enable(&usb_dev, true); - usbd_connect(&usb_dev, true); + usbd_init(&st_usb.usb_dev, &usbd_hw, USB_EP0_SIZE, st_usb.usb_buf, sizeof(st_usb.usb_buf)); + usbd_reg_config(&st_usb.usb_dev, usb_setconf); + usbd_reg_control(&st_usb.usb_dev, usb_control); + usbd_reg_descr(&st_usb.usb_dev, usb_getdesc); + usbd_enable(&st_usb.usb_dev, true); + usbd_connect(&st_usb.usb_dev, true); NVIC_EnableIRQ(USB_IRQn); } void USB_IRQHandler() { - usbd_poll(&usb_dev); + usbd_poll(&st_usb.usb_dev); USB->ISTR = 0; }