Split into modules, add error handling

This commit is contained in:
jaseg 2023-05-03 17:51:01 +02:00
parent 23be368b68
commit e7532b40a7
14 changed files with 946 additions and 790 deletions

View file

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

19
include/adc.h Normal file
View file

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

94
include/adc_interface.h Normal file
View file

@ -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<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_256 (1<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_512 (2<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_1024 (3<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_2048 (4<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_4096 (5<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_8192 (6<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_16384 (7<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_PWR_VERY_LOW (0<<ADC_CLOCK_PWR_Pos)
#define ADC_CLOCK_PWR_LOW (1<<ADC_CLOCK_PWR_Pos)
#define ADC_CLOCK_PWR_HIRES (2<<ADC_CLOCK_PWR_Pos)
#define ADC_GAIN1_PGAGAIN1_1 (0<<4)
#define ADC_GAIN1_PGAGAIN1_2 (1<<4)
#define ADC_GAIN1_PGAGAIN1_4 (2<<4)
#define ADC_GAIN1_PGAGAIN1_8 (3<<4)
#define ADC_GAIN1_PGAGAIN1_16 (4<<4)
#define ADC_GAIN1_PGAGAIN1_32 (5<<4)
#define ADC_GAIN1_PGAGAIN1_64 (5<<4)
#define ADC_GAIN1_PGAGAIN1_128 (7<<4)
#define ADC_GAIN1_PGAGAIN0_1 (0<<0)
#define ADC_GAIN1_PGAGAIN0_2 (1<<0)
#define ADC_GAIN1_PGAGAIN0_4 (2<<0)
#define ADC_GAIN1_PGAGAIN0_8 (3<<0)
#define ADC_GAIN1_PGAGAIN0_16 (4<<0)
#define ADC_GAIN1_PGAGAIN0_32 (5<<0)
#define ADC_GAIN1_PGAGAIN0_64 (5<<0)
#define ADC_GAIN1_PGAGAIN0_128 (7<<0)
#define ADC_CFG_GC_DLY_Pos 9
#define ADC_CFG_GC_EN (1<<8)
#define ADC_CFG_CD_ALLCH (1<<7)
#define ADC_CFG_CD_NUM_Pos 4
#define ADC_CFG_CD_LEN_Pos 1
#define ADC_CFG_CD_EN (1<<0)
#define ADC_CHn_CFG_PHASE_Pos 6
#define ADC_CHn_CFG_DCBLK_DIS (1<<2)
#define ADC_CHn_CFG_MUX_Pos 0
#endif /* __ADC_INTERFACE_H__ */

View file

@ -17,4 +17,16 @@
#define APB1_PRESC (1<<(APBPrescTable[(RCC->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__ */

21
include/lcd.h Normal file
View file

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

60
include/lcd_interface.h Normal file
View file

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

24
include/led.h Normal file
View file

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

2
include/stm32.h Normal file
View file

@ -0,0 +1,2 @@
/* libusb_stm compatibility file */
#include "stm32f072xb.h"

8
include/usb_if.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef __USB_IF_H__
#define __USB_IF_H__
#include <global.h>
void usb_init(void);
#endif /* __USB_IF_H__ */

189
src/adc.c Normal file
View file

@ -0,0 +1,189 @@
#include <global.h>
#include <adc.h>
#include <adc_interface.h>
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<<SPI_CR1_BR_Pos) | SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_CPHA;
SPI1->CR2 = (0x7<<SPI_CR2_DS_Pos) | SPI_CR2_FRXTH | SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN; /* 8 bit frames, all DMA all the time */
SPI1->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<<DMA_CCR_MSIZE_Pos) | (2<<DMA_CCR_PSIZE_Pos) | DMA_CCR_TEIE | DMA_CCR_DIR | DMA_CCR_CIRC;
DMA1_Channel1->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<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_TEIE | DMA_CCR_TCIE | DMA_CCR_CIRC;
DMA1_Channel2->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<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TEIE;
DMA1_Channel3->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<<TIM_CR2_MMS_Pos); /* set trigger output to OC2REF */
TIM2->DIER = TIM_DIER_CC3DE;
TIM2->CCMR2 = (0<<TIM_CCMR2_CC3S_Pos) | (7<<TIM_CCMR2_OC3M_Pos);
TIM2->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<<ADC_MODE_WLENGTH_Pos) | ADC_MODE_TIMEOUT);
/* wait for ~DRDY to go high (inactive) */
int i;
const int timeout = 100000;
for (i=0; i<timeout; i++) {
if (GPIOA->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<<GPIO_MODER_MODER2_Pos); /* CS */
DMA1_Channel1->CCR |= DMA_CCR_EN;
DMA1_Channel2->CCR |= DMA_CCR_EN;
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;
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;
}
}
}

