RasPi  Desk

C言語で温湿度をLCD表示する

 (ネット検索して得られた情報を参考にしてプログラムを試行する)


C言語で温湿度をLCDに表示するプログラムを作る

*プログラムを記述するにはテキストエディタが必要になる。
 RasPiのOSにGUIに馴染むいくつかのテキストエディタがあるが教則本にある Geany が
 プログラム開発では使い勝手が良いと思う。

*RasPi5のI2Cンターフェースを有効にすること。
 → I2Cモード切り替えページへ
 一度でうまくいかないときは再度モード切替の操作を行う必要がある

*回路図


*I2Cインターフェース上にLCDを接続したらI2Cのアドレスを確認する
 プログラムにI2Cアドレスとして記述する必要がある

 sudo i2cdetect -y 1
 正しく回路が繋がっているとLCDのアドレスがhex数値で表示される
 例では 0x27 LCDユニットのI2Cアドレスとなる
 20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- --

*GPIOピンにてDHT11のDATA-INと接続する番号を回路図と異なるピンに変更する場合は
 プログラムにてGPIO番号を変更する必要がある
 ------------------------------------------------------------------------
 RasPi5ではgccコンパイラを使用する。
 GPIOを操作するライブラリとしてwiringPiを使用するので予めインストールしておくこと
 このライブラリを使用することによりI2Cインターフェースなどの細かい制御が予め関数化
 されているのでC言語のプログラムでは関数を呼び出すだけでI2Cを使用できる

 *コンパイル例(CソースファイルがLCD_DHT11.cの場合)
 gcc LCD_DHT11.c -o LCD_DHT11 -lwiringPi

*実行例
 ./LCD_DHT11

 サンプルプログラムの概要は、温湿度センサーDHT11からデータを読み込んで
 I2Cインターフェースに接続されたLCDに温度/湿度を表示する

<サンプルプログラムの動作手順>
  (続にいうフローチャートを文書化したもの)
 ------------------------------------------------------------------------
1.C言語のプログラムで使用するヘッダー類を記述する
 ・stdio.h stdlib.h string.h wiringPi.h wiringPiI2C.h

2.プログラムで使用する定数類を定義する
  // LCD コマンド定義
  LCD_CHR  1 // データモード
  LCD_CMD  0 // コマンドモード

  LCD_LINE_1 0x80 // 1行目
  LCD_LINE_2 0xC0 // 2行目

  LCD_BACKLIGHT 0x08  // バックライト ON
  ENABLE 0b00000100   // Enable ビット

  // LCD の I2C アドレス(モジュールによって異なる)
  I2C_ADDR 0x27

  // DHT11からデータを読み込むビットクロック数
  MAX_TIMINGS 85

  // GPIOの物理ピン番号(DHT11のデータピンと接続)
  DHT_PIN 7 // GPIO4

 ------------------------------------------------------------------------
3.ソース内に記述されている関数のプロトタイプ宣言を行う
  void lcd_init(void);
  void lcd_byte(int bits, int mode);
  void lcd_toggle_enable(int bits);
  void lcd_loc(int line);
  void lcd_text(const char *s);

  int read_dht11_data() ;

 ------------------------------------------------------------------------
4.グローバルで使用するワーク変数
  int data[5] = {0, 0, 0, 0, 0};  //DHT11から受取る5バイト分
  char TempStr[40];  // 温度表示文字列
  char HumiStr[40];  // 湿度表示文字列

  int fd; // I2C ファイルディスクリプタ

 ------------------------------------------------------------------------
5.C言語のプログラムにて最初に実行される関数main()を記述する
 ・変数定義
  int res ;
  char dispBuf[40]; //LCDに書込む文字列形成用バッファ
  int FlgLcdMark=0; //LCDの文字列に*を表示するタイミングを決めるフラグ

 ・ライブラリwiringPi関数にてwiringPi 初期化する
  エラーなら「wiringPi の初期化に失敗しました」を表示して終了

 ・ライブラリwiringPiI2CSetup関数にてアドレス0x27のI2Cハンドルを取得する
  エラーなら「I2C デバイスが見つかりません (アドレス 0x27)」を表示して終了

 ・関数lcd_init()にてLCDの初期化を行う

 ・LCDに以下のオープニングメッセージを表示する
   1行目:Hello Raspberry
   2行目:Pi 5 LCD Test
  2秒間、時間を費やす

 ・以下の処理を繰り返す
   a.関数read_dht11_data()にてDHT11から温湿度データ5バイト(40bit)を取得
   b.正常に温湿度データを取得できたら以下をLCDに表示
    LCD1行目:Temp:<取得した温度> C <*>
    LCD2行目:Humi:<取得した湿度> % <*>
    <*>は、繰り返すごとに*マークを表示、非表示する
      →プログラムが動いていることを示すため
   c.温湿度データを取得できなかったら以下をLCDに表示する
    LCD1行目:Temp:------------
    LCD2行目:Humi:------------

   d.2秒間、時間を費やす

 ・プログラムを終了するには ctrl-Cで終了させる
 ------------------------------------------------------------------------
