serial WIP

This commit is contained in:
jaseg 2023-05-08 23:23:30 +02:00
parent f0291bc8e3
commit ebb9c72384
14 changed files with 783 additions and 75 deletions

View file

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

View file

@ -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__ */

23
include/cobs.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef __COBS_H__
#define __COBS_H__
#include <stdint.h>
#include <unistd.h>
#include <string.h>
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__

View file

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include <assert.h>
#include <string.h>
@ -13,6 +14,10 @@
#include <stm32f0xx.h>
#include <core_cm0.h>
#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;

View file

@ -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__ */

80
include/serial.h Normal file
View file

@ -0,0 +1,80 @@
#ifndef __SERIAL_H__
#define __SERIAL_H__
#include <global.h>
#include <cobs.h>
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__

View file

@ -2,7 +2,28 @@
#define __USB_IF_H__
#include <global.h>
#include <usb_cdc.h>
#include <usb.h>
#include <cobs.h>
#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__ */

23
include/usb_proto.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef __USB_PROTO_H__
#define __USB_PROTO_H__
#include <global.h>
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__ */

View file

@ -142,22 +142,11 @@ void adc_init() {
TIM2->SMCR = TIM_SMCR_ETP | (7<<TIM_SMCR_TS_Pos) | (6<<TIM_SMCR_SMS_Pos);
}
void DMA1_Channel1_IRQHandler() {
int flags = DMA1->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) {

212
src/cobs.c Normal file
View file

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

View file

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

View file

@ -2,9 +2,11 @@
#include <global.h>
#include <adc.h>
#include <serial.h>
#include <lcd.h>
#include <led.h>
#include <usb_if.h>
#include <usb_proto.h>
#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)

246
src/serial.c Normal file
View file

@ -0,0 +1,246 @@
#include <global.h>
#include <serial.h>
#include <cobs.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
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; i<COUNT_OF(uart_st.tx_buf.packet_end); i++)
uart_st.tx_buf.packet_end[i] = -1;
cobs_decode_incremental_initialize((struct cobs_decode_state *)&uart_st.cobs_state);
}
void uart_dma_init(USART_TypeDef *usart, int tx_dma_ch, int addr) {
uart_dma_reset();
uart_st.tx_dma_ch = tx_dma_ch;
uart_st.usart = usart;
uart_st.addr = addr;
/* Configure DMA 1 Channel 2 to handle uart transmission */
DMA1_Channel[uart_st.tx_dma_ch].CPAR = (uint32_t)&(USART1->TDR);
DMA1_Channel[uart_st.tx_dma_ch].CCR = (0<<DMA_CCR_PL_Pos)
| DMA_CCR_DIR
| (0<<DMA_CCR_MSIZE_Pos) /* 8 bit */
| (0<<DMA_CCR_PSIZE_Pos) /* 8 bit */
| DMA_CCR_MINC
| DMA_CCR_TCIE; /* Enable transfer complete interrupt. */
usart->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<<CRC_CR_REV_IN_Pos) | CRC_CR_RESET;
for (size_t i=offsetof(struct ll_pkt, pid); i<pkt_len; i++)
CRC->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;
}

View file

@ -1,14 +1,11 @@
#include <global.h>
#include <usb_if.h>
#include <usb.h>
#include <usb_cdc.h>
#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<nread; i++) {
int rc = cobs_decode_incremental(&st_usb.cobs_state, (char*)st_usb.usb_pkbuf, sizeof(st_usb.usb_pkbuf), buf[i]);
if (rc < 0) { /* Framing error */
st_usb.error = ERR_PROTOCOL;
st_usb.cobs_synchronized = false;
} else if (rc > 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;
}