AQM0802A for ESP-IDF

AQM0802Aは、8桁2行のLCDです。今回は秋月さんから購入しました。単品はこちらキットはこちらです。

ハードの準備

このモジュールのインターフェイスはI2C。信号線2本でコントロール出来るので配線が楽に済みます。今回は単品で購入して秋月さんのHPの回路を元にESP32と下記の様に接続しています。

ESP32 AQM0802A 
 GPIO22    SCL
 GPIO21   SDA

ソフトの準備

ArduinoでAQM0802A用ソフトを探したら下記を見つけました。

これを元にESP-IDF用に変更しました。

  • FaBoLCDmini_AQM0802A.cpp -> AQM0802A.c
  • FaBoLCDmini_AQM0802A.h -> AQM0802A.h
AQM0802A.c

// i2c - AQM0802A // i2c - AQM0802A 

#include <stdio.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "AQM0802A.h"

// i2c master initialization
esp_err_t i2c_master_init(void)
{
    int i2c_master_port = I2C_MASTER_NUM;

    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };

    i2c_param_config(i2c_master_port, &conf);

    return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}

// Write a byte to a AQM0802A 
esp_err_t AQM0802A_write_byte(uint8_t data, uint8_t reg_addr)
{
    int ret;
    uint8_t write_buf[2] = {reg_addr, data};

    ret = i2c_master_write_to_device(I2C_MASTER_NUM, AQM0802A_ADDR, write_buf, sizeof(write_buf), I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);

    return ret;
}

// Command write
void AQM0802A_command(uint8_t value)
{
  	AQM0802A_write_byte(value, (uint8_t)0x00);
}

// Data write
void AQM0802A_data(uint8_t value)
{
	AQM0802A_write_byte(value, (uint8_t)0x40);
}

void AQM0802A_init(void)
{
	uint8_t value = 0x00;
  	
  	// Initializing
  	// Wait time >40mS After VDD stable
	vTaskDelay(40 / portTICK_PERIOD_MS);

  	//Function Set: Normal mode:
  	//  Dl: interface data 8/4bits
  	//  N: number of line 2/1
  	//  001D Nxxx
  	//  0011 1000 : 0x38
  	value = 0x38; 
  	AQM0802A_command(value);
  	// Wait time >26.3uS

  	//Function Set: Extension mode:
  	//  Dl: interface data 8/4bits
  	//  N: number of line 2/1
  	//  dH: double height font
  	//  Is: instruction table select
  	//  001D NH0I
  	//  0011 1001 : 0x39
  	value = 0x39; 
  	AQM0802A_command(value);
  	// Wait time >26.3uS

  	//Internal OSC frequency:
  	//  Bs: 1=1/4 bias 0=1/5 bias
  	//  F2-0: adjust internal OSC frequency for FR frequency.
  	//  0001 BFFF
  	//  0001 0100 : 0x14
  	value = 0x14;
  	AQM0802A_command(value);
  	// Wait time >26.3uS

  	//Contrast set:
  	//  C3-0: Contrast set for internal follower mode.
  	//  0111 CCCC
  	//  0111 0000 : 0x70
  	value = 0x70;
  	AQM0802A_command(value);
  	// Wait time >26.3uS

  	//Power/ICON control/Contrast set:
  	//  Ion: ICON display on/off
  	//  Bon: set booster circuit on/off
  	//  C5-4: Contrast set for internal follower mode.
  	//  0101 IBCC
  	//  0101 0110 : 0x56
  	value = 0x56;
  	AQM0802A_command(value);
  	// Wait time >26.3uS

  	// Contrast initialize: xx10 0000(32d)

  	//Follower control:
  	//  Fon: set follower circuit on/off
  	//  Rab2-0: select follower amplified ratio.
  	//  0110 FRRR
  	//  0101 1100 : 0x6C
  	value = 0x6C;
  	AQM0802A_command(value);
  	// Wait time >26.3uS

  	//Function Set: Normal mode:
  	//  Dl: interface data 8/4bits
  	//  N: number of line 2/1
  	//  001D Nxxx
  	//  0011 1000 : 0x38
  	value = 0x38;
  	AQM0802A_command(value);
  	// Wait time >26.3uS

  	// turn the display on with no cursor or blinking default
  	_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;

  	// Initialize to default text direction (for romance languages)
  	_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;

  	AQM0802A_display();
  	AQM0802A_clear();
  	AQM0802A_home();
}

void AQM0802A_print(char* buf)
{
	int a;
	for(a = 0; a < strlen(buf); a++) AQM0802A_data(buf[a]);
}

void AQM0802A_clear()
{
  	AQM0802A_command(LCD_CLEARDISPLAY);  		// clear display, set cursor position to zero
  	vTaskDelay(200 / portTICK_PERIOD_MS);  		// this command takes a long time!
}

void AQM0802A_home()
{
  	AQM0802A_command(LCD_RETURNHOME);  			// set cursor position to zero
  	vTaskDelay(200 / portTICK_PERIOD_MS);  		// this command takes a long time!
}

void AQM0802A_setCursor(uint8_t col, uint8_t row)
{
	if(row) col += 0x40;
  	AQM0802A_command(LCD_SETDDRAMADDR | col);
}

