salt_009
2023年7月28日 17時12分
シリアル通信のデータ受信について質問させていただきます。
UART5を用いて下記条件でシリアル通信を試みています。
通信規格 :RS-232c
ボーレート :19200
また、受信する際は pselectで読み取り可能になってから、
readでデータを取得しています。
このデータ受信時に、
データが分割されて受信することがあります。
例)
送信されたデータ: 0x40 0x41 0x42 0x43
受信データ :0x40
次の受信データ : 0x41 0x42 0x43
この受信データの分割に対して、
ドライバ/カーネルの設定等で対処できるものはございますか?
コメント
at_mizo
溝渕です。
読み出しにread(システムコール)を利用しているとのことですが、システムコールは割り込みを受けると処理の途中であっても中断して抜けてしまいます。
これを避けるには、次のようにラップされたreadを利用してみてください。
https://sources.debian.org/src/inn2/2.7.1-1/lib/readin.c/?hl=15#L15
salt_009
salt_009
追記させていただきます。
610とX1とのシリアル通信の受信処理の違いについて
質問がございます。
別の用途でX1での開発も行っており、
232Cのアドオンモジュールを使用しシリアル通信を行っております。
X1と610で同様のプログラム(デバイスのパスのみ変更)で送受信を行いました。
X1と610で実行したプログラムは、データ受信を行い
一度のreadで受信したデータを横に並べて表示するものになっています。
送信側はC#で作成したアプリになっており、
このアプリから3byteのデータをX1と610各々へ送信します。
X1の結果は下記のようになっており、
一度のデータで3byte のデータを受信できています。
rxbuf: 04 31 05
一方610では、
下記のようなログになっており、
1byteの受信を3回行ったような結果になっておりました。
rxbuf: 04 rxbuf: 31 rxbuf: 05
このX1と610の受信処理の差は、どういった点が原因でしょうか?
また、610の受信処理をX1のような処理にすることは可能でしょうか?
at_mizo
溝渕です。
> 現在想定している条件としては、
> 送受信するバイト数は可変、終端文字無し
> 言語はC言語を考えております。
>
> このような条件では、
> カーネル/ドライバ等の機能だけで、
> 受信データの分割に対処することは難しい
> という認識になるでしょうか?
恐らくethernet frameのように、データのまとまりとして送受信するような通信を想定しているものと思います(私の憶測ですので、誤認あればご指摘ください)。
ethernetの場合はプロトコル実装がkernel内にあり、まとまったデータを扱う事ができます。対して、uartの場合はプロトコル実装がkernel内にありません。
また、ハードウェアレベルでも、「文字(stop bit -> start bit)間の時間がnミリ秒未満であれば1つの送信とみなす」ような機能が無いため、文字列が1回の送信で来たものか、2回の送信で来たものかを判別する事は不可能です。
その為、uartでまとまったデータを送受信する場合は、(例えばzmodemのような)プロトコルをユーザーランドに実装する必要があります。
以上で回答になっていますか?
salt_009
> 恐らくethernet frameのように、データのまとまりとして送受信するような通信を想定しているものと思います(私の憶測ですので、誤認あればご指摘ください)。
ご認識の通りです。
> ethernetの場合はプロトコル実装がkernel内にあり、まとまったデータを扱う事ができます。対して、uartの場合はプロトコル実装がkernel内にありません。
> また、ハードウェアレベルでも、「文字(stop bit -> start bit)間の時間がnミリ秒未満であれば1つの送信とみなす」ような機能が無いため、文字列が1回の送信で来たものか、2回の送信で来たものかを判別する事は不可能です。
> その為、uartでまとまったデータを送受信する場合は、(例えばzmodemのような)プロトコルをユーザーランドに実装する必要があります。
追記させていただいた内容に関連するのですが、
X1でデータのまとまりとして受信できているのはカーネル内でそういった処理がされているからでしょうか?
at_mizo
溝渕です.
> X1でデータのまとまりとして受信できているのはカーネル内でそういった処理がされているからでしょうか?
恐らくですが、送受信のタイミングによるものと思います。
uartのデータフローは、概ね以下の"Figure 3"のようになっています。
https://www.linux.it/~rubini/docs/serial/serial.html
read()から読めるデータは、tty_bufferに書かれたものになります。Hardwareにはfifoと呼ばれるデータバッファがあり、一定量が貯まるかまたは一定時間が経過するとflip_bufferにpushされます。
このタイミング次第でデータが分割されたり統合されたりします。これは、read()だけの話ではなく、writeについても同じ事が言えます。
> 送受信するバイト数は可変、終端文字無し
という条件に於て、先程説明した内容により、「1回のread()で、nバイトごとにまとめて入力されるはず」という前提を持ってアプリケーションを書くべきではないと思います。
salt_009
ご回答ありがとうございます。
>「1回のread()で、nバイトごとにまとめて入力されるはず」という前提を持ってアプリケーションを書くべきではないと思います。
承知いたしました。
受信時にデータを確認し、データが末端まであるか確認する処理を追加しようと思います。
後学のために質問させて下さい。
X1と610動作違いついて、
dtc -I fs /proc/device-tree -O dts
を用いてdtsの設定を読み出しました。
その設定値に下記のような違いがありました
この設定値の違いは、受信時の動作に関与していますか?
特にdmaに関わる設定でX1と似たような受信動作になることは考えられますか?
・x1
serial@30a90000 { compatible = "fsl,imx7d-uart\0fsl,imx6q-uart"; clocks = < 0x01 0xfa 0x01 0xfa >; fsl,uart-has-rtscts; clock-names = "ipg\0per"; status = "okay"; interrupts = < 0x00 0x7e 0x04 >; dma-names = "rx\0tx"; phandle = < 0x91 >; reg = < 0x30a90000 0x10000 >; pinctrl-0 = < 0xb0 >; dmas = < 0x20 0x22 0x04 0x00 0x20 0x23 0x04 0x00 >; linux,phandle = < 0x91 >; pinctrl-names = "default"; };
・610
serial@21f4000 { compatible = "fsl,imx6ul-uart\0fsl,imx6q-uart"; clocks = < 0x01 0xc5 0x01 0xc6 >; clock-names = "ipg\0per"; status = "okay"; interrupts = < 0x00 0x1e 0x04 >; reg = < 0x21f4000 0x4000 >; pinctrl-0 = < 0x2f >; pinctrl-names = "default"; };
at_mizo
溝渕です。
> その設定値に下記のような違いがありました
> この設定値の違いは、受信時の動作に関与していますか?
> 特にdmaに関わる設定でX1と似たような受信動作になることは考えられますか?
知りたい事は、X1と610に挙動の違いが出るかどうかでしょうか?
であれば、SoCのuart core自体は互換品ですが、linuxカーネル自体およびドライバが異なるので、違いが出ないとは言い難いです。
# 意図して違いを出そうという意図はありません
いちおうですが、ドライバの差分も添付しておきます。
ファイル | ファイルの説明 |
---|---|
linux-4.9_4.14_drivers-tty-serial-imx_c.patch |
salt_009
at_mizo
溝渕です。
本題とはあまり関係ありませんが、以前お伝えしたreadのラップ関数よりもスマートな実装のコードを見つけたのでお知らせします。
https://sources.debian.org/src/audit/1:3.1.1-1/src/auditd-listen.c/?hl=…
参考になれば幸いです。
at_mizo
2023年7月28日 17時30分
溝渕です。
> このデータ受信時に、
> データが分割されて受信することがあります。
> 例)
> 送信されたデータ: 0x40 0x41 0x42 0x43
> 受信データ :0x40
> 次の受信データ : 0x41 0x42 0x43
>
> この受信データの分割に対して、
> ドライバ/カーネルの設定等で対処できるものはございますか?
UARTには送受信するデータにフレームの概念が(基本的には)ありません。よって、どこからどこまでが1つのデータであるかは判別できません。
以下のようにttyの設定項目を確認可能です。
例えば、送信文字数が固定なのであれば、"VMIN"で対処可能と思います。また、終端文字があるのであればカノニカルモードを利用する事ができると思います。
任意の文字数のデータを送受信する場合は、ユーザーランド(アプリケーション)で対応するのが良いかと思います。