液晶モジュールAQM1602(ST7032)のカスタム文字登録・表示方法

I2C通信の液晶モジュール「AQM1602(ST7032)」のカスタム文字の登録と表示方法を解説します。

目次

AQM1602

液晶モジュール「AQM1602」は小型のキャラクターモジュールで、I2C接続なので配線も少なく使いやすい表示部品です。

この液晶モジュールのコントローラは「ST7032」で、8文字のカスタム文字を登録・表示できるようになっています。

この記事では、PICマイコンを使用したAQM1602(ST7032コントローラ)のカスタム文字の登録方法と表示方法を解説します。

AQM1602のカスタム文字仕様

AQM1602のカスタム文字の扱いについて仕様面から説明します。

文字コード表のカスタム文字登録コード

AQM1602の文字コード表は次のようになっています。

              秋月電子通商 AQM1602取扱説明書より引用

文字コードは8ビット(0x00〜0xFF)で256文字の文字を扱うことができます。

このうち、文字コードが0x00〜0x07の「———- CG RAM ———-」と書かれている部分がカスタム文字です。

文字コード0x00から0x07の8文字分は、好きな形の文字や絵を登録、表示することができるようになっています。

文字サイズは横5ドット、縦8ドットになります。サイズは小さいものの、頑張ればちょっとしたアイコンは作れそうですよね。

なお、登録したデータは電源を切ると消えてしまいますので、プログラムの最初で必要な文字を登録、そのプログラム内で使用するということになります。

文字のデータ形式

カスタム文字を登録するとき、カスタム文字の字形データをAQM1602に送ります。

その字形データの形式は次のようになっています。

例えば、❶ 字形が左側のハートマークの場合、❷ 白いセルを0、黒いセルを1に対応づけます。

このようにすると、それぞれの行は❸の2進数で表現できることになります。

実際に登録する際、それぞれの行を1バイトデータとして扱いますので、ハートマークのデータは❹のように8ビットデータの8バイトの配列で表現できることになります。

カスタム文字を登録する場合、❹の配列を使ってプログラムで処理をします。

カスタム文字データの登録

カスタム文字は1文字8バイトデータで、8文字登録できますので、カスタム文字を登録するには合計で8×8=64バイトのメモリが必要です。

AQM1602では、カスタム文字登録用にこの64バイトのメモリが搭載されていますが、アドレスは次のように0x00〜0x3Fが割り振られています。

例えば、文字コード0x00の文字データを登録する場合、字形の8バイトのデータを0x00〜0x07に記憶することになります。

文字コード0x01の場合、字形データは0x08〜0x0Fに登録することになります。

つまり一般化すると、例えば文字コードがcharCodeの場合、次のアドレスに登録することになります。

(charCode × 8) 〜 (charCode × 8 +7)

カスタム文字データを登録するとき、登録開始アドレスを指定しますので、プログラムでは(文字コード×8)という計算式を使用することにします。

カスタム文字の表示

登録したカスタム文字は、他の既存の文字と同様に扱うことができます。

例えば文字コード0x00にカスタム文字を登録した場合、その文字を表示するには文字コード0x00の文字を表示します。

カスタム文字処理のコマンド

次に、カスタム文字を登録、表示する際、どのようなコマンドをAQM1602に対して発行すれば良いか説明します。

カスタム文字の登録コマンド

AQM1602にカスタム文字を登録するには、「コマンド」に続いて「カスタム文字データ」を送信します。

「コマンド」は「0x40+登録メモリの先頭アドレス」です。

文字コードをcharCode(uint8_t型)とした場合、コマンドは次の値になります。

0x40 + (charCode * 8)

「カスタム文字データ」は8バイトの文字形データです。

例えば、最初のハートマークであれば、次の8バイトのデータを送信すればOKです。

// カスタム文字のパターン(ハートマーク)
uint8_t heartPattern[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000
};

カスタム文字の表示コマンド

カスタム文字を表示するには、登録した文字の文字コードで表示できます。

AQM1602では、文字表示をする場合、制御コードとして0x40を送信したあとに文字コードを送信します。

実装例

最後に、PICマイコンとAQM1602を使用した実装例をご紹介します。

概要

動作確認として次の環境で行いました。

項目内容
開発環境MPLAB X IDE
コンパイラXC v3.00
PICマイコンPIC16F18857
電源PICkitより供給
書き込み機PICkit5

回路

回路図

動作確認のため、なるべくシンプルな回路にしました。

