2020年8月4日火曜日

サイクルカウントのウェイトを作成してGPIOのPull Up/Downを実装してみました。

150Cycleのウェイトループが作れるようになったのでドライバ側でpull up/downの実装も行ってみました。これで一歩踏み込んだ形になります。
ただし、データシート上に記載してありましたが、pull up/downの設定値はcpuから見えなくなるため、設定値はソフト側で管理する必要があるとのこと。なので、これを行うとRpi.gpioの内部バッファと実際値のズレが生じてRpi.gpoiでうまく機能しなくなる可能性があります。

実装中に色々と新たな発見というか記載を見かけて色々な部分を書き換えながら実装していましたが、一番気になったのがioremapでアクセスできるようになったメモリーに対してアクセスするためにioread/iowriteといった関数を通す必要があるらしいという点。
色々みていると、現状の実装だと実際にアクセスできてしまっているだけで、本来はioread/iowriteを通してアクセスする物のようです。ちゃんとアクセスマスクやらなにやら前処理も行われているようです。(iowrite32で検索して見ると「おなかすいたwiki」がトップに来たので見てみると、windowsとlinuxのヘッダなどが同時に見れてなかなか便利そうでした。そこで見てみたところvolatileがかけてあるだけに見えました…intel系では特にこれと言って何もしてなさそう。armはアセンブラが良くわかりませんけど、そのままのような感じがしました。結果的に…ioread/iowrite使わなくてもいい様な気がしますが、volatile漏れが無くなるので、自前でごにょごにょするよりはすっきりはしそう)

そもそもioremapでどのぐらいの大きさまで確保できるのかよくわからず、細切れにioremapしていましたが、その辺も一つにしたりとかもしてみました。

:
#define ADDR_GPIO    0x00200000
#define ADDR_GPIO_GPPUD        (address_gpio + 0x0094)
unsigned int base_address;
void *address_gpio;
:
    address_gpio = ioremap_nocache(base_address + ADDR_GPIO, 0x9F);
:   
    iowrite32(0x3 & pud, ADDR_GPIO_GPPUD);
:
    iounmap(address_gpio);
:

こんな感じに。defineがマクロ的な使い方でちょっと引っかかるんですが、扱う上ではやっぱりダイレクトに指定できた方が気持ちがいいのと、現状グローバルに展開されちゃってる部分が結構あれなんですけが(-_-;)

pull up/downの設定時間が想像していた以上にかかっているようなので実際の時間を確認する必要が出てきたのでデバッグ用の出力を行うようにして確認してみました。
[579874.219293] start:1 end:1 idx:84
[579874.219300] t_p[0]:63873664
[579874.219305] t_p[1]:63873666
[579874.219309] t_p[2]:63873666
[579874.219314] t_p[3]:63873666
[579874.219319] t_p[4]:63873666
[579874.219323] t_p[5]:63873679
[579874.219328] t_p[6]:63887680
[579874.219332] t_p[7]:63887698
[579874.219377] gpioread_close
[579876.293300] gpioread_open
[579876.307603] start:0 end:1 idx:83
[579876.307616] t_p[0]:65961907
[579876.307627] t_p[1]:65961911
[579876.307639] t_p[2]:65961912
[579876.307650] t_p[3]:65961912
[579876.307671] t_p[4]:65961913
[579876.307685] t_p[5]:65961945
[579876.307697] t_p[6]:65975927
[579876.307708] t_p[7]:65976005
上記のt_p[4]とt_p[5]の間でpull up/downの設定を行っています。
1回目 63873679   -   63873666 = 13
2回目 65961945   -   65961913 = 32
時間単位はμ秒ですが、32μ秒はちょっとかかりすぎてる気がするのでセンサーのデータシートを確認。DHT11では特に記載されていませんが、DHT22では20μsとなっていました。DHT11の実測値を逆算すると、17~21μs程度のようです。
この処理時間はcycleカウントのウェイト処理があるので実行時間はcpuクロックに依存しています。
試しに単独の150サイクルの実行時間を計測してみると
[582645.457671] t_p[7]:2835126042
[582645.457676] t_p[8]:2835126048

                :

[582647.545719] t_p[7]:2837214013
[582647.545731] t_p[8]:2837214030
という結果で、それぞれ1回目6μs、2回目27μsかかっていていました。2回目が、最低クロックまでおちていたとすればraspberry pi 2bでも同様の時間だと思います。
さらに悪いことに、150サイクルを2回行う必要があるので、実際には54μs必要ということになります。DHT11も22もどちらもLOW信号が80μsあるので、なんとか間に合いそうです。
ただしサンプリングの開始はLOW始まりで、16μs程度の短い信号になってしまいます。LOW信号さえ捉えられれば条件的にはOKなので大丈夫…と思いたいですね。
DHT11/22のデータシートでは「ホスト側はLOW信号を出力し、18ms(DHT22では800μs)後に、入力に切り替えて、プルアップさせてセンサー側の信号を待つ」という風に記載されています。言葉通りに実装すると結構タイトな感じですが、実際はLOW信号を出力し、HIGH信号を出力した後にGPIOピンの入出力を切り替えるだけでセンサーからの応答がありました。ブレークアウト基盤に載っているプルアップ抵抗が効いているだけかもしれませんが。
このパターンだと、切り替え時間はt_p[3]とt_p[4]の間ということで、時間にして0~1μsで終わるので十分に間に合っていました。

そんなこんなで試していると、突然pythonのスクリプトが落ちることがありました。
pi@rasp4b4g:~/gpioread $ python3 /usr/local/sbin/dht.py --debug
Output for debugging.
gpio_no : 5
Traceback (most recent call last):
  File "/usr/local/sbin/dht.py", line 465, in <module>
    result = dht.read()
  File "/usr/local/sbin/dht.py", line 429, in read
    result = self.__one_read()
  File "/usr/local/sbin/dht.py", line 404, in __one_read
    result = self.__get_data()
  File "/usr/local/sbin/dht.py", line 339, in __get_data
    data = self.__sampling_ioctl()
  File "/usr/local/sbin/dht.py", line 197, in __sampling_ioctl
    self.sampling[i] = sampling_values.sampling[i]
OverflowError: can't convert negative value to unsigned int
インターバルを計算するときにちゃんと確認しないで勢いだけで実装していて0xFFFFFFFFという値を返してしまったことと、intとunsigned intをいい加減に放置したままだったのでpython側でOverflowErrorが出てしまいました。
そもそもインターバル時間でマイナスが出てしまっているのが原因なのですが、気持ち悪いので変数周りの型も揃えておきました。

サイクルカウンタができたので、データシートの記載通りに綺麗に実装できたのですが、処理時間がタイトになってしまいちょっと残念なことになってしまいました。

あとドライバとして実装しておきたさそうな部分としては、デバッグがしやすいということでサンプリングデータをもとにビットを起こしていますが、ダイレクトにビットを立てていってもいいんじゃないかなぁなんていう気もしてます。
ただ、そうなるとかなりデバッグが困難になりそうですが。

0 件のコメント:

コメントを投稿