質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

4972閲覧

ESP32のシリアル通信で、バイトの入れ替わりが発生する

WoodenHamlet

総合スコア306

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2021/04/28 07:24

編集2021/05/07 03:03

ESP32のシリアル通信機能を利用して、外部機器とコマンドのやり取りをしようとしています。
115.2kボー、Serial2で通信を行い、通常はコマンドを正しく送受出来ていることが確認できています。
ただ、ごくまれに隣り合った2つのバイトデータの順序が逆になることがあり、その原因を調査しています。
「00 01 02 03 04」→ 「00 01 03 02 04」のような感じです。

通信ラインをオシロスコープで監視し、入れ替わった瞬間の通信データを確認しましたが、通信ライン上では入れ替わっておらず、Serial2.readBytesした時に入れ替わっているようでした。

読取る方法を変えてみようと、Serial.readBytesをreadやreadBytesUntilに変えてみましたが、同様の現象が発生しました。
どのように解決すればよいでしょうか?

ESP32はESP32-WROOM-32Eを使用しています。

C++

1// 簡略化したソース 2#define DEB_OUT(...) Serial.printf(__VA_ARGS__) 3 4void setup() 5{ 6// ~~~ 略 ~~~ 7 Serial.begin(115200, SERIAL_8N1); 8 Serial2.begin(115200, UART_CONF, RX2PortNo, TX2PortNo); 9 xTaskCreatePinnedToCore(UART_ReadTask, "UART_ReadTask", 4096, NULL, 10, &TaskUART, 0); 10// ~~~ 略 ~~~ 11 12} 13 14void UART_ReadTask(void* arg) 15{ 16 while (1) 17 { 18 char tmp[64]; // ESP32シリアル通信用の受信バッファは64バイトまで格納することが出来る 19 size_t readBts = 0; 20 int len = Serial2.available(); 21 if (len > 0) 22 { 23 if (len > 64) {len = 64;} 24 readBts = Serial2.readBytes(tmp, len); 25 if (readBts) 26 { 27 DEB_OUT("\tResponse: "); // ここのデバッグ出力で既に入れ替わっている 28 for (int i=0; i<readBts; i++)DEB_OUT("%02X ", tmp[i]); 29 DEB_OUT("\n"); 30 cmdCtrl.SetData((UByte*)tmp, (UWord)readBts); // コマンド解析 31 } 32 } 33 vTaskDelay(1); 34 } 35} 36

//-------------------------------------------------------------------------------------
// 以下追記
//-------------------------------------------------------------------------------------
GW期間中、PCを触れる環境でなく、ご回答、ご指摘いただいていながらお返事が遅くなり申し訳ございません。
デバッグ出力ですが、減少が起きた時のスクリーンショットが残っていました。
B7 50 の入れ替わり
以下が、この入れ替わった時の通信ラインの波形です。
入れ替わった時(上記 ID IS CHANGED を出力した時)に青いラインがハイになるようにして、直前の通信データを確認しています。
イメージ説明
02 をスタートコード、0D をエンドコードとしたフォーマットで、赤枠内のデータを収集するような動作を行っています。
デバッグ出力自体はこのフォーマット全体を出力しています。

これらのデータが、以下のような間隔で送られてきています(黄色の縦線一つが上図のデータ全部相当)
受信間隔
およそ100ms強ほどの間隔で「送られてくるとき」「送られてこないとき」が切り替わり、「送られてくるとき」は最短15ms間隔ほどで数発送られてきます。

//-------------------------------------------------------------------------------------
// 以下再追記
//-------------------------------------------------------------------------------------
通信処理部をメインループで行うようにすると、現象が起きなくなりました。シリアル通信をマルチタスクですると問題があるのかもしれません。
現象の原因はわからないのですが、もう少し様子を見て、問題なさそうならシングルタスクにして(ひとまず)解決にするのもアリかもしれません。

該当部のみ書き出した、現象確認用のソースを作成しました。

C++

