第44回 液晶モジュール動作確認プログラム

液晶モジュールに温湿度・気圧データを表示するプログラムを作成する前に、動作確認目的として簡単な文字列を表示するプログラムを作成します。

目次

動作確認で行うこと

温湿度・気圧センサから読み取った値を液晶モジュールに表示する前に、簡単な文字列を液晶モジュールに表示するプログラムを作成して動作確認します。

この記事では、固定の文字列(例えば「Hello!」など)を、液晶ディスプレイの左上の位置から表示するプログラムを作成してみます。作成したプログラムをベースに、ご自分で表示文字列や表示位置を変更して、液晶モジュールの制御方法の理解を深めていただければと思います。

関数作成

実践編第40回でI2C通信の関数を作成しました。今回はそれらのI2C通信関数を使用して順番に機能関数を作成していきます。

I2C通信関数

最初に作成する関数は、以下のI2C通信手順を実現する関数です。

Pic practice 44 i2c protocol lcd

なお、ACK/NACKについてはチェックしないことにします。というのは、ACK/NACKチェックをしてNACK(了解できなかった)という場合のPICマイコン側での対処方法が難しいためです。

このI2C通信手順を実現する関数名は「lcdI2CProtocol」とし、引数は、スレーブアドレス(address)、データ種別を示す1バイト(control_code)、データ1バイト(data)にします。

ACK/NACKチェックはしませんので、関数は以下のように簡単に作成してみました。

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

    return;
}

液晶モジュールのスレーブアドレスは0x7cですので、例えば表示文字として0x41(=’A’)を送りたい場合は、

lcdI2CProtocol(0x7c, 0x40, 0x41);

と記述すればOKです。

動作設定関数と文字表示関数

動作設定や文字表示を行う場合、先ほどのように「lcdI2CProtocol(0x7c, 0x40, 0x41);」などと関数コールすればとりあえず文字表示できますが、このようにするとプログラム記述がちょっと面倒になります。

というのは、動作設定を行う場合、動作設定のためのデータを液晶モジュールに送信したあと、液晶モジュール側で設定処理が行われるためちょっとの時間待ってあげる必要があります。動作確認したところ、動作設定の場合は1つの設定を行うたびに1ms程度時間待ちをした方が安定して動作するようです。また文字表示の場合は待ち時間なしで動作しました。

実はデータシートを見ると、以下のようにそれぞれの動作設定後は26.3μsより長い時間待つ必要があります。

Pic practice 44 command wait

ただ、いろいろな条件で試したところ、50μs〜70μsの処理待ちをしてもうまく動作しないケースがありました。特に深い意味はありませんが、動作設定後の待ち時間は1msでプログラムを作成しました。

先ほどのlcdI2CProtocol関数を使用して、待ち時間込みの関数として「lcdSendCommandData」という関数を作成します。引数は動作設定データの1バイトとしました。

void lcdSendCommandData(uint8_t command){

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

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

また文字表示ですが、こちらは色々試したところ表示文字データを液晶モジュールに送信したあと、特に待ち時間なしでも問題なく表示されていました。ただ電源の状態によっては問題が発生する可能性がありますので、コメントとして記述しておきました。関数名は「lcdSendCharacterData」とし、引数は表示文字データの1バイトとしました。

void lcdSendCharacterData(uint8_t data){

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

    return;
}

液晶モジュール初期化関数

液晶モジュールの初期化関数は、データシートを元に作成しました。関数名は「lcdInitialization」として引数はありません。先ほどの初期化処理を参考に以下のように作成しました。

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;
}

なお、コントラスト設定はC5〜C0の6ビットで行います。上のプログラムの設定は0b100000(=32)としていますが、濃度調整をしたい場合はこの値を変更してみてください。

また、0x6cの設定のあとはデータシートによると200ms以上のウエイトが必要となっていますので、200msのウエイトを入れてあります。

表示内容クリア関数

表示内容をクリアする関数も作成しておきます。関数にするまでもないですが、わかりやすくしたいので「lcdClearDisplay」という関数を作成しておきます。表示内容をクリアするには、動作設定の0x01を液晶モジュールに送信するだけでOKです。以下のように作成しました。

void lcdClearDisplay(void){
    
    lcdSendCommandData(0x01);
    
    return;
}

