From c56e00a0848c418c8110928a5242314ccb4901f0 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Fri, 5 Aug 2016 00:06:11 +0200 Subject: [PATCH] Moved LCD control code to separate module --- src/lcd.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lcd.h | 29 ++++++ src/main.c | 258 ++--------------------------------------------------- 3 files changed, 291 insertions(+), 252 deletions(-) create mode 100644 src/lcd.c create mode 100644 src/lcd.h diff --git a/src/lcd.c b/src/lcd.c new file mode 100644 index 0000000..0aadba3 --- /dev/null +++ b/src/lcd.c @@ -0,0 +1,256 @@ +#include + +#include "lcd.h" + +#define LCD_USE_4BIT_MODE 1 + +#define LCD_PORT GPIOA +#define LCD_E GPIO10 +#define LCD_RW GPIO11 +#define LCD_RS GPIO12 +#define LCD_D4 GPIO4 +#define LCD_D5 GPIO5 +#define LCD_D6 GPIO6 +#define LCD_D7 GPIO7 + +struct LCDCommand { + uint8_t data; + enum LCDRegType reg_type; +}; + +#define LCD_QUEUE_LEN 64 +struct LCDCommand lcd_cmd_buffer[LCD_QUEUE_LEN]; +uint16_t lcd_buf_read_pos, lcd_buf_write_pos; + +uint8_t lcd_init_sequence_idx = 0; + +static inline void busy_wait(uint32_t cycles) +{ + for (uint32_t i = 0; i < cycles; i++) { + __asm__("nop"); + } +} + +#if LCD_USE_4BIT_MODE +/* + * Send 4 bits to the LCD. Only use directly during initialization. + * + * The display samples data on the falling edge of E. + * + * \param data lower nibble contains the data to send + */ +static void lcd_send_4bit(uint8_t data, enum LCDRegType reg_type) +{ + uint8_t gpios2set = 0, gpios2clear = 0; + + // set E high (rising edge has no effect) + gpio_set(LCD_PORT, LCD_E); + + if(reg_type == LCD_REG_CONTROL) { + gpio_clear(LCD_PORT, LCD_RS); + } else { // LCD_REG_DATA + gpio_set(LCD_PORT, LCD_RS); + } + + busy_wait(100); + + // calculate target gpio states + if(data & (1 << 0)) { gpios2set |= LCD_D4; } else { gpios2clear |= LCD_D4; } + if(data & (1 << 1)) { gpios2set |= LCD_D5; } else { gpios2clear |= LCD_D5; } + if(data & (1 << 2)) { gpios2set |= LCD_D6; } else { gpios2clear |= LCD_D6; } + if(data & (1 << 3)) { gpios2set |= LCD_D7; } else { gpios2clear |= LCD_D7; } + + // apply data pins + gpio_set(LCD_PORT, gpios2set); + gpio_clear(LCD_PORT, gpios2clear); + + busy_wait(100); + + // set E low (display samples on falling edge) + gpio_clear(LCD_PORT, LCD_E); + + busy_wait(100); +} +#else /* !LCD_USE_4BIT_MODE */ +static void lcd_send_8bit(uint8_t data, enum LCDRegType reg_type) +{ + uint32_t gpios2set = 0, gpios2clear = 0; + + // set E high (rising edge has no effect) + gpio_set(LCD_PORT, LCD_E); + + if(reg_type == LCD_REG_CONTROL) { + gpio_clear(LCD_PORT, LCD_RS); + } else { // LCD_REG_DATA + gpio_set(LCD_PORT, LCD_RS); + } + + busy_wait(100); + + // calculate target gpio states + if(data & (1 << 0)) { gpios2set |= LCD_D0; } else { gpios2clear |= LCD_D0; } + if(data & (1 << 1)) { gpios2set |= LCD_D1; } else { gpios2clear |= LCD_D1; } + if(data & (1 << 2)) { gpios2set |= LCD_D2; } else { gpios2clear |= LCD_D2; } + if(data & (1 << 3)) { gpios2set |= LCD_D3; } else { gpios2clear |= LCD_D3; } + if(data & (1 << 4)) { gpios2set |= LCD_D4; } else { gpios2clear |= LCD_D4; } + if(data & (1 << 5)) { gpios2set |= LCD_D5; } else { gpios2clear |= LCD_D5; } + if(data & (1 << 6)) { gpios2set |= LCD_D6; } else { gpios2clear |= LCD_D6; } + if(data & (1 << 7)) { gpios2set |= LCD_D7; } else { gpios2clear |= LCD_D7; } + + // apply data pins + gpio_set(LCD_PORT, gpios2set); + gpio_clear(LCD_PORT, gpios2clear); + + busy_wait(100); + + // set E low (display samples on falling edge) + gpio_clear(LCD_PORT, LCD_E); + + busy_wait(100); +} +#endif /* LCD_USE_4BIT_MODE */ + +void lcd_send_init(uint8_t data, enum LCDRegType reg_type) +{ +#if LCD_USE_4BIT_MODE + lcd_send_4bit((data >> 4) & 0x0F, reg_type); +#else + lcd_send_8bit(data, reg_type); +#endif +} + +void lcd_init(void) +{ + lcd_buf_write_pos = 0; + lcd_buf_read_pos = 0; + + lcd_init_sequence_idx = 0; + + // Set up GPIOs. RCC setup must be done externally! + gpio_mode_setup(LCD_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + LCD_E | LCD_RW | LCD_RS | LCD_D4 | LCD_D5 | LCD_D6 | LCD_D7); +} + +void lcd_send(uint8_t data, enum LCDRegType reg_type) +{ +#if LCD_USE_4BIT_MODE + lcd_send_4bit((data >> 4) & 0x0F, reg_type); + lcd_send_4bit((data >> 0) & 0x0F, reg_type); +#else + lcd_send_8bit(data, reg_type); +#endif +} + +/*! + * Send next command from the queue. + */ +int lcd_process(void) +{ + if(lcd_buf_read_pos != lcd_buf_write_pos) { + lcd_send(lcd_cmd_buffer[lcd_buf_read_pos].data, + lcd_cmd_buffer[lcd_buf_read_pos].reg_type); + + lcd_buf_read_pos++; + if(lcd_buf_read_pos >= LCD_QUEUE_LEN) { + lcd_buf_read_pos = 0; + } + + return 0; // success + } else { + return -1; // queue empty + } +} + +int lcd_enqueue(uint8_t data, enum LCDRegType reg_type) +{ + uint16_t tmp_pos = lcd_buf_write_pos + 1; + if(tmp_pos >= LCD_QUEUE_LEN) { + tmp_pos = 0; + } + + if(tmp_pos != lcd_buf_read_pos) { + lcd_cmd_buffer[tmp_pos].data = data; + lcd_cmd_buffer[tmp_pos].reg_type = reg_type; + + lcd_buf_write_pos = tmp_pos; + + return 0; // success + } else { + return -1; // queue full + } +} + +void lcd_send_string(char *data) +{ + while(*data) { + lcd_enqueue((uint8_t)(*data), LCD_REG_DATA); + data++; + } +} + +void lcd_set_cursor_pos(uint8_t line, uint8_t col) +{ + lcd_enqueue(0x80 | (line << 6) | col, LCD_REG_CONTROL); +} + +/* called in a 1-ms cycle */ +int lcd_setup(void) +{ + /* Display INIT: + * Power On + * sleep(15) minimum + * send(0x30, RW = RS = 0) -> command = 0x3X + * sleep(4.1) minimum + * send(0x30, RW = RS = 0) -> command = 0x33 -> 8-bit mode + * sleep(0.1) minimum + * send(0x20, RW = RS = 0) -> command = 0x2X -> 4-bit mode active + * now write upper 4 bits and lower 4 bits afterwards + */ + if(lcd_init_sequence_idx < 38) { + // init sequence + switch(lcd_init_sequence_idx) { + case 19: + lcd_send_init(0x30, LCD_REG_CONTROL); + break; + case 24: + lcd_send_init(0x30, LCD_REG_CONTROL); + break; + case 25: + lcd_send_init(0x30, LCD_REG_CONTROL); + break; +#if LCD_USE_4BIT_MODE + case 26: + lcd_send_init(0x20, LCD_REG_CONTROL); // switch to 4-bit mode + break; +#endif + case 27: +#if LCD_USE_4BIT_MODE + lcd_send(0x28, LCD_REG_CONTROL); // function set: 4-bit mode, 2 lines, 5x7 font +#else + lcd_send(0x38, LCD_REG_CONTROL); // function set: 8-bit mode, 2 lines, 5x7 font +#endif + break; + case 28: + lcd_send(0x08, LCD_REG_CONTROL); // display off + break; + case 30: + lcd_send(0x01, LCD_REG_CONTROL); // display clear + break; + case 32: + lcd_send(0x06, LCD_REG_CONTROL); // entry mode set: increment, no shift + break; + case 34: + lcd_send(0x0C, LCD_REG_CONTROL); // display on, cursor off + break; + case 36: + lcd_send(0x02, LCD_REG_CONTROL); // cursor home, needs 1.6 ms to execute + break; + } + + lcd_init_sequence_idx++; + + return 0; + } else { + return 1; + } +} diff --git a/src/lcd.h b/src/lcd.h new file mode 100644 index 0000000..8264843 --- /dev/null +++ b/src/lcd.h @@ -0,0 +1,29 @@ +#ifndef LCD_H +#define LCD_H + +#include + +enum LCDRegType { + LCD_REG_CONTROL, + LCD_REG_DATA +}; + +void lcd_init(void); + +/*! + * This function should be called from the main loop every millisecond. + * + * \returns 1 if the initialization of the display is complete. + */ +int lcd_setup(void); + +void lcd_send_init(uint8_t data, enum LCDRegType reg_type); +void lcd_send(uint8_t data, enum LCDRegType reg_type); +void lcd_send_string(char *data); + +int lcd_process(void); +int lcd_enqueue(uint8_t data, enum LCDRegType reg_type); + +void lcd_set_cursor_pos(uint8_t line, uint8_t col); + +#endif // LCD_H diff --git a/src/main.c b/src/main.c index b76b8ed..743aad8 100644 --- a/src/main.c +++ b/src/main.c @@ -9,6 +9,7 @@ #include #include +#include "lcd.h" #include "debug.h" volatile int wait_frame = 1; @@ -16,201 +17,9 @@ volatile int wait_frame = 1; #define ADC_NUM_CHANNELS 3 volatile uint16_t adc_values[ADC_NUM_CHANNELS]; -#define LCD_USE_4BIT_MODE 1 - -#define LCD_PORT GPIOA -#define LCD_E GPIO10 -#define LCD_RW GPIO11 -#define LCD_RS GPIO12 -#define LCD_D4 GPIO4 -#define LCD_D5 GPIO5 -#define LCD_D6 GPIO6 -#define LCD_D7 GPIO7 - -enum LCDRegType { - LCD_REG_CONTROL, - LCD_REG_DATA -}; - -struct LCDCommand { - uint8_t data; - enum LCDRegType reg_type; -}; - -#define LCD_QUEUE_LEN 64 -struct LCDCommand lcd_cmd_buffer[LCD_QUEUE_LEN]; -uint16_t lcd_buf_read_pos, lcd_buf_write_pos; - -static inline void busy_wait(uint32_t cycles) -{ - for (uint32_t i = 0; i < cycles; i++) { - __asm__("nop"); - } -} - -#if LCD_USE_4BIT_MODE -/* - * Send 4 bits to the LCD. Only use directly during initialization. - * - * The display samples data on the falling edge of E. - * - * \param data lower nibble contains the data to send - */ -static void lcd_send_4bit(uint8_t data, enum LCDRegType reg_type) -{ - uint8_t gpios2set = 0, gpios2clear = 0; - - // set E high (rising edge has no effect) - gpio_set(LCD_PORT, LCD_E); - - if(reg_type == LCD_REG_CONTROL) { - gpio_clear(LCD_PORT, LCD_RS); - } else { // LCD_REG_DATA - gpio_set(LCD_PORT, LCD_RS); - } - - busy_wait(100); - - // calculate target gpio states - if(data & (1 << 0)) { gpios2set |= LCD_D4; } else { gpios2clear |= LCD_D4; } - if(data & (1 << 1)) { gpios2set |= LCD_D5; } else { gpios2clear |= LCD_D5; } - if(data & (1 << 2)) { gpios2set |= LCD_D6; } else { gpios2clear |= LCD_D6; } - if(data & (1 << 3)) { gpios2set |= LCD_D7; } else { gpios2clear |= LCD_D7; } - - // apply data pins - gpio_set(LCD_PORT, gpios2set); - gpio_clear(LCD_PORT, gpios2clear); - - busy_wait(100); - - // set E low (display samples on falling edge) - gpio_clear(LCD_PORT, LCD_E); - - busy_wait(100); -} -#else /* !LCD_USE_4BIT_MODE */ -static void lcd_send_8bit(uint8_t data, enum LCDRegType reg_type) -{ - uint32_t gpios2set = 0, gpios2clear = 0; - - // set E high (rising edge has no effect) - gpio_set(LCD_PORT, LCD_E); - - if(reg_type == LCD_REG_CONTROL) { - gpio_clear(LCD_PORT, LCD_RS); - } else { // LCD_REG_DATA - gpio_set(LCD_PORT, LCD_RS); - } - - busy_wait(100); - - // calculate target gpio states - if(data & (1 << 0)) { gpios2set |= LCD_D0; } else { gpios2clear |= LCD_D0; } - if(data & (1 << 1)) { gpios2set |= LCD_D1; } else { gpios2clear |= LCD_D1; } - if(data & (1 << 2)) { gpios2set |= LCD_D2; } else { gpios2clear |= LCD_D2; } - if(data & (1 << 3)) { gpios2set |= LCD_D3; } else { gpios2clear |= LCD_D3; } - if(data & (1 << 4)) { gpios2set |= LCD_D4; } else { gpios2clear |= LCD_D4; } - if(data & (1 << 5)) { gpios2set |= LCD_D5; } else { gpios2clear |= LCD_D5; } - if(data & (1 << 6)) { gpios2set |= LCD_D6; } else { gpios2clear |= LCD_D6; } - if(data & (1 << 7)) { gpios2set |= LCD_D7; } else { gpios2clear |= LCD_D7; } - - // apply data pins - gpio_set(LCD_PORT, gpios2set); - gpio_clear(LCD_PORT, gpios2clear); - - busy_wait(100); - - // set E low (display samples on falling edge) - gpio_clear(LCD_PORT, LCD_E); - - busy_wait(100); -} -#endif /* LCD_USE_4BIT_MODE */ - -static void lcd_send_init(uint8_t data, enum LCDRegType reg_type) -{ -#if LCD_USE_4BIT_MODE - lcd_send_4bit((data >> 4) & 0x0F, reg_type); -#else - lcd_send_8bit(data, reg_type); -#endif -} - -static void lcd_init(void) -{ - lcd_buf_write_pos = 0; - lcd_buf_read_pos = 0; -} - -static void lcd_send(uint8_t data, enum LCDRegType reg_type) -{ -#if LCD_USE_4BIT_MODE - lcd_send_4bit((data >> 4) & 0x0F, reg_type); - lcd_send_4bit((data >> 0) & 0x0F, reg_type); -#else - lcd_send_8bit(data, reg_type); -#endif -} - -/*! - * Send next command from the queue. - */ -static int lcd_process(void) -{ - if(lcd_buf_read_pos != lcd_buf_write_pos) { - lcd_send(lcd_cmd_buffer[lcd_buf_read_pos].data, - lcd_cmd_buffer[lcd_buf_read_pos].reg_type); - - lcd_buf_read_pos++; - if(lcd_buf_read_pos >= LCD_QUEUE_LEN) { - lcd_buf_read_pos = 0; - } - - return 0; // success - } else { - return -1; // queue empty - } -} - -static int lcd_enqueue(uint8_t data, enum LCDRegType reg_type) -{ - uint16_t tmp_pos = lcd_buf_write_pos + 1; - if(tmp_pos >= LCD_QUEUE_LEN) { - tmp_pos = 0; - } - - if(tmp_pos != lcd_buf_read_pos) { - lcd_cmd_buffer[tmp_pos].data = data; - lcd_cmd_buffer[tmp_pos].reg_type = reg_type; - - lcd_buf_write_pos = tmp_pos; - - return 0; // success - } else { - return -1; // queue full - } -} - -static void lcd_send_string(char *data) -{ - while(*data) { - lcd_enqueue((uint8_t)(*data), LCD_REG_DATA); - data++; - } -} - -static void lcd_set_cursor_pos(uint8_t line, uint8_t col) -{ - lcd_enqueue(0x80 | (line << 6) | col, LCD_REG_CONTROL); -} - static void init_gpio(void) { - // Display GPIOs - gpio_mode_setup(LCD_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, - LCD_E | LCD_RW | LCD_RS | LCD_D4 | LCD_D5 | LCD_D6 | LCD_D7); - // Set up UART TX on PB6 for debugging gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6); gpio_set_af(GPIOB, GPIO_AF0, GPIO6); @@ -225,10 +34,10 @@ static void init_clock(void) rcc_clock_setup_in_hsi_out_48mhz(); // enable GPIO clocks: - // Port B is needed for debugging + // Port A is needed for the Display and more rcc_periph_clock_enable(RCC_GPIOA); - // Port A is needed for the Display and more + // Port B is needed for debugging rcc_periph_clock_enable(RCC_GPIOB); // enable TIM3 for scheduling @@ -545,62 +354,7 @@ int main(void) sentSomething = 0; } - /* Display INIT: - * Power On - * sleep(15) minimum - * send(0x30, RW = RS = 0) -> command = 0x3X - * sleep(4.1) minimum - * send(0x30, RW = RS = 0) -> command = 0x33 -> 8-bit mode - * sleep(0.1) minimum - * send(0x20, RW = RS = 0) -> command = 0x2X -> 4-bit mode active - * now write upper 4 bits and lower 4 bits afterwards - */ - if(timebase_ms < 50) { - // init sequence - switch(timebase_ms) { - case 19: - lcd_send_init(0x30, LCD_REG_CONTROL); - break; - case 24: - lcd_send_init(0x30, LCD_REG_CONTROL); - break; - case 25: - lcd_send_init(0x30, LCD_REG_CONTROL); - break; -#if LCD_USE_4BIT_MODE - case 26: - lcd_send_init(0x20, LCD_REG_CONTROL); // switch to 4-bit mode - break; -#endif - case 27: -#if LCD_USE_4BIT_MODE - lcd_send(0x28, LCD_REG_CONTROL); // function set: 4-bit mode, 2 lines, 5x7 font -#else - lcd_send(0x38, LCD_REG_CONTROL); // function set: 8-bit mode, 2 lines, 5x7 font -#endif - break; - case 28: - lcd_send(0x08, LCD_REG_CONTROL); // display off - break; - case 30: - lcd_send(0x01, LCD_REG_CONTROL); // display clear - break; - case 32: - lcd_send(0x06, LCD_REG_CONTROL); // entry mode set: increment, no shift - break; - case 34: - lcd_send(0x0C, LCD_REG_CONTROL); // display on, cursor off - break; - case 31: - lcd_send(0x02, LCD_REG_CONTROL); // cursor home, needs 1.6 ms to execute - break; - case 49: - lcd_send_string("L:"); - lcd_set_cursor_pos(1, 0); - lcd_send_string("Hello World!"); - break; - } - } else { + if(lcd_setup()) { lcd_process(); if((timebase_ms % 500) == 0) { @@ -635,11 +389,11 @@ int main(void) if((timebase_ms % 1000) == 10) { cpuload /= 1000; - lcd_set_cursor_pos(0, 2); + lcd_set_cursor_pos(0, 0); fxp_format_int((int32_t)cpuload, number); fxp_right_align(number, msg, 3, '0'); - lcd_send_string("0."); + lcd_send_string("L:0."); lcd_send_string(msg); } }