1#include <Arduino.h> 2#include "comm.h" 3#include <rom/rtc.h> 4 5TaskHandle_t TaskUSB = NULL; 6TaskHandle_t TaskUART = NULL; 7const UByte RX2PortNo = 16; 8const UByte TX2PortNo = 17; 9 10void USB_ReadTask(void* arg); 11void UART_ReadTask(void* arg); 12 13// *** USE_UARTReadTask をコメントアウトしていると、減少が発生しない 14//#define USE_UARTReadTask 15//#define USE_USBReadTask 16 17void setup() { 18 // put your setup code here, to run once: 19 SetupIO(); 20 Serial.begin(115200, SERIAL_8N1); 21 Serial2.begin(115200, SERIAL_8N1, RX2PortNo, TX2PortNo); 22// Serial2.setTimeout(10); // 読み取りタイムオーバー時間の設定:readuntil系で必要。 23 24#ifdef USE_UARTReadTask 25 xTaskCreatePinnedToCore(UART_ReadTask, "UART_ReadTask", 4096, NULL, 10, &TaskUART, 0); 26#endif 27#ifdef USE_USBReadTask 28 xTaskCreatePinnedToCore(USB_ReadTask, "USB_ReadTask", 4096, NULL, 10, &TaskUSB, 0); 29#endif 30} 31 32void UART_ReadTask(void* arg) 33{ 34 while (1) 35 { 36 char tmp[64]; // ESP32シリアル通信用の受信バッファは64バイトまで格納することが出来る 37 size_t readBts = 0; 38 39 int len = Serial2.available(); 40 if (len > 0) 41 { 42 if (len > 64) {len = 64;} 43 readBts = Serial2.readBytes(tmp, len); 44 if (readBts) 45 { 46 DEB_OUT("\tIF < RW: "); 47 for (int i=0; i<readBts; i++) 48 { 49 Serial.write(tmp[i]); 50 DEB_OUT("%02X ", tmp[i]); 51 } 52 DEB_OUT("\n"); 53 } 54 } 55 vTaskDelay(1); 56 } 57} 58 59void USB_ReadTask(void* arg) 60{ 61 while (1) 62 { 63 int len = Serial.available(); 64 if(len) 65 { 66 for(int i=0;i<len;i++) 67 { 68 int ch = Serial.read(); 69 Serial2.write(ch); 70 } 71 } 72 vTaskDelay(1); // delay(1); 73 } 74} 75 76void loop() { 77 int len; 78 // put your main code here, to run repeatedly: 79 if (digitalRead(SW_RD_TRIG) == 0) 80 { 81 digitalWrite(EN_POWER, 0); 82 } 83#ifndef USE_UARTReadTask 84 //------------------------------------------------------------------------------- 85 // UART_ReadTask 86 len = Serial2.available(); 87 char tmp[64]; // ESP32シリアル通信用の受信バッファは64バイトまで格納することが出来る 88 if (len > 0) 89 { 90 if (len > 64) {len = 64;} 91 size_t readBts = Serial2.readBytes(tmp, len); 92 if (readBts) 93 { 94 for (int i=0; i<readBts; i++) 95 { 96// while(Serial.availableForWrite() == 0); 97 Serial.write(tmp[i]); 98 } 99 DEB_OUT("\n"); 100 } 101 } 102#endif 103#ifndef USE_USBReadTask 104 //------------------------------------------------------------------------------- 105 // USB_ReadTask 106 len = Serial.available(); 107 if(len) 108 { 109 for(int i=0;i<len;i++) 110 { 111 int ch = Serial.read(); 112// while(Serial.availableForWrite() == 0); 113 Serial2.write(ch); 114 } 115 } 116 //------------------------------------------------------------------------------- 117#endif 118}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikadzuchi

2021/04/28 15:12

とりあえずデバッグ出力の内容を書いてください。
thkana

2021/04/29 01:38

「外部機器」でのデータの発生具合ってどんなものでしょう? 再現実験ってできるかなぁ? と思ったもので。
WoodenHamlet

2021/05/07 00:20

的外れなことを言っていたら申し訳ないのですが、このボードライブラリというのは、Espressif ESP32 Dev Module などの中身になるのでしょうか? と言いますのも、platformIO で上記のボードを選んだだけで、ボードライブラリのバージョン等を意識せずに使っていたもので、設定の仕方がわからないのです
thkana

2021/05/07 16:16

PlatformIOは使っていないので知りません。一応入れてはあるのでみると、ライブラリはArduino IDEとは違うのを使うのかしら。だとすると、ライブラリ云々はちょっと的外れということになりますね。
guest

回答3

0

自己解決

通信処理部をメインループで行うようにすると、現象が起きなくなりました。シリアル通信をマルチタスクですると問題があるのかもしれません。

該当部のみ書き出した、現象確認用のソースを作成しました。

C++