表示位置指定関数

文字の表示位置を指定する関数も作成しておきます。前回の記事で説明しましたが、以下のように座標位置指定できるようにしておきます。

Pic practice 43 positioning

関数名は「lcdLocateCursor」とします。引数は座標(x, y)として、position_xとposition_yとしました。

表示位置はアドレスで指定できることを説明しましたが、アドレスの計算は以下のようになります(前回の説明内容です)。

  • yが0の場合
    アドレス = 0x00 + x
  • yが1の場合
    アドレス = 0x40 + x

アドレスがわかったら、コマンドは第7ビットを1にすればいいので、コマンドは以下のようになります。

コマンド = アドレス + 0x80
または、
コマンド = アドレス | 0x80
(|は論理和)

これをこのままプログラムにすると以下のようになります。

void lcdLocateCursor(uint8_t position_x, uint8_t position_y){
    
    uint8_t address;  // 文字表示位置(アドレス)
    uint8_t command;  // 表示位置指定コマンド
    
    // Y座標位置のアドレス算出
    switch( position_y ) {
        // 1行目の場合
        case 0:
            address = 0x00;
            break;
        // 2行目の場合
        case 1:
            address = 0x40;
            break;
    }
    
    // X座標を加えてアドレスを算出する
    address += position_x;

    // 第7ビットを1にしてコマンドにする
    command = address + 0x80;
    
    // 文字表示位置指定コマンド送信
    lcdSendCommandData( command );
    
    return;
}

ただ、このようにプログラムを作成すると冗長になってしまいますので、短くしたいと思います。

y座標が0の時は0x00、1のときは0x40です。これは条件判断で算出するのではなく、「0x40 * y」を計算すれば求めることができます。

0x40 * 0 = 0x00
0x40 * 1 = 0x40

となりますので、ここまで計算できれば、あとはx座標を加算して、コマンドにするためにさらに0x80を加算すればアドレスが求められます。これらのことから、表示位置指定の関数は以下のように作成してみました。

void lcdLocateCursor(uint8_t position_x, uint8_t position_y){
    
    lcdSendCommandData( 0x80 + 0x40 * position_y + position_x );
    
    return;
}

文字列表示

文字を表示する場合、1文字の場合は簡単です。例えば「A」という文字を表示したい場合、

lcdSendCharacterData(0x41);

または、’A’は0x41ですので、

lcdSendCharacterData('A');

とすれば、現在設定されている表示位置に「A」が表示されます。

文字列を表示した場合はどうすればよいでしょうか。「Hello!」と表示したい場合は、一文字ずつ上のようにlcdSendCharacterData関数で表示すればよいですが、この方法ですと面倒です。

ちょっと話が飛びますが、C言語を勉強された時、一番最初に出てきたのは「printf」だったのではないでしょうか。「printf(“Hello, world.”);」というようなプログラムが出てきたのではないかと思います。このprintfを使って液晶モジュールに文字列が表示できれば便利ですよね。

XCコンパイラでは、このprintf関数の出力先をカスタマイズできるようになっています。printf関数の引数の文字列は、通常ディスプレイなどに表示されます。XCコンパイラのprintf関数は、文字表示の部分はputch関数を使用しており、自分でputch関数を定義することにより、出力先をカスタマイズすることができるようになっています。

putchは引数がchar型の1バイトですので、以下のようにputc関数を記述しておくと液晶モジュールに文字列を表示することができます。

void putch(char character) {

    lcdSendCharacterData(character);
    
    return;
}

この関数をプログラム内に記述しておけば、通常のprintf関数の表示先が液晶モジュールになります。printf関数ですので、文字列だけではなく、以下のように数字を表示することが可能となります。

printf("Humidity = %d", 57);
printf("Temp. = %2.1f", 27.5);

実行結果 :

Humidity = 57
Temp. = 27.5

次回の記事では、温湿度と気圧データを表示するプログラムを作成しますが、数値の表示はこのようなprintf関数を使用します。

動作確認プログラム

これで必要な関数ができました。動作確認のプログラムは以下のようになります。注意点としては、電源投入後は液晶モジュールの動作が安定するまで100ms1程度ウエイトします。