I2C通信はPICマイコンのRC2とRC3ポートを使用してします。

ブレッドボード回路

上の回路図は、ブレッドボードに次のように実装してみました。

プログラム

♡(ハートマーク)を登録して表示するプログラムです。

/*
 * AQM1602 カスタム文字登録&表示サンプルプログラム
 * (ハートマークを表示)
 */

//
// PIC16F18857コンィグレーション設定
//
// CONFIG1
#pragma config FEXTOSC = OFF    // External Oscillator mode selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINT1  // Power-up default value for COSC bits (HFINTOSC (1MHz))
#pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; i/o or oscillator function on OSC2)
#pragma config CSWEN = ON       // Clock Switch Enable bit (Writing to NOSC and NDIV is allowed)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable bit (FSCM timer enabled)
//
// CONFIG2
#pragma config MCLRE = OFF      // Master Clear Enable bit (IO)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config LPBOREN = OFF    // Low-Power BOR enable bit (ULPBOR disabled)
#pragma config BOREN = ON       // Brown-out reset enable bits (Brown-out Reset Enabled, SBOREN bit is ignored)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (VBOR) set to 1.9V on LF, and 2.45V on F Devices)
#pragma config ZCD = OFF        // Zero-cross detect disable (Zero-cross detect circuit is disabled at POR.)
#pragma config PPS1WAY = ON     // Peripheral Pin Select one-way control (The PPSLOCK bit can be cleared and set only once in software)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will cause a reset)
//
// CONFIG3
#pragma config WDTCPS = WDTCPS_31// WDT Period Select bits (Divider ratio 1:65536; software control of WDTPS)
#pragma config WDTE = OFF       // WDT operating mode (WDT Disabled, SWDTEN is ignored)
#pragma config WDTCWS = WDTCWS_7// WDT Window Select bits (window always open (100%); software control; keyed access not required)
#pragma config WDTCCS = SC      // WDT input clock selector (Software Control)
//
// CONFIG4
#pragma config WRT = OFF        // UserNVM self-write protection bits (Write protection off)
#pragma config SCANE = available// Scanner Enable bit (Scanner module is available for use)
#pragma config LVP = ON         // Low Voltage Programming Enable bit (Low Voltage programming enabled. MCLR/Vpp pin function is MCLR.)
//
// CONFIG5
#pragma config CP = OFF         // UserNVM Program memory code protection bit (Program Memory code protection disabled)
#pragma config CPD = OFF        // DataNVM code protection bit (Data EEPROM code protection disabled)

//
// ヘッダファイル
//   int_t型を使用するため、stdint.hをインクルード
//
#include <xc.h>
#include <stdint.h>


//
// 定数定義
//

// I2C Ack/Nack定義
#define I2C_ACK  0x00
#define I2C_NACK 0xff

// LCDモジュールI2Cアドレス
#define LCD_I2C_ADDRESS 0x7c

//
// カスタム文字テータ
//

// カスタム文字の文字コード
const uint8_t CODE_HEART = 0x00;

// カスタム文字のパターン(ハートマーク)
uint8_t heartPattern[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000
};


//
// 関数プロトタイプ宣言
//

// LCDカスタム文字登録関数
void lcdRegisterCustomChar(uint8_t charCode, uint8_t pattern[]);


// LCDモジュール表示制御関数
void lcdInitialize(void);               // LCD初期化
void lcdClearDisplay(void);             // ディスプレイ全消去
void lcdSendCommandData(uint8_t);       // コマンド送信
void lcdSendCharacterData(uint8_t);     // 1文字表示
void lcdSendString(char *);             // 文字列表示
void lcdLocateCursor(uint8_t,uint8_t);  // カーソル位置指定

// LCDモジュールI2Cプロトコル関数
void lcdI2CProtocol(uint8_t, uint8_t, uint8_t);

// I2Cプロトコル各信号の生成関数
void    i2cProtocolStart(void);        // スタートコンディション生成
void    i2cProtocolRepeatStart(void);  // リピートスタートコンディション生成
void    i2cProtocolStop(void);         // ストップコンディション生成
void    i2cProtocolSendData(uint8_t);  // 1バイトデータ送信
uint8_t i2cProtocolReceiveData(void);  // バイトデータ受信
uint8_t i2cProtocolCheckAck(void);     // ACK/NACK信号チェック
void    i2cProtocolSendAck(void);      // ACK送信
void    i2cProtocolSendNack(void);     // NACK送信