1#include <Arduino.h> 2#include "comm.h" 3#include <rom/rtc.h> 4 5TaskHandle_t TaskUSB = NULL; 6TaskHandle_t TaskUART = NULL; 7const UByte RX2PortNo = 16; 8const UByte TX2PortNo = 17; 9 10void USB_ReadTask(void* arg); 11void UART_ReadTask(void* arg); 12 13// *** USE_UARTReadTask をコメントアウトしていると、減少が発生しない 14//#define USE_UARTReadTask 15//#define USE_USBReadTask 16 17void setup() { 18 // put your setup code here, to run once: 19 SetupIO(); 20 Serial.begin(115200, SERIAL_8N1); 21 Serial2.begin(115200, SERIAL_8N1, RX2PortNo, TX2PortNo); 22// Serial2.setTimeout(10); // 読み取りタイムオーバー時間の設定:readuntil系で必要。 23 24#ifdef USE_UARTReadTask 25 xTaskCreatePinnedToCore(UART_ReadTask, "UART_ReadTask", 4096, NULL, 10, &TaskUART, 0); 26#endif 27#ifdef USE_USBReadTask 28 xTaskCreatePinnedToCore(USB_ReadTask, "USB_ReadTask", 4096, NULL, 10, &TaskUSB, 0); 29#endif 30} 31 32void UART_ReadTask(void* arg) 33{ 34 while (1) 35 { 36 char tmp[64]; // ESP32シリアル通信用の受信バッファは64バイトまで格納することが出来る 37 size_t readBts = 0; 38 39 int len = Serial2.available(); 40 if (len > 0) 41 { 42 if (len > 64) {len = 64;} 43 readBts = Serial2.readBytes(tmp, len); 44 if (readBts) 45 { 46 DEB_OUT("\tIF < RW: "); 47 for (int i=0; i<readBts; i++) 48 { 49 Serial.write(tmp[i]); 50 DEB_OUT("%02X ", tmp[i]); 51 } 52 DEB_OUT("\n"); 53 } 54 } 55 vTaskDelay(1); 56 } 57} 58 59void USB_ReadTask(void* arg) 60{ 61 while (1) 62 { 63 int len = Serial.available(); 64 if(len) 65 { 66 for(int i=0;i<len;i++) 67 { 68 int ch = Serial.read(); 69 Serial2.write(ch); 70 } 71 } 72 vTaskDelay(1); // delay(1); 73 } 74} 75 76void loop() { 77 int len; 78 // put your main code here, to run repeatedly: 79 if (digitalRead(SW_RD_TRIG) == 0) 80 { 81 digitalWrite(EN_POWER, 0); 82 } 83#ifndef USE_UARTReadTask 84 //------------------------------------------------------------------------------- 85 // UART_ReadTask 86 len = Serial2.available(); 87 char tmp[64]; // ESP32シリアル通信用の受信バッファは64バイトまで格納することが出来る 88 if (len > 0) 89 { 90 if (len > 64) {len = 64;} 91 size_t readBts = Serial2.readBytes(tmp, len); 92 if (readBts) 93 { 94 for (int i=0; i<readBts; i++) 95 { 96// while(Serial.availableForWrite() == 0); 97 Serial.write(tmp[i]); 98 } 99 DEB_OUT("\n"); 100 } 101 } 102#endif 103#ifndef USE_USBReadTask 104 //------------------------------------------------------------------------------- 105 // USB_ReadTask 106 len = Serial.available(); 107 if(len) 108 { 109 for(int i=0;i<len;i++) 110 { 111 int ch = Serial.read(); 112// while(Serial.availableForWrite() == 0); 113 Serial2.write(ch); 114 } 115 } 116 //------------------------------------------------------------------------------- 117#endif 118}

投稿2021/05/09 23:37

WoodenHamlet

総合スコア306

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ライブラリ抜きで自前のドライバでやろうとすると沼に落ちてしまいますので、
Serial2.begin()
の後にFIFOに関するI/Oレジスタをアクセスして無効化してはどうでしょう。

前述の通りESP32の知識は有していませんが、C言語ならレジスタアドレスと構造が判ればアクセス自体は容易なはずです。

FIFOを無効化される場合、受信オーバーランエラーを起こす恐れがあるのでボーレートは落とした方が良いでしょう。

もしかするとボーレートを落とすだけで直るかもしれません。 一定のボーレート以上だと動作がおかしくなるときがあるチップは私も経験していますので。

投稿2021/05/06 09:19

tenma

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

WoodenHamlet

2021/05/06 09:26

改めまして、ご回答ありがとうございます。 むむむ、それが、受信するデータ量に間に合わせるため、115200ボーは必須で、落とすことはできないのです。FIFO関連のレジスタについて調べてみます。
guest

0

該当チップの使用経験はありませんが、エラッタではないでしょうか
https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf
のエラッタリスト3.17にUARTに関する記述があります
FIFO絡みなので、FIFOを無効にして現象が出なければ当たりかも

それにしても、異様にエラッタが多いチップに思います
仕事柄いくつかのマイコンチップの経験があるますが、ここまで多いのは初めて見ました

投稿2021/04/28 15:24

tenma

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

WoodenHamlet

2021/05/06 08:18

ありがとうございます。 UARTの使用はライブラリを利用して行っていましたので、それがFIFOを使っているか、使っているなら、ライブラリ抜きで動かせないか試してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問