/*
 * 液晶モジュール動作確認プログラム
 * 
 * https://tool-lab.com/
 *
 * 更新
 *   2018. 7.28: 新規作成
 * 
 */

//
// 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>
#include <stdio.h>


//
// 定数
//

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

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

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

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

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

// I2Cプロトコル各信号の生成関数
void    i2cProtocolStart(void);        // スタートビット生成
void    i2cProtocolStop(void);         // ストップビット生成
void    i2cProtocolSendData(uint8_t);  // 1バイトデータ送信
uint8_t i2cProtocolCheckAck(void);     // ACK信号チェック

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

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

    // 動作周波数設定
    OSCCON1bits.NDIV = 0b0000;  // 分周1:1
    OSCFRQbits.HFFRQ = 0b010;   // 4MHz
    
    // ピン属性設定
    ANSELC = 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();

    // LCD表示位置を左上に設定
    lcdLocateCursor(0, 0);

    // 表示位置からtest_文字列を表示
    printf("Hello!");

    // 動作ストップ
    while(1){
    }

}

//
// 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;
}

//
// printf関数の文字出力部分
//
void putch(char character) {

    lcdSendCharacterData(character);
    
    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 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;
}

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

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

	return ackStatus;
}

更新履歴

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

毎回、しっかりと学習させていただいています。
参考コードをそのままコピペすれば確実に作動しますが、
学習のためと思い、1から自分で入力してみました。
しかし、次のようなエラーが出ます。

make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory ‘C:/Users/yasug/MPLABXProjects/kimu_/18857_I2C_Saidokakunin_R6.10.30.X’
make -f nbproject/Makefile-default.mk dist/default/debug/18857_I2C_Saidokakunin_R6.10.30.X.debug.elf
make[2]: Entering directory ‘C:/Users/yasug/MPLABXProjects/kimu_/18857_I2C_Saidokakunin_R6.10.30.X’
“C:\Program Files\Microchip\xc8\v2.46\bin\xc8-cc.exe” -mcpu=16F18857 -c -D__DEBUG=1 -mdebugger=none  -mdfp=”C:/Program Files/Microchip/MPLABX/v6.10/packs/Microchip/PIC16F1xxxx_DFP/1.18.352/xc8″ -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -mwarn=-3 -Wa,-a -DXPRJ_default=default -msummary=-psect,-class,+mem,-hex,-file -ginhx32 -Wl,–data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -mno-default-config-bits  -std=c99 -gdwarf-3 -mstack=compiled:auto:auto   -o build/default/debug/newmain.p1 newmain.c 
newmain.c:147:5: warning: implicit declaration of function ‘lcdLocateCursor’ is invalid in C99 [-Wimplicit-function-declaration]
  lcdLocateCursor(0, 0);
  ^
