├── _config.yml ├── docs ├── _config.yml ├── README.md └── README.RU.md ├── .gitignore ├── README.md ├── LICENSE ├── Inc └── lcd_hd44780_i2c.h └── Src └── lcd_hd44780_i2c.c /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STM32 HAL Libriary for LCD Based on Hitachi HD44780 2 | ## What is it for? 3 | I wrote this libriary as I'm sick and tired of other libs based on Arduino lib [LiquidCrystal_I2C](https://github.com/marcoschwartz/LiquidCrystal_I2C). This one is for STM32 but can be adopted for every chip using C99. 4 | 5 | Anyway it is written for I2C expander based on [PCF8574](https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf?pspll=1). 6 | 7 | ## Why is it better? 8 | There are several reasons: 9 | 1. This lib **does not** use any hardware delays. 10 | 2. This lib uses DMA to send data to display 11 | 3. This lib uses some tricks using I2C timings 12 | 4. This lib is written on clean C99 13 | 14 | ## How it works and how to use it? 15 | Read full description and documentation [here](http://blog.bulki.me/STM32-LCD-HD44780-I2C/). 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nikita Bulaev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Language: **English** [Русский](http://blog.bulki.me/STM32-LCD-HD44780-I2C/README.RU) 2 | 3 | # STM32 HAL Libriary for LCD Based on Hitachi HD44780 4 | ## What is it for? 5 | I wrote this libriary as I'm sick and tired of other libs based on Arduino lib [LiquidCrystal_I2C](https://github.com/marcoschwartz/LiquidCrystal_I2C). This one is for STM32 but can be adopted for every chip using C99. 6 | 7 | Anyway it is written for I2C expander based on [PCF8574](https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf?pspll=1). 8 | 9 | ## Why is it better? 10 | There are several reasons: 11 | 1. This lib **does not** use any hardware delays. 12 | 2. This lib uses DMA to send data to display 13 | 3. This lib uses some tricks using I2C timings 14 | 4. This lib is written on clean C99 15 | 16 | ## How it works? 17 | I will add text later 18 | 19 | 20 | ## How to use it? 21 | This libriary is for STM32 HAL and uses its functions and FreeRTOS. So first of all you need to configure STM32CubeMX project. 22 | 23 | #### Turn on I2C 24 | PCF8574 supports only Standart 100kHz mode: 25 | 26 | ![Screenshot](http://blog.bulki.me/assets/img/stm32-lcd-hitachi/i2c-params.png) 27 | 28 | Turn on DMA: 29 | 30 | ![Screenshot](http://blog.bulki.me/assets/img/stm32-lcd-hitachi/i2c-dma.png) 31 | 32 | Turn on interrupts: 33 | 34 | ![Screenshot](http://blog.bulki.me/assets/img/stm32-lcd-hitachi/i2c-nvic.png) 35 | 36 | #### FreeRTOS 37 | Turn on FreeRTOS globally and include ```vTaskDelayUntil()``` function: 38 | 39 | ![Screenshot](http://blog.bulki.me/assets/img/stm32-lcd-hitachi/freertos_include_params.png) 40 | 41 | #### Include correct HAL Libriary 42 | Fix path in *Inc/lcd_hd44780_i2c.h*: 43 | ```c 44 | #include "stm32f3xx_hal.h" 45 | ``` 46 | 47 | > You can see correct name in top of your *Src/main.c* created by STM32CubeMX. 48 | 49 | #### Connect display 50 | Connect I2C expander to I2C pins you defined in STM32CubeMX and don't forget about PullUP resitors to 3.3V! Better results from 1,5K to 3.3K. It depends on wire length between STM32 and display module. Also better connect power of display module to 5V. 51 | 52 | #### Init display 53 | Put such code to your FreeRTOS task. Let us connect to module with address **0x27** and I2C **hi2c1**. We will connect to LCD2004 with 4 lines and 20 columns: 54 | 55 | ```c 56 | void StartDefaultTask(void const * argument) { 57 | /* USER CODE BEGIN StartDefaultTask */ 58 | lcdInit(&hi2c1, (uint8_t)0x27, (uint8_t)4, (uint8_t)20); 59 | 60 | // Print text and home position 0,0 61 | lcdPrintStr((uint8_t*)"Hello,", 6); 62 | 63 | // Set cursor at zero position of line 3 64 | lcdSetCursorPosition(0, 2); 65 | 66 | // Print text at cursor position 67 | lcdPrintStr((uint8_t*)"World!", 6); 68 | 69 | for (;;) { 70 | vTaskDelay(1000); 71 | } 72 | 73 | /* USER CODE END StartDefaultTask */ 74 | } 75 | ``` 76 | 77 | **Documentation is not finished yet!** 78 | -------------------------------------------------------------------------------- /docs/README.RU.md: -------------------------------------------------------------------------------- 1 | Language: [English](http://blog.bulki.me/STM32-LCD-HD44780-I2C/) **Русский** 2 | 3 | # Библиотека для STM32 HAL для LCD-дисплеев на МК Hitachi HD44780 и I2C 4 | ## Зачем она нужна? 5 | Я написал эту библиотеку, т.к. мне надоело всё время натыкаться на готовые библиотеки, которые основаны на Ардуиновской [LiquidCrystal_I2C](https://github.com/marcoschwartz/LiquidCrystal_I2C). 6 | Библиотека написана для STM32, но может быть адаптирована для любого МК, который программируется на C99. 7 | 8 | Библиотека написана для классического 8-битного расширителя [PCF8574](https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF8574A.pdf?pspll=1). 9 | 10 | ## Чем она лучше? 11 | Есть несколько причин: 12 | 1. Я **не** использую никакие аппаратные задержки; 13 | 2. Я отправляю данные на дисплей через DMA; 14 | 3. Я использую некоторые трюки с таймингами, основанные на особенностях I2C и PCF8574; 15 | 4. Библиотека написана на чистом C99. 16 | 17 | ## Как она работает? 18 | Добавлю описание позже 19 | 20 | ## Как ей пользоваться? 21 | Библиотека использует функции STM32 HAL и FreeRTOS. Поэтому первым делом необходимо добавить параметры в проект STM32CubeMX. 22 | 23 | #### Включить I2C 24 | PCF8574 supports only Standart 100kHz mode: 25 | ![Screenshot](http://blog.bulki.me/assets/img/stm32-lcd-hitachi/i2c-params.png) 26 | 27 | Включить DMA: 28 | ![Screenshot](http://blog.bulki.me/assets/img/stm32-lcd-hitachi/i2c-dma.png) 29 | 30 | Включить прерывания: 31 | ![Screenshot](http://blog.bulki.me/assets/img/stm32-lcd-hitachi/i2c-nvic.png) 32 | 33 | #### FreeRTOS 34 | Turn on FreeRTOS globally and include ```vTaskDelayUntil()``` function: 35 | ![Screenshot](http://blog.bulki.me/assets/img/stm32-lcd-hitachi/freertos_include_params.png) 36 | 37 | #### Подключить HAL Libriary 38 | Пропишите правильную библиотеку HAL в *Inc/lcd_hd44780_i2c.h*: 39 | ```c 40 | #include "stm32f3xx_hal.h" 41 | ``` 42 | 43 | > Правильное название можете найти в вашем *Src/main.c*, который был создан STM32CubeMX. 44 | 45 | #### Подключить дисплей 46 | Connect I2C expander to I2C pins you defined in STM32CubeMX and don't forget about PullUP resitors to 3.3V! Better results from 1,5K to 3.3K. It depends on wire length between STM32 and display module. Also better connect power of display module to 5V. 47 | 48 | #### Инициализация дисплея 49 | Put such code to your FreeRTOS task. Let us connect to module with address **0x27** and I2C **hi2c1**. We will connect to LCD2004 with 4 lines and 20 columns: 50 | 51 | ```c 52 | void StartDefaultTask(void const * argument) { 53 | /* USER CODE BEGIN StartDefaultTask */ 54 | lcdInit(&hi2c1, (uint8_t)0x27, (uint8_t)4, (uint8_t)20); 55 | 56 | // Вывести текст на начальной позиции 0,0 57 | lcdPrintStr((uint8_t*)"Hello,", 6); 58 | 59 | // Выставить курсор в начало строки №3 60 | lcdSetCursorPosition(0, 2); 61 | 62 | // Вывести текст с позиции курсора 63 | lcdPrintStr((uint8_t*)"World!", 6); 64 | 65 | for (;;) { 66 | vTaskDelay(1000); 67 | } 68 | 69 | /* USER CODE END StartDefaultTask */ 70 | } 71 | ``` 72 | 73 | **Документация ещё не закончена!** 74 | -------------------------------------------------------------------------------- /Inc/lcd_hd44780_i2c.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Nikita Bulaev 2017 3 | * 4 | * STM32 HAL libriary for LCD display based on HITACHI HD44780U chip. 5 | * 6 | * =========================================================================== 7 | * WARNING! 8 | * 9 | * YOU MUST INCLUDE CORRECT STM32 HAL LIB HEAR. THIS LIB WAS TESTED ON STM32F3 10 | * PLEASE, INCLUDE CORRECT ONE! 11 | * =========================================================================== 12 | * 13 | * THIS SOFTWARE IS PROVIDED "AS IS" 14 | * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 15 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 16 | * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY 17 | * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 18 | * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 21 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 22 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef LCD_HD44780_I2C_H 28 | #define LCD_HD44780_I2C_H 120 29 | 30 | /* C++ detection */ 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | #include "stm32f3xx_hal.h" 36 | 37 | #define LCD_BIT_RS ((uint8_t)0x01U) 38 | #define LCD_BIT_RW ((uint8_t)0x02U) 39 | #define LCD_BIT_E ((uint8_t)0x04U) 40 | #define LCD_BIT_BACKIGHT_ON ((uint8_t)0x08U) 41 | #define LCD_BIT_BACKIGHT_OFF ((uint8_t)0x00U) 42 | 43 | #define LCD_MODE_4BITS ((uint8_t)0x02U) 44 | #define LCD_BIT_1LINE ((uint8_t)0x00U) 45 | #define LCD_BIT_2LINE ((uint8_t)0x08U) 46 | #define LCD_BIT_4LINE LCD_BIT_2LINE 47 | #define LCD_BIT_5x8DOTS ((uint8_t)0x00U) 48 | #define LCD_BIT_5x10DOTS ((uint8_t)0x04U) 49 | #define LCD_BIT_SETCGRAMADDR ((uint8_t)0x40U) 50 | #define LCD_BIT_SETDDRAMADDR ((uint8_t)0x80U) 51 | 52 | #define LCD_BIT_DISPLAY_CONTROL ((uint8_t)0x08U) 53 | #define LCD_BIT_DISPLAY_ON ((uint8_t)0x04U) 54 | #define LCD_BIT_CURSOR_ON ((uint8_t)0x02U) 55 | #define LCD_BIT_CURSOR_OFF ((uint8_t)0x00U) 56 | #define LCD_BIT_BLINK_ON ((uint8_t)0x01U) 57 | #define LCD_BIT_BLINK_OFF ((uint8_t)0x00U) 58 | 59 | #define LCD_BIT_DISP_CLEAR ((uint8_t)0x01U) 60 | #define LCD_BIT_CURSOR_HOME ((uint8_t)0x02U) 61 | 62 | #define LCD_BIT_ENTRY_MODE ((uint8_t)0x04U) 63 | #define LCD_BIT_CURSOR_DIR_RIGHT ((uint8_t)0x02U) 64 | #define LCD_BIT_CURSOR_DIR_LEFT ((uint8_t)0x00U) 65 | #define LCD_BIT_DISPLAY_SHIFT ((uint8_t)0x01U) 66 | 67 | // TODO: Update commands with this defines 68 | #define LCD_BIT_CURSOR_SHIFT_MODE ((uint8_t)0x10U) 69 | #define LCD_BIT_CURSOR_DISP_SHIFT ((uint8_t)0x08U) 70 | #define LCD_BIT_CURSOR_MOVE ((uint8_t)0x00U) 71 | #define LCD_BIT_CURSOR_SHIFT_DIR_R ((uint8_t)0x40U) 72 | #define LCD_BIT_CURSOR_SHIFT_DIR_L ((uint8_t)0x00U) 73 | 74 | 75 | /* Function defines */ 76 | #define lcdBacklightOn() lcdBacklight(LCD_BIT_BACKIGHT_ON) 77 | #define lcdBacklightOff() lcdBacklight(LCD_BIT_BACKIGHT_OFF) 78 | #define lcdAutoscrollOn() lcdCommand(LCD_DISPLAY_SHIFT, LCD_PARAM_SET) 79 | #define lcdAutoscrollOff() lcdCommand(LCD_DISPLAY_SHIFT, LCD_PARAM_UNSET) 80 | #define lcdDisplayClear() lcdCommand(LCD_CLEAR, LCD_PARAM_SET) 81 | #define lcdDisplayOn() lcdCommand(LCD_DISPLAY, LCD_PARAM_SET) 82 | #define lcdDisplayOff() lcdCommand(LCD_DISPLAY, LCD_PARAM_UNSET) 83 | #define lcdCursorOn() lcdCommand(LCD_CURSOR, LCD_PARAM_SET) 84 | #define lcdCursorOff() lcdCommand(LCD_CURSOR, LCD_PARAM_UNSET) 85 | #define lcdBlinkOn() lcdCommand(LCD_CURSOR_BLINK, LCD_PARAM_SET) 86 | #define lcdBlinkOff() lcdCommand(LCD_CURSOR_BLINK, LCD_PARAM_UNSET) 87 | #define lcdCursorDirToRight() lcdCommand(LCD_CURSOR_DIR_RIGHT, LCD_PARAM_SET) 88 | #define lcdCursorDirToLeft() lcdCommand(LCD_CURSOR_DIR_LEFT, LCD_PARAM_SET) 89 | #define lcdCursorHome() lcdCommand(LCD_CURSOR_HOME, LCD_PARAM_SET) 90 | 91 | #ifndef bool 92 | typedef enum { 93 | false, 94 | true 95 | } bool; 96 | #endif 97 | 98 | typedef struct { 99 | I2C_HandleTypeDef * hi2c; // I2C Struct 100 | uint8_t lines; // Lines of the display 101 | uint8_t columns; // Columns 102 | uint8_t address; // I2C address shifted left by 1 103 | uint8_t backlight; // Backlight 104 | uint8_t modeBits; // Display on/off control bits 105 | uint8_t entryBits; // Entry mode set bits 106 | } LCDParams; 107 | 108 | typedef enum { 109 | LCD_PARAM_UNSET = 0, 110 | LCD_PARAM_SET 111 | } LCDParamsActions; 112 | 113 | typedef enum { 114 | LCD_BACKLIGHT = 0, 115 | LCD_DISPLAY, 116 | LCD_CLEAR, 117 | LCD_CURSOR, 118 | LCD_CURSOR_BLINK, 119 | LCD_CURSOR_HOME, 120 | LCD_CURSOR_DIR_LEFT, 121 | LCD_CURSOR_DIR_RIGHT, 122 | LCD_DISPLAY_SHIFT 123 | } LCDCommands; 124 | 125 | 126 | bool lcdInit(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t lines, uint8_t rows); 127 | bool lcdCommand(LCDCommands command, LCDParamsActions action); 128 | bool lcdBacklight(uint8_t command); 129 | bool lcdSetCursorPosition(uint8_t line, uint8_t row); 130 | bool lcdPrintStr(uint8_t * data, uint8_t length); 131 | bool lcdPrintChar(uint8_t data); 132 | bool lcdLoadCustomChar(uint8_t cell, uint8_t * charMap); 133 | 134 | /* C++ detection */ 135 | #ifdef __cplusplus 136 | } 137 | #endif 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /Src/lcd_hd44780_i2c.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Nikita Bulaev 2017 3 | * 4 | * STM32 HAL libriary for LCD display based on HITACHI HD44780U chip. 5 | * 6 | * THIS SOFTWARE IS PROVIDED "AS IS" 7 | * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 8 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 9 | * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY 10 | * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 11 | * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 12 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 13 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 14 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 15 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 16 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 17 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | */ 19 | 20 | /* 21 | Here are some cyrillic symbols that you can use in your code 22 | 23 | uint8_t symD[8] = { 0x07, 0x09, 0x09, 0x09, 0x09, 0x1F, 0x11 }; // Д 24 | uint8_t symZH[8] = { 0x11, 0x15, 0x15, 0x0E, 0x15, 0x15, 0x11 }; // Ж 25 | uint8_t symI[8] = { 0x11, 0x11, 0x13, 0x15, 0x19, 0x11, 0x11 }; // И 26 | uint8_t symL[8] = { 0x0F, 0x09, 0x09, 0x09, 0x09, 0x11, 0x11 }; // Л 27 | uint8_t symP[8] = { 0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 }; // П 28 | uint8_t symSHi[8] = { 0x10, 0x15, 0x15, 0x15, 0x15, 0x1F, 0x03 }; // Щ 29 | uint8_t symJU[8] = { 0x12, 0x15, 0x15, 0x1D, 0x15, 0x15, 0x12 }; // Ю 30 | uint8_t symJA[8] = { 0x0F, 0x11, 0x11, 0x0F, 0x05, 0x09, 0x11 }; // Я 31 | 32 | 33 | */ 34 | 35 | #include "stdlib.h" 36 | #include "string.h" 37 | #include "FreeRTOS.h" 38 | #include "task.h" 39 | #include "lcd_hd44780_i2c.h" 40 | 41 | uint8_t lcdCommandBuffer[6] = {0x00}; 42 | 43 | static LCDParams lcdParams; 44 | 45 | static bool lcdWriteByte(uint8_t rsRwBits, uint8_t * data); 46 | 47 | /** 48 | * @brief Turn display on and init it params 49 | * @note We gonna make init steps according to datasheep page 46. 50 | * There are 4 steps to turn 4-bits mode on, 51 | * then we send initial params. 52 | * @param hi2c I2C struct to which display is connected 53 | * @param address Display I2C 7-bit address 54 | * @param lines Number of lines of display 55 | * @param columns Number of colums 56 | * @return true if success 57 | */ 58 | bool lcdInit(I2C_HandleTypeDef *hi2c, uint8_t address, uint8_t lines, uint8_t columns) { 59 | 60 | TickType_t xLastWakeTime; 61 | 62 | uint8_t lcdData = LCD_BIT_5x8DOTS; 63 | 64 | lcdParams.hi2c = hi2c; 65 | lcdParams.address = address << 1; 66 | lcdParams.lines = lines; 67 | lcdParams.columns = columns; 68 | lcdParams.backlight = LCD_BIT_BACKIGHT_ON; 69 | 70 | lcdCommandBuffer[0] = LCD_BIT_E | (0x03 << 4); 71 | lcdCommandBuffer[1] = lcdCommandBuffer[0]; 72 | lcdCommandBuffer[2] = (0x03 << 4); 73 | 74 | /* First 3 steps of init cycles. They are the same. */ 75 | for (uint8_t i = 0; i < 3; ++i) { 76 | if (HAL_I2C_Master_Transmit_DMA(lcdParams.hi2c, lcdParams.address, (uint8_t*)lcdCommandBuffer, 3) != HAL_OK) { 77 | return false; 78 | } 79 | 80 | xLastWakeTime = xTaskGetTickCount(); 81 | 82 | while (HAL_I2C_GetState(lcdParams.hi2c) != HAL_I2C_STATE_READY) { 83 | vTaskDelay(1); 84 | } 85 | 86 | if (i == 2) { 87 | // For the last cycle delay is less then 1 ms (100us by datasheet) 88 | vTaskDelayUntil(&xLastWakeTime, (TickType_t)1); 89 | } else { 90 | // For first 2 cycles delay is less then 5ms (4100us by datasheet) 91 | vTaskDelayUntil(&xLastWakeTime, (TickType_t)5); 92 | } 93 | } 94 | 95 | /* Lets turn to 4-bit at least */ 96 | lcdCommandBuffer[0] = LCD_BIT_BACKIGHT_ON | LCD_BIT_E | (LCD_MODE_4BITS << 4); 97 | lcdCommandBuffer[1] = lcdCommandBuffer[0]; 98 | lcdCommandBuffer[2] = LCD_BIT_BACKIGHT_ON | (LCD_MODE_4BITS << 4); 99 | 100 | if (HAL_I2C_Master_Transmit_DMA(lcdParams.hi2c, lcdParams.address, (uint8_t*)lcdCommandBuffer, 3) != HAL_OK) { 101 | return false; 102 | } 103 | 104 | while (HAL_I2C_GetState(lcdParams.hi2c) != HAL_I2C_STATE_READY) { 105 | vTaskDelay(1); 106 | } 107 | 108 | /* Lets set display params */ 109 | /* First of all lets set display size */ 110 | lcdData |= LCD_MODE_4BITS; 111 | 112 | if (lcdParams.lines > 1) { 113 | lcdData |= LCD_BIT_2LINE; 114 | } 115 | 116 | lcdWriteByte((uint8_t)0x00, &lcdData); // TODO: Make 5x10 dots font usable for some 1-line display 117 | 118 | /* Now lets set display, cursor and blink all on */ 119 | lcdDisplayOn(); 120 | 121 | /* Set cursor moving to the right */ 122 | lcdCursorDirToRight(); 123 | 124 | /* Clear display and Set cursor at Home */ 125 | lcdDisplayClear(); 126 | lcdCursorHome(); 127 | 128 | return true; 129 | } 130 | 131 | /** 132 | * @brief Send command to display 133 | * @param command One of listed in LCDCommands enum 134 | * @param action LCD_PARAM_SET or LCD_PARAM_UNSET 135 | * @return true if success 136 | */ 137 | bool lcdCommand(LCDCommands command, LCDParamsActions action) { 138 | uint8_t lcdData = 0x00; 139 | 140 | /* First of all lest store the command */ 141 | switch (action) { 142 | case LCD_PARAM_SET: 143 | switch (command) { 144 | case LCD_DISPLAY: 145 | lcdParams.modeBits |= LCD_BIT_DISPLAY_ON; 146 | break; 147 | 148 | case LCD_CURSOR: 149 | lcdParams.modeBits |= LCD_BIT_CURSOR_ON; 150 | break; 151 | 152 | case LCD_CURSOR_BLINK: 153 | lcdParams.modeBits |= LCD_BIT_BLINK_ON; 154 | break; 155 | 156 | case LCD_CLEAR: 157 | lcdData = LCD_BIT_DISP_CLEAR; 158 | 159 | if (lcdWriteByte((uint8_t)0x00, &lcdData) == false) { 160 | return false; 161 | } else { 162 | vTaskDelay(2); 163 | return true; 164 | } 165 | 166 | case LCD_CURSOR_HOME: 167 | lcdData = LCD_BIT_CURSOR_HOME; 168 | 169 | if (lcdWriteByte((uint8_t)0x00, &lcdData) == false) { 170 | return false; 171 | } else { 172 | vTaskDelay(2); 173 | return true; 174 | } 175 | 176 | case LCD_CURSOR_DIR_RIGHT: 177 | lcdParams.entryBits |= LCD_BIT_CURSOR_DIR_RIGHT; 178 | break; 179 | 180 | case LCD_CURSOR_DIR_LEFT: 181 | lcdParams.entryBits |= LCD_BIT_CURSOR_DIR_LEFT; 182 | break; 183 | 184 | case LCD_DISPLAY_SHIFT: 185 | lcdParams.entryBits |= LCD_BIT_DISPLAY_SHIFT; 186 | break; 187 | 188 | default: 189 | return false; 190 | } 191 | 192 | break; 193 | 194 | case LCD_PARAM_UNSET: 195 | switch (command) { 196 | case LCD_DISPLAY: 197 | lcdParams.modeBits &= ~LCD_BIT_DISPLAY_ON; 198 | break; 199 | 200 | case LCD_CURSOR: 201 | lcdParams.modeBits &= ~LCD_BIT_CURSOR_ON; 202 | break; 203 | 204 | case LCD_CURSOR_BLINK: 205 | lcdParams.modeBits &= ~LCD_BIT_BLINK_ON; 206 | break; 207 | 208 | case LCD_CURSOR_DIR_RIGHT: 209 | lcdParams.entryBits &= ~LCD_BIT_CURSOR_DIR_RIGHT; 210 | break; 211 | 212 | case LCD_CURSOR_DIR_LEFT: 213 | lcdParams.entryBits &= ~LCD_BIT_CURSOR_DIR_LEFT; 214 | break; 215 | 216 | case LCD_DISPLAY_SHIFT: 217 | lcdParams.entryBits &= ~LCD_BIT_DISPLAY_SHIFT; 218 | break; 219 | 220 | default: 221 | return false; 222 | } 223 | 224 | break; 225 | 226 | default: 227 | return false; 228 | } 229 | 230 | /* Now lets send the command */ 231 | switch (command) { 232 | case LCD_DISPLAY: 233 | case LCD_CURSOR: 234 | case LCD_CURSOR_BLINK: 235 | lcdData = LCD_BIT_DISPLAY_CONTROL | lcdParams.modeBits; 236 | break; 237 | 238 | case LCD_CURSOR_DIR_RIGHT: 239 | case LCD_CURSOR_DIR_LEFT: 240 | case LCD_DISPLAY_SHIFT: 241 | lcdData = LCD_BIT_ENTRY_MODE | lcdParams.entryBits; 242 | break; 243 | 244 | default: 245 | break; 246 | } 247 | 248 | return lcdWriteByte((uint8_t)0x00, &lcdData); 249 | } 250 | 251 | /** 252 | * @brief Turn display's Backlight On or Off 253 | * @param command LCD_BIT_BACKIGHT_ON to turn display On 254 | * LCD_BIT_BACKIGHT_OFF (or 0x00) to turn display Off 255 | * @return true if success 256 | */ 257 | bool lcdBacklight(uint8_t command) { 258 | lcdParams.backlight = command; 259 | 260 | if (HAL_I2C_Master_Transmit_DMA(lcdParams.hi2c, lcdParams.address, &lcdParams.backlight, 1) != HAL_OK) { 261 | return false; 262 | } 263 | 264 | while (HAL_I2C_GetState(lcdParams.hi2c) != HAL_I2C_STATE_READY) { 265 | vTaskDelay(1); 266 | } 267 | 268 | return true; 269 | } 270 | 271 | /** 272 | * @brief Set cursor position on the display 273 | * @param column counting from 0 274 | * @param line counting from 0 275 | * @return true if success 276 | */ 277 | bool lcdSetCursorPosition(uint8_t column, uint8_t line) { 278 | // We will setup offsets for 4 lines maximum 279 | static const uint8_t lineOffsets[4] = { 0x00, 0x40, 0x14, 0x54 }; 280 | 281 | if ( line >= lcdParams.lines ) { 282 | line = lcdParams.lines - 1; 283 | } 284 | 285 | uint8_t lcdCommand = LCD_BIT_SETDDRAMADDR | (column + lineOffsets[line]); 286 | 287 | return lcdWriteByte(0x00, &lcdCommand); 288 | } 289 | 290 | /** 291 | * @brief Print string from cursor position 292 | * @param data Pointer to string 293 | * @param length Number of symbols to print 294 | * @return true if success 295 | */ 296 | bool lcdPrintStr(uint8_t * data, uint8_t length) { 297 | for (uint8_t i = 0; i < length; ++i) { 298 | if (lcdWriteByte(LCD_BIT_RS, &data[i]) == false) { 299 | return false; 300 | } 301 | } 302 | 303 | return true; 304 | } 305 | 306 | /** 307 | * @brief Print single char at cursor position 308 | * @param data Symbol to print 309 | * @return true if success 310 | */ 311 | bool lcdPrintChar(uint8_t data) { 312 | return lcdWriteByte(LCD_BIT_RS, &data); 313 | } 314 | 315 | 316 | /** 317 | * @brief Loading custom Chars to one of the 8 cells in CGRAM 318 | * @note You can create your custom chars according to 319 | * documentation page 15. 320 | * It consists of array of 8 bytes. 321 | * Each byte is line of dots. Lower bits are dots. 322 | * @param cell Number of cell from 0 to 7 where to upload 323 | * @param charMap Pointer to Array of dots 324 | * Example: { 0x07, 0x09, 0x09, 0x09, 0x09, 0x1F, 0x11 } 325 | * @return true if success 326 | */ 327 | bool lcdLoadCustomChar(uint8_t cell, uint8_t * charMap) { 328 | 329 | // Stop, if trying to load to incorrect cell 330 | if (cell > 7) { 331 | return false; 332 | } 333 | 334 | uint8_t lcdCommand = LCD_BIT_SETCGRAMADDR | (cell << 3); 335 | 336 | if (lcdWriteByte((uint8_t)0x00, &lcdCommand) == false) { 337 | return false; 338 | } 339 | 340 | for (uint8_t i = 0; i < 8; ++i) { 341 | if (lcdWriteByte(LCD_BIT_RS, &charMap[i]) == false) { 342 | return false; 343 | } 344 | } 345 | 346 | return true; 347 | } 348 | 349 | /** 350 | * @brief Local function to send data to display 351 | * @param rsRwBits State of RS and R/W bits 352 | * @param data Pointer to byte to send 353 | * @return true if success 354 | */ 355 | static bool lcdWriteByte(uint8_t rsRwBits, uint8_t * data) { 356 | 357 | /* Higher 4 bits*/ 358 | lcdCommandBuffer[0] = rsRwBits | LCD_BIT_E | lcdParams.backlight | (*data & 0xF0); // Send data and set strobe 359 | lcdCommandBuffer[1] = lcdCommandBuffer[0]; // Strobe turned on 360 | lcdCommandBuffer[2] = rsRwBits | lcdParams.backlight | (*data & 0xF0); // Turning strobe off 361 | 362 | /* Lower 4 bits*/ 363 | lcdCommandBuffer[3] = rsRwBits | LCD_BIT_E | lcdParams.backlight | ((*data << 4) & 0xF0); // Send data and set strobe 364 | lcdCommandBuffer[4] = lcdCommandBuffer[3]; // Strobe turned on 365 | lcdCommandBuffer[5] = rsRwBits | lcdParams.backlight | ((*data << 4) & 0xF0); // Turning strobe off 366 | 367 | 368 | if (HAL_I2C_Master_Transmit_DMA(lcdParams.hi2c, lcdParams.address, (uint8_t*)lcdCommandBuffer, 6) != HAL_OK) { 369 | return false; 370 | } 371 | 372 | while (HAL_I2C_GetState(lcdParams.hi2c) != HAL_I2C_STATE_READY) { 373 | vTaskDelay(1); 374 | } 375 | 376 | return true; 377 | } 378 | --------------------------------------------------------------------------------