540 lines
22 KiB
C
540 lines
22 KiB
C
|
/* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
*/
|
||
|
/** library to communication with Hitacho HD44780 LCD controller
|
||
|
* @file
|
||
|
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||
|
* @date 2019
|
||
|
* @note peripherals used: GPIO @ref lcd_hd44780_gpio
|
||
|
*/
|
||
|
/* standard libraries */
|
||
|
#include <stdint.h> // standard integer types
|
||
|
#include <stdlib.h> // general utilities
|
||
|
#include <stdbool.h> // boolean utilities
|
||
|
|
||
|
/* STM32 (including CM3) libraries */
|
||
|
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||
|
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||
|
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||
|
|
||
|
/* own libraries */
|
||
|
#include "global.h" // common methods
|
||
|
#include "lcd_hd44780.h" // own definitions
|
||
|
|
||
|
/** interface data length: 1 for 8-bit, 0 for 4-bit */
|
||
|
#define LCD_HD44780_INTERFACE_DL 0
|
||
|
/** include busy time waiting in writes
|
||
|
* @note this removes the need to call lcd_hd44780_wait_busy but prevents you to to something else meanwhile, particularly when reading is enabled
|
||
|
*/
|
||
|
#define LCD_HD44780_BUSY_WAIT_INCLUDE 1
|
||
|
/** busy wait time for most short writes (37 us with margin) */
|
||
|
#define LCD_HD44780_BUSY_WAIT_SHORT (37 + 5)
|
||
|
/** busy wait time for some long writes (1520 us, but experience shows it's more)
|
||
|
* @note I have no idea why, but longer times increase the contrast darkness
|
||
|
*/
|
||
|
#define LCD_HD44780_BUSY_WAIT_LONG (1520 + 500)
|
||
|
|
||
|
/* usual HD44780 pinout:
|
||
|
* - 1 GND: ground
|
||
|
* - 2 VCC: 5V (3.3V versions also exist, but a less common)
|
||
|
* - 3 V0 : LCD bias voltage, connect to 10-20k potentiometer (VCC to GND)
|
||
|
* - 4 RS : Register Select (high = data, low = instruction)
|
||
|
* - 5 R/W: Read/Write (high = read, low = write)
|
||
|
* - 6 E : enable (falling edge to latch data, high to output register)
|
||
|
* - 7 DB0: Data Bit 0 (for 8-bit transfer)
|
||
|
* - 8 DB1: Data Bit 1 (for 8-bit transfer)
|
||
|
* - 9 DB2: Data Bit 2 (for 8-bit transfer)
|
||
|
* - 10 DB3: Data Bit 3 (for 8-bit transfer)
|
||
|
* - 11 DB4: Data Bit 4 (for 4-bit transfer)
|
||
|
* - 12 DB5: Data Bit 5 (for 4-bit transfer)
|
||
|
* - 13 DB6: Data Bit 6 (for 4-bit transfer)
|
||
|
* - 14 DB7: Data Bit 7 (for 4-bit transfer)
|
||
|
* - 15 BLA: Backlight Anode
|
||
|
* - 16 BLK: Backlight Cathode
|
||
|
*/
|
||
|
|
||
|
/** @defgroup lcd_hd44780_gpio GPIO used to control the HD44780
|
||
|
* @{
|
||
|
*/
|
||
|
#define LCD_HD44780_GPIO_RS PB9 /**< register select */
|
||
|
#if !LCD_HD44780_WRITE_ONLY
|
||
|
#define LCD_HD44780_GPIO_RW PB10 /**< select read or write */
|
||
|
#endif
|
||
|
#define LCD_HD44780_GPIO_E PB11 /**< start data read/write */
|
||
|
#define LCD_HD44780_GPIO_DB7 PB12 /**< data bit 7 */
|
||
|
#define LCD_HD44780_GPIO_DB6 PB13 /**< data bit 6 */
|
||
|
#define LCD_HD44780_GPIO_DB5 PB14 /**< data bit 5 */
|
||
|
#define LCD_HD44780_GPIO_DB4 PB15 /**< data bit 4 */
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
#define LCD_HD44780_GPIO_DB3 P /**< data bit 3 */
|
||
|
#define LCD_HD44780_GPIO_DB2 P /**< data bit 2 */
|
||
|
#define LCD_HD44780_GPIO_DB1 P /**< data bit 1 */
|
||
|
#define LCD_HD44780_GPIO_DB0 P /**< data bit 0 */
|
||
|
#endif
|
||
|
/** @} */
|
||
|
|
||
|
/** direction of data lines */
|
||
|
static bool lcd_hd44780_db_input = true;
|
||
|
|
||
|
/** if the display is configured having 2 lines */
|
||
|
static bool lcd_hd44780_n_2lines = true;
|
||
|
|
||
|
/** switch direction of data bits
|
||
|
* @param[in] input true to switch direction to input, false to output
|
||
|
*/
|
||
|
static void lcd_hd44780_db_direction(bool input)
|
||
|
{
|
||
|
if (lcd_hd44780_db_input == input) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (input) { // configure GPIO to input to read data
|
||
|
#if LCD_HD44780_WRITE_ONLY
|
||
|
return; // we can't read
|
||
|
#endif
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB7));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB6));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB5));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB4));
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB3));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB2));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB1));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB0));
|
||
|
#endif
|
||
|
#if !LCD_HD44780_WRITE_ONLY
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_PIN(LCD_HD44780_GPIO_RW)); // set high to read
|
||
|
#endif
|
||
|
} else { // configure GPIO to output to write data
|
||
|
#if !LCD_HD44780_WRITE_ONLY
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_PIN(LCD_HD44780_GPIO_RW)); // set low write
|
||
|
#endif
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_DB7));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_DB6));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_DB5));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_DB4));
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_DB3));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_DB2));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_DB1));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_DB0));
|
||
|
#endif
|
||
|
}
|
||
|
lcd_hd44780_db_input = input; // remember direction
|
||
|
}
|
||
|
|
||
|
/** write to controller
|
||
|
* @param[in] command true if it is an command, false if it is data
|
||
|
* @param[in] data data bit to write
|
||
|
*/
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
static void lcd_hd44780_write(bool command, uint8_t data)
|
||
|
#else
|
||
|
static void lcd_hd44780_write(bool command, uint8_t data, bool first_nibble_only)
|
||
|
#endif
|
||
|
{
|
||
|
lcd_hd44780_db_direction(false); // switch DB direction to output to write data
|
||
|
if (command) {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set low to write command
|
||
|
} else {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set high to write data
|
||
|
}
|
||
|
// no need to wait tAS = 40 ns before next step since the instructions are slower
|
||
|
|
||
|
// write data
|
||
|
if (data & 0x80) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7));
|
||
|
}
|
||
|
if (data & 0x40) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6));
|
||
|
}
|
||
|
if (data & 0x20) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5));
|
||
|
}
|
||
|
if (data & 0x10) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4));
|
||
|
}
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
if (data & 0x08) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3));
|
||
|
}
|
||
|
if (data & 0x04) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2));
|
||
|
}
|
||
|
if (data & 0x02) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1));
|
||
|
}
|
||
|
if (data & 0x01) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0));
|
||
|
}
|
||
|
#else
|
||
|
if (!first_nibble_only) { // write second nibble
|
||
|
// pulse E to send data
|
||
|
sleep_us(1); // wait t_cycE = 500 ns
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set high change output data
|
||
|
sleep_us(1); // wait PW_EH = 230 ns
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set low to latch data
|
||
|
// no need to wait t_H = 10 ns before next step since next instructions are slower
|
||
|
|
||
|
// send second nibble
|
||
|
if (data & 0x08) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7));
|
||
|
}
|
||
|
if (data & 0x04) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6));
|
||
|
}
|
||
|
if (data & 0x02) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5));
|
||
|
}
|
||
|
if (data & 0x01) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4));
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4));
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
// pulse E to send data
|
||
|
sleep_us(1); // wait t_cycE = 500 ns
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set high change output data
|
||
|
sleep_us(1); // wait PW_EH = 230 ns
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set low to latch data
|
||
|
// no need to wait t_H = 10 ns before next step since next instructions are slower
|
||
|
// no need to wait t_AH = 10 ns before next step since next instructions are slower
|
||
|
}
|
||
|
|
||
|
/** write function set command
|
||
|
* @param[in] dl_8bit 8-bit (true) or 4-bit (false) data length
|
||
|
* @param[in] n_2lines 2 (true) or 1 (false) lines
|
||
|
* @param[in] f_5x10 5x10 (true) or 5x8 (false) dots font
|
||
|
*/
|
||
|
static void lcd_hd44780_function_set(bool dl_8bit, bool n_2lines, bool f_5x10)
|
||
|
{
|
||
|
uint8_t data = 0x20;
|
||
|
if (dl_8bit) {
|
||
|
data |= 0x10;
|
||
|
}
|
||
|
if (n_2lines) {
|
||
|
data |= 0x08;
|
||
|
}
|
||
|
if (f_5x10) {
|
||
|
data |= 0x04;
|
||
|
}
|
||
|
lcd_hd44780_write(true, data, false);
|
||
|
#if LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_setup(bool n_2lines, bool f_5x10)
|
||
|
{
|
||
|
// enable all GPIO
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB7)); // enable clock for GPIO port
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB7)); // set as input to read
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB6)); // enable clock for GPIO port
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB6)); // set as input to read
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB5)); // enable clock for GPIO port
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB5)); // set as input to read
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB4)); // enable clock for GPIO port
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB4)); // set as input to read
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB3)); // enable clock for GPIO port
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB3)); // set as input to read
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB2)); // enable clock for GPIO port
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB2)); // set as input to read
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB1)); // enable clock for GPIO port
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB1)); // set as input to read
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_DB0)); // enable clock for GPIO port
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB0)); // set as input to read
|
||
|
#endif
|
||
|
lcd_hd44780_db_input = true; // all lines are inputs
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_RS)); // enable clock for GPIO port
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set low to read busy flag
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_RS)); // set GPIO as output
|
||
|
#if !LCD_HD44780_WRITE_ONLY
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_RW)); // enable clock for GPIO port
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_PIN(LCD_HD44780_GPIO_RW)); // set high to read
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_RW)); // set GPIO as output
|
||
|
#endif
|
||
|
rcc_periph_clock_enable(GPIO_RCC(LCD_HD44780_GPIO_E)); // enable clock for GPIO port
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // start idle low
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO_PIN(LCD_HD44780_GPIO_E)); // set GPIO as output
|
||
|
// initialize device
|
||
|
sleep_ms(40 + 2); // wait for display to initialise after power on: 15 ms for VCC > 4.5 V, 40 ms for VCC > 2.7 V
|
||
|
lcd_hd44780_write(true, 0x30, true); // 1st function write set to go to state 1 (8-bit) or 2 (4-bit first nibble) (BF cannot be checked)
|
||
|
sleep_ms(4 + 1); // wait 4.1 ms
|
||
|
lcd_hd44780_write(true, 0x30, true); // 2st function write set to go to state 1 (8-bit) or 3 (4-bit second nibble) (BF cannot be checked)
|
||
|
sleep_us(100 + 10); // wait 100 us
|
||
|
lcd_hd44780_write(true, 0x30, true); // 3rd function write set to go to state 1 (8-bit) (BF cannot be checked)
|
||
|
sleep_us(LCD_HD44780_BUSY_WAIT_SHORT); // wait 37 us
|
||
|
#if !LCD_HD44780_INTERFACE_DL
|
||
|
lcd_hd44780_write(true, 0x20, true); // switch to 4-bit mode
|
||
|
sleep_us(LCD_HD44780_BUSY_WAIT_SHORT); // wait 37 us
|
||
|
#endif
|
||
|
lcd_hd44780_function_set(LCD_HD44780_INTERFACE_DL, n_2lines, f_5x10); // function set
|
||
|
lcd_hd44780_n_2lines = n_2lines; // remember number of lines
|
||
|
#if !LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
||
|
#endif
|
||
|
lcd_hd44780_display_control(false, false, false); // display off
|
||
|
#if !LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
||
|
#endif
|
||
|
lcd_hd44780_clear_display(); // display clear
|
||
|
#if !LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_LONG);
|
||
|
#endif
|
||
|
lcd_hd44780_entry_mode_set(true, false); // entry mode set
|
||
|
#if !LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_release(void)
|
||
|
{
|
||
|
// switch back GPIO back to input
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB7));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB6));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB5));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB4));
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB3));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB2));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB1));
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_DB0));
|
||
|
#endif
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_RS));
|
||
|
#if !LCD_HD44780_WRITE_ONLY
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_RW), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_RW));
|
||
|
#endif
|
||
|
gpio_set_mode(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN(LCD_HD44780_GPIO_E));
|
||
|
// don't disable clock for GPIO domain since other pins might be used by others
|
||
|
}
|
||
|
|
||
|
#if !LCD_HD44780_WRITE_ONLY
|
||
|
/** read from controller
|
||
|
* @param[in] data read data (true) or busy flag and address counter (false)
|
||
|
* @return data/AC read
|
||
|
*/
|
||
|
static uint8_t lcd_hd44780_read(bool data)
|
||
|
{
|
||
|
lcd_hd44780_db_direction(true); // switch DB direction to input to read data
|
||
|
if (data) {
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set high to read data
|
||
|
} else {
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_RS), GPIO_PIN(LCD_HD44780_GPIO_RS)); // set low to read busy flag and AC
|
||
|
}
|
||
|
// no need to wait t_AS = 40 ns before next step since the instructions are slower
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // set high to have data output
|
||
|
sleep_us(0); // wait t_DDR = 160 ns before reading
|
||
|
// read data bits
|
||
|
uint8_t input = 0; // to store read data
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7))) {
|
||
|
input |= 0x80;
|
||
|
}
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) {
|
||
|
input |= 0x40;
|
||
|
}
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB5), GPIO_PIN(LCD_HD44780_GPIO_DB5))) {
|
||
|
input |= 0x20;
|
||
|
}
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB4), GPIO_PIN(LCD_HD44780_GPIO_DB4))) {
|
||
|
input |= 0x10;
|
||
|
}
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB3), GPIO_PIN(LCD_HD44780_GPIO_DB3))) {
|
||
|
input |= 0x08;
|
||
|
}
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB2), GPIO_PIN(LCD_HD44780_GPIO_DB2))) {
|
||
|
input |= 0x04;
|
||
|
}
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB1), GPIO_PIN(LCD_HD44780_GPIO_DB1))) {
|
||
|
input |= 0x02;
|
||
|
}
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB0), GPIO_PIN(LCD_HD44780_GPIO_DB0))) {
|
||
|
input |= 0x01;
|
||
|
}
|
||
|
#else
|
||
|
// get second nibble
|
||
|
// don't wait PW_EH = 230 ns since reading took longer
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // end current data read
|
||
|
sleep_us(1); // wait t_cycE = 500 ns
|
||
|
gpio_set(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // have next data output
|
||
|
sleep_us(0); // wait t_DDR = 160 ns before reading
|
||
|
// read second nibble
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB7), GPIO_PIN(LCD_HD44780_GPIO_DB7))) {
|
||
|
input |= 0x08;
|
||
|
}
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) {
|
||
|
input |= 0x04;
|
||
|
}
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) {
|
||
|
input |= 0x02;
|
||
|
}
|
||
|
if (gpio_get(GPIO_PORT(LCD_HD44780_GPIO_DB6), GPIO_PIN(LCD_HD44780_GPIO_DB6))) {
|
||
|
input |= 0x01;
|
||
|
}
|
||
|
#endif
|
||
|
// don't wait PW_EH = 230 ns since reading took longer
|
||
|
gpio_clear(GPIO_PORT(LCD_HD44780_GPIO_E), GPIO_PIN(LCD_HD44780_GPIO_E)); // end data read
|
||
|
|
||
|
return input;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
uint8_t lcd_hd44780_read_data(void)
|
||
|
{
|
||
|
return lcd_hd44780_read(true);
|
||
|
}
|
||
|
|
||
|
#if LCD_HD44780_WRITE_ONLY
|
||
|
uint8_t lcd_hd44780_wait_busy(uint16_t timeout)
|
||
|
{
|
||
|
sleep_us(timeout); // just wait
|
||
|
return 0;
|
||
|
}
|
||
|
#else
|
||
|
uint8_t lcd_hd44780_wait_busy(uint16_t timeout)
|
||
|
{
|
||
|
uint8_t ac = 0x80; // address counter to return
|
||
|
while (timeout && ((ac = lcd_hd44780_read(false)) >= 0x80)) { // wait until busy flag is low or timeout
|
||
|
// wait a bit
|
||
|
sleep_us(10);
|
||
|
if (timeout > 10) {
|
||
|
timeout -= 10;
|
||
|
} else {
|
||
|
timeout = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ac;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
void lcd_hd44780_write_data(uint8_t data)
|
||
|
{
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
lcd_hd44780_write(false, data);
|
||
|
#else
|
||
|
lcd_hd44780_write(false, data, false);
|
||
|
#endif
|
||
|
#if LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_write_command(uint8_t data)
|
||
|
{
|
||
|
#if LCD_HD44780_INTERFACE_DL
|
||
|
lcd_hd44780_write(true, data);
|
||
|
#else
|
||
|
lcd_hd44780_write(true, data, false);
|
||
|
#endif
|
||
|
#if LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_clear_display(void)
|
||
|
{
|
||
|
lcd_hd44780_write_command(0x01);
|
||
|
#if LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_LONG);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_return_home(void)
|
||
|
{
|
||
|
lcd_hd44780_write_command(0x02);
|
||
|
#if LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_LONG);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_entry_mode_set(bool increment, bool shift)
|
||
|
{
|
||
|
uint8_t command = 0x04;
|
||
|
if (increment) {
|
||
|
command |= 0x02;
|
||
|
}
|
||
|
if (shift) {
|
||
|
command |= 0x01;
|
||
|
}
|
||
|
lcd_hd44780_write_command(command);
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_display_control(bool d, bool c, bool b)
|
||
|
{
|
||
|
uint8_t command = 0x08;
|
||
|
if (d) {
|
||
|
command |= 0x04;
|
||
|
}
|
||
|
if (c) {
|
||
|
command |= 0x02;
|
||
|
}
|
||
|
if (b) {
|
||
|
command |= 0x01;
|
||
|
}
|
||
|
lcd_hd44780_write_command(command);
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_set_cgram_address(uint8_t acg)
|
||
|
{
|
||
|
lcd_hd44780_write_command(0x40 | (acg & 0x3f));
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_set_ddram_address(uint8_t add)
|
||
|
{
|
||
|
lcd_hd44780_write_command(0x80 | (add & 0x7f));
|
||
|
}
|
||
|
|
||
|
void lcd_hd44780_write_line(bool line2, const char* data, uint8_t length)
|
||
|
{
|
||
|
if (line2 && !lcd_hd44780_n_2lines) { // writing line 2 when it does not exists is not possible
|
||
|
return;
|
||
|
}
|
||
|
lcd_hd44780_set_ddram_address(line2 ? 0x40 : 0x00); // set start of line
|
||
|
#if !LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
||
|
#endif
|
||
|
for (uint8_t i = 0; i < length && i < (lcd_hd44780_n_2lines ? 0x27 : 0x4f); i++) { // write line
|
||
|
lcd_hd44780_write_data(data[i]); // write character
|
||
|
#if !LCD_HD44780_BUSY_WAIT_INCLUDE
|
||
|
lcd_hd44780_wait_busy(LCD_HD44780_BUSY_WAIT_SHORT);
|
||
|
#endif
|
||
|
}
|
||
|
}
|