Отображение произвольных символов на ЖКИ 1602

23 июня 2021

Небольшое дополнение к посту Микроконтроллеры STM32: работа с экранчиком 1602 по I2C. HD44780-совместимые ЖК-индикаторы позволяют определить до восьми собственных символов 5x8 точек. Вот как этим пользоваться.

Обратимся к таблице доступных инструкций:

Команды, поддерживаемые чипом HD44780

Нас интересует строка «Set CGRAM Address». CGRAM означает Character Generator Random Access Memory. CGRAM состоит из 64-х ячеек памяти по 5 бит, что позволяет хранить восемь символов 5x8. Этим символам будут соответствовать коды \x00..\x07.

Обладая этой информацией, нетрудно написать код. Вот его полная версия:

#define PIN_RS    (1 << 0)
#define PIN_EN    (1 << 2)
#define BACKLIGHT (1 << 3)

#define LCD_DELAY_MS 5

uint8_t lcd_addr = (0x27 << 1);

HAL_StatusTypeDef LCD_SendInternal(uint8_t data, uint8_t flags) {
    HAL_StatusTypeDef res;
    for(;;) {
        res = HAL_I2C_IsDeviceReady(&hi2c1, lcd_addr, 1,
                                    HAL_MAX_DELAY);
        if(res == HAL_OK)
            break;
    }

    uint8_t up = data & 0xF0;
    uint8_t lo = (data << 4) & 0xF0;

    uint8_t data_arr[4];
    data_arr[0] = up|flags|BACKLIGHT|PIN_EN;
    data_arr[1] = up|flags|BACKLIGHT;
    data_arr[2] = lo|flags|BACKLIGHT|PIN_EN;
    data_arr[3] = lo|flags|BACKLIGHT;

    res = HAL_I2C_Master_Transmit(&hi2c1, lcd_addr, data_arr,
                                  sizeof(data_arr), HAL_MAX_DELAY);
    HAL_Delay(LCD_DELAY_MS);
    return res;
}

void LCD_SendCommand(uint8_t cmd) {
    LCD_SendInternal(cmd, 0);
}

void LCD_SendData(uint8_t data) {
    LCD_SendInternal(data, PIN_RS);
}

void LCD_Init() {
    // 4-bit mode, 2 lines, 5x7 format
    LCD_SendCommand(0b00110000);
    // display & cursor home (keep this!)
    LCD_SendCommand(0b00000010);
    // display on, underline off, blink off
    LCD_SendCommand(0b00001100);
    // clear display (optional here)
    LCD_SendCommand(0b00000001);
}

void LCD_SendString(char *str) {
    while(*str) {
        LCD_SendData((uint8_t)(*str));
        str++;
    }
}

void init() {
    // check for an alternative LCD address
    HAL_StatusTypeDef res;
    res = HAL_I2C_IsDeviceReady(&hi2c1, (0x3F << 1), 1, HAL_MAX_DELAY);
    if(res == HAL_OK) {
        lcd_addr = (0x3F << 1);
    }

    LCD_Init();

    // create 8 custom characters for the S-meter
    for(uint8_t ch = 0; ch < 8; ch++) {
        // set CGRAM Address
        LCD_SendCommand(0b01000000 + (ch << 3));
        for(uint8_t line = 0; line < 8; line++) {
            if(ch >= (7-line)) {
                LCD_SendData(0b11111);
            } else {
                LCD_SendData(0b00000);
            }
        }
    }

    LCD_SendCommand(0b11000000);
    LCD_SendString(" ");
    for(uint8_t ch = 0; ch < 8; ch++) {
        LCD_SendData(ch);
    }

    while(1) { HAL_Delay(1000); }
}

Результат:

Отображение произвольных символов на символьных ЖК-индикаторах

Код был успешно протестирован на пяти разных ЖК-индикаторах 1602 и 0802.

Приведенные символы могут быть использованы для отображения громкости или изображать S-метр в самодельном трансивере. Само собой разумеется, можно определить и другие символы. Заметьте, что ничто не мешает переопределять символы на лету. То есть, собственных символов может быть и больше восьми, но одновременно отобразить можно только восемь разных символов.

Метки: , .