2019年5月22日水曜日

Raspberry PiでDRV8830を動かした話

 研究室課題の論文輪講やマウスの通信線の断線と圧着に四苦八苦している中の人です。
 数か月前に投稿したこちらの記事ですが、DRV8830だけ自分でコードを書いた?(ライブラリ使っただけでは?)ので少し解説していきたいと思います。(数か月放置してたのは内緒)


ICについて

DRV8830は1chのモータドライバで、最大の特徴はI2Cのみで出力電圧の制御を行う点です。別段精度の良いPWMが出ればいいのですが(Raspberry Piでも結構精度の高いPWMを出すことのできるライブラリとかもあるようです)、あまりあてにならない部分があったためI2Cが使えるこのモータドライバを使いました。
 

回路について


 DRV8830周りの回路はこんな感じです。データシート上ではVCCとGNDPWR間には0.1uF(最小)を挟めと書いてありますが、どうやら容量不足になりやすいらしいので追加で10uFを載せてあります。ISENSEには0Ω抵抗を入れましたが、ここの抵抗を変えることで最大電流を変えることができます。FAULTnは障害通知用の端子で、障害が発生するとLowになるのでLEDはこの向きでつけてあります。
 A0/A1はアドレス設定用の端子で、Low/NC/Highの3パターン^2=9通りで設定し、それに応じて書き込み用のアドレスと読み取り用のアドレスが変わります(データシートのp.12)。ここで注意したいのが、データシートでのアドレスがプログラム上のアドレスと一致しない(正確には1つシフトされている)点です。これについては後述。
 SDA/SCLは一応10kΩのプルアップ抵抗を挟んであります(多分いらない気もする)。

PCBについて


 今回は秋月とかで販売しているブレークアウトボードを使わずに自前でプリント基板を設計したのですが、このICの裏面にはサーマルパッドがあるため、普通のはんだでやるには少し工夫が必要でした。ざっくりいえばサーマルパッドに穴をあけ、裏側から熱を与えてはんだ付けを行う方法です。注意してほしいのが、穴からはんだを流し込むのではなくICをつける前にはんだで穴をふさいでおき、その後はんだごてを裏から当てるということです(参考:QFNパッケージをリフローオーブンを使わずにハンダ付け)。配線は...酷いです。

この配線は何があっても参考にしないこと。
(こんなにひどい状態でも動くあたりがすごい)

プログラミングについて

 今回はPythonから操作することとしました。I2Cを使う場合、幸運なことにPython-smbusが提供されているのでこいつを使うことにします。

#-*-coding:utf-8-*-
import smbus
import time

DRV8830_addr = 0x60
i2c = smbus.SMBus(1)
min_pow = 0x06
max_pow = 0x26

try:
    #test DC motor
    for i in range(min_pow, max_pow + 1, 1):
        i2c.write_byte_data(DRV8830_addr, 0, (i<<2)+1)
 time.sleep(0.1)

こんな感じで書けば動くはず。データシートではA0/A1をLowにした場合、書き込み時のDRV8830_addrは0xC0になるはずです。ですがI2Cの仕様上、7bitのICを区別するためのアドレスにR/W用のビットをくっつけているため、実際には0xC0 >> 1 = 0x60となります。最後のR/W用のビットはライブラリ側でつけられるようです。出力電圧と回転方向に関してはデータシートのp.13を見つつ、D7-D2に電圧、D1とD0に回転方向が来るように設定しました。

最後に

そこまで難しいわけではありませんが、データシートと違うじゃんみたいなところもあったのでそこだけ気を付ければ大丈夫な気がします。で、結構重要な話がありまして、実はこのIC...



