気ままに備忘録

電子工作・人力飛行機についてのメモ

デジタル風向計開発物語 ①LCDつかう

「そうだ、風向計を作ろう」
ということでここからは風向計に関する記事を書いていきます。

何を言っているかわからないと思うので説明します。現在私は風向計を作っています。目的は、テストフライトの時に風速・風向を観測する必要があり、現在の吹き流しよりも、リアルタイムに、かつ正確に風向・風速を計測できるようにするためです。

そういうわけで、この風速・風向計には次のようなものを搭載します。
風速・風向を読む…ロータリーエンコーダ―
PCに結果を送信する…Xbee
手元でも見れるように…LCD
さらに、オプションとして、さらにいろいろなものを搭載していく予定です。

では、さっそくその風速・風向計の開発の様子を書いていきます。

[本題]LCDを使用する

というわけで今回はLCDを使っていきます。ちょうどそのころ、マイクロマウスサークルで「こんなものを使っている人がいる」ということで、それを参考にLCDを買ってきました。
「I2C接続小型LCDモジュール(8×2行)ピッチ変換モジュール[AE-AQM0802]」(秋月電子通商
http://akizukidenshi.com/catalog/g/gM-09109/
LCD本体のみ、DIP化基板付き、組み立て済みが売られています。今回は組み立て済みのモジュールを使用しました。DIP化基板にはプルアップ抵抗までついているので、こちらをまずは使うことがおすすめです。

では、やっていきましょう。

[準備]開発用意

・用意するもの

マイコン:STM32F303K8 Nucleo(STmicroelectronics製)
LCD:AE-AQM0802
抵抗:10kΩ×1

マイコンにはSTM32を使用しました。このNucleoボードなら、電源回りなどの回路を作る必要がないので、試作が簡単にできます。
LCDは先に述べた通り。
最後に、RESETピンをプルアップするため、10kΩの抵抗を用意しました。

・開発環境

STM32の開発環境を明記しておきます。
ライブラリ:HAL
HAL初期設定:CubeMX
IDE:System Workbench for STM32(SW4STM32)

[回路]

回路の組み方は次の図のようになります。
今回はLCDのみのRESETはかけないことにするので、RESETピンは3.3Vに直結します。
I2Cペリフェラルを用いることができるピンは決まっているので、図のように配線します。

f:id:tomo_amber:20190309010953p:plain
LCD_配線図

[設定]

まずはCubeMXの設定。左側のタブの中から、「Connectivity > I2C1」を選択し、その横に現れるボックスの一番上を「Disable」を「I2C」に変更すればOKです。
下の画像では、他にもいろいろ設定していますが、特に必要はないです。

f:id:tomo_amber:20190309005540p:plain
LCD用CubeMXの設定

[プログラム]

これが一番難しい…。要はI2C接続で必要なデータを送り込めばいいのだが…。いかんせん知識不足でどう書けばいいのかわからない。Arduino用のライブラリはたくさんありますが、これをSTM32化するのも一苦労。やってみて、結局失敗。手詰まりか…と思っていると光明が。ある日、「STM32用のライブラリあるよ」と聞きつけ、目にしたのが下のサイト。
https://lawn-tech.jp/aqm0802a.html
これをベースに、アレンジや、風向・風速表示を見越した改造を施した結果のプログラムが下の通り。最終的に読み込んだ風速・風向を出力できるような関数を用意しています。

定義したグローバル関数・マクロ

#define LCDADD 0x7c
uint32_t LastCount2 = 0;
float velocity = 0.0;

初期化(先のブログリンク参照)

//send Instruction for LCD
int LCD_WriteInstraction(uint8_t data)
{
    uint8_t buf[] = { 0x00, data };
    int status = HAL_I2C_Master_Transmit(&hi2c1, LCDADD, buf, 2, 1000);
    HAL_Delay(1);
    return status == HAL_OK;
}
//initialize LCD
void LCD_Init()
{
    LCD_WriteInstraction(0x38);     // Function set
    LCD_WriteInstraction(0x39);     // Function set
    LCD_WriteInstraction(0x14);     // Internal OSC frequency
    LCD_WriteInstraction(0x70);     // Contrast set
    LCD_WriteInstraction(0x56);     // Power/ICON/Contrast set
    LCD_WriteInstraction(0x6c);     // Follower control
    HAL_Delay(200);
    LCD_WriteInstraction(0x38);     // Function set
    LCD_WriteInstraction(0x0c);     // Display ON/OFF control
    LCD_WriteInstraction(0x01);     // Clear Display
    HAL_Delay(1);
}

文字の表示(先のブログリンク参照)

//write data on DDRAM
int LCD_WriteData(uint8_t data)
{
    uint8_t buf[] = { 0x40, data };
    int status = HAL_I2C_Master_Transmit(&hi2c1, LCDADD, buf, 2, 1000);
    HAL_Delay(1);
    return status == HAL_OK;
}
//print characters on LCD
void Puts(const char *p)
{
    for ( ; *p ; p++)
    {
        LCD_WriteData(*p);
    }
}
//print start signals on LCD
void HelloWorld()
{
    LCD_Init();
    Puts("Hello,");
    LCD_WriteInstraction(0x80 + 0x40);  // 2 行目の先頭に移動
    Puts("world!");
    HAL_Delay(1000);
    LCD_WriteInstraction(0x80 + 0x00);
    Puts("2019WASA");
    LCD_WriteInstraction(0x80 + 0x40);
    Puts("Birdman");
    HAL_Delay(2000);
}

風速・風向の表示

//send Speed data for LCD
void speedview(float speed){
	char value[8] = {"00.0 m/s"};

	if(speed < 0) return;

	speed = (speed * 10);

	value[0] = (char)speed / 100;
	value[1] = (char)(speed - 100 * value[0]) / 10;
	value[3] = (char)(speed - 100 * (float)value[0] - 10 * (float)value[1]);

	for(int i=0; i<4; i++){
		value[i] += '0';
	}
	value[2] = '.';

    LCD_WriteInstraction(0x80 + 0x00);
	Puts(value);
    LCD_WriteInstraction(0x80 + 0x40);
}
//send Direct data for LCD
void directview(uint16_t direct){
	char dir[8] = {"000 deg "};

	dir[0] = (char)(direct / 100);
	dir[1] = (char)((direct - 100 * dir[0]) / 10);
	dir[2] = (char)(direct - 100 * dir[0] - 10 * dir[1]);

	for(int i=0; i<3; i++){
		dir[i] += '0';
	}

    LCD_WriteInstraction(0x80 + 0x40);
	Puts(dir);
    LCD_WriteInstraction(0x80 + 0x00);
}

メイン文

user code 2に書いたもの
  HelloWorld();
  HAL_Delay(1000);
user code whileに書いたもの
	  velocity += 0.5;
	  degree ++;
	  speedview(velocity);
	  directview(degree);
	  HAL_Delay(250);
<備考>

CubeMXから吐き出したコードには、HALを用いた設定部分などがすでに記されています。ここに自分で書くプログラムを加えればよいのです。この時、各部分に仕込まれている「//user code 〇〇 bigin」と「//user code 〇〇 end」の間に書けば、幸せになれます。のちにCubeMXで機能追加などの調整を行い、再度コードをはきなおさせても、この間にあるコードは消えません(逆にそのほかの部分はすべて消えてしまいます)。上のプログラムはすべてそういった部分に書き込むとよいでしょう。

[動作]

実際に組んで動かしてみた結果がこちら

[解説]

プログラムではテストとして風速を0.5m/sずつ、風向を1 degずつインクリメントさせています。途中で風速が急に小さい数字を表示するようになったことがわかると思います。確証はありませんが、恐らくは風速を示す変数がfloat型で定義されていることが原因と思われます。数字は
25.0→25.5→0.4→0.9
というように変化していることから、「25.6」という数がキーになっていることがわかります。したがって、STM32のfloat型が2byteの範囲内でしか数を格納できないのではないか、と考えています。確証は持てないので、詳しい方いらっしゃいましたら教えてください…。

あと、上記のプログラムではchar型の数をそのままLCDに送っていますが、データシートを見る限り、LCDで表示される文字データは「char型ではない」と考えられます。数字、アルファベットのみ表示すればよいという方はこのままで問題ないですが、「→」など、他のデータシート記載の文字を使用したい方はそちらをお使いください。

終わりに

はじめはLCDについてやってみました。とりあえず使えるようにはなりましたが、まだまだ味が出そうです。また面白いことを見つけたら書きたいと思います。