diff --git a/src/main.c b/src/main.c index cdec345..24169db 100644 --- a/src/main.c +++ b/src/main.c @@ -284,13 +284,14 @@ static void led_init() { static void i2c_init(void); static void lcd_init(void); -static void lcd_pex_set_noblock(uint8_t value); +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 lcd_poll(void); static void delay_us(int duration_us); static void lcd_command(uint8_t value); -static void lcd_write(const char **buf); +static void lcd_write_dma(const char **buf); /* https://www.lcd-module.de/eng/pdf/zubehoer/ks0066.pdf */ @@ -342,9 +343,8 @@ enum LCD_FUNCTION_Cmd { struct { uint8_t buf[512]; - size_t bpos; - size_t ndt; - bool run; + uint32_t cr2_buf; + bool blocking; } st_i2c; bool lcd_led; @@ -353,12 +353,31 @@ 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, then tweaked looking at an oscilloscope. 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. */ + /* Magic value for 100kHz I2C @ 48MHz CLK. Fell out of STMCubeMX. I love downloading 120MB of software to download + * another 100MB of software, only this time over unsecured HTTP, to generate 3.5 bytes of configuration values + * using a Java(TM) GUI. */ I2C1->TIMINGR = (0<CR1 = I2C_CR1_PE; + 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<CPAR = (uint32_t)&(I2C1->TXDR); + DMA1_Channel6->CMAR = (uint32_t)&st_i2c.buf; + + /* I2C1 START trigger */ + DMA1_Channel7->CCR = (2<CPAR = (uint32_t)&(I2C1->CR2); + DMA1_Channel7->CMAR = (uint32_t)&st_i2c.cr2_buf; + + NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn); + NVIC_EnableIRQ(I2C1_IRQn); } /* @@ -379,17 +398,13 @@ enum I2C1_Address { LCD_PEX_ADDR = 0x4E, }; -void lcd_pex_set_noblock(uint8_t value) { +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; -} - -void lcd_pex_set(uint8_t value) { - lcd_pex_set_noblock(value); while (I2C1->ISR & I2C_ISR_BUSY || !(I2C1->ISR & I2C_ISR_TXE)) { /* do nothing. */ } @@ -414,29 +429,18 @@ void lcd_command(uint8_t value) { lcd_tx_nibble(value, 0); } -void lcd_poll() { - if (!st_i2c.run) { - return; +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 */ } - if (!(I2C1->ISR & I2C_ISR_BUSY) && (12C1->ISR & I2C_ISR_TXE)) { - I2C1->TXDR = st_i2c.txd[st_i2c.bpos]; - I2C1->CR2 = (1 << I2C_CR2_NBYTES_Pos) | I2C_CR2_AUTOEND | (LCD_PEX_ADDR << I2C_CR2_SADD_Pos) | I2C_CR2_START; - st_i2c.bpos++; - if (st_i2c.bpos == st_i2c.ndt) { - st_i2c.run = false; - st_i2c.bpos = 0; - } - } -} - -void lcd_write(const char **buf) { 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; @@ -458,13 +462,20 @@ void lcd_write(const char **buf) { *p++ = led; *p++ = led; - st_i2c.ndt = p - st_i2c.buf; - st_i2c.bpos = 0; - st_i2c.run = true; + 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); @@ -473,6 +484,47 @@ void lcd_init() { 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() { @@ -486,6 +538,16 @@ void DMA1_Channel4_5_6_7_IRQHandler() { } } + +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 @@ -864,7 +926,7 @@ int main(void) { "LINE 2 LINE 2 LINE 2", "LINE 3 LINE 3 LINE 3", "LINE 4 LINE 4 LINE 4"}; - lcd_write(lines); + lcd_write_dma(lines); led_init(); usbd_init(&usb_dev, &usbd_hw, USB_EP0_SIZE, usb_buf, sizeof(usb_buf)); @@ -903,7 +965,7 @@ int main(void) { st_led.led[i].b += 3; } for (size_t i=0; i<100000; i++) { - lcd_poll(); + asm volatile ("nop"); } } }