はい、使いにくいです、いろいろと。
Serial.print()関数のカッコの中身の書き方が覚えにくいとか、あるいはデバッグで変数の内容を書き出す時にはプログラムがうまく動かないことも重なってフラストレーションがたまります。いえ、ハードウェアUARTポートは嫌いじゃないんです。むしろUART使いたさにこういう改造をしたりもします。

一番厄介なのは、Serial.print()で大量のデータを送信するとしばらくの間戻ってこないことではないでしょうか? 先に書いたのは人間が我慢すればいいのですが、ほかの処理のブロックはセンサーやアクチュエータのレスポンスに影響が出る可能性があります。Arduino UNO R4 MINIMAではUSBシリアルによるSerial.print()とハードウェアUARTによるSerial1.print()が使えますが、UARTでは特に送信時間が長くかかりそうです。
ここでは、Arduino UNO R4 MINIMAにおけるSerial1.print()の動作を調べてみます。
Serial.print()、Serial1.print()の送信時間を調べてみる
ここではSerial.print()、Serial1.print()の両方で1000バイトを送信してみます。送信にかかった時間を測るために以下のテストプログラムを作成しました。UARTのビットレートは19200bpsに設定しています。
char chararray[1001];
uint32_t $TEST0;
void setup() {
Serial.begin(115200); //USB-CDC port begin
Serial1.begin(19200); //hardware UART port begin
delay(1000);
for (int i=0;i<1000;i++) {chararray[i] = (i % 10) + 0x30;}
chararray[1000] = 0;
}
void loop() {
$TEST0 = micros();
Serial.print(chararray); //USB-CDC port write
$TEST0 = micros() - $TEST0;
Serial.println();
Serial.print("USB-CDC: ");
Serial.println($TEST0);
$TEST0 = micros();
Serial1.print(chararray); //hardware UART port write
$TEST0 = micros() - $TEST0;
Serial.print("HW UART: ");
Serial.println($TEST0);
Serial.println();
delay(5000);
}

Arduino UNO R4とPCをUSBケーブルで接続しシリアルモニタを立ち上げた状態で、USBシリアル、UARTそれぞれ1000byteを5sec毎に送信して、結果をμsec単位でモニタに出力します。多少ばらつきはありますが、USBで約2msec、UARTで約0.52secという結果になりました。USBのほうはデータを分割して送るなど工夫をすれば、loop()がブロックされる影響を減らせそうです。一方UARTはUSBの250倍ほどの時間がかかり、対応はちょっと考えあぐねてしまいます。まあ、ビットレートを上げれば時間は短くなりますが。
UARTでの動作について詳細を見てみましょう。Arduino UNO R4のコアライブラリにあるSerial.cppを読むと理解できました。以下がSerial.cppにある送信動作の最終段階のコードです。なお、ライブラリのバージョンは1.5.1になります。
/* -------------------------------------------------------------------------- */
size_t UART::write_raw(uint8_t* c, size_t len) {
/* -------------------------------------------------------------------------- */
size_t i = 0;
while (i < len) {
uart_ctrl.p_reg->TDR = *(c+i);
while (uart_ctrl.p_reg->SSR_b.TEND == 0) {}
i++;
}
return len;
}
送信したいデータ1byteを送信データレジスタTDRに書き込むとUARTのシリアル送信が始まり、同時にシリアルステータスレジスタの送信完了フラグSSR_b.TENDが0(キャラクタ送信中)になります。このコードでは内側のwhileループで1byteの送信完了(SSR_b.TENDが1になる)まで待ち、さらに関数を呼んだときの全byteの送信が終了するまで外側のwhileループで待っています。
この間は割り込みを除いて処理はできませんし、もし優先レベルが高いISRでSerial1.print()を使うとloop()に加えてより低優先レベルのISRもブロックされてしまいます。Arduinoでは割り込み内でSerial.print()を使うな、と言われますが、UNO R4でも注意を要します。
補足
コアライブラリ1.5.1のSerial.cppではwhile()ループの完了を待ちますが、これは将来変わる可能性があります。RA4M1自体は送信レジスタエンプティで割り込みをトリガーできるので、Serial1.print()から送られたデータをいったんため込んでリターン、その後バックグラウンドの割り込み処理で逐次送信するアルゴリズムも実現可能な範囲です。
ちなみにUNO R3では送信レジスタエンプティの割り込みで次のデータを送っているので、Serial.print()自体は早く抜けることが期待されます。実際にやってみるとUNO R3で1000byteを送信した時間は約0.49secの結果となり、UNO R4よりわずかに速いところにとどまりました。これは送信する1000byteに対してライブラリ側の送信バッファサイズが小さいのが原因と思われ、60byte送信であればbyteあたり送信時間はかなり短かったです。
単にデータのログのために通信するのであれば、待ち時間の少ないUSBシリアル通信を選択するのもありです。通信相手の例として、AndroidスマホではSerial USB Terminalというアプリが利用できました。ただしArduino UNO R4では低確率ながら若干の落とし穴もあって、それについてはまたの機会にでも。
私的なお話。送信完了フラグTENDを見ると、私にとって神様のようなあるお方を思い出します…