Grove Starter Kit for Raspberry Piに付属するGrove Shield for Pi PicoとGrove - 16x2 LCDをRaspberry Pi Picoで使う方法を説明します。
公式にはMicroPythonのサンプルコードがありますが、ここではC++を使って実装しました。
Grove Starter Kit for Raspberry PiやGrove Shield for Pi Picoについては、こちらの記事を参照してください。Starter Kitに付属する他のGroveモジュールの使い方へのリンクもこの記事にあります。
今回実装したソースコードはGithubにアップしています。
Grove - 16x2 LCDについて
横16文字、縦2行の文字を出力できるLCDです。文字と背景の色は変えられません。
PicoへはI2Cで接続します。よって、Grove ShieldのI2C0かI2C1に接続します。
公式のwikiでデバイスの詳細説明やArduinoを使ったサンプルコードが説明されています。
また、Grove Shield for Pi Picoのwikiでは、Pico + MicroPythonを使ったサンプルコードが説明されています。
ソースコード
実装方針
今回ほしいのはLCDモジュールをPicoで使うためのC++コードです。しかし、以下3つのサンプルコードはいずれも今回ほしいコードから少しずつずれています。
そのため3つのサンプルコードを組み合わせて実装しました。
参考にしたサンプルコード:
-
Grove-LCD RGB Backlight
- LCDの公式wikiよりダウンロード可能
- Arduino用のサンプルコード
-
rgb_lcd.cpp / rgb_lcd.hというC++で実装されたソースコードがある。
- Arduinoで使うことを想定しているため、Picoではそのまま使えない。(具体的にはI2C通信の箇所を変更する必要がある)
- 公式wikiでも説明されているが、背景色を自由に設定できるLCD用のコードとなっている。今回のLCDは背景色を変えられないので、背景色を設定する機能については使用できない
-
Beginners Guide of Raspberry Pi Pico Based on MicroPython
- Starter Kitのマニュアルや、それに含まれる複数のモジュールのサンプルコードが説明されている
- Picoで使用できるMicroPythonのコード
-
Picoのサンプルコード (pico-examples)
- C++で実装されたサンプルコード
- LCDのサンプルコードもあるが、今回のLCD用というわけではない
- Arduino用のrgb_lcd.cppと比べるとかなり差分がある
実装方針は、はArduino用のソースコード(No.1)をベースとして、Arduinoに依存した実装をPicoサンプルコード(No.3)を参考にして修正しました。
ソースコード
実装したコードをここに全て載せると長くなるので、重要な箇所だけ抜粋します。ソースコード全体はGithubにアップしています。
1. I2Cへデータ送信する処理
WireはArduino用のライブラリなので、PicoのI2C用の関数**i2c_write_blocking()**に変更します。
変更前:
void i2c_send_byte(unsigned char dta) {
Wire.beginTransmission(LCD_ADDRESS); // transmit to device #4
Wire.write(dta); // sends five bytes
Wire.endTransmission(); // stop transmitting
}
void i2c_send_byteS(unsigned char* dta, unsigned char len) {
Wire.beginTransmission(LCD_ADDRESS); // transmit to device #4
for (int i = 0; i < len; i++) {
Wire.write(dta[i]);
}
Wire.endTransmission(); // stop transmitting
}
変更後:
void Lcd1602::SendByte(unsigned char dta) {
i2c_write_blocking(i2c_inst_, LCD_ADDRESS, &dta, 1, false);
}
void Lcd1602::SendByteS(const unsigned char* dta, unsigned char len) {
i2c_write_blocking(i2c_inst_, LCD_ADDRESS, dta, len, false);
}
2. 文字列を出力する関数
文字列を出力するためにArduino用のサンプルではprint()
関数を読んでいます(サンプルのHelloWorld.ino参照)がrgb_lcd.cppにはそのような関数の実装がありません。
これはrgb_lcdクラス
がPrintクラス`を継承しており、その継承元クラスの関数を呼んでいるためです。Picoにはそのようなクラスがないため、独自で実装する必要があります。
class rgb_lcd : public Print {
...
void Lcd1602::Print(const std::string& str) {
for (int i = 0; i < str.length(); i++) {
unsigned char dta[2] = {LCD_SETCGRAMADDR, str.c_str()[i]};
SendByteS(dta, 2);
}
}
3. I2Cの初期化処理
PicoのI2Cを使うために、初期化処理を実装します。Picoのサンプルコードではi2c_default
を使うようになっています。これは実際にはi2c0
です。
I2C0とI2C1のそれぞれに対応できるように、以下のように実装します。
Lcd1602::Lcd1602(const ShieldPort i2c) {
if (i2c == ShieldPort::kI2C0) {
i2c_inst_ = i2c0;
} else if (i2c == ShieldPort::kI2C1) {
i2c_inst_ = i2c1;
} else {
i2c_inst_ = i2c0;
}
i2c_init(i2c_inst_, 100 * 1000);
...
4. 背景色設定機能の削除
今回使用するLCDでは背景色の設定ができないため、該当する関数を削除します。
void setRGB(unsigned char r, unsigned char g, unsigned char b);
void setPWM(unsigned char color, unsigned char pwm);
void setColor(unsigned char color);
void setColorAll();
void setColorWhite();
5. その他変更箇所
クラス名や変数名、コーディングスタイルなどを自分の好みに変更しています。
ビルド
pico-examplesと同じようなCMakeファイルを用意してビルドします。
set(GROVE_LIB grove-sensors)
file(GLOB SOURCE *.cpp)
add_library(${GROVE_LIB} STATIC ${SOURCE})
target_link_libraries(${GROVE_LIB} pico_stdlib hardware_i2c)