214
src/lcd.c Normal file
View file

@ -0,0 +1,214 @@
#include <global.h>
#include <lcd.h>
#include <lcd_interface.h>
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<<I2C_TIMINGR_PRESC_Pos) | (11<<I2C_TIMINGR_SCLDEL_Pos) | (0<<I2C_TIMINGR_SDADEL_Pos) |
(51<<I2C_TIMINGR_SCLH_Pos) | (46<<I2C_TIMINGR_SCLL_Pos);
I2C1->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<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TEIE;
DMA1_Channel6->CPAR = (uint32_t)&(I2C1->TXDR);
DMA1_Channel6->CMAR = (uint32_t)&st_lcd.buf;
/* I2C1 START trigger */
DMA1_Channel7->CCR = (2<<DMA_CCR_MSIZE_Pos) | (2<<DMA_CCR_PSIZE_Pos) | DMA_CCR_DIR | DMA_CCR_TEIE;
DMA1_Channel7->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)<<LCD_D_Pos;
int rc = lcd_pex_set(value | B_LCD_E | flags);
if (rc) {
return rc;
}
rc = lcd_pex_set(value | flags);
if (rc) {
return rc;
}
return ERR_SUCCESS;
}
ErrorCode lcd_command(uint8_t value) {
int rc = lcd_tx_nibble(value>>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;
}
}

29
src/led.c Normal file
View file

