ジャンル不定の日記です。

spidevでSPIフラッシュの読み書きした

昨日はspidevの実験でデバイスのIDだけ取得してみたが、読み書きも試してみた。

ソースコード
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

void CmdRdid(int fd);
char CmdRdsr(int fd);
char CmdRdscur(int fd);
void CmdWren(int fd);
void CmdRead(int fd);
void CmdPp(int fd);

int main(int argc,char** argv){
    char* dev=argv[1];
    int fd=open(dev,O_RDWR);
    if(fd<0){
        perror("open");
        exit(1);
    }
    CmdRdid(fd);
//    CmdRdsr(fd);
//    CmdWren(fd);
    CmdRead(fd);
    CmdPp(fd);
    CmdRead(fd);
    exit(0);
}
char SendMessage(int fd,char* tx_buf,int tx_len,int rx_len){
    int tr_len=rx_len?2:1;
    struct spi_ioc_transfer tr[2];
    char rx_buf[rx_len];
    memset(tr,0,sizeof tr);
    memset(rx_buf,0,sizeof rx_buf);
    tr[0].tx_buf=(unsigned long)tx_buf;
    tr[0].len=tx_len;
    tr[1].rx_buf=(unsigned long)rx_buf;
    tr[1].len=rx_len;
    if (ioctl(fd,SPI_IOC_MESSAGE(tr_len),tr)<0){
        perror("SPI_IOC_MESSAGE");
        exit(-1);
    }
    printf("%02x:",tx_buf[0]);
    for (int i=0;i<rx_len;i++){
        printf("%02x ",rx_buf[i]);
    }
    printf("\n");
    return(rx_buf[0]);
}
void CmdRdid(int fd){
    SendMessage(fd,"\x9f",1,3);
}
char CmdRdsr(int fd){
    return(SendMessage(fd,"\x05",1,1));
}
char CmdRdscur(int fd){
    return(SendMessage(fd,"\x2b",1,1));
}
void CmdWren(int fd){
    SendMessage(fd,"\x06",1,0);
}
void CmdRead(int fd){
    if(CmdRdsr(fd)&1){
        printf("WIP=1\n");
        exit(0);
    }
    SendMessage(fd,"\x03\x00\x00\x00",4,10);
}
void CmdPp(int fd){
    CmdWren(fd);
    if(!CmdRdsr(fd)&(1<<1)){
        printf("WEL=0\n");
        exit(0);
    }
    char tx_buf[260];
    tx_buf[0]=0x02;
    tx_buf[1]=0;
    tx_buf[2]=0;
    tx_buf[3]=0;
    for(int i=255;i>=0;i--){
        tx_buf[3+(256-i)]=i;
    }
    SendMessage(fd,tx_buf,260,0);
    while(CmdRdsr(fd)&1){
        printf("WIP=1\n");
    }
    if(CmdRdscur(fd)&(1<<5)){
        printf("P_FAIL=1\n");
        exit(0);
    }
}
昨日使ったサンプルはincludeがもっと多かったが、上記だけでコンパイルできた。

複数のコマンドを実装したんで、実際のデバイスアクセス部分はSendMessageに分割した。
ステータスの確認が必要なコマンドもあるんで先頭1byteだけ返す。
応答のないコマンドはSPI_IOC_MESSAGE(1)にすれば構造体が2要素になってても2番目は無視される模様。
tx_bufで送信文字列渡してるから長さはここで取得できるかと思ったが、NULLが含まれててうまく行かなかったんでtx_lenで長さも別途渡すことにした。

READ (0x03)→ PP (0x02) → READ(0x03)
の順でコマンドを送る。

RDSR(0x05)で最下位bit(WIP)が1の場合は書き込み中でREAD不能ぽいんで先にRDSRをする必要がある。
READは0x03に続けて3byteのアドレスを送信すると、そこからCSが上がるまで何バイトでも読み続ける。
指定しなければ、CSはioctlの開始時に下がって終了時に上がってくれるみたいなのでrx_lenで指定したbyte数だけ読み込まれる。

書き込みの際はWREN(0x06)を実行するとRDSR(0x05)で下から2bit目(WEL)が1になる。WEL=1じゃないと書き込み不可なので先に確認。
PPは0x03に続けて3byteのアドレスを送信だが、アドレスの3byte目(最下位)は0x00にする必要がある。
先頭4byteに続けて256byteのデータを続けるんで260byteをデバイスに送信する。
送信されたデータは一旦バッファに保存されて、データ送信完了後にCSが上がると書き込みが開始される。
書き込み中はWIPが1になるんでRDSRで確認する必要がある。
書き込みが成功するとRDSCURで取得できる1byteの下から5byte目(P_FAIL)が0になるが失敗すると1になるらしい。
WELは書き込みが完了すると0になるんで書き込み系の命令を使うときは毎回WRENが必要。


実行結果
# ./spi /dev/spidev1.0
9f:c2 20 18  
05:02  
03:ff ff ff ff ff ff ff ff ff ff  
06:
05:02  
02:
05:03  
WIP=1
05:03  
WIP=1
05:03  
WIP=1
05:03  
WIP=1
05:03  
WIP=1
05:03  
WIP=1
05:03  
WIP=1
05:00  
2b:00  
05:00  
03:ff fe fd fc fb fa f9 f8 f7 f6
こんな感じになった。
255から0のASCIIコードを書き込んだ。(最後がNULLのがいいと思ったんでデクリメントにした)
PPは256byte単位だがREADは10byteだけにした。
想定通りの結果と思う。

データの初期値は1なのかな?実験中に書き換わっちゃった可能性も否定できないが・・・
なお、書き込み中のWIPの確認で6回ループしてるが、2回目に実行するとPPして次の
RDSRでWIP=0になった。
同じデータだと書き込まれない?PCの処理速度の問題か?

spidevを使ってみた

先日Orange Pi Zeroでspidevが出てくるようにしたが、
前にZeroの2MBフラッシュと交換しようとして買ったSPIフラッシュ(macronix MX25L12835F)が手元にあって、今日SOP8のDIP変換ボードが届いたんで試してみることにした。

https://www.emcraft.com/stm32f769i-discovery-board/accessing-spi-devices-in-linux
↑にSPIフラッシュのIDを確認するコマンドを送るサンプルがあったんでそのまま使ってみた。

# ./spi /dev/spidev1.0
response(7): c2 20 18 c2 20 18
データシートの9-5のとこに0x9Fの出力がc2 20 18って書いてあるんでデバイスは機能してるぽい。
が、応答3byteだと思うんだがなんで6byteなんだろ。サンプルのソースコードでも6byteになってるが・・・

# ./spi /dev/spidev1.0
response(6): 18 c2 20 00 00 00
サンプルのソースコードからxfer[1].lenを3にしたら一応3byteになった。

あとググって調べたところxfer(変数名がxferではなくtrの例が多かったが)の配列要素が1で同じとこにtx_bufとrx_bufを設定してる例が多かった。
これは要素数が1だとSPI_IOC_MESSAGE(2)のとこもSPI_IOC_MESSAGE(1)になるぽいんだが、
試しに要素数1でやってみたら、
# ./spi /dev/spidev1.0
response(6): 00 c2 20 18 c2 20
こんな感じで返ってきた。
TXの送信中もrx_bufに入る感じと思う。
だから配列の要素数を増やして分離すれば応答だけを取れる?

データシート見るとコマンドの送信がコマンド番号1byteに続いてダミー2byteとアドレス1byteを送信しているようにも見えるんだが、
xfer[0].len = 1;を3に(ダミーが1byteと勘違いした)んだが、応答ずれちゃいそうな気がするが結果が変わらなかった。
配列の要素が切り替わるときにCSが切り替わってたりするのか?
調べてもよくわからんかった。

改造の際にmemset(xfer, 0, sizeof xfer);を消してみたら、
# ./spi /dev/spidev1.0
SPI_IOC_MESSAGE: Invalid argument
で機能しなかった。
設定しない構造体の要素は0に初期化しないとダメな感じ?

12Vを駆動するNOT回路

Orange Pi ZeroのGPIOが足りないからNOT回路で1本の信号で2回路制御するのを考えたが、


こんな感じでMOSFET3つ使わないとできない?
回路の上流側にスイッチをつけるのがハイサイドで下流側につけるのがローサイドというみたいだが、
ステッピングモーターの駆動でHブリッジにするんでハイサイドにスイッチをつける必要がある。
電圧は12VでGPIOは3.3Vなんで、NMOSは12Vを制御するのに12Vよりも大きい電圧が必要。PMOSなら12Vで制御できるが3.3Vではできない。

右上がPMOSだが、ゲートに電源電圧をつなげちゃえばオフにできる。
なので、右のPMOS部分だけ見るとゲートに12Vがかかっているので回路はOFF。

真ん中のMOSFETはNMOSでゲートに直接3.3VがかかっててソースがGNDなので、
ここだけ見るとNMOSはONで、右のPMOSのゲートからGNDに短絡するんで右のPMOSのゲートは0Vになって回路に通電してる。
ここの電圧は3.3Vである必要はないかな。

左もNMOSでGPIOとゲートをつなぐ。
これがONになると真ん中のNMOSのゲートがGNDになるんで真ん中のNMOSがOFFになる。
真ん中のNMOSがOFFだと右のPMOSのゲートが12Vになるんで回路が遮断される。


ような気がする・・・

Orange Pi Zeroのspidev

Orange Pi Zeroで制御する感じで3Dプリンタ作ろうといろいろ勉強中だが、
3Dプリンタのホットエンドにはサーミスタがついてて温度によってヒーターのON/OFFを制御しなきゃならない。
サーミスタで温度を取得するのにADコンバータが要るが、Orange Pi Zeroには付いてない。
Raspberry Piでサーミスタ使う情報だと外部のSPI接続のADコンバータを使ってた。

まあそんな感じでSPI接続のADコンバータ使うことになるのかなと思うが、手元のOrange Piではspidevが出てなかった。

Buildrootのパッチ参考に、
sun8i-h2-plus-orangepi-zero.dts
    aliases {
        serial0 = &uart0;
        /* ethernet0 is the H3 emac, defined in sun8i-h3.dtsi */
        ethernet0 = &emac;
        ethernet1 = &xr819;
        spi0 = &spi0;
        spi1 = &spi1;
    };

&spi1 {
    status = "okay";

    spidev@0 {
        compatible = "rohm,dh2228fv";
        reg = <0>;
        spi-max-frequency = <10000000>;
    };
};
dtsをいじってdtbを作り直した。
が、これでまだ出てこない。

menuconfigで、
Device Drivers  --->
  [*] SPI support  --->
    <*>   User mode SPI device driver support
↑ここチェックしないとダメみたい。

/dev/spidev1.0 が出てきた。
これでSPI使えるんだと思う。

LOOX U/C40でVIA VNT-6656認識した

LOOX U/C40のGMA500で動画支援が使えない感じだが、
LOOX U/C40に2つあるminiPCIeスロットの片方にBroadCom Crystal HDをつけるってのもちょい考え中。

ただ、LOOX U/C40の裏カバーすぐにある空きのminiPCIeスロットはUSBしか配線されてないとかで、Atherosかintelのカードしか認識しないとか。
そんなチェックが入ってるとは思えないんで、おそらく裏カバーのスロットがUSB専用で、元々AtherosのWifiがついてるのがPCIe専用なのを理解しないで装着して認識しなかった人が誤情報を発信して広まった感じでは?

というわけで、工人舎SXについてたwifiカード(VIA VNT-6656)をLOOX U/C40の裏カバーすぐのminiPCIeスロットに装着してみた。
工人舎SXでUSBデバイスと認識されてたんでVIA VNT-6656は間違いなくUSBデバイス。
で、普通に認識した。
アンテナのケーブル届かないがナシで接続できた。
工人舎SXではVIA VNT-6656が不安定で頻繁に切断したりして使い物にならなかったが、工人舎SXはかなり熱くなったんで熱が問題じゃないかと思ってるんで、LOOX Uで問題なければ元々ついてるAtherosを外せる。
そうすればこちらのスロットはPCIeなんでBroadCom Crystal HDが機能するんじゃないかと。

モータードライバ買った

Orange Pi Zeroと MOSFET でのモーター制御ができたが、
次の段階として今回は5V駆動したんで12V駆動のために2.54mm化のDCジャックを注文して、
N-MOSFETだけだとモーター1つにGPIO4ピン使うが半分P-MOSFETならGPIOx2で制御できるはず。
というわけでP-MOSFETを買おうと思ったんだが、
デュアルMOSFETなるものを知った。

デュアルMOSFETはそのまんまMOSFETが2つのIC?なんだが、N+Pのものがある。
NのデュアルとPのデュアルに部品を分けた方が1個で電流を1本流せるが、
今回SOT-23のMOSFET使ってわかったが、肉眼で印字が見えないからNとPの2種類あったらどっちかわからん。
N+PのデュアルMOSFET使えば部品が1種で済むから見分ける必要がない。
というわけで、AO4606ってSOIC-8(SOP-8とは幅が違うぽい)のデュアルMOSFETを買おうかと思った。
SOT-23のシングルMOSFET50個分の値段で10個入りくらいの価格。
L9110SってフルブリッジのモータードライバICと似たような価格。
L9110は12V0.8Aしか流せないんでギリギリな感じだがAO4606は30V6Aまででかなり大電流が流せる。

が、よく考えるとフルブリッジのモータードライバはMOSFET4個分なんだよね。
ということに気づいたんでL9110買ってみることにした。
SOP-8だとAO4606と大差ない価格だったが、
変換基板につけるのもめんどいし勿体無いから、テスト用にL9110HってDIPパッケージのやつを5個にした。
本番ではSOP-8のL9110Sを別途買うつもり。

MOSFETでモーターを動かす前にLEDで実験

昨日タクトスイッチとMOSFETを使ってLEDを1個光らせる実験をしたが、
Orange Pi ZeroでMOSFET8個を制御する実験やった。

#include <unistd.h>
#include <fcntl.h>

int main(void){
    char phase[4][4]={
        {'1','0','0','1'},
        {'1','0','1','0'},
        {'0','1','1','0'},
        {'0','1','0','1'}
    };
    int gpio[4];
    gpio[0]=open("/sys/class/gpio/gpio12/value",O_WRONLY);
    gpio[1]=open("/sys/class/gpio/gpio11/value",O_WRONLY);
    gpio[2]=open("/sys/class/gpio/gpio6/value",O_WRONLY);
    gpio[3]=open("/sys/class/gpio/gpio1/value",O_WRONLY);
    int i,j,p;
    for(i=0;i<100;i++){
        p=i%4;
        for(j=0;j<4;j++){
            write(gpio[j],&phase[p][j],1);
        }
        sleep(1);
    }
    char b='0';
    for(j=0;j<4;j++){
        write(gpio[j],&b,1);
    }
    close(gpio[0]);
    close(gpio[1]);
    close(gpio[2]);
    close(gpio[3]);
    return(0);
}
↑ソースコードはこんな感じ。


1秒毎に2個ずつ光るLEDが変わる。
ミニブレッドボード4個は全部同じでハーフブリッジ回路?にした。
MOSFETは8個だが2個は同時にON/OFFするんでGPIOは4本。
12,11,6,1のGPIO使った。6と1の間にはGNDがあるんで1ピン飛ぶ。
MOSFETは全部Nチャネルだが、半分Pチャネルにすれば2本で制御できると思ってる。

Orange Pi ZeroにUSB wifiつけた

ルーターがTKIPだからぽいんだが、Orange Pi ZeroのXR819が使い物にならんのでUSB wifi買ってたが昨日届いたんでつけてみた。

USBなんでとりあえず母艦に装着して、
$ lsusb
0bda:f179 Realtek Semiconductor Corp. 802.11n
出力省略してるけど、デバイス名わからんが 0bda:f179 のデバイスだった。
で、デバイスIDでググったら rtl8188fuぽかった。
なんか8188FU=8189FTVとか書いてある情報が出てくるけど、8189FTVはOrange Pi R1でお世話になったSDIOデバイスだからUSBデバイスじゃないよ。

linuxのmenuconfig確認すると8188xxはあるんだが8188FUはなさげ・・・
というわけで探したが、
https://github.com/kelebek333/rtl8188fu
↑のでビルドできた。
DKMSの説明しか無いが普通に
$ make ARCH=arm CROSS_COMPILE=arm-none-eabi- KSRC=../linux-5.8.3 modules
で rtl8188fu.ko が出来上がる。
Makefile はそのまま。
最初
https://github.com/ulli-kroll/rtl8188fu
↑のがDKMSじゃないんでこっちでmakeしたんだが、こっちは5.8.x非対応だった。
rtl8188fu.koを /lib/modules/5.8.3 に、
firmware/rtl8188fufw.bin を /lib/firmware/rtlwifi にコピー
で使える。

が、なんかカーネルメッセージがコンソールに出まくる。
sshなら出てこないけどシリアルコンソールだと出力が邪魔すぎ・・・

/etc/network/interfaces
auto wlan1
iface wlan1 inet dhcp
  pre-up start-stop-daemon -S -x wpa_supplicant -- -B -iwlan1 -c/etc/wpa_supplicant.conf
  pre-down start-stop-daemon -K -q -x wpa_supplicant
こんな感じで起動時に有効になった。
wifiを/etc/network/interfacesでDHCPは初だが、pre-upでwpa_supplicantしないとDHCPできない?

MOSFETでLED光らせてみた

昨日MOSFET届いてたんでLED光らせる実験してみた。

注文したMOSFETはAO3406ってやつなんだが、届いたの顕微鏡で見たらA69Tって書いてあった。
最初A59Tに見えて調べたらPチャネルのようで違うの送られたのかと思ったが、(まあAO3406じゃない時点で違うがw)
A69TというのがNチャネルのようで、よく見たら印字はA69だった。
A69Tのデータシートは見つからなかったがAO3406と同等品と信じる・・・
AO3406は30V,3.6Aまで流せてチャージ速度が速そうなものを選んだ。


↑LED光らせてる実験。指の下にタクトスイッチがある。

SOT23変換ボードは先日届いてたのでMOSFETは変換ボードに載せたが、ハンダが思いっきし曲がったw
まあ電気的には問題なさそうだったんで曲がったまま。
テープで固定してからハンダすべき?
端子はAO3406のデータシートのとおりにやったが、1本だけの側がDで左下(Dが上として)がGで右下がS。

以前買った中華フラックスが未使用で、国産無洗浄フラックスももったいないし中華フラックス使ってみたがいい感じだった。
MOSFETのハンダは失敗して曲がっちゃったが、ヘッダーピンのハンダはランドに吸い込まれる感じできれいにハンダできた。
無洗浄じゃないフラックスと思うんで、一応ウェットティッシュで軽く拭いといた。

後から書いたが、

回路図こんな感じ。
LEDのVFわからんので適当に300Ωで事前に光ること確認してからやった。
最初MOSFETのゲートのところに抵抗つけなかったんだが、電源つないだらスイッチ押して無いのに光った。
ゲートをGNDに繋がないと電圧不定で誤動作しちゃうわけね。
100kΩつけたら期待通りの動作になった。

EMGDドライバ入れてみた

工人舎SXとLOOX U/CのGMA500だが、現行カーネルで標準では入るドライバだと動画再生支援が効いて無く、クローズドなIntel EMGDドライバだと効くとか。
だが、Xorg 1.9までにしか対応していなくArch Linuxで入れるのは難しい。
で、https://launchpad.net/~thopiekar/+archive/ubuntu/emgd にドライバとかXorg 1.9とかいろいろあるんでXubuntu 16.04入れてみた。
上記のドライバはubuntu 16.04までしかなさげ。

Xubuntu 16.04なんだが、最初16.04.6入れたらkernelが4.15だった。
で、linux 4.15でemgdドライバがビルドエラー出てた。
他のドライバだが、4.4.xxxのどっかで仕様が変わってダメになるみたいな情報を見たんで同じだと思った。
kernelをダウングレードしてみたんだが、4.4.143より古いのはaptでmodulesが見つからなくて4.4.143にしてみたがダメだった。
16.04に種類があるのは知らなかったが、16.04.1だとkernelバージョンが古いぽいんで入れてみたら4.4.31だった。これは通った。
だからインストールの際に16.04.1のISOを使ってインストール中にダウンロードしないようにしないとめんどいことになる。
http://ubuntutym2.u-toyama.ac.jp/xubuntu/16.04/release/
ISOは↑で拾ってきた。

emgdドライバは、
$ sudo add-apt-repository ppa:thopiekar/emgd
$ sudo apt update
$ sudo apt install emgd-driver
で入る。
Xも入れてくれてる感じだったが、
xorg-serverがなさげだったんで、
$ sudo apt install xserver-xorg-1.9
は別途入れる必要があるかも。

そのまま再起動すると画面表示されず操作不能になる。
事前に、
$ sudo Xorg :1 -configure
してホームディレクトリにxorg.conf.newを作っといたんで、
/etc/X11/xorg.conf にコピーして、Section "Device"の部分以外全部消して、Driver "emgd"
にして再起動した。
すると、画面は出るんだがなんか全然おかしいし、パスワード入れてもlightdmからxfceにログインできない。
というかそもそもオートログインにしてたんだが・・・
ってなことになったが、Alt+Ctrl+F1でlightdmを落として
$ sudo systemctl stop lightdm
$ sudo startxfce4
でXfceが起動した。
がやっぱ画面がおかしい。
上に表示されるべき部分が切れて下に表示されてたり、横幅もなんか変。

http://steadyassimilation.blogspot.com/2011/06/note-looxuc40ubuntu1104.html
↑11.04の情報だが、ちょうどLOOX U/C40の情報があった。
これ参考にxorg.confを修正したら正常に表示された。

なお、当初工人舎SXを使うつもりでそっちで作業してて、
ヒンジの角度?が悪いと画面消えたりして大変だったんだが、
最終的にwifiが何故かhardlockedになっちゃって解除できず、LOOXに代えた。
FANエラーがあるがLOOXのが小さいしいいわ。

これでXfceは起動したが、libvaは別途入れる必要があるみたい。
$ sudo apt install libva-1.0.16
$ sudo apt install vainfo-1.0.16
同じリポジトリにあるバージョンを入れた。
これでvainfoしたら動画支援効きそうな感じだった。
まだブラウザでは試してない。