本記事は読者からの寄稿です。
著者:陳顕達
原題:【マイコン入門】(三)アプリケーション層ソフトウェア開発者のためのマイコン学習ロード-----UARTシリアル通信とC#インタラクション
原文リンク:https://www.cnblogs.com/1996-Chinese-Chen/p/16826558.html
はじめに
第1章のブログでは、ArduinoでのESP32の環境設定と、よく使われるバス通信プロトコル(SPI、I2C、UARTなど)について説明しました。今回はUARTシリアル通信とC#シリアルポートを用いた通信の実例、ならびに割り込みとは何か、その役割と実践についてご紹介します。前置きはこれくらいにして、さっそく本題に入りましょう。
UART
最初のブログで、UARTには受信と送信用のピンがそれぞれ1本ずつ、合計2本必要であると説明しました。具体的にはTXD(送信ピン)とRXD(受信ピン)です。どの種類のマイコンのシリアルポートピンでも同じですが、末尾のDが省略されている場合もありますが、本質は同じです。ESP32の開発ボードには3組のUARTピンが存在します。つまり、ボード上に3つのシリアルポートが使用可能です。下図のように、Serial0はピン1と3、Serial1はピン9と10、Serial2はピン16と17に対応しています。ただし、ファームウェア書き込み時にはピン1と3は使用できません。これはUSB経由でマイコンをPCに接続する際、使用されるシリアルポートピンが1と3だからです。そのため、実際に使用できるシリアルポートは2つだけです。また、Arduino IDE上のSerialに対応する静的クラスは、Serial、Serial1、Serial2、Serial3の4つです。その数はESP32のシリアルポート数と同じですが、最初のものだけが使用可能で、残りの2つは使用できません。なぜなら、Serial1とSerial2のピン配置がESP32のピンと一致しないからです。下の2番目の図からわかるように、Serial1、Serial2のPINSはESP32のピンと一致していません。そのため、シリアルポート開発ではこれら2つは使用せず、最初のSerialのみ使用可能です。


ESP32でシリアルポート開発を行う必要がある場合、ESPの開発パッケージには公式から提供されたHardwareSerialというシリアルポートライブラリが含まれています。これを使用すると、開発ボード上のシリアルポートを利用でき、かつピン割り当てをピン配置図上のピンに指定できます。このライブラリはArduino IDEのディレクトリ内、hardware/espressif/esp32/cores/esp32にあり、このフォルダにはESP32の公式ライブラリがいくつか含まれています。このHardwareSerial.hファイルを使用することで、ESP32開発ボード上のシリアルポートを使った開発が可能になります。次に、コード内でその使用方法を理解していきましょう。

コーディング
以下のコードでは、簡単なシリアル通信を開始しています。コードの1行目はC言語と同様に必要なライブラリファイルをインクルードし、2行目でHardwareSerialクラスのMySerial1オブジェクトを定義しています。コンストラクタの値が1であることは、1番目のシリアルポートを使用することを意味します。その後のsetup関数内で、MySerial1シリアルポートオブジェクトを起動します。起動時のボーレートは9600、データ長は8、パリティはNONE、ストップビットは1、そしてシリアルポートのRXピンは16、TXピンは17に設定しています。次の行では、後で定義するreceiveEventメソッドをコールバックとして渡し、シリアルポートがデータを受信したときにこのメソッドが呼び出されるようにしています。
最後の行では、0番目のシリアルポートをボーレート9600で有効にしています。
上記のコードで疑問に思う方もいるかもしれません。ピン配置図ではピン16と17はシリアルポート2に対応しているのに、なぜここでは1と定義しているのでしょうか?実際には、このシリアルポートの定義とピン割り当ては自分で変更できます。コンストラクタに渡す値の範囲は0、1、2で、これは開発ボード上の3つのUARTシリアルポートに対応します。beginメソッドで渡すピン番号はこの0、1、2とは無関係ですが、渡すピンは開発ボード上の3つのUARTシリアルポートのいずれかでなければなりません。そのため、MySerial2.begin(9600, SERIAL_8N1, 10, 9);のように定義することも可能です。ここでの0、1、2は単に3組のシリアルポートに対応し、ピンを指定するものではなく、beginメソッドでシリアルポートのピンを指定します。
以下のシリアルメッセージ受信時のコールバックでは、最初の行でavailableメソッドを呼び出しています。このメソッドはint型の値を返します。もちろん、available() > 0と書いても構いません。このメソッドはシリアルバッファから受信したデータ長を読み取ります。この条件が成立すればデータを受信したことを意味し、その中でデータの読み取りを開始します。
すべてのSerialクラスはArduinoのStream基本クラスを継承しており、このクラスはデータ処理のためのメソッドを提供します。そのため、以下のコードでは読み取ったデータを文字列に変換し、1秒の遅延を入れた後、シリアルポートオブジェクトを使って受信したデータをバッファに書き込みます。バッファに書き込まれたデータは送信され、つまりprintlnに渡された引数がシリアルポートの送信元に送られます。データを送信した相手は、"i am receive!!" + str を受け取ることになります。
#include <HardwareSerial.h>
HardwareSerial MySerial1(1);
void setup() {
// put your setup code here, to run once:
MySerial1.begin(9600,SERIAL_8N1,16,17);
MySerial1.onReceive(receiveEvent);
Serial.begin(9600);
}
void loop() {
}
void receiveEvent()
{
if(MySerial1.available())
{
String str= MySerial1.readString();
delay(1000);
MySerial1.println("i am receive!!"+str);
}
delay(1000);
}
Streamクラスには以下のメソッドが含まれており、Streamを継承するクラスとしてはシリアルポート、I2C通信のWire、SDカード関連クラス、ネットワーク接続用のEthernetクラスなどがあり、いずれもこれらのメソッドを使用してデータを操作できます。