6.関数lcd_init(void)
 ・LCDユニットに対してI2Cを通して電源投入後の初期化手順の6コマンドをLCDに送る
 a)コマンド(0x33)Initialize
 b)コマンド(0x32)Initialize
 c)コマンド(0x06)Cursor move direction
 d)コマンド(0x0C)Display On,Cursor Off, Blink Off
 e)コマンド(0x28)Data length, number of lines, font size
 f)コマンド(0x01)Clear display
 ・時間(5ms)を待つ(LCD側の動作との整合)

 ------------------------------------------------------------------------
7.関数lcd_byte(int bits, int mode)
 ・LCDに対する1バイトを2バイトに成形してLCDに書込み、その後、取込タイミング情報を書込む
   mode=0:コマンド/mode=1:表示データ、LCDの文字を照らすLEDをonにする
 a)上位データ形成:bits8bitから上位4bitを取得してmodeとLCD_BACKLIGHTビットを付加する
 b)下位データ形成:bits8bitから下位4bitを取得して上位4ビットへシフトして
          modeとLCD_BACKLIGHTビットを付加する

 c)I2CインターフェースでLCDアドレスに上位データを書込む
 d)LCDにデータを取り込むタイミング情報を書込む

 e)I2CインターフェースでLCDアドレスに下位データを書込む
 d)LCDにデータを取り込むタイミング情報を書込む

 ------------------------------------------------------------------------
8.関数lcd_toggle_enable(int bits)   //(E) Enable パルス作成
 ・I2Cを通して書込んだデータをLCDが内部に取込むタイミング情報を送る
   タイミング情報(E)をlow→hi→low(トグル)する
 a)時間(500μs)を待つ(LCD側の動作との整合)
 b)LCDに対する1バイトに取込タイミング情報=1を付加して書込む
 c)時間(500μs)を待つ(LCD側の動作との整合)
 d)LCDに対する1バイトに取込タイミング情報=0を付加して書込む
 e)時間(500μs)を待つ(LCD側の動作との整合)

 ------------------------------------------------------------------------
9.関数 lcd_loc(int line)
 ・LCDに対してカーソル(表示位置)のコマンドを送る

 ------------------------------------------------------------------------
10.関数 lcd_text(const char *s)
 ・lineで指定された行にs文字列を表示する
 a)LCDの1行分の文字列sを1文字ずつ データ書込みを繰り返す

 ------------------------------------------------------------------------
11.関数 read_dht11_data()
 ・温湿度センサーDHT11からGPIOピンを通してデジタル値を取得する

 ・変数定義
  int laststate = HIGH; DHT11と接続されているピンの状態を保存する
  int counter = 0;    GPIOピンから取得するデータビット数
  int i=0, j = 0;    整数値のワーク
  int iBitChgCnt = 0;    デバッグ用変数
  int res = -1;     関数の戻り値

 ・DHT11から取得する5バイト(40ビット)バッファに0を書込み
 ・GPIOピンを出力モードにする
 ・GPIOピンに0レベルを書込む(lowレベルにする)
 ・18ms待つ
 ・GPIOピンに1レベルを書込む(Highレベルにする)
 ・40μs待つ

 ・GPIOピンを入力モードにする
 ・以下の処理を85回程度繰り返す
   (正常にデータが取得できていると84回繰り返す)

   a)直前の入力レベルと異なる状態になるまでGPIOピン7のレベルを読み続ける
    およそ255μS以上レベルが変化しなかったらDHT11データ取得ループから抜ける
    およそ255μS以内にレベルが変化したら、そのレベルを保存する
   b)ループに入って4回以上レベルが変化して偶数回のレベル変化のタイミングで
     データビット取得を行う

    データバイトを1ビットずつ左にシフトして最下位ビットの位置に
    →およそ17μs以上でレベルが変化したら1、
        16μs以内のレベル変化だったら0を置く

  c)上記のデータ形成の繰り返しで40ビット(5バイト)を取得できていたら
   チェックサムを確認する
   チェックサムが合致したら正常データと判定して温度文字列、湿度文字列を作成する

  d)40ビットを正常に取得できていなかったら温度文字列、湿度文字列を空にする
   エラー表示として[Data not good,<5バイトの取得データバイト>]

  e)40ビット(5バイト)のデータが正しく取得できたら1、失敗なら-1を返して
   関数を終了する

 ------------------------------------------------------------------------