newmain.c:222:6: error: conflicting types for ‘lcdLocateCursor’
void lcdLocateCursor(uint8_t position_x, uint8_t position_y) {
   ^
newmain.c:147:5: note: previous implicit declaration is here
  lcdLocateCursor(0, 0);
  ^
1 warning and 1 error generated.
(908) exit status = 1
make[2]: *** [build/default/debug/newmain.p1] Error 1
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2
nbproject/Makefile-default.mk:92: recipe for target ‘build/default/debug/newmain.p1’ failed
make[2]: Leaving directory ‘C:/Users/yasug/MPLABXProjects/kimu_/18857_I2C_Saidokakunin_R6.10.30.X’
nbproject/Makefile-default.mk:85: recipe for target ‘.build-conf’ failed
make[1]: Leaving directory ‘C:/Users/yasug/MPLABXProjects/kimu_/18857_I2C_Saidokakunin_R6.10.30.X’
nbproject/Makefile-impl.mk:39: recipe for target ‘.build-impl’ failed

BUILD FAILED (exit value 2, total time: 769ms)

gonta
gonta
返信  管理者
17 日 前

早速ご回答頂き、有り難う御座います。

エラーがでるのは、みすたいぷしているのではと、何回も入力し直したりしていました。
入力時にコメントを入れるときに全角文字にして、半角に戻さずリターンして、
全角スペースが残ってしまっているとも考え、再入力などもしてみました。
再度、慎重に再入力してみます。

gonta
gonta
返信  管理者
16 日 前

結果のご報告です。

結果的には、ご指摘の通り、ミス入力をしていたことでした。
朝から、全角文字を使わずに入力したり、ミスの出た行の周辺を徹底的に再入力したりしていました。
質問したエラーの出たサンプルコードもその周辺を再度入力し直して、OKとなりました。
今入力していたのは、この回(第44回のサンプルコードです)。

有り難う御座いました。

gonta
gonta
返信  管理者
15 日 前

おはよう御座います。
そうか、写経なんだ。心込めて一文字ずつ打ち込まなければいけないのだ。
エラーメッセージは、打ち込んだ人間のエラーも示してくれているので、しっかりと確認しなければならないことを学びました。
もう少し、実践編でI2C通信で楽しみます。
有り難う御座いました。

gonta
gonta
4 月 前

入門編から学ばせて頂き有り難う御座います。
実践編で、I2C通信に出会い、楽しませて頂いています。
さて、今回の第44回のI2C通信の動作確認プログラムでは、沢山の関数を作成してI2C通信によりLCDに文字表示ができるようになりましたが、沢山作成された関数がどのように結びついているのかが気になりました。
メイン関数で、I2Cの初期設定を行った後、サンプルプログラムの138行のLCD初期化というコメントの次の lcdInitialize(); と言う関数をたどって行っても、スタートコンディション生成などの一連の関数にたどり着けません。 どこか間違っているのだと思いますが、それが解りません。
よろしくお願いいたします。

gonta
gonta
返信  管理者
4 月 前

早速の回答有り難う御座います。
どこかで、I2C通信の基本関数を呼び出しているとはそれなりに理解しているつもりでも、関数を追いかけると迷ってしまう未熟さで質問しました。
今はもう、よれよれですので、明日、じっくりと確認させて頂きます。
自分が解っていると他の人もこれで解るだろうと思ってしまうようです。
ですが、そうでも無いことが世の常ですね。

CKW
CKW
1 年 前

こんにちは。現在、pic16f1827を用いて、LCDモジュール「AQM1602Y」を使おうとしているのですが、一瞬文字が出たと思ったらすぐに    ◾️◾️◾️◾️◾️
                          ◾️◾️◾️◾️◾️
のように文字が潰されてしまいます。原因がわからず困っているのですが、お力添えをいただけないでしょうか?
以下、プログラム全文です(ほぼ他人のもののコピペですが)。

//ヘッダファイルの読み込み
//————————————————————–
#include <xc.h>
#include <stdio.h> // printfを使うために必要なライブラリ

// クロック周波数指定
//————————————————————–
#define _XTAL_FREQ 32000000

// LCDディスプレイ(AE-AQM1602A)のアドレス
//————————————————————–
#define LCD_addr 0x7C

// プロトタイプ宣言
//————————————————————–
void I2C_init(void);
void I2C_start(void);
void I2C_stop(void);
void I2C_write(unsigned char dat);
void LCD_cmd(unsigned char cmd);
void LCD_char(unsigned char dat);
void LCD_init(void);
void LCD_clear(void);
void LCD_home(void);
void LCD_cursor(unsigned x,unsigned y);
void putch(char text);

// ============================================================
//
//        メインプログラム
//
// ============================================================
void main() {
   
  //———————————————————-
  //        PICマイコン初期設定
  //———————————————————-
  OSCCON = 0b01110000;  // 内部クロック周波数:8MHz
  ANSELA = 0b00000000;  // 全てデジタルモード
  ANSELB = 0b00000000;
  TRISA = 0b00000000;   
  TRISB = 0b00010010;  // SCL、SDAを入力ピンに設定
   
   
  //———————————————————-
  //      シリアル通信(I2C)初期設定
  //———————————————————-
  SSP1STAT = 0b10000000; // 標準モード(100kHz)
  SSP1CON1 = 0b00101000; // マスター(制御する側)に設定
  SSP1ADD = 0b01001111;  // 通信速度 = Fosc/(4*Clock)-1 = 32MHz/(4*100kHz)-1 = 79(10進数)
   
   
  //———————————————————-
  //       LCDディスプレイと通信
  //———————————————————-
   
  // ディスプレイの初期化
  LCD_init();
    
  // 1列目に表示
  LCD_cmd(0x80);
  printf(“Hello World!”);

  // 2列目に表示
  LCD_cmd(0xC0);
  printf(“0123456789”);
   
}

// ============================================================
//
//          I2C 通信開始
//
// ============================================================
void I2C_start() {
  SSP1CON2bits.SEN = 1;
  while(SSP1CON2bits.SEN);
}

// ============================================================
//
//          I2C 通信終了
//
// ============================================================
void I2C_stop() {
  SSP1IF = 0;
  SSP1CON2bits.PEN = 1;
  while(SSP1CON2bits.PEN);
  SSP1IF = 0;
}

// ============================================================
//
//          I2C データ送信
//
// ============================================================
void I2C_write(unsigned char dat) {
  SSP1IF = 0;
  SSP1BUF = dat;
  while(!SSP1IF);
}

// ============================================================
//
//      LCDディスプレイの初期化
//
// ============================================================
void LCD_init() {
  __delay_ms(40); //40ms wait
  LCD_cmd(0x38); //8bit,2line
  LCD_cmd(0x39); //IS=1 : extention mode set
  LCD_cmd(0x14); //Internal OSC Frequency
  LCD_cmd(0x7A); //Contrast set
  LCD_cmd(0x56); //Power/ICON/Contrast Control
  LCD_cmd(0x6C); //Follower control
  __delay_ms(200);//200ms wait
  LCD_cmd(0x38); //IS=0 : extention mode cancel
  LCD_cmd(0x0C); //Display ON
  LCD_cmd(0x01); //Clear Display
  __delay_ms(2); //wait more than 1.08ms
}

// ============================================================
//
//          LCDへコマンドを送信
//
// ============================================================
void LCD_cmd(unsigned char cmd) {
  I2C_start();
  I2C_write(LCD_addr);
  I2C_write(0x00);  // 0x00(コマンドの送信を示すbit)
  I2C_write(cmd);
  I2C_stop();
}

// ============================================================
//
//       LCDへ表示するデータの送信
//    printf文を使用するためにputch関数を使用する
//
// ============================================================
void putch(char text) {
  I2C_start();
  I2C_write(LCD_addr);
  I2C_write(0x40);  // 0x40(データの送信を示すbit)
  I2C_write(text);
  I2C_stop(); 
}

CKW
CKW
返信  CKW
1 年 前

すみません、自己解決しました。単純にコントラストの設定が高すぎて文字が潰れているだけでした。お騒がせして申し訳ないです

ゆかだんぼう
ゆかだんぼう
2 年 前

このサイトを毎日見てマイコンの勉強をしています!
I2Cに関しての質問なのですが、お力添えいただけますでしょうか。

LCDセグメントドライバについての質問です。
開発環境:MPLAB X IDE
開発言語:C
CPU:PIC16F18346
ドライバ:bu9797afuv
I2C通信で全点灯コマンドを入力しているのですが、動作しません。
以下、プログラムです。

//****************C header file config.h**************//

// PIC16F18346 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FEXTOSC = OFF    // FEXTOSC External Oscillator mode Selection bits (Oscillator not enabled)
#pragma config RSTOSC = HFINT32 // Power-up default value for COSC bits (HFINTOSC with 2x PLL (32MHz))
#pragma config CLKOUTEN = OFF   // Clock Out Enable bit (CLKOUT function is disabled; I/O or oscillator function on OSC2)
#pragma config CSWEN = OFF      // Clock Switch Enable bit (The NOSC and NDIV bits cannot be changed by user software)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config MCLRE = OFF      // Master Clear Enable bit (MCLR/VPP pin function is digital input; MCLR internally disabled; Weak pull-up under control of port pin's WPU control bit.)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config WDTE = OFF       // Watchdog Timer Enable bits (WDT disabled; SWDTEN is ignored)
#pragma config LPBOREN = OFF    // Low-power BOR enable bit (ULPBOR disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled)
#pragma config BORV = LOW       // Brown-out Reset Voltage selection bit (Brown-out voltage (Vbor) set to 2.45V)
#pragma config PPS1WAY = OFF    // PPSLOCK bit One-Way Set Enable bit (The PPSLOCK bit can be set and cleared repeatedly (subject to the unlock sequence))
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable bit (Stack Overflow or Underflow will not cause a Reset)
#pragma config DEBUG = OFF      // Debugger enable bit (Background debugger disabled)

// CONFIG3
#pragma config WRT = OFF        // User NVM self-write protection bits (Write protection off)
#pragma config LVP = OFF        // Low Voltage Programming Enable bit (High Voltage on MCLR/VPP must be used for programming.)

// CONFIG4
#pragma config CP = OFF         // User NVM Program Memory Code Protection bit (User NVM code protection disabled)
#pragma config CPD = OFF        // Data NVM Memory Code Protection bit (Data NVM code protection disabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.


#include <xc.h>
#include <stdio.h>
#include <stdint.h>         /* For uint8_t definition */

#define _XTAL_FREQ 32000000

//プロトタイプ宣言//
void PIC_init(void);

// LCDドライバモジュール表示制御関数
void lcdInitialize(void);               // LCD初期化

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

// I2Cプロトコル各信号の生成関数
void    i2cProtocolStart(void);        // スタートビット生成
void    i2cProtocolStop(void);         // ストップビット生成
void    i2cProtocolSendData(uint8_t);  // 1バイトデータ送信
uint8_t i2cProtocolCheckAck(void);     // ACK信号チェック


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

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

//**************C header file drvcon.h**************//
#include "config.h"

//
// LCDモジュール初期化
//
void lcdInitialize(void){
    __delay_us(100);
    i2cProtocolStop();       //ストップコンディション生成
    i2cProtocolStart();      //スタートコンディション生成
    i2cProtocolSendData(LCD_I2C_ADDRESS);  //I2Cアドレス送信
    i2cProtocolSendData(0xEA);  //ICSET Software Reset実行
    i2cProtocolSendData(0xF0);  //BLKCTL Blink OFF
    i2cProtocolSendData(0xA4);  //DISCTL 80Hz、フレーム反転、Power save mode1
    i2cProtocolSendData(0xE8);  //ICSET 内部クロック入力
    i2cProtocolStop();       //ストップコンディション生成
    
    i2cProtocolStart();     //スタートコンディション生成
    i2cProtocolSendData(LCD_I2C_ADDRESS);  //I2Cアドレス送信
    i2cProtocolSendData(0xFA);  //APCTL 全点灯コマンド
    i2cProtocolSendData(0xA8);  //MODESET Display ON
    i2cProtocolStop();      //ストップコンディション生成
    
    return;
}


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

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

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

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

	return ackStatus;
}



