display works

This commit is contained in:
jaseg 2023-04-28 17:34:00 +02:00
parent 82ed399b4b
commit 30868c5b48

View file

@ -237,6 +237,262 @@ void DMA1_Channel2_3_IRQHandler() {
}
}
static void i2c_init(void);
static void lcd_init(void);
static void i2c_write(uint8_t addr, size_t n);
static void i2c_read(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_char(char d);
static void lcd_write_str(size_t line, char *str);
/* 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[90];
bool blocking;
} st_i2c;
bool lcd_led;
void i2c_init() {
memset(&st_i2c, 0, sizeof(st_i2c));
SYSCFG->CFGR1 |= SYSCFG_CFGR1_I2C1_DMA_RMP;
/* 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_RXDMAEN | I2C_CR1_TXDMAEN | I2C_CR1_ERRIE | I2C_CR1_PE;
/* 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 RX */
DMA1_Channel7->CCR = (0<<DMA_CCR_MSIZE_Pos) | (0<<DMA_CCR_PSIZE_Pos) | DMA_CCR_MINC | DMA_CCR_TEIE | DMA_CCR_TCIE;
DMA1_Channel7->CPAR = (uint32_t)&(I2C1->RXDR);
DMA1_Channel7->CMAR = (uint32_t)&st_i2c.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_char(char d) {
i2c_blocking_mode();
lcd_tx_nibble(d>>4, B_LCD_RS);
lcd_tx_nibble(d, B_LCD_RS);
}
void lcd_write_str(size_t line, char *str) {
i2c_blocking_mode();
uint8_t line_offset[4] = {0, 64, 20, 84};
delay_us(50);
lcd_command(LCD_CMD_DDRAM_ADDR | line_offset[line]);
while (*str) {
lcd_write_char(*str);
str++;
}
}
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 i2c_read(uint8_t addr, size_t n) {
DMA1_Channel7->CCR &= ~DMA_CCR_EN;
while (DMA1_Channel7->CCR & DMA_CCR_EN) {
/* do nothing */
}
DMA1_Channel7->CNDTR = n;
DMA1_Channel7->CCR |= DMA_CCR_EN;
I2C1->CR2 = (n << I2C_CR2_NBYTES_Pos) | I2C_CR2_START | I2C_CR2_RD_WRN | (addr << I2C_CR2_SADD_Pos);
if (st_i2c.blocking) {
while ((!(DMA1->ISR & DMA_ISR_TCIF6)) || (I2C1->ISR & I2C_ISR_BUSY)) {
/* do nothing */
}
}
}
void DMA1_Channel4_5_6_7_IRQHandler() {
int flags = DMA1->ISR;
if (flags & (DMA_ISR_TEIF6 | DMA_ISR_TEIF7)) {
DMA1->IFCR = DMA_IFCR_CTEIF6 | DMA_IFCR_CTEIF7;
asm volatile ("bkpt");
} else if (flags & DMA_ISR_TCIF6) { /* I2C TX complete */
DMA1->IFCR = DMA_IFCR_CTCIF6;
} else if (flags & DMA_ISR_TCIF7) { /* I2C RX complete */
DMA1->IFCR = DMA_IFCR_CTCIF7;
}
}
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");
}
}
int main(void) {
/* Enable HSE w/ 8 MHz crystal */
/* FIXME */
@ -250,7 +506,7 @@ int main(void) {
(4<<RCC_CFGR_PLLMUL_Pos) | /* Set PLL multiplier to 6x, leave divider at 1 */
(1<<RCC_CFGR_PLLSRC_Pos) | /* Select HSI as PLL source */
/* Leave HPRE at 1, not dividing clock. */
(4<<RCC_CFGR_PPRE_Pos); /* Use SYSCLK / 2 for APB bus clock */
(0<<RCC_CFGR_PPRE_Pos); /* Use SYSCLK for APB bus clock */
/* Enable PLL */
RCC->CR |= RCC_CR_PLLON;
@ -266,9 +522,10 @@ int main(void) {
/* Enable peripheral clocks */
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN | RCC_AHBENR_DMAEN;
RCC->APB2ENR |= RCC_APB2ENR_TIM16EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_SPI1EN | RCC_APB2ENR_ADCEN | RCC_APB2ENR_TIM15EN;
RCC->APB2ENR |= RCC_APB2ENR_TIM16EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_SPI1EN | RCC_APB2ENR_ADCEN | RCC_APB2ENR_TIM15EN | RCC_APB2ENR_SYSCFGEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN | RCC_APB1ENR_SPI2EN | RCC_APB1ENR_USART3EN |
RCC_APB1ENR_I2C1EN | RCC_APB1ENR_USBEN;
RCC->CFGR3 |= RCC_CFGR3_I2C1SW;
#define AFRL(pin, val) ((val) << ((pin)*4))
#define AFRH(pin, val) ((val) << (((pin)-8)*4))
@ -365,6 +622,11 @@ int main(void) {
SystemCoreClockUpdate();
adc_init();
lcd_init();
lcd_write_str(0, "LINE 1 LINE 1 LINE 1");
lcd_write_str(1, "LINE 2 LINE 2 LINE 2");
lcd_write_str(2, "LINE 3 LINE 3 LINE 3");
lcd_write_str(3, "LINE 4 LINE 4 LINE 4");
// int apb2_clock = SystemCoreClock / APB2_PRESC;
//
// TIM15->PSC = apb2_clock / 1000000 * 100 - 1; /* 100us ticks */