電子工作の定番アイテムの一つであるLCDキャラクターディスプレイモジュール(LCD1602)、簡単なメッセージを表示するのに便利です。
しかし、最近のこの界隈では、LCD1602を使う場合もI2C通信用のバックパック(アダプタ)を介して接続するのが主流なようです。これは、LCDの操作に必要な多くのGPIOポート(パラレル接続だと最大12ピン)を消費せずに済むという大きなメリットがあるからでしょう。
そんな中、私は「あえて」パラレル接続で動作させるLCD1602を Raspberry Pi Pico で動かそうと考えました(本当は、押し入れに買ってあったのが残ってただけなのですが)。誰かやってるだろうと調べたのですが、日本語の情報が意外と少ないことに気づきました。(やはり、ポートを多く使うので敬遠されがちなのかもしれませんね)
そこで、この度、私が Raspberry Pi Pico の C/C++ 環境で LCD1602 を動かすために行った作業の記録をここに残しておきます。同じようにパラレル接続で動かしたい方の参考になれば幸いです。
参考資料
PICマイコンでLCD1602を動かしている方の記事を参考にさせていただきました。
LCD1602 の制御はHD44780互換コントローラの仕様に基づいているため、初期化の手順やコマンドの送信方法といった基本的な制御ロジックは共通です。この PIC での実装例を Raspberry Pi Pico の C/C++ 環境に移植する形で進めました。
構成
部品
- Raspberry Pi Pico
- LCDキャラクターディスプレイモジュール(16×2行バックライト付)
接続(4ビットモード)
データ線が4本で済む、4bitモードで動作させます。RS、E、DB4~DB7 の計6本を Raspberry Pi Pico の GPIO に接続します。
GPIOはどこでもOKですが、私は以下のポートに接続しました。
| Raspbery Pi Pico | LCD |
|---|---|
| GPIO 16 | RS |
| GPIO 17 | E |
| GPIO 18 | DB4 |
| GPIO 19 | DB5 |
| GPIO 20 | DB6 |
| GPIO 21 | DB7 |
補足: R/W ピンは、書き込み専用とするため、GND に接続します。
制御ロジック
4ビットデータ送信関数 lcd_send4()
4ビットモードでは、8ビットのデータを上位4ビット、下位4ビットに分けて2回送信します。この関数は、その4ビットのデータ(ニブル)を LCD へ送る役割を担います。ポイント: E ピンを High にセットした後、E ピンを Low にすることで、LCD のコントローラは E の立下りのタイミングでデータを取り込みます。確実にデータを認識させるために、データセット後にわずかな遅延(5μs) を入れています。
void lcd_send4(char c)
{
gpio_put(LCD_E_GPIO, 1); // EをHighにする
// 4ビットのデータをGPIOに出力
gpio_put(LCD_DB7_GPIO, (c >> 3) & 0x01);
gpio_put(LCD_DB6_GPIO, (c >> 2) & 0x01);
gpio_put(LCD_DB5_GPIO, (c >> 1) & 0x01);
gpio_put(LCD_DB4_GPIO, (c >> 0) & 0x01);
sleep_us(5); // データ安定化のための待ち時間
gpio_put(LCD_E_GPIO, 0); // Eの立下りでデータ確定
}
初期化
raspberry pi picoのポート設定を行った後、LCD の起動シーケンスを実施します。
N=1: 2 lines
F=0: 5x7dots
I/D=1: Increment
S=0
で設定します。
LCD コントローラを4ビットモードにし、表示設定を行う重要なシーケンスです。資料を参考に、pico の C/C++ SDK の sleep_ms()/sleep_us() を用いて、正確なタイミングでコマンドを送信しています。
void lcd_init(void)
{
// GPIO初期化処理
int gpio_out_list[6] = {
LCD_RS_GPIO,
LCD_E_GPIO,
LCD_DB4_GPIO,
LCD_DB5_GPIO,
LCD_DB6_GPIO,
LCD_DB7_GPIO,
};
for (int i = 0; i < 6; i++)
{
gpio_init(gpio_out_list[i]);
gpio_set_dir(gpio_out_list[i], GPIO_OUT);
}
gpio_put(LCD_RS_GPIO, 0);
gpio_put(LCD_E_GPIO, 0);
sleep_ms(45);
lcd_send4(0x3);
sleep_ms(5);
lcd_send4(0x3);
sleep_us(100);
lcd_send4(0x3);
lcd_send4(0x2); // 4bitモードに設定
sleep_us(100);
lcd_instruction(0x28); // Function Set 4bit 2 lines 7dots
lcd_instruction(0x08); // Display OFF, Cursor OFF, Blink OFF
lcd_instruction(0x01); // Clear Display
sleep_ms(2);
lcd_instruction(0x06); // Entry Mode Set: Increment,
lcd_instruction(0x0C); // Display ON
}
void lcd_instruction(char command)
{
gpio_put(LCD_RS_GPIO, 0); // コマンドモード
lcd_send4(command >> 4); // 上位4ビット
lcd_send4(command); // 下位4ビット
sleep_us(40);
}
文字表示
文字(データ)の送信は、RS ピンを High にする点以外はコマンド送信と同じです。
void lcd_data(char data)
{
gpio_put(LCD_RS_GPIO, 1); // データモード
lcd_send4(data >> 4); // 上位4ビット
lcd_send4(data); // 下位4ビット
sleep_us(40);
}
カーソルの移動
void lcd_set_cursor(char row, char column)
{
const char line[2] = {0x00, 0x40};
if (row > 1)
row = 1;
char address = line[row] + column;
lcd_instruction(0x80 + address);
}
使い方
#include <stdio.h>
#include "pico/stdlib.h"
#include "lcd.h"
int main()
{
lcd_init();
lcd_print(0, "Hello World!");
lcd_print(1, "Counter: ");
int counter = 0;
while (true)
{
lcd_set_cursor(1, 9);
lcd_data(0x30 + (char)((counter / 100) % 10));
lcd_data(0x30 + (char)((counter / 10 ) % 10));
lcd_data(0x30 + (char)((counter / 1 ) % 10));
counter++;
sleep_ms(1000);
}
}
void lcd_init(void);
void lcd_send4(char c);
void lcd_instruction(char command);
void lcd_data(char data);
void lcd_set_cursor(char row, char column);
void lcd_print(int line, char *text);
#include <string.h>
#include "pico/stdlib.h"
#include "lcd.h"
#define LCD_RS_GPIO 16
#define LCD_E_GPIO 17
#define LCD_DB4_GPIO 18
#define LCD_DB5_GPIO 19
#define LCD_DB6_GPIO 20
#define LCD_DB7_GPIO 21
void lcd_init(void)
{
int gpio_out_list[6] = {
LCD_RS_GPIO,
LCD_E_GPIO,
LCD_DB4_GPIO,
LCD_DB5_GPIO,
LCD_DB6_GPIO,
LCD_DB7_GPIO,
};
for (int i = 0; i < 6; i++)
{
gpio_init(gpio_out_list[i]);
gpio_set_dir(gpio_out_list[i], GPIO_OUT);
}
gpio_put(LCD_RS_GPIO, 0);
gpio_put(LCD_E_GPIO, 0);
sleep_ms(45);
lcd_send4(0x3);
sleep_ms(5);
lcd_send4(0x3);
sleep_us(100);
lcd_send4(0x3);
lcd_send4(0x2); // 4bitモードに設定
sleep_us(100);
lcd_instruction(0x28); // Function Set 4bit 2 lines 7dots
lcd_instruction(0x08); // Display OFF, Cursor OFF, Blink OFF
lcd_instruction(0x01); // Clear Display
sleep_ms(2);
lcd_instruction(0x06); // Entry Mode Set: Increment,
lcd_instruction(0x0C); // Display ON
}
void lcd_send4(char c)
{
gpio_put(LCD_E_GPIO, 1); // EをHighにする
// 4ビットのデータをGPIOに出力
gpio_put(LCD_DB7_GPIO, (c >> 3) & 0x01);
gpio_put(LCD_DB6_GPIO, (c >> 2) & 0x01);
gpio_put(LCD_DB5_GPIO, (c >> 1) & 0x01);
gpio_put(LCD_DB4_GPIO, (c >> 0) & 0x01);
sleep_us(5); // データ安定化のための待ち時間
gpio_put(LCD_E_GPIO, 0); // Eの立下りでデータ確定
}
void lcd_instruction(char command)
{
gpio_put(LCD_RS_GPIO, 0); // コマンドモード
lcd_send4(command >> 4); // 上位4ビット
lcd_send4(command); // 下位4ビット
sleep_us(40);
}
void lcd_data(char data)
{
gpio_put(LCD_RS_GPIO, 1); // データモード
lcd_send4(data >> 4); // 上位4ビット
lcd_send4(data); // 下位4ビット
sleep_us(40);
}
void lcd_set_cursor(char row, char column)
{
const char line[2] = {0x00, 0x40};
if (row > 1)
row = 1;
char address = line[row] + column;
lcd_instruction(0x80 + address);
}
void lcd_print(int line, char *text)
{
lcd_set_cursor(line, 0);
int len = strlen(text);
for (int i = 0; i < len; i++)
{
lcd_data(text[i]);
}
}
まとめ
Raspberry Pi Pico で LCD1602 をパラレル接続(4ビットモード)で制御する記録をまとめました。この情報が、同様の課題に挑戦する方々の助けになれば幸いです。