単品なくなってたのね...(´・ω・`)
モジュールの販売は続けているようなのでほしい方はぜひそちらをお求めください。(この記事の大半意味なくなったのは?)


©2018 shts All Right Reserved.

2019年5月12日日曜日

CubeIDE+STM32F413CHでprintfを使う話



 PYNQかZYBO Z7-20が欲しい中の人です(誰かくれないかなぁ)。前回からの続きで、printfでfloatを表示させる話です。


 マイコンのデバッグでは、UARTを出力先に指定したprintfを実行させることがあります(なんか変な感じするけど)。STM32の場合syscalls.cの__io_putchar()を書き換えてUARTに吐き出したり(weak定義に関してはこちらから)、_write()関数の中身を書き換えることで実装するのが楽なようです(こことかここを参照)。

1. とりあえずprintfをUARTで使えるようにする

*UARTが使えている前提で話を進めていきます。HAL_UART_Transmitとかが使えていれば大丈夫。

 CubeIDEでも同様にしてprintfの出力先をUARTに変更できます。/(プロジェクト名)/Srcの中にsyscalls.cがあるはずなので、それを開き_write()のところまで行きます。で、その中身をこんな感じで書き換えます。
__attribute__((weak)) int _write(int file, char *ptr, int len) {
 int DataIdx;

 for (DataIdx = 0; DataIdx < len; DataIdx++) {
//  __io_putchar(*ptr++);
  HAL_UART_Transmit(&huart1, ptr++, 1, 1);
 }
 return len;
}

 このままコンパイルすると"huart1ってなんだよ"って怒られるので、ヘッダと変数の宣言部分に

/* Includes */
#include 
#include 
#include 
#include 
#include 
#include 
...
と書き換えておきます。これでprintf()を実行するとTerminalとかになんか出てくるはずです(%fに関しては後述)。

2. %fを出力する

さて、ここまででprintfは%fフォーマットを除いて動くようになったはずです。で、%fはこのままでは動きません(TrueStudioやSW4STM32同様)。原因はリンカの設定なのでここを修正していきます。

*ここからの話はSTM32Cube FW_F4 V1.24.1での話です(2019/05/10時点で最新)。バージョンの違いに注意してください。

 とりあえず、リンカオプションの設定から。



Project->Propertiesを選択し、C++/C Buildを選択します。


 次にSettings->MCU GCC Linkerを選択し、Other flagsに"-u _printf_float"を追加します。
 ここまで終われば動きそうな気がしますが、うまくいかない(なぜいきなりTrueStudioやSW4STM32をNRNDにしたし)。この問題に気が付いたのがGWの中ごろで、そこから1週間ほどとかしました。(´・ω・`)
 この原因についてはまだわからないこともありますが、ここを参考にすることで%fを有効化できました。手順としては、STM32F413CHUX_FLASH.ldの_estackの値を0x2004FFFFから0x20050000に書き換えるだけです。この値に関しては、データシートの21ページに書いてある

"CPU can access SRAM1 memory via S-bus, when SRAM1 is mapped at the address range: 0x2000 0000 to 0x2003 FFFF. CPU can access SRAM2 memory via S-bus, when SRAM2 is mapped at the address range: 0x2004 0000 to 0x2004 FFFF."

を見ながら決めました。_estackにはRAM領域の終端アドレスを指定するのですが、どうやら奇数だと動かず、偶数でないと動かないらしいです(この辺検証とかもできていないので怪しいですが、0x20050000や0x2004e200、0x20040000の場合は動き、0x2003FFFF、0x2004FFFFでは動いていないことは説明できそう)。

2019/05/13追記
@osaboh さんからの指摘で、Application Binary Interface(ABI)の呼び出し規約の中に

5.2.1.1 Universal stack constraints At all times the following basic constraints must hold:
(中略)
 SP mod 4 = 0. The stack must at all times be aligned to a word boundary.
(中略)
5.2.1.2 Stack constraints at a public interface The stack must also conform to the following constraint at a public interface:
 SP mod 8 = 0. The stack must be double-word aligned.

とあるので、アドレスは4 or 8 Byteで割り切れる必要があるそうです。

参考:Procedure Call Standard for the ARM Architecture

謝辞

@osaboh さん@sora_siro1 さん、@Sakurai_Tatuyosさんには%fが表示できない件に関して、動作検証や改善手順の提案などで有益な情報をいただきました。本当にありがとうございます。


©2018 shts All Right Reserved.

2019年5月9日木曜日