// クロック周波数
// __delay_ms()関数が時間基準に使用する
#define _XTAL_FREQ 4000000


//
// main関数
//
void main(void) {

    // 動作周波数設定
    OSCCON1bits.NDIV = 0b0000;  // 分周1:1
    OSCFRQbits.HFFRQ = 0b010;   // 4MHz
    
    // ピン属性設定
    ANSELA = 0b00000000;
    ANSELB = 0b00000000;
    ANSELC = 0b00000000;
    TRISA  = 0b00000000;
    TRISB  = 0b00000000;
    TRISC  = 0b01001100;
    
    //
    // I2C通信ピンのPPS設定
    //
    // 設定ロック解除
    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x00;

    // SCL, SDAピンの割り当て
    SSP1DATPPS = 0x12; // RC2をMSSP1:SDA1に設定
    SSP1CLKPPS = 0x13; // RC3をMSSP1:SCL1に設定
    RC3PPS = 0x14;     // RC3をMSSP1:SCL1に設定
    RC2PPS = 0x15;     // RC2をMSSP1:SDA1に設定

    // 設定ロック
    PPSLOCK = 0x55;
    PPSLOCK = 0xAA;
    PPSLOCKbits.PPSLOCKED = 0x01;

    //
    // I2C通信設定
    //
    // SMP Standard Speed; CKE disabled; 
    SSP1STAT = 0x80;
    // SSPEN enabled; CKP Idle:Low, Active:High; SSPM FOSC/4_SSPxADD_I2C; 
    SSP1CON1 = 0x28;
    // SBCDE disabled; BOEN disabled; SCIE disabled; PCIE disabled; DHEN disabled; SDAHT 300ns; AHEN disabled; 
    SSP1CON3 = 0x00;
    // Baud Rate Generator = 100kHz 
    SSP1ADD = 0x09;
    
    // LCDモジュール電源安定化時間待ち
    __delay_ms(100);
    
    // LCD初期化
    lcdInitialize();
    
    // LCD表示クリア
    lcdClearDisplay();
  
    // カスタム文字の登録
    lcdRegisterCustomChar(CODE_HEART, heartPattern);

    // カスタム文字の表示
    lcdLocateCursor(0, 0);
    lcdSendCharacterData(CODE_HEART);

    // 動作停止
    while(1){
    }

}


//
// LCD制御関数
//

// カスタム文字の登録関数
void lcdRegisterCustomChar(uint8_t charCode, uint8_t pattern[]) {
  // CG RAMアドレス設定(0x40 + 文字位置*8)
  lcdSendCommandData(0x40 + (charCode * 8));
  
  // パターンデータ書き込み(8バイト)
  for (int i = 0; i < 8; i++) {
    lcdSendCharacterData(pattern[i]);
  }
}

//
// LCDモジュールに制御コードまたはデータを送信
//
void lcdI2CProtocol(uint8_t address, uint8_t control_code, uint8_t data) {
    
    i2cProtocolStart();                 // スタートコンディション
    i2cProtocolSendData(address);       // アドレス送信
    i2cProtocolSendData(control_code);  // 制御コード送信 (動作設定=0x00/文字表示=0x40)
    i2cProtocolSendData(data);          // データ送信
    i2cProtocolStop();                  // ストップコンディション

    return;
}

//
// 表示文字データ送信
//   0x40の後にデータを送信
void lcdSendCharacterData(uint8_t data){

    // 表示文字のデータを送信する場合の制御コードは0x40
    lcdI2CProtocol(LCD_I2C_ADDRESS, 0x40, data);
    
    // ウエイト
    //   文字表示の場合はウエイトは必要なくても動作しているが
    //   表示されない場合は1ms程度のウエイトを入れる
    // __delay_ms(1);

    return;
}

//
// コマンド送信
//   0x00の後にコマンドを送信
//
void lcdSendCommandData(uint8_t command){

    // コマンドを送信する場合の制御コードは0x00
    lcdI2CProtocol(LCD_I2C_ADDRESS, 0x00, command);

    // ウエイト
    //   データシートではウエイト時間は26.3us以上になっているが、
    //   それより長くしないと初期化できないケースがあったため1msのウエイトを入れている
    __delay_ms(1);
    
    return;
}

//
// ディスプレイ消去
//
void lcdClearDisplay(void){
    
    lcdSendCommandData(0x01);
    
    return;
}