@ -0,0 +1,29 @@
#include <global.h>
#include <led.h>
struct led_state st_led;
void led_set_global_brightness(int brightness) {
/* 5-bit brightness value from 0-31 */
for (size_t i=0; i<COUNT_OF(st_led.led); i++) {
st_led.led[i].global = 0xe0 | brightness;
}
}
void led_init() {
memset(&st_led, 0, sizeof(st_led));
led_set_global_brightness(31);
st_led._ones = 0xffffffff;
SPI2->CR1 = (7<<SPI_CR1_BR_Pos) | SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI;
SPI2->CR2 = (0x7<<SPI_CR2_DS_Pos) | SPI_CR2_FRXTH | SPI_CR2_TXDMAEN; /* 16 bit frames, all DMA all the time */
SPI2->CR1 |= SPI_CR1_SPE;
DMA1_Channel5->CCR = (0<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_TEIE | DMA_CCR_DIR | DMA_CCR_CIRC;
DMA1_Channel5->CPAR = (uint32_t)&(SPI2->DR);
DMA1_Channel5->CMAR = (uint32_t)&st_led;
DMA1_Channel5->CNDTR = sizeof(st_led);
DMA1_Channel5->CCR |= DMA_CCR_EN;
}

View file

@ -1,788 +1,10 @@
#include <global.h>
#include <stdarg.h>
#include <usb.h>
#include <usb_cdc.h>
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<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_256 (1<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_512 (2<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_1024 (3<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_2048 (4<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_4096 (5<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_8192 (6<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_OSR_16384 (7<<ADC_CLOCK_OSR_Pos)
#define ADC_CLOCK_PWR_VERY_LOW (0<<ADC_CLOCK_PWR_Pos)
#define ADC_CLOCK_PWR_LOW (1<<ADC_CLOCK_PWR_Pos)
#define ADC_CLOCK_PWR_HIRES (2<<ADC_CLOCK_PWR_Pos)
#define ADC_GAIN1_PGAGAIN1_1 (0<<4)
#define ADC_GAIN1_PGAGAIN1_2 (1<<4)
#define ADC_GAIN1_PGAGAIN1_4 (2<<4)
#define ADC_GAIN1_PGAGAIN1_8 (3<<4)
#define ADC_GAIN1_PGAGAIN1_16 (4<<4)
#define ADC_GAIN1_PGAGAIN1_32 (5<<4)
#define ADC_GAIN1_PGAGAIN1_64 (5<<4)
#define ADC_GAIN1_PGAGAIN1_128 (7<<4)
#define ADC_GAIN1_PGAGAIN0_1 (0<<0)
#define ADC_GAIN1_PGAGAIN0_2 (1<<0)
#define ADC_GAIN1_PGAGAIN0_4 (2<<0)
#define ADC_GAIN1_PGAGAIN0_8 (3<<0)
#define ADC_GAIN1_PGAGAIN0_16 (4<<0)
#define ADC_GAIN1_PGAGAIN0_32 (5<<0)
#define ADC_GAIN1_PGAGAIN0_64 (5<<0)
#define ADC_GAIN1_PGAGAIN0_128 (7<<0)
#define ADC_CFG_GC_DLY_Pos 9
#define ADC_CFG_GC_EN (1<<8)
#define ADC_CFG_CD_ALLCH (1<<7)
#define ADC_CFG_CD_NUM_Pos 4
#define ADC_CFG_CD_LEN_Pos 1
#define ADC_CFG_CD_EN (1<<0)
#define ADC_CHn_CFG_PHASE_Pos 6
#define ADC_CHn_CFG_DCBLK_DIS (1<<2)
#define ADC_CHn_CFG_MUX_Pos 0
void adc_init(void);
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 void 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);
}
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<<SPI_CR1_BR_Pos) | SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_CPHA;
SPI1->CR2 = (0x7<<SPI_CR2_DS_Pos) | SPI_CR2_FRXTH | SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN; /* 8 bit frames, all DMA all the time */
SPI1->CR1 |= SPI_CR1_SPE;
/* CH1 -> DRDY/TIM2-triggered start of conversion */
DMA1_Channel1->CCR = (2<<DMA_CCR_MSIZE_Pos) | (2<<DMA_CCR_PSIZE_Pos) | DMA_CCR_TEIE | DMA_CCR_DIR | DMA_CCR_CIRC;
DMA1_Channel1->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<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_TEIE | DMA_CCR_TCIE | DMA_CCR_CIRC;
DMA1_Channel2->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<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TEIE;
DMA1_Channel3->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<<TIM_CR2_MMS_Pos); /* set trigger output to OC2REF */
TIM2->DIER = TIM_DIER_CC3DE;
TIM2->CCMR2 = (0<<TIM_CCMR2_CC3S_Pos) | (7<<TIM_CCMR2_OC3M_Pos);
TIM2->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<<ADC_MODE_WLENGTH_Pos) | ADC_MODE_TIMEOUT);
while (!(GPIOA->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<<GPIO_MODER_MODER2_Pos); /* CS */
DMA1_Channel1->CCR |= DMA_CCR_EN;
DMA1_Channel2->CCR |= DMA_CCR_EN;
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;
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; i<COUNT_OF(st_led.led); i++) {
st_led.led[i].global = 0xe0 | brightness;
}
}
static void led_init() {
memset(&st_led, 0, sizeof(st_led));
led_set_global_brightness(31);
st_led._ones = 0xffffffff;
SPI2->CR1 = (7<<SPI_CR1_BR_Pos) | SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI;
SPI2->CR2 = (0x7<<SPI_CR2_DS_Pos) | SPI_CR2_FRXTH | SPI_CR2_TXDMAEN; /* 16 bit frames, all DMA all the time */
SPI2->CR1 |= SPI_CR1_SPE;
DMA1_Channel5->CCR = (0<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_TEIE | DMA_CCR_DIR | DMA_CCR_CIRC;
DMA1_Channel5->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<<I2C_TIMINGR_PRESC_Pos) | (11<<I2C_TIMINGR_SCLDEL_Pos) | (0<<I2C_TIMINGR_SDADEL_Pos) |
(51<<I2C_TIMINGR_SCLH_Pos) | (46<<I2C_TIMINGR_SCLL_Pos);
I2C1->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<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TEIE | DMA_CCR_TCIE;
DMA1_Channel6->CPAR = (uint32_t)&(I2C1->TXDR);
DMA1_Channel6->CMAR = (uint32_t)&st_i2c.buf;
/* I2C1 START trigger */
DMA1_Channel7->CCR = (2<<DMA_CCR_MSIZE_Pos) | (2<<DMA_CCR_PSIZE_Pos) | DMA_CCR_DIR | DMA_CCR_TEIE;
DMA1_Channel7->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)<<LCD_D_Pos;
lcd_pex_set(value | B_LCD_E | flags);
lcd_pex_set(value | flags);
}
void delay_us(int duration_us) {
while (duration_us--) {
for (int i=0; i<3; i++) {
asm volatile ("nop");
}
}
}
void lcd_command(uint8_t value) {
lcd_tx_nibble(value>>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 <adc.h>
#include <lcd.h>
#include <led.h>
#include <usb_if.h>
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;

254
src/usb_if.c Normal file
View file

@ -0,0 +1,254 @@
#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
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);
}