C#コーディング
C#側のコードは非常にシンプルです。インターフェースにはシリアルポートを開くボタン、データを送信するボタンとテキストボックス、受信データを表示するためのテキストボックスがあります。
コードではシリアルポートを開き、どのシリアルポートを開くかを指定しています。設定するプロパティはESP32側と一致させる必要があります。上記ではボーレート9600、データビット8、ストップビット1、パリティNONEと設定したので、C#側でも同様に設定します。ただし、パリティはデフォルトでNONEのため、ここでは設定していません。その後シリアルポートを開き、データ受信時のコールバックを登録し、1024バイトの配列を定義してシリアルポートからデータを読み取り、読み取ったデータ長を取得します。次に定義した1024バイト配列を切り詰め、UTF-8形式で文字列に変換し、画面上のリッチテキストボックスに表示します。送信ボタンのイベントでは、入力ボックスからデータを読み取りバイト配列に変換し、そのデータをシリアルポートに書き込みます。

public partial class Form1 : Form
{
private SerialPort serialPort = new SerialPort("COM6");
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
serialPort.BaudRate = 9600;
serialPort.StopBits = StopBits.One;
serialPort.DataBits = 8;
serialPort.Open();
serialPort.DataReceived += (a, b) => {
var serial = a as SerialPort;
var data = new byte[1024];
var res=serial.Read(data,0, data.Length);
data = data[..res];
string st = Encoding.UTF8
.GetString(data);
BeginInvoke(() => { richTextBox1.Text += st; });
};
}
private void button2_Click(object sender, EventArgs e)
{
var str = Encoding.UTF8.GetBytes(textBox1.Text);
serialPort.Write(str, 0, str.Length);
}
}
配線図
この例では、USB-シリアル変換モジュール(USB-TTL)と、メス-メスのジャンパーワイヤ4本を用意する必要があります。プログラムを書き込んだ後、ジャンパーワイヤでUSB-TTLモジュールとマイコンを接続します。VCCまたは5Vはマイコンの5Vピンに、USB-TTLのGNDはマイコンのGNDに接続します。そしてUSB-TTLのRXDピンはマイコンの17番ピンに、TXDピンはマイコンの16番ピンに接続します。下図のように配線します。5VとGNDを逆接続しないよう注意してください。逆接するとモジュールが破損する恐れがあります。配線に誤りがないことを確認したら、USB-TTLモジュールをPCに挿入し、コード内のC#プログラムを実行し、シリアルポートを開くボタンをクリックし、その後データを送信すると、マイコンからのフィードバックを受信できます。




おわりに
シリアル通信はIoTにおいて必要不可欠な通信方式の一つです。通常はRXをTXに、TXをRXに接続します。モジュールメーカーの指定がない限り、このような配線になります。今後の講座では、I2CやPWM、SPI、そして割り込みについて個別に解説していく予定です。皆様のご関心、ご学習、ご議論を歓迎します。私の知る限りを全て共有します。また、その後はSTM32シリーズのチュートリアルも予定しています。ご興味のある方は、QQグループ(822084696)にご参加いただき、一緒に議論しましょう。