void AQM0802A_noDisplay()
{
  	_displaycontrol &= ~LCD_DISPLAYON;
  	AQM0802A_command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void AQM0802A_display()
{
  	_displaycontrol |= LCD_DISPLAYON;
  	AQM0802A_command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void AQM0802A_noCursor()
{
  	_displaycontrol &= ~LCD_CURSORON;
  	AQM0802A_command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void AQM0802A_cursor()
{
  	_displaycontrol |= LCD_CURSORON;
  	AQM0802A_command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void AQM0802A_noBlink()
{
  	_displaycontrol &= ~LCD_BLINKON;
  	AQM0802A_command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void AQM0802A_blink()
{
  	_displaycontrol |= LCD_BLINKON;
  	AQM0802A_command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void AQM0802A_scrollDisplayLeft(void)
{
  	AQM0802A_command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}

void AQM0802A_scrollDisplayRight(void)
{
  	AQM0802A_command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

void AQM0802A_leftToRight(void)
{
  	_displaymode |= LCD_ENTRYLEFT;
  	AQM0802A_command(LCD_ENTRYMODESET | _displaymode);
}

void AQM0802A_rightToLeft(void)
{
  	_displaymode &= ~LCD_ENTRYLEFT;
  	AQM0802A_command(LCD_ENTRYMODESET | _displaymode);
}

void AQM0802A_autoscroll(void)
{
  	_displaymode |= LCD_ENTRYSHIFTINCREMENT;
  	AQM0802A_command(LCD_ENTRYMODESET | _displaymode);
}

void AQM0802A_noAutoscroll(void)
{
  	_displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
  	AQM0802A_command(LCD_ENTRYMODESET | _displaymode);
}

void AQM0802A_createChar(uint8_t location, uint8_t charmap[])
{
  	location &= 0x7; // we only have 8 locations 0-7
  	AQM0802A_command(LCD_SETCGRAMADDR | (location << 3));
  	for (int i=0; i<8; i++) AQM0802A_data(charmap[i]);
}

AQM0802A.h

#include "driver/i2c.h"
#define I2C_MASTER_SCL_IO           22      	/*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO           21      	/*!< GPIO number used for I2C master data  */
#define I2C_MASTER_NUM              0           /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ          400000      /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE   0           /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE   0           /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS       1000

#define AQM0802A_ADDR		        0x3e        

// commands

#define LCD_CLEARDISPLAY 			0x01
#define LCD_RETURNHOME 				0x02
#define LCD_ENTRYMODESET 			0x04
#define LCD_DISPLAYCONTROL 			0x08
#define LCD_CURSORSHIFT 			0x10
#define LCD_FUNCTIONSET 			0x20
#define LCD_SETCGRAMADDR 			0x40
#define LCD_SETDDRAMADDR 			0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 				0x00
#define LCD_ENTRYLEFT 				0x02
#define LCD_ENTRYSHIFTINCREMENT 	0x01
#define LCD_ENTRYSHIFTDECREMENT 	0x00

// flags for display on/off control
#define LCD_DISPLAYON 				0x04
#define LCD_DISPLAYOFF 				0x00
#define LCD_CURSORON 				0x02
#define LCD_CURSOROFF 				0x00
#define LCD_BLINKON 				0x01
#define LCD_BLINKOFF 				0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 			0x08
#define LCD_CURSORMOVE 				0x00
#define LCD_MOVERIGHT 				0x04
#define LCD_MOVELEFT 				0x00

void AQM0802A_init(void);
void AQM0802A_print(char* buf);
//------------------- high level commands, for the user! ------------------
void AQM0802A_clear(void);
void AQM0802A_home(void);
void AQM0802A_setCursor(uint8_t col, uint8_t row);
void AQM0802A_noDisplay(void);
void AQM0802A_display(void);
void AQM0802A_noCursor(void);
void AQM0802A_cursor(void);
void AQM0802A_noBlink(void);
void AQM0802A_blink(void);
void AQM0802A_scrollDisplayLeft(void);
void AQM0802A_scrollDisplayRight(void);
void AQM0802A_leftToRight(void);
void AQM0802A_rightToLeft(void);
void AQM0802A_autoscroll(void);
void AQM0802A_noAutoscroll(void);
void AQM0802A_createChar(uint8_t location, uint8_t charmap[]);
void AQM0802A_setContrast(uint8_t contrast);
//-------------------  mid level commands, for sending data/cmds ------------------
void AQM0802A_command(uint8_t value);
void AQM0802A_data(uint8_t value);
esp_err_t AQM0802A_write_byte(uint8_t data, uint8_t reg_addr);
esp_err_t i2c_master_init(void);

uint8_t _displaycontrol;
uint8_t _displaymode;

簡単なデモプログラムと実行

プログラムの構造を以下に示します。


- i2c_sample/
      - components/
                   - AQM0802A/
                              - AQM0802A.c
                              - AQM0802A.h
                   - CMakeLists.txt
      - main/
                   - CMakeLists.txt
                   - i2c_sample_main.c
      - CMakeLists.txt

使用するCPUはESP32。モニターから、idf.py -p [USB PORT] flash monitor でコンパイル実行出来ます。

実行すると、LCDの1行、5桁目から ”ABC”。2行 2桁目から ”123” と表示されます。