朝起きたらTrueStudioがNRNDだったので、CubeIDEに乗り換えた話


 春の連休は何してましたか?寝てましたか?もう一度大型連休くれませんか?

 どうも中の人です。今回は、"朝起きたらTrueStudioがNRNDだったので、CubeIDEに乗り換えた話"です。CubeIDEに関しては、こちらを参照してください。基本的にTrueStudioにCubeMXをくっつけた感じですが(SW4STM32はいじったことないからわからない)、詰まったところもあったのでとりあえずカキカキしておきます。

1. ダウンロード&インストール


 まずはここからインストーラを落として、とりあえずインストールします。TrueStudioは残しておいてもとりあえず大丈夫でした。

2. プロジェクト作成


 インストールが終わったらプロジェクトを作成しますが、プロジェクトの作成段階でCubeMX用のファイル(.ioc)も同時に生成されるあたりがTrueStudioとは異なる点になります。



3.さあ、動かしてみよう


 プロジェクトの作成が終わると見慣れたCubeMXの画面が出てくるので、テキトーに設定しておきます。初期設定だとCtrl+sで保存するとGenerate Codeするかどうかを聞かれるので、Yesに入れておきましょう(手動でもGenerate Code可能)。あとはEclipseのエディタ画面が出てくる(またはmain.cあたりをクリック)ので、コードをカキカキすればOKです。

 TrueStudioにはRunボタンとDebugボタンがありましたが、CubeIDEではDebugボタンのみになっています(なんでだろ)。SWDで書き込むときはDebugボタンをポチりましょう。(UARTは使ってないんでワカラン)

 とりあえずこれでLチカはできるはずです。ただしこのままだとprintf周りができていないので、その話を今度しようと思います。
©2019 shts All Right Reserved.

2019年5月3日金曜日

ZYBO Z7 (Z7-20)でHDMI Paththroughを作る(IP使用)


 精神統一にははんだ付けとよく言われますが、

ただし0603(metric)、テメーはダメだ

 どうも中の人です。マウスの進捗はぼちぼちです。朝起きたらTrueStudioがNRNDになってCubeIDEが登場していましたね。そのうち部内向けに記事でも書こうかなぁ(現状printfでfloatが吐き出せないのでツラい)。
 今回は研究室に入って触り始めたFPGAの話です。単にFPGAといってもいろいろあるのですが、Digilentから発売されているZYBO Z7を使用して、HDMIの入力をただ出力するだけの回路を作成しようと思います(ZYBO Z7についてはこちらを参照)。まあ備忘録的なものなので、動かなくても責任は負えません。はい。



 このボードには入力用と出力用のHDMIが別々についており入出力用の回路を組むことで、FPGA(が入ったSoC)上でのデータ処理ができます。しゅごい。とりあえず今回組んだ回路はこんな感じ。

 回路構成自体はここのP.22 Figure 3.2を参照し、出力サイズは720pのみに限定しました。ここからは各IPの設定になります。


 Clocking Wizardの設定。Reference Clockとして200MHzを供給しています。(入力の125MHzは外部クロックを引き込んでいるためで、PSから持ってくるなら100MHzになるはず)


 DVI to RGBの設定。720pを使用しますがTMDSのClock Rangeは< 120MHzに設定しないと動きませんでした(なんでだろ)。


 Video in to AXI4-Streamの設定。Click ModeをIndependentにするのを忘れずに。FIFO Depthは適当に4096を選択。


 AXI4-Stream to Video Outの設定。Video in to AXI4-Streamの設定に合わせる形で設定。Timing ModeはSlave、Hysteresis Levelは推奨されている値のうちから12を選択。


 RGB to DVIの設定。DVI to RGBと同じく、TMDS Clock Range は< 120MHzにすること(入力と出力が同期している関係)。


 Constant基HDMIのHPDの設定。常にHIGHになるように設定。

2019/05/06追記:Video Timing Controlerの設定を載せ忘れてました...。



 ここまでで大体の設定が終了し、ここから配線作業になります。今回は時間を溶かしまくったPixel ClockとReference Clock、AXI4-Stream to Video OutのVTG_CEを置いておきます。




 制約ファイルに関しては、ここの必要なところだけ持ってきた感じです。あとはテキトーにBitstream生成すれば動く...はずです(動かなかったらすんません)。