From e7532b40a746b73dc24dfe8178f7d983fb0eb862 Mon Sep 17 00:00:00 2001 From: jaseg Date: Wed, 3 May 2023 17:51:01 +0200 Subject: [PATCH] Split into modules, add error handling --- Makefile | 4 + include/adc.h | 19 + include/adc_interface.h | 94 +++++ include/global.h | 12 + include/lcd.h | 21 ++ include/lcd_interface.h | 60 +++ include/led.h | 24 ++ include/stm32.h | 2 + include/usb_if.h | 8 + src/adc.c | 189 ++++++++++ src/lcd.c | 214 +++++++++++ src/led.c | 29 ++ src/main.c | 806 +--------------------------------------- src/usb_if.c | 254 +++++++++++++ 14 files changed, 946 insertions(+), 790 deletions(-) create mode 100644 include/adc.h create mode 100644 include/adc_interface.h create mode 100644 include/lcd.h create mode 100644 include/lcd_interface.h create mode 100644 include/led.h create mode 100644 include/stm32.h create mode 100644 include/usb_if.h create mode 100644 src/adc.c create mode 100644 src/lcd.c create mode 100644 src/led.c create mode 100644 src/usb_if.c diff --git a/Makefile b/Makefile index 2412a52..ff1d99d 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,10 @@ DEVICE := STM32F072CB ASM_SOURCES := startup.s C_SOURCES := src/main.c \ + src/adc.c \ + src/lcd.c \ + src/led.c \ + src/usb_if.c \ upstream/libusb_stm32/src/usbd_stm32l052_devfs.c \ upstream/libusb_stm32/src/usbd_core.c diff --git a/include/adc.h b/include/adc.h new file mode 100644 index 0000000..4d1b227 --- /dev/null +++ b/include/adc.h @@ -0,0 +1,19 @@ +#ifndef __ADC_H__ +#define __ADC_H__ + +#include + +struct adc_state { + uint8_t txbuf[12]; + uint8_t rxbuf[12]; + uint32_t dma_ccr3; + int32_t data[2]; + bool has_adc; + ErrorCode error; +}; + +extern struct adc_state st_adc; + +void adc_init(void); + +#endif /* __ADC_H__ */ diff --git a/include/adc_interface.h b/include/adc_interface.h new file mode 100644 index 0000000..bfe1116 --- /dev/null +++ b/include/adc_interface.h @@ -0,0 +1,94 @@ +#ifndef __ADC_INTERFACE_H__ +#define __ADC_INTERFACE_H__ + +enum adc_reg_addr { + ADC_REG_ID = 0x00, + ADC_REG_STATUS = 0x01, + ADC_REG_MODE = 0x02, + ADC_REG_CLOCK = 0x03, + ADC_REG_GAIN = 0x04, + ADC_REG_CFG = 0x06, + ADC_REG_THRSHLD_MSB = 0x07, + ADC_REG_THRSHLD_LSB = 0x08, + ADC_REG_CH0_CFG = 0x09, + ADC_REG_CH0_OCAL_MSB = 0x0a, + ADC_REG_CH0_OCAL_LSB = 0x0b, + ADC_REG_CH0_GCAL_MSB = 0x0c, + ADC_REG_CH0_GCAL_LSB = 0x0d, + ADC_REG_CH1_CFG = 0x0e, + ADC_REG_CH1_OCAL_MSB = 0x0f, + ADC_REG_CH1_OCAL_LSB = 0x10, + ADC_REG_CH1_GCAL_MSB = 0x11, + ADC_REG_CH1_GCAL_LSB = 0x12, + ADC_REG_REGMAP_CRC = 0x3e, +}; + +#define ADC_STATUS_LOCK (1<<15) +#define ADC_STATUS_F_RESYNC (1<<14) +#define ADC_STATUS_REG_MAP (1<<13) +#define ADC_STATUS_CRC_ERR (1<<12) +#define ADC_STATUS_CRC_TYPE (1<<11) +#define ADC_STATUS_RESET (1<<10) +#define ADC_STATUS_WLENGTH_Pos 8 +#define ADC_STATUS_DRDY1 (1<<1) +#define ADC_STATUS_DRDY0 (1<<0) + +#define ADC_MODE_REG_CRC_EN (1<<13) +#define ADC_MODE_RX_CRC_EN (1<<12) +#define ADC_MODE_CRC_TYPE (1<<11) +#define ADC_MODE_RESET (1<<10) +#define ADC_MODE_WLENGTH_Pos 8 +#define ADC_MODE_TIMEOUT (1<<4) +#define ADC_MODE_DRDY_SEL_Pos 2 +#define ADC_MODE_DRDY_HiZ (1<<1) +#define ADC_MODE_DRDY_FMT (1<<0) + +#define ADC_CLOCK_CH1_EN (1<<9) +#define ADC_CLOCK_CH2_EN (1<<8) +#define ADC_CLOCK_TBM (1<<5) +#define ADC_CLOCK_OSR_Pos 2 +#define ADC_CLOCK_PWR_Pos 0 +#define ADC_CLOCK_OSR_64 ADC_CLOCK_TBM +#define ADC_CLOCK_OSR_128 (0<CFGR & RCC_CFGR_PPRE1_Msk) >> RCC_CFGR_PPRE1_Pos])) #define AHB_PRESC (1<<(AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE_Msk) >> RCC_CFGR_HPRE_Pos])) +enum ErrorCode { + ERR_SUCCESS = 0, + ERR_TIMEOUT, + ERR_PHYSICAL_LAYER, + ERR_PROTOCOL, + ERR_DMA, +}; +typedef enum ErrorCode ErrorCode; + + +void delay_us(int duration_us); + #endif /* __GLOBAL_H__ */ diff --git a/include/lcd.h b/include/lcd.h new file mode 100644 index 0000000..f9895f0 --- /dev/null +++ b/include/lcd.h @@ -0,0 +1,21 @@ +#ifndef __LCD_H__ +#define __LCD_H__ + +void lcd_init(void); +void lcd_write_dma(const char **buf); + +#ifndef LCD_PEX_ADDR +#define LCD_PEX_ADDR 0x4E +#endif + +struct lcd_state { + uint8_t buf[512]; + uint32_t cr2_buf; + bool led; + bool has_lcd; + ErrorCode error; +}; + +extern struct lcd_state st_lcd; + +#endif /* __LCD_H__ */ diff --git a/include/lcd_interface.h b/include/lcd_interface.h new file mode 100644 index 0000000..1a6bff6 --- /dev/null +++ b/include/lcd_interface.h @@ -0,0 +1,60 @@ +#ifndef __LCD_INTERFACE_H__ +#define __LCD_INTERFACE_H__ + +/* https://www.lcd-module.de/eng/pdf/zubehoer/ks0066.pdf */ +enum LCD_Command { + LCD_CMD_CLEAR = 0x01, + LCD_CMD_HOME = 0x02, + LCD_CMD_ENTRY_MODE = 0x04, + LCD_CMD_ON_OFF = 0x08, + LCD_CMD_SHIFT = 0x10, + LCD_CMD_FUNCTION = 0x20, + LCD_CMD_CGRAM_ADDR = 0x40, + LCD_CMD_DDRAM_ADDR = 0x80, +}; + +enum LCD_ENTRY_MODE_Cmd { + LCD_ENTRY_CUR_RIGHT = 0x02, + LCD_ENTRY_CUR_LEFT = 0x00, + LCD_ENTRY_DSP_RIGHT = 0x03, + LCD_ENTRY_DSP_LEFT = 0x01, +}; + +enum LCD_ON_OFF_Cmd { + LCD_DSP_ON = 0x04, + LCD_DSP_OFF = 0x00, + LCD_CUR_ON = 0x02, + LCD_CUR_OFF = 0x00, + LCD_BLK_ON = 0x01, + LCD_BLK_OFF = 0x00, +}; + +enum LCD_SHIFT_Cmd { + LCD_SH_CUR_LEFT = 0x00, + LCD_SH_CUR_RIGHT = 0x04, + LCD_SH_DSP_LEFT = 0x08, + LCD_SH_DSP_RIGHT = 0x0C, +}; + +enum LCD_FUNCTION_Cmd { + LCD_FN_4BIT = 0x00, + LCD_FN_8BIT = 0x10, + LCD_FN_1LINE = 0x00, + LCD_FN_2LINE = 0x08, + LCD_FN_5X8 = 0x00, + LCD_FN_5X11 = 0x04, +}; + +#define LCD_ENTER_8BIT 0x3 +#define LCD_ENTER_4BIT 0x2 + +enum LCD_Pins { + B_LCD_RS = (1<<0), + B_LCD_RW = (1<<1), + B_LCD_E = (1<<2), + B_LCD_LED = (1<<3), +}; + +#define LCD_D_Pos 4 + +#endif /* __LCD_INTERFACE_H__ */ diff --git a/include/led.h b/include/led.h new file mode 100644 index 0000000..e1a17ab --- /dev/null +++ b/include/led.h @@ -0,0 +1,24 @@ +#ifndef __LED_H__ +#define __LED_H__ + +#include + +struct apa102_pkt { + uint8_t global; + uint8_t b; /* each channel 8 bit 0-255 */ + uint8_t g; + uint8_t r; +} __attribute__((packed)); + +struct led_state { + uint32_t _zeros; + struct apa102_pkt led[10]; + uint32_t _ones; +} __attribute__((packed)); + +extern struct led_state st_led; + +void led_init(void); +void led_set_global_brightness(int brightness); + +#endif /* __LED_H__ */ diff --git a/include/stm32.h b/include/stm32.h new file mode 100644 index 0000000..1db4903 --- /dev/null +++ b/include/stm32.h @@ -0,0 +1,2 @@ +/* libusb_stm compatibility file */ +#include "stm32f072xb.h" diff --git a/include/usb_if.h b/include/usb_if.h new file mode 100644 index 0000000..9f4c680 --- /dev/null +++ b/include/usb_if.h @@ -0,0 +1,8 @@ +#ifndef __USB_IF_H__ +#define __USB_IF_H__ + +#include + +void usb_init(void); + +#endif /* __USB_IF_H__ */ diff --git a/src/adc.c b/src/adc.c new file mode 100644 index 0000000..3ff77c9 --- /dev/null +++ b/src/adc.c @@ -0,0 +1,189 @@ + +#include +#include +#include + +struct adc_state st_adc; + +static void adc_cs(bool value); +static uint8_t adc_write_byte(uint8_t data); +static uint16_t adc_write_word(uint16_t data); +static void adc_sync_write(int reg_addr, int reg_data); +static int adc_sync_reset(void); +static uint16_t adc_sync_null(void); + + +void adc_cs(bool value) { + if (value) { + GPIOA->BSRR = (1<<2); + } else { + GPIOA->BRR = (1<<2); + } +} + +uint8_t adc_write_byte(uint8_t data) { + *((uint8_t*)&SPI1->DR) = data; + while (!(SPI1->SR & SPI_SR_RXNE)) { + /* do nothing */ + } + return *((uint8_t*)&SPI1->DR); +} + +uint16_t adc_write_word(uint16_t data) { + uint16_t res = 0; + res |= adc_write_byte(data>>8) << 8; + res |= adc_write_byte(data&0xff); + adc_write_byte(0); + return res; +} + +void adc_sync_write(int reg_addr, int reg_data) { + adc_cs(0); + adc_write_word(0x6000 | (reg_addr <<7)); + adc_write_word(reg_data); + adc_write_word(0); + adc_write_word(0); + adc_cs(1); +} + +int adc_sync_reset() { + adc_cs(0); + adc_write_word(0x0011); + adc_write_word(0); + adc_write_word(0); + adc_write_word(0); + + for (int i=0; i<100; i++) { + if (adc_sync_null() == 0xff22) { + adc_cs(1); + return 0; + } + } + + adc_cs(1); + return 1; +} + +uint16_t adc_sync_null() { + uint16_t res = adc_write_word(0); + adc_write_word(0); + adc_write_word(0); + adc_write_word(0); + return res; +} + +void adc_init() { + memset(&st_adc, 0, sizeof(st_adc)); + SPI1->CR1 = (1<CR2 = (0x7<CR1 |= SPI_CR1_SPE; + + int rc = adc_sync_reset(); + if (rc) { + st_adc.has_adc = false; + return; + } + st_adc.has_adc = true; + + /* CH1 -> DRDY/TIM2-triggered start of conversion */ + DMA1_Channel1->CCR = (2<CMAR = (uint32_t)&st_adc.dma_ccr3; + DMA1_Channel1->CPAR = (uint32_t)&(DMA1_Channel3->CCR); + DMA1_Channel1->CNDTR = 1; + /* CH2 -> RX DMA: 8 bit peripheral -> 8 bit memory */ + DMA1_Channel2->CCR = (0<CPAR = (uint32_t)&(SPI1->DR); + DMA1_Channel2->CMAR = (uint32_t)&st_adc.rxbuf; + DMA1_Channel2->CNDTR = 4*3; + /* CH3 -> TX DMA: 8 bit memory -> 8 bit peripheral */ + DMA1_Channel3->CCR = (0<CPAR = (uint32_t)&(SPI1->DR); + DMA1_Channel3->CMAR = (uint32_t)&st_adc.txbuf; + DMA1_Channel3->CNDTR = 4*3; + st_adc.dma_ccr3 = DMA1_Channel3->CCR | DMA_CCR_EN; + + TIM2->CR1 = TIM_CR1_OPM; + TIM2->CR2 = TIM_CR2_CCDS | (5<DIER = TIM_DIER_CC3DE; + TIM2->CCMR2 = (0<CCER = TIM_CCER_CC3E; + TIM2->CCR3 = 8; /* ~CS pulse */ + TIM2->ARR = 32; /* Time to TX DMA request */ + + adc_sync_write(ADC_REG_CLOCK, ADC_CLOCK_CH1_EN | ADC_CLOCK_CH2_EN | ADC_CLOCK_OSR_8192 | ADC_CLOCK_PWR_HIRES); + adc_sync_write(ADC_REG_GAIN, ADC_GAIN1_PGAGAIN1_4 | ADC_GAIN1_PGAGAIN0_4); + adc_sync_write(ADC_REG_MODE, (1<IDR & (1<<0)) { + break; + } + adc_sync_null(); + } + + if (i == timeout) { + st_adc.error = ERR_TIMEOUT; + return; + } + + adc_cs(0); + adc_sync_null(); + adc_cs(1); + + NVIC_EnableIRQ(DMA1_Channel1_IRQn); + NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); + + GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODER2_Msk) | (2<CCR |= DMA_CCR_EN; + DMA1_Channel2->CCR |= DMA_CCR_EN; + 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; + st_adc.error = ERR_DMA; + + } else if (flags & DMA_ISR_TCIF2) { + DMA1->IFCR = DMA_IFCR_CTCIF2; + DMA1_Channel3->CCR &= ~DMA_CCR_EN; + + if ((st_adc.rxbuf[0] & 0x3) != 0x1) { + st_adc.error = ERR_PROTOCOL; + /* Stop timer, stopping automatic ADC acquisition */ + TIM2->SMCR = 0; + + } else { + /* measurements ready */ + int32_t val = (st_adc.rxbuf[3] << 16) | (st_adc.rxbuf[4] << 8) | st_adc.rxbuf[5]; + if (val&0x00800000) { + val |= 0xff000000; + } + st_adc.data[0] = val; + val = (st_adc.rxbuf[6] << 16) | (st_adc.rxbuf[7] << 8) | st_adc.rxbuf[8]; + if (val&0x00800000) { + val |= 0xff000000; + } + st_adc.data[1] = val; + + while (DMA1_Channel3->CCR & DMA_CCR_EN) { + /* do nothing */ + } + DMA1_Channel3->CNDTR = 4*3; + } + } +} + + diff --git a/src/lcd.c b/src/lcd.c new file mode 100644 index 0000000..15819e8 --- /dev/null +++ b/src/lcd.c @@ -0,0 +1,214 @@ + +#include +#include +#include + + +struct lcd_state st_lcd; + + +static void i2c_init(void); +static ErrorCode lcd_tx_nibble(uint8_t value, uint8_t flags); +static ErrorCode lcd_pex_set(uint8_t value); +static ErrorCode lcd_command(uint8_t value); + + +void i2c_init() { + SYSCFG->CFGR1 |= SYSCFG_CFGR1_I2C1_DMA_RMP | SYSCFG_CFGR1_TIM17_DMA_RMP2; + + /* Magic value for 100kHz I2C @ 48MHz CLK. Fell out of STMCubeMX. I love downloading 120MB of software to download + * another 100MB of software, only this time over unsecured HTTP, to generate 3.5 bytes of configuration values + * using a Java(TM) GUI. */ + I2C1->TIMINGR = (0<CR1 = I2C_CR1_ERRIE | I2C_CR1_TXDMAEN | I2C_CR1_PE; + + /* TIM1 for DMA timing */ + TIM17->PSC = 47; + TIM17->ARR = 50; + TIM17->DIER = TIM_DIER_UDE; + TIM17->CR1 = TIM_CR1_CEN; + + /* I2C1 TX */ + DMA1_Channel6->CCR = (0<CPAR = (uint32_t)&(I2C1->TXDR); + DMA1_Channel6->CMAR = (uint32_t)&st_lcd.buf; + + /* I2C1 START trigger */ + DMA1_Channel7->CCR = (2<CPAR = (uint32_t)&(I2C1->CR2); + DMA1_Channel7->CMAR = (uint32_t)&st_lcd.cr2_buf; + + NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn); + NVIC_EnableIRQ(I2C1_IRQn); +} + +ErrorCode lcd_pex_set(uint8_t value) { + if (st_lcd.led) { + value |= B_LCD_LED; + } + + I2C1->TXDR = value; + I2C1->CR2 = (1 << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND | (LCD_PEX_ADDR << I2C_CR2_SADD_Pos) | I2C_CR2_START; + + for (int i=0; i<100000; i++) { + if (!(I2C1->ISR & I2C_ISR_BUSY) && (I2C1->ISR & I2C_ISR_TXE)) { + return ERR_SUCCESS; + } + + if (I2C1->ISR & I2C_ISR_NACKF) { + return ERR_PHYSICAL_LAYER; + } + } + + return ERR_TIMEOUT; +} + +ErrorCode lcd_tx_nibble(uint8_t value, uint8_t flags) { + value = (value&0xf)<>4, 0); + if (rc) { + return rc; + } + + rc = lcd_tx_nibble(value, 0); + if (rc) { + return rc; + } + + return ERR_SUCCESS; +} + +void lcd_write_dma(const char **buf) { + DMA1_Channel6->CCR &= ~DMA_CCR_EN; + DMA1_Channel7->CCR &= ~DMA_CCR_EN; + while ((DMA1_Channel6->CCR & DMA_CCR_EN) | (DMA1_Channel7->CCR & DMA_CCR_EN)) { + /* do nothing */ + } + + uint8_t line_offset[4] = {0, 64, 20, 84}; + uint8_t led = st_lcd.led ? B_LCD_LED : 0; + + uint8_t *p = st_lcd.buf; + for (size_t line=0; line<4; line++) { + uint8_t command = LCD_CMD_DDRAM_ADDR | line_offset[line]; + /* set address */ + *p++ = ((command>>4)<<4) | B_LCD_E | led; + *p++ = ((command>>4)<<4) | led; + *p++ = ((command&0xf)<<4) | B_LCD_E | led; + *p++ = ((command&0xf)<<4) | led; + /* delay */ + *p++ = led; + *p++ = led; + + for (size_t i=0; i<20; i++) { + uint8_t data = buf[line][i]; + *p++ = ((data>>4)<<4) | B_LCD_RS | B_LCD_E | led; + *p++ = ((data>>4)<<4) | B_LCD_RS | led; + *p++ = ((data&0xf)<<4) | B_LCD_RS | B_LCD_E | led; + *p++ = ((data&0xf)<<4) | B_LCD_RS | led; + } + } + *p++ = led; + *p++ = led; + + size_t n = p - st_lcd.buf; + DMA1_Channel6->CNDTR = n; + DMA1_Channel6->CCR |= DMA_CCR_EN; + + st_lcd.cr2_buf = (1 << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND | (LCD_PEX_ADDR << I2C_CR2_SADD_Pos) | I2C_CR2_START; + DMA1_Channel7->CNDTR = n; + DMA1_Channel7->CCR |= DMA_CCR_EN; +} + +void lcd_init() { + memset(&st_lcd, 0, sizeof(st_lcd)); + i2c_init(); + delay_us(40000); + + int rc = lcd_tx_nibble(LCD_ENTER_8BIT, 0); + if (rc) { + st_lcd.error = rc; + return; + } + delay_us(4100); + st_lcd.has_lcd = true; + + rc = lcd_tx_nibble(LCD_ENTER_8BIT, 0); + if (rc) { + st_lcd.error = rc; + return; + } + delay_us(100); + + rc = lcd_tx_nibble(LCD_ENTER_8BIT, 0); + if (rc) { + st_lcd.error = rc; + return; + } + + rc = lcd_tx_nibble(LCD_ENTER_4BIT, 0); + if (rc) { + st_lcd.error = rc; + return; + } + + rc = lcd_command(LCD_CMD_FUNCTION | LCD_FN_4BIT | LCD_FN_2LINE | LCD_FN_5X8); + if (rc) { + st_lcd.error = rc; + return; + } + + rc = lcd_command(LCD_CMD_ON_OFF | LCD_DSP_ON | LCD_CUR_OFF | LCD_BLK_OFF); + if (rc) { + st_lcd.error = rc; + return; + } + + rc = lcd_command(LCD_CMD_CLEAR); + if (rc) { + st_lcd.error = rc; + return; + } + + delay_us(2000); + rc = lcd_command(LCD_CMD_ENTRY_MODE | LCD_ENTRY_CUR_RIGHT); + if (rc) { + st_lcd.error = rc; + return; + } + + 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; + st_lcd.error = ERR_DMA; + } +} + + +void I2C1_IRQHandler() { + int flags = I2C1->ISR; + if (flags & (I2C_ISR_ARLO | I2C_ISR_BERR)) { + I2C1->ICR = I2C_ISR_ARLO | I2C_ISR_BERR; + st_lcd.error = ERR_PHYSICAL_LAYER; + } +} + diff --git a/src/led.c b/src/led.c new file mode 100644 index 0000000..b5ce5e0 --- /dev/null +++ b/src/led.c @@ -0,0 +1,29 @@ + +#include +#include + +struct led_state st_led; + +void led_set_global_brightness(int brightness) { + /* 5-bit brightness value from 0-31 */ + for (size_t i=0; iCR1 = (7<CR2 = (0x7<CR1 |= SPI_CR1_SPE; + + DMA1_Channel5->CCR = (0<CPAR = (uint32_t)&(SPI2->DR); + DMA1_Channel5->CMAR = (uint32_t)&st_led; + DMA1_Channel5->CNDTR = sizeof(st_led); + DMA1_Channel5->CCR |= DMA_CCR_EN; +} + diff --git a/src/main.c b/src/main.c index 24169db..4fbc630 100644 --- a/src/main.c +++ b/src/main.c @@ -1,788 +1,10 @@ #include -#include -#include -#include - -struct adc_state { - uint8_t txbuf[12]; - uint8_t rxbuf[12]; - uint32_t dma_ccr3; - int data[2]; -} st_adc; - -enum adc_reg_addr { - ADC_REG_ID = 0x00, - ADC_REG_STATUS = 0x01, - ADC_REG_MODE = 0x02, - ADC_REG_CLOCK = 0x03, - ADC_REG_GAIN = 0x04, - ADC_REG_CFG = 0x06, - ADC_REG_THRSHLD_MSB = 0x07, - ADC_REG_THRSHLD_LSB = 0x08, - ADC_REG_CH0_CFG = 0x09, - ADC_REG_CH0_OCAL_MSB = 0x0a, - ADC_REG_CH0_OCAL_LSB = 0x0b, - ADC_REG_CH0_GCAL_MSB = 0x0c, - ADC_REG_CH0_GCAL_LSB = 0x0d, - ADC_REG_CH1_CFG = 0x0e, - ADC_REG_CH1_OCAL_MSB = 0x0f, - ADC_REG_CH1_OCAL_LSB = 0x10, - ADC_REG_CH1_GCAL_MSB = 0x11, - ADC_REG_CH1_GCAL_LSB = 0x12, - ADC_REG_REGMAP_CRC = 0x3e, -}; - -#define ADC_STATUS_LOCK (1<<15) -#define ADC_STATUS_F_RESYNC (1<<14) -#define ADC_STATUS_REG_MAP (1<<13) -#define ADC_STATUS_CRC_ERR (1<<12) -#define ADC_STATUS_CRC_TYPE (1<<11) -#define ADC_STATUS_RESET (1<<10) -#define ADC_STATUS_WLENGTH_Pos 8 -#define ADC_STATUS_DRDY1 (1<<1) -#define ADC_STATUS_DRDY0 (1<<0) - -#define ADC_MODE_REG_CRC_EN (1<<13) -#define ADC_MODE_RX_CRC_EN (1<<12) -#define ADC_MODE_CRC_TYPE (1<<11) -#define ADC_MODE_RESET (1<<10) -#define ADC_MODE_WLENGTH_Pos 8 -#define ADC_MODE_TIMEOUT (1<<4) -#define ADC_MODE_DRDY_SEL_Pos 2 -#define ADC_MODE_DRDY_HiZ (1<<1) -#define ADC_MODE_DRDY_FMT (1<<0) - -#define ADC_CLOCK_CH1_EN (1<<9) -#define ADC_CLOCK_CH2_EN (1<<8) -#define ADC_CLOCK_TBM (1<<5) -#define ADC_CLOCK_OSR_Pos 2 -#define ADC_CLOCK_PWR_Pos 0 -#define ADC_CLOCK_OSR_64 ADC_CLOCK_TBM -#define ADC_CLOCK_OSR_128 (0<BSRR = (1<<2); - } else { - GPIOA->BRR = (1<<2); - } -} - -uint8_t adc_write_byte(uint8_t data) { - *((uint8_t*)&SPI1->DR) = data; - while (!(SPI1->SR & SPI_SR_RXNE)) { - /* do nothing */ - } - return *((uint8_t*)&SPI1->DR); -} - -uint16_t adc_write_word(uint16_t data) { - uint16_t res = 0; - res |= adc_write_byte(data>>8) << 8; - res |= adc_write_byte(data&0xff); - adc_write_byte(0); - return res; -} - -void adc_sync_write(int reg_addr, int reg_data) { - adc_cs(0); - adc_write_word(0x6000 | (reg_addr <<7)); - adc_write_word(reg_data); - adc_write_word(0); - adc_write_word(0); - adc_cs(1); -} - -void adc_sync_reset() { - adc_cs(0); - adc_write_word(0x0011); - adc_write_word(0); - adc_write_word(0); - adc_write_word(0); - - for (uint16_t res = 0; res != 0xff22; res = adc_sync_null()) { - /* do nothing */ - } - adc_cs(1); -} - -uint16_t adc_sync_null() { - uint16_t res = adc_write_word(0); - adc_write_word(0); - adc_write_word(0); - adc_write_word(0); - return res; -} - -void adc_init() { - memset(&st_adc, 0, sizeof(st_adc)); - SPI1->CR1 = (1<CR2 = (0x7<CR1 |= SPI_CR1_SPE; - - /* CH1 -> DRDY/TIM2-triggered start of conversion */ - DMA1_Channel1->CCR = (2<CMAR = (uint32_t)&st_adc.dma_ccr3; - DMA1_Channel1->CPAR = (uint32_t)&(DMA1_Channel3->CCR); - DMA1_Channel1->CNDTR = 1; - /* CH2 -> RX DMA: 8 bit peripheral -> 8 bit memory */ - DMA1_Channel2->CCR = (0<CPAR = (uint32_t)&(SPI1->DR); - DMA1_Channel2->CMAR = (uint32_t)&st_adc.rxbuf; - DMA1_Channel2->CNDTR = 4*3; - /* CH3 -> TX DMA: 8 bit memory -> 8 bit peripheral */ - DMA1_Channel3->CCR = (0<CPAR = (uint32_t)&(SPI1->DR); - DMA1_Channel3->CMAR = (uint32_t)&st_adc.txbuf; - DMA1_Channel3->CNDTR = 4*3; - - TIM2->CR1 = TIM_CR1_OPM; - TIM2->CR2 = TIM_CR2_CCDS | (5<DIER = TIM_DIER_CC3DE; - TIM2->CCMR2 = (0<CCER = TIM_CCER_CC3E; - TIM2->CCR3 = 8; /* ~CS pulse */ - TIM2->ARR = 32; /* Time to TX DMA request */ - - st_adc.dma_ccr3 = DMA1_Channel3->CCR | DMA_CCR_EN; - - adc_sync_reset(); - adc_sync_write(ADC_REG_CLOCK, ADC_CLOCK_CH1_EN | ADC_CLOCK_CH2_EN | ADC_CLOCK_OSR_8192 | ADC_CLOCK_PWR_HIRES); - adc_sync_write(ADC_REG_GAIN, ADC_GAIN1_PGAGAIN1_64 | ADC_GAIN1_PGAGAIN0_64); - adc_sync_write(ADC_REG_MODE, (1<IDR & (1<<0))) { /* wait for ~DRDY to go high (inactive) */ - adc_sync_null(); - } - - NVIC_EnableIRQ(DMA1_Channel1_IRQn); - NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); - - GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODER2_Msk) | (2<CCR |= DMA_CCR_EN; - DMA1_Channel2->CCR |= DMA_CCR_EN; - TIM2->SMCR = TIM_SMCR_ETP | (7<ISR; - if (flags & (DMA_ISR_TEIF1)) { - DMA1->IFCR = DMA_IFCR_CTEIF1; - asm volatile ("bkpt"); - } -} - -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; - asm volatile ("bkpt"); - - } else if (flags & DMA_ISR_TCIF2) { - DMA1->IFCR = DMA_IFCR_CTCIF2; - /* measurements ready */ - st_adc.data[0] = (st_adc.rxbuf[4] << 16) | (st_adc.rxbuf[5] << 8) | st_adc.rxbuf[6]; - st_adc.data[1] = (st_adc.rxbuf[7] << 16) | (st_adc.rxbuf[8] << 8) | st_adc.rxbuf[9]; - DMA1_Channel3->CCR &= ~DMA_CCR_EN; - while (DMA1_Channel3->CCR & DMA_CCR_EN) { - /* do nothing */ - } - DMA1_Channel3->CNDTR = 4*3; - } -} - - -struct apa102_pkt { - uint8_t global; - uint8_t b; /* each channel 8 bit 0-255 */ - uint8_t g; - uint8_t r; -} __attribute__((packed)); - -struct { - uint32_t _zeros; - struct apa102_pkt led[10]; - uint32_t _ones; -} __attribute__((packed)) st_led; - - -static void led_init(void); -static void led_set_global_brightness(int brightness); - -static void led_set_global_brightness(int brightness) { - /* 5-bit brightness value from 0-31 */ - for (size_t i=0; iCR1 = (7<CR2 = (0x7<CR1 |= SPI_CR1_SPE; - - DMA1_Channel5->CCR = (0<CPAR = (uint32_t)&(SPI2->DR); - DMA1_Channel5->CMAR = (uint32_t)&st_led; - DMA1_Channel5->CNDTR = sizeof(st_led); - DMA1_Channel5->CCR |= DMA_CCR_EN; -} - - -static void i2c_init(void); -static void lcd_init(void); -static void i2c_write(uint8_t addr, size_t n); -static void i2c_nonblocking_mode(void); -static void i2c_blocking_mode(void); -static void lcd_tx_nibble(uint8_t value, uint8_t flags); -static void lcd_pex_set(uint8_t value); -static void delay_us(int duration_us); -static void lcd_command(uint8_t value); -static void lcd_write_dma(const char **buf); - - -/* https://www.lcd-module.de/eng/pdf/zubehoer/ks0066.pdf */ -enum LCD_Command { - LCD_CMD_CLEAR = 0x01, - LCD_CMD_HOME = 0x02, - LCD_CMD_ENTRY_MODE = 0x04, - LCD_CMD_ON_OFF = 0x08, - LCD_CMD_SHIFT = 0x10, - LCD_CMD_FUNCTION = 0x20, - LCD_CMD_CGRAM_ADDR = 0x40, - LCD_CMD_DDRAM_ADDR = 0x80, -}; - -enum LCD_ENTRY_MODE_Cmd { - LCD_ENTRY_CUR_RIGHT = 0x02, - LCD_ENTRY_CUR_LEFT = 0x00, - LCD_ENTRY_DSP_RIGHT = 0x03, - LCD_ENTRY_DSP_LEFT = 0x01, -}; - -enum LCD_ON_OFF_Cmd { - LCD_DSP_ON = 0x04, - LCD_DSP_OFF = 0x00, - LCD_CUR_ON = 0x02, - LCD_CUR_OFF = 0x00, - LCD_BLK_ON = 0x01, - LCD_BLK_OFF = 0x00, -}; - -enum LCD_SHIFT_Cmd { - LCD_SH_CUR_LEFT = 0x00, - LCD_SH_CUR_RIGHT = 0x04, - LCD_SH_DSP_LEFT = 0x08, - LCD_SH_DSP_RIGHT = 0x0C, -}; - -enum LCD_FUNCTION_Cmd { - LCD_FN_4BIT = 0x00, - LCD_FN_8BIT = 0x10, - LCD_FN_1LINE = 0x00, - LCD_FN_2LINE = 0x08, - LCD_FN_5X8 = 0x00, - LCD_FN_5X11 = 0x04, -}; - -#define LCD_ENTER_4BIT 0x2 - - -struct { - uint8_t buf[512]; - uint32_t cr2_buf; - bool blocking; -} st_i2c; - -bool lcd_led; - -void i2c_init() { - memset(&st_i2c, 0, sizeof(st_i2c)); - SYSCFG->CFGR1 |= SYSCFG_CFGR1_I2C1_DMA_RMP | SYSCFG_CFGR1_TIM17_DMA_RMP2; - - /* Magic value for 100kHz I2C @ 48MHz CLK. Fell out of STMCubeMX. I love downloading 120MB of software to download - * another 100MB of software, only this time over unsecured HTTP, to generate 3.5 bytes of configuration values - * using a Java(TM) GUI. */ - I2C1->TIMINGR = (0<CR1 = I2C_CR1_ERRIE | I2C_CR1_TXDMAEN | I2C_CR1_PE; - - /* TIM1 for DMA timing */ - TIM17->PSC = 47; - TIM17->ARR = 50; - TIM17->DIER = TIM_DIER_UDE; - TIM17->CR1 = TIM_CR1_CEN; - - /* I2C1 TX */ - DMA1_Channel6->CCR = (0<CPAR = (uint32_t)&(I2C1->TXDR); - DMA1_Channel6->CMAR = (uint32_t)&st_i2c.buf; - - /* I2C1 START trigger */ - DMA1_Channel7->CCR = (2<CPAR = (uint32_t)&(I2C1->CR2); - DMA1_Channel7->CMAR = (uint32_t)&st_i2c.cr2_buf; - - NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn); - NVIC_EnableIRQ(I2C1_IRQn); -} - -/* - |P7|P6|P5|P4|P3|P2|P1|P0| - |B7|B6|B5|B4|LED|E|RW|RS| -*/ - -enum LCD_Pins { - B_LCD_RS = (1<<0), - B_LCD_RW = (1<<1), - B_LCD_E = (1<<2), - B_LCD_LED = (1<<3), -}; - -#define LCD_D_Pos 4 - -enum I2C1_Address { - LCD_PEX_ADDR = 0x4E, -}; - -void lcd_pex_set(uint8_t value) { - if (lcd_led) { - value |= B_LCD_LED; - } - - I2C1->TXDR = value; - I2C1->CR2 = (1 << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND | (LCD_PEX_ADDR << I2C_CR2_SADD_Pos) | I2C_CR2_START; - while (I2C1->ISR & I2C_ISR_BUSY || !(I2C1->ISR & I2C_ISR_TXE)) { - /* do nothing. */ - } -} - -void lcd_tx_nibble(uint8_t value, uint8_t flags) { - value = (value&0xf)<>4, 0); - lcd_tx_nibble(value, 0); -} - -void lcd_write_dma(const char **buf) { - DMA1_Channel6->CCR &= ~DMA_CCR_EN; - DMA1_Channel7->CCR &= ~DMA_CCR_EN; - while ((DMA1_Channel6->CCR & DMA_CCR_EN) | (DMA1_Channel7->CCR & DMA_CCR_EN)) { - /* do nothing */ - } - - uint8_t line_offset[4] = {0, 64, 20, 84}; - uint8_t led = lcd_led ? B_LCD_LED : 0; - - uint8_t *p = st_i2c.buf; - for (size_t line=0; line<4; line++) { - uint8_t command = LCD_CMD_DDRAM_ADDR | line_offset[line]; - /* set address */ - *p++ = ((command>>4)<<4) | B_LCD_E | led; - *p++ = ((command>>4)<<4) | led; - *p++ = ((command&0xf)<<4) | B_LCD_E | led; - *p++ = ((command&0xf)<<4) | led; - /* delay */ - *p++ = led; - *p++ = led; - - for (size_t i=0; i<20; i++) { - uint8_t data = buf[line][i]; - *p++ = ((data>>4)<<4) | B_LCD_RS | B_LCD_E | led; - *p++ = ((data>>4)<<4) | B_LCD_RS | led; - *p++ = ((data&0xf)<<4) | B_LCD_RS | B_LCD_E | led; - *p++ = ((data&0xf)<<4) | B_LCD_RS | led; - } - } - *p++ = led; - *p++ = led; - - size_t n = p - st_i2c.buf; - DMA1_Channel6->CNDTR = n; - DMA1_Channel6->CCR |= DMA_CCR_TCIE; - DMA1_Channel6->CCR |= DMA_CCR_EN; - - st_i2c.cr2_buf = (1 << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND | (LCD_PEX_ADDR << I2C_CR2_SADD_Pos) | I2C_CR2_START; - DMA1_Channel7->CNDTR = n; - DMA1_Channel7->CCR |= DMA_CCR_EN; -} - -void lcd_init() { - lcd_led = false; - i2c_init(); - i2c_blocking_mode(); - delay_us(40000); - - lcd_tx_nibble(LCD_ENTER_4BIT, 0); - lcd_command(LCD_CMD_FUNCTION | LCD_FN_4BIT | LCD_FN_2LINE | LCD_FN_5X8); - lcd_command(LCD_CMD_ON_OFF | LCD_DSP_ON | LCD_CUR_OFF | LCD_BLK_OFF); - lcd_command(LCD_CMD_CLEAR); - delay_us(2000); - lcd_command(LCD_CMD_ENTRY_MODE | LCD_ENTRY_CUR_RIGHT); - lcd_led = true; - i2c_nonblocking_mode(); -} - -void i2c_write(uint8_t addr, size_t n) { - DMA1_Channel6->CCR &= ~DMA_CCR_EN; - while (DMA1_Channel6->CCR & DMA_CCR_EN) { - /* do nothing */ - } - - DMA1_Channel6->CNDTR = n; - DMA1_Channel6->CCR |= DMA_CCR_EN; - - I2C1->CR2 = (n << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND | (addr << I2C_CR2_SADD_Pos); - I2C1->CR2 |= I2C_CR2_START; - - if (st_i2c.blocking) { - while ((!(DMA1->ISR & DMA_ISR_TCIF6)) || (I2C1->ISR & I2C_ISR_BUSY)) { - /* do nothing */ - } - } -} - -void i2c_nonblocking_mode() { - DMA1_Channel6->CCR &= ~DMA_CCR_EN; - while (DMA1_Channel6->CCR & DMA_CCR_EN) { - /* do nothing */ - } - DMA1_Channel6->CCR |= DMA_CCR_TCIE; - - st_i2c.blocking = false; -} - -void i2c_blocking_mode() { - DMA1_Channel6->CCR &= ~DMA_CCR_EN; - while (DMA1_Channel6->CCR & DMA_CCR_EN) { - /* do nothing */ - } - DMA1_Channel6->CCR &= ~DMA_CCR_TCIE; - - st_i2c.blocking = 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; - asm volatile ("bkpt"); - - } else if (flags & DMA_ISR_TCIF6) { /* I2C TX complete */ - DMA1->IFCR = DMA_IFCR_CTCIF6; - } -} - - -void I2C1_IRQHandler() { - int flags = I2C1->ISR; - if (flags & (I2C_ISR_ARLO | I2C_ISR_BERR)) { - I2C1->ICR = I2C_ISR_ARLO | I2C_ISR_BERR; - asm volatile ("bkpt"); - } -} - - -#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 - -struct cdc_config { - struct usb_config_descriptor config; - struct usb_iad_descriptor comm_iad; - struct usb_interface_descriptor comm; - struct usb_cdc_header_desc cdc_hdr; - struct usb_cdc_call_mgmt_desc cdc_mgmt; - struct usb_cdc_acm_desc cdc_acm; - struct usb_cdc_union_desc cdc_union; - struct usb_endpoint_descriptor comm_ep; - struct usb_interface_descriptor data; - struct usb_endpoint_descriptor data_eprx; - struct usb_endpoint_descriptor data_eptx; -} __attribute__((packed)); - -static const struct usb_device_descriptor device_desc = { - .bLength = sizeof(struct usb_device_descriptor), - .bDescriptorType = USB_DTYPE_DEVICE, - .bcdUSB = VERSION_BCD(2,0,0), - .bDeviceClass = USB_CLASS_IAD, - .bDeviceSubClass = USB_SUBCLASS_IAD, - .bDeviceProtocol = USB_PROTO_IAD, - .bMaxPacketSize0 = USB_EP0_SIZE, - .idVendor = 0x0483, - .idProduct = 0x5740, - .bcdDevice = VERSION_BCD(1,0,0), - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = INTSERIALNO_DESCRIPTOR, - .bNumConfigurations = 1, -}; - -/* Device configuration descriptor */ -static const struct cdc_config config_desc = { - .config = { - .bLength = sizeof(struct usb_config_descriptor), - .bDescriptorType = USB_DTYPE_CONFIGURATION, - .wTotalLength = sizeof(struct cdc_config), - .bNumInterfaces = 2, - .bConfigurationValue = 1, - .iConfiguration = NO_DESCRIPTOR, - .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, - .bMaxPower = USB_CFG_POWER_MA(100), - }, - .comm_iad = { - .bLength = sizeof(struct usb_iad_descriptor), - .bDescriptorType = USB_DTYPE_INTERFASEASSOC, - .bFirstInterface = 0, - .bInterfaceCount = 2, - .bFunctionClass = USB_CLASS_CDC, - .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, - .bFunctionProtocol = USB_PROTO_NONE, - .iFunction = NO_DESCRIPTOR, - }, - .comm = { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_CDC, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_PROTO_NONE, - .iInterface = NO_DESCRIPTOR, - }, - .cdc_hdr = { - .bFunctionLength = sizeof(struct usb_cdc_header_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_HEADER, - .bcdCDC = VERSION_BCD(1,1,0), - }, - .cdc_mgmt = { - .bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT, - .bmCapabilities = 0, - .bDataInterface = 1, - - }, - .cdc_acm = { - .bFunctionLength = sizeof(struct usb_cdc_acm_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_ACM, - .bmCapabilities = 0, - }, - .cdc_union = { - .bFunctionLength = sizeof(struct usb_cdc_union_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_UNION, - .bMasterInterface0 = 0, - .bSlaveInterface0 = 1, - }, - .comm_ep = { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = USB_CDC_NTF_EP, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = USB_CDC_NTF_SIZE, - .bInterval = 0xFF, - }, - .data = { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = USB_SUBCLASS_NONE, - .bInterfaceProtocol = USB_PROTO_NONE, - .iInterface = NO_DESCRIPTOR, - }, - .data_eprx = { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = USB_CDC_RX_EP, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = USB_CDC_DATA_SIZE, - .bInterval = 0x01, - }, - .data_eptx = { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = USB_CDC_TX_EP, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = USB_CDC_DATA_SIZE, - .bInterval = 0x01, - }, -}; - -static const struct usb_string_descriptor lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); -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; - -static usbd_respond usb_getdesc (usbd_ctlreq *req, void **address, uint16_t *length) { - const uint8_t dtype = req->wValue >> 8; - const uint8_t dnumber = req->wValue & 0xFF; - switch (dtype) { - case USB_DTYPE_DEVICE: *address = &device_desc; *length = device_desc.bLength; return usbd_ack; - case USB_DTYPE_CONFIGURATION: *address = &config_desc; *length = sizeof(config_desc); return usbd_ack; - case USB_DTYPE_STRING: break; - default: return usbd_fail; - } - - switch (dnumber) { - case 0: *address = &lang_desc; *length = lang_desc.bLength; return usbd_ack; - case 1: *address = &manuf_desc_en; *length = manuf_desc_en.bLength; return usbd_ack; - case 2: *address = &prod_desc_en; *length = prod_desc_en.bLength; return usbd_ack; - default: return usbd_fail; - } -} - - -static usbd_respond usb_control(usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback) { - if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS) - && req->wIndex == 0 ) { - 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)); - return usbd_ack; - case USB_CDC_GET_LINE_CODING: - dev->status.data_ptr = &cdc_line; - dev->status.data_count = sizeof(cdc_line); - return usbd_ack; - default: - return usbd_fail; - } - } - - 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; - } - } - 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 usbd_respond usb_setconf (usbd_device *dev, uint8_t cfg) { - switch (cfg) { - case 0: - /* deconfiguring device */ - usbd_ep_deconfig(dev, USB_CDC_NTF_EP); - usbd_ep_deconfig(dev, USB_CDC_TX_EP); - usbd_ep_deconfig(dev, USB_CDC_RX_EP); - 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_ep_write(dev, USB_CDC_TX_EP, 0, 0); - return usbd_ack; - default: - return usbd_fail; - } -} - -void USB_IRQHandler() { - usbd_poll(&usb_dev); -} +#include +#include +#include +#include int main(void) { @@ -823,6 +45,7 @@ int main(void) { 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; + /* Enable USB clock recovery */ CRS->CR = CRS_CR_AUTOTRIMEN | CRS_CR_CEN; #define AFRL(pin, val) ((val) << ((pin)*4)) @@ -920,6 +143,8 @@ int main(void) { */ SystemCoreClockUpdate(); + + led_init(); adc_init(); lcd_init(); const char *lines[4] = {"LINE 1 LINE 1 LINE 1", @@ -927,15 +152,8 @@ int main(void) { "LINE 3 LINE 3 LINE 3", "LINE 4 LINE 4 LINE 4"}; lcd_write_dma(lines); - led_init(); + 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); - NVIC_EnableIRQ(USB_IRQn); - usbd_enable(&usb_dev, true); - usbd_connect(&usb_dev, true); // int apb2_clock = SystemCoreClock / APB2_PRESC; // // TIM15->PSC = apb2_clock / 1000000 * 100 - 1; /* 100us ticks */ @@ -974,6 +192,14 @@ void HardFault_Handler() { asm volatile ("bkpt"); } +void delay_us(int duration_us) { + while (duration_us--) { + for (int i=0; i<3; i++) { + asm volatile ("nop"); + } + } +} + void *memcpy(void *restrict dest, const void *restrict src, size_t n) { unsigned char *d = dest; diff --git a/src/usb_if.c b/src/usb_if.c new file mode 100644 index 0000000..4079951 --- /dev/null +++ b/src/usb_if.c @@ -0,0 +1,254 @@ + +#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 + + +struct cdc_config { + struct usb_config_descriptor config; + struct usb_iad_descriptor comm_iad; + struct usb_interface_descriptor comm; + struct usb_cdc_header_desc cdc_hdr; + struct usb_cdc_call_mgmt_desc cdc_mgmt; + struct usb_cdc_acm_desc cdc_acm; + struct usb_cdc_union_desc cdc_union; + struct usb_endpoint_descriptor comm_ep; + struct usb_interface_descriptor data; + struct usb_endpoint_descriptor data_eprx; + struct usb_endpoint_descriptor data_eptx; +} __attribute__((packed)); + +static const struct usb_device_descriptor device_desc = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2,0,0), + .bDeviceClass = USB_CLASS_IAD, + .bDeviceSubClass = USB_SUBCLASS_IAD, + .bDeviceProtocol = USB_PROTO_IAD, + .bMaxPacketSize0 = USB_EP0_SIZE, + .idVendor = 0x0483, + .idProduct = 0x5740, + .bcdDevice = VERSION_BCD(1,0,0), + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = INTSERIALNO_DESCRIPTOR, + .bNumConfigurations = 1, +}; + +/* Device configuration descriptor */ +static const struct cdc_config config_desc = { + .config = { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct cdc_config), + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .comm_iad = { + .bLength = sizeof(struct usb_iad_descriptor), + .bDescriptorType = USB_DTYPE_INTERFASEASSOC, + .bFirstInterface = 0, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_CDC, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_PROTO_NONE, + .iFunction = NO_DESCRIPTOR, + }, + .comm = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = NO_DESCRIPTOR, + }, + .cdc_hdr = { + .bFunctionLength = sizeof(struct usb_cdc_header_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_HEADER, + .bcdCDC = VERSION_BCD(1,1,0), + }, + .cdc_mgmt = { + .bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT, + .bmCapabilities = 0, + .bDataInterface = 1, + + }, + .cdc_acm = { + .bFunctionLength = sizeof(struct usb_cdc_acm_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_ACM, + .bmCapabilities = 0, + }, + .cdc_union = { + .bFunctionLength = sizeof(struct usb_cdc_union_desc), + .bDescriptorType = USB_DTYPE_CS_INTERFACE, + .bDescriptorSubType = USB_DTYPE_CDC_UNION, + .bMasterInterface0 = 0, + .bSlaveInterface0 = 1, + }, + .comm_ep = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_CDC_NTF_EP, + .bmAttributes = USB_EPTYPE_INTERRUPT, + .wMaxPacketSize = USB_CDC_NTF_SIZE, + .bInterval = 0xFF, + }, + .data = { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = USB_SUBCLASS_NONE, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = NO_DESCRIPTOR, + }, + .data_eprx = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_CDC_RX_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_CDC_DATA_SIZE, + .bInterval = 0x01, + }, + .data_eptx = { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_CDC_TX_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_CDC_DATA_SIZE, + .bInterval = 0x01, + }, +}; + +static const struct usb_string_descriptor lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); +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; + +static usbd_respond usb_getdesc (usbd_ctlreq *req, void **address, uint16_t *length) { + const uint8_t dtype = req->wValue >> 8; + const uint8_t dnumber = req->wValue & 0xFF; + switch (dtype) { + case USB_DTYPE_DEVICE: *address = &device_desc; *length = device_desc.bLength; return usbd_ack; + case USB_DTYPE_CONFIGURATION: *address = &config_desc; *length = sizeof(config_desc); return usbd_ack; + case USB_DTYPE_STRING: break; + default: return usbd_fail; + } + + switch (dnumber) { + case 0: *address = &lang_desc; *length = lang_desc.bLength; return usbd_ack; + case 1: *address = &manuf_desc_en; *length = manuf_desc_en.bLength; return usbd_ack; + case 2: *address = &prod_desc_en; *length = prod_desc_en.bLength; return usbd_ack; + default: return usbd_fail; + } +} + + +static usbd_respond usb_control(usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback) { + if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS) + && req->wIndex == 0 ) { + 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)); + return usbd_ack; + case USB_CDC_GET_LINE_CODING: + dev->status.data_ptr = &cdc_line; + dev->status.data_count = sizeof(cdc_line); + return usbd_ack; + default: + return usbd_fail; + } + } + + 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; + } + } + 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 usbd_respond usb_setconf (usbd_device *dev, uint8_t cfg) { + switch (cfg) { + case 0: + /* deconfiguring device */ + usbd_ep_deconfig(dev, USB_CDC_NTF_EP); + usbd_ep_deconfig(dev, USB_CDC_TX_EP); + usbd_ep_deconfig(dev, USB_CDC_RX_EP); + 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_ep_write(dev, USB_CDC_TX_EP, 0, 0); + 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); + NVIC_EnableIRQ(USB_IRQn); + usbd_enable(&usb_dev, true); + usbd_connect(&usb_dev, true); +} + +void USB_IRQHandler() { + usbd_poll(&usb_dev); +} +