//***************C header file picinit.h**************// 
#include "config.h"

void PIC_init(void){
    ANSELB = 0;  //PORTBをデジタルピンに設定
    TRISB = 0b10100000;//RB7、RB5を入力それ以外のPORTBは出力

    ODCB5  = 1;           // RB5をオープンドレイン
    ODCB7  = 1;           // RB7をオープンドレイン

    SSP1CLKPPS = 0x0F;    // RB7をCLK入力に指定
    RB7PPS     = 24;      // RB7をCLK出力に指定
    SSP1DATPPS = 0x0D;    // RB5をDATに入力指定
    RB5PPS     = 25;      // RB5をDAT出力に指定
    
    SSP1STAT = 0x80;   //スルーレート制御はOff
    SSP1ADD = 0x13;      //クロック設定 400kHz 32M
    SSP1CON1 = 0x28;     //I2C Master mode
    
}

//***********main.c**************//
#include "config.h"
#include "drvcon.h"
#include "picinit.h"


void main(void){
    PIC_init();      //PICマイコン初期設定
    lcdInitialize(); //LCDセグメントドライバ初期設定
}
https://teratail.com/img/icon/copy_12.svg

PIC16F18346データシート
[https://akizukidenshi.com/download/ds/microchip/pic16f183x6.pdf]
bu9797afuvデータシート
[https://fscdn.rohm.com/jp/products/databook/datasheet/ic/driver/lcd_segment/bu9797afuv-j.pdf]
マイコン、LCDドライバ、LCD表示器の接続は完了していて
電源を入れ、上記のプログラムを書き込みましたがLCD表示器の動作は確認できませんでした。

よろしくお願いします。

目次