//
// カーソル位置移動
//    引数は水平方向右側プラスのX軸、垂直方向下側プラスのY軸で、それぞれ0から開始
//    左上の座標が(x=0, y=0)
//
void lcdLocateCursor(uint8_t position_x, uint8_t position_y){
    
    // 文字表示位置指定コマンド送信
    lcdSendCommandData( 0x80 + 0x40 * position_y + position_x );
    
    return;
}

//
// 文字列を送信
//
void lcdSendString(char *str){
    
    // strの文字列を*strが0になるまでLCDモジュールに送信
    while(*str) {
        lcdSendCharacterData(*str);
        str++;
    }
    
    return;
}


//
// LCDモジュール初期化
//
void lcdInitialize(void){

    // 初期化コマンド送信
    lcdSendCommandData(0x38); // 2行モードに設定
    lcdSendCommandData(0x39); // 拡張コマンド選択
    lcdSendCommandData(0x14); // 内部クロック周波数設定
    lcdSendCommandData(0x70); // コントラスト設定(C3:C0 = 0b0000に設定)
    lcdSendCommandData(0x56); // 電源電圧が3.3VなのでBooster=ON、コントラスト設定(C5:C4 = 0b10に設定)
    lcdSendCommandData(0x6c); // オペアンプのゲイン設定
    
    // モジュール内電源安定化のための時間待ち
    __delay_ms(200);
    
    // 初期化コマンド続き
    lcdSendCommandData(0x38); // 通常コマンド選択
    lcdSendCommandData(0x01); // ディスプレイ表示内容クリア
    lcdSendCommandData(0x0c); // ディスプレイ表示
    
    return;
}


//
// I2Cプロトコル制御関数
//

// スタートコンディション生成
void i2cProtocolStart() {
    
    // SSP1CON2レジスタのSENビットを1に設定すると
    // スタートコンディションが生成される
    // 発行が完了するとSSP1IFが1になるのでwhile文で待つ
    SSP1IF = 0;
	SSP1CON2bits.SEN = 1;
	while (SSP1IF == 0) {}
    SSP1IF = 0;
    
	return;
}

// リピートスタートコンディション生成
void i2cProtocolRepeatStart() {
    
	SSP1IF = 0;
	SSP1CON2bits.RSEN = 1;
	while (SSP1IF == 0) {}
    SSP1IF = 0;

	return;
}

// ストップコンディション生成
void i2cProtocolStop() {

    // SSP1CON2レジスタのPENビットを1に設定すると
    // ストップコンディションが生成される
    // 発行が完了するとSSP1IFが1になるのでwhile文で待つ
	SSP1IF = 0;
	SSP1CON2bits.PEN = 1;
	while (SSP1IF == 0) {}
	SSP1IF = 0;

	return;
}

// 1バイトデータ送信
void i2cProtocolSendData(uint8_t data) {

    // SSP1BUFに送信したいデータをセットすると、そのデータが送信される
    // 発行が完了するとSSP1IFが1になるのでwhile文で待つ
	SSP1IF = 0;
	SSP1BUF = data;
	while (SSP1IF == 0) {}
    SSP1IF = 0;
    
	return;
}

// 1バイトデータ受信
uint8_t i2cProtocolReceiveData() {
    
	SSP1IF = 0;
	SSP1CON2bits.RCEN = 1;
	while (SSP1IF == 0) {}
    SSP1IF = 0;

	return SSP1BUF;
}

// Ack/Nackチェック
uint8_t i2cProtocolCheckAck() {
    
	uint8_t ackStatus;

	if (SSP1CON2bits.ACKSTAT) {
		ackStatus = I2C_NACK;
	} else {
		ackStatus = I2C_ACK;
	}

	return ackStatus;
}

// Ack送信
void i2cProtocolSendAck() {
    
    // ACKDTにACKをセット(負論理なので0を設定)
	SSP1CON2bits.ACKDT = 0;

    // NACK信号生成
	SSP1CON2bits.ACKEN = 1;
	while (SSP1CON2bits.ACKEN) {}

	return;
}

// Nack送信
void i2cProtocolSendNack() {
    
    // ACKDTにNACKをセット(負論理なので1を設定)
	SSP1CON2bits.ACKDT = 1;

    // NACK信号生成
	SSP1CON2bits.ACKEN = 1;
	while (SSP1CON2bits.ACKEN) {}

	return;
}

変更履歴

日付内容
2025.4.8新規投稿
通知の設定
通知タイミング
guest
0 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
目次