Split into modules, add error handling
This commit is contained in:
parent
23be368b68
commit
e7532b40a7
14 changed files with 946 additions and 790 deletions
4
Makefile
4
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
|
||||
|
||||
|
|
|
|||
19
include/adc.h
Normal file
19
include/adc.h
Normal 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
94
include/adc_interface.h
Normal 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__ */
|
||||
|
|
@ -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
21
include/lcd.h
Normal 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
60
include/lcd_interface.h
Normal 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
24
include/led.h
Normal 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
2
include/stm32.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/* libusb_stm compatibility file */
|
||||
#include "stm32f072xb.h"
|
||||
8
include/usb_if.h
Normal file
8
include/usb_if.h
Normal 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
189
src/adc.c
Normal 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
214
src/lcd.c
Normal 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
29
src/led.c
Normal 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;
|
||||
}
|
||||
|
||||
806
src/main.c
806
src/main.c
|
|
@ -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
254
src/usb_if.c
Normal 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);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue