spidevでSPIフラッシュの読み書きした
昨日はspidevの実験でデバイスのIDだけ取得してみたが、読み書きも試してみた。
ソースコード
複数のコマンドを実装したんで、実際のデバイスアクセス部分は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が必要。
実行結果
255から0のASCIIコードを書き込んだ。(最後がNULLのがいいと思ったんでデクリメントにした)
PPは256byte単位だがREADは10byteだけにした。
想定通りの結果と思う。
データの初期値は1なのかな?実験中に書き換わっちゃった可能性も否定できないが・・・
なお、書き込み中のWIPの確認で6回ループしてるが、2回目に実行するとPPして次のRDSRでWIP=0になった。
同じデータだと書き込まれない?PCの処理速度の問題か?
ソースコード
#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がもっと多かったが、上記だけでコンパイルできた。#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);
}
}
複数のコマンドを実装したんで、実際のデバイスアクセス部分は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
こんな感じになった。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の処理速度の問題か?