Raspberry Pi Pico(d_25)SDカードのWrite、Read、Erase
SD Card Write/Read/Erase (SPI)
SDカードのWrite、Read、Eraseについて記載します。
Write/Read/Eraseのコマンドで送る32bitの引数はアドレスです。
High Capacity SD Cards(SDHC 2GB<)は512bytesのブロック単位、Standard Capacity Cards(SDC <=2GB)はバイト単位なので512バイトの倍数で指定します。
下方のスクリプトでは、
spi_cmd(cmd24,0x00,0x00,0x02,0x00,0x01) ※spi_cmd スクリプト内の関数
コマンドcmd24の後の32bitはアドレスです。例では0x00,0x00,0x02,0x00がアドレスです。
SDCの場合のアドレスは0x0200(512)、SDHCの場合は512bytesブロック(512*0x0200 ‘0x40000’)になります。
WRITE_BLOCK(CMD24)
CMD24(WRITE_BLOCK)でシングルブロックを書込みます。単一ブロックは512バイトです。SD(<=2GB)の場合512byteの倍数でアドレスを指定します。
※「Physical Layer Simplified Specification Version 2.00」より抜粋
CDM24を送信し、応答(R1)を待ってから、開始トークン(0xFE)を送信し、続いて512バイトのデータを送信します。
正常に書込みが終わるとData Responseが返ります。レスポンス値と’00011111(0x1f)’と積で評価します。’00000101(0x05)’で正常です。
※「Physical Layer Simplified Specification Version 2.00」より抜粋
READ_SINGLE_BLOCK(CMD17)
CMD17(READ_SINGLE_BLOCK)でシングルブロック読み取りをします。単一ブロックは512バイトです。
SD(<=2GB)の場合512byteの倍数でアドレスを指定します。
CMD17を送った後のレスポンス’0x00’に続いてトークン’0xfe’を得た後に512バイトデータを読み込みます。512バイトデータの後に2バイトのCRCが続きます。
※「Physical Layer Simplified Specification Version 2.00」より抜粋
ERASE
Eraseのコマンドは以下になります。CDM32で開始アドレス、CMD33で終了アドレスを指定してCMD38でブロックを消去します。単一ブロックは512バイトです。SD(<=2GB)の場合512byteの倍数でアドレスを指定します。
cmd32=0x60 #ERASE_WR_BLK_START_ADDR R1
cmd33=0x61 #ERASE_WR_BLK_END_ADDR R1
cmd38=0x66 #ERASE Erases R1b
スクリプト
SDカードのWrite、Read、Eraseを行うスクリプトは以下のようにしました。
※開発環境はThonnyです。ThonnyでMicroPythonをRaspberry Pi Pico with RP2040にインストールして使っています。
※Raspberry Pi Pico単独で動作させるには’main.py’としてRaspberry Pi Picoにuploadして使います。
pico_sd_test_05b_02.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from machine import SPI, Pin
import sys
import time
# CMD ERASE
cmd32=0x60 #ERASE_WR_BLK_START_ADDR R1
cmd33=0x61 #ERASE_WR_BLK_END_ADDR R1
cmd38=0x66 #ERASE Erases R1b
# CMD READ
cmd17=0x51 #READ_SINGLE_BLOCK
# CMD WRITE
cmd24=0x58 #WRITE_BLOCK
#SPI.init
spi = SPI(1, baudrate=100_000,sck=Pin(14), mosi=Pin(15), miso=Pin(12))
#Chip Select Pin
CS_pin=Pin(13, mode=Pin.OUT, value=1) #CS GP1
# Chip Select CS(0)
def CS(dat):
CS_pin.value(dat)
# card send data
def spi_cmd(cmd,da1,da2,da3,da4,crc):
buf=bytearray(6)
buf[0]=cmd
buf[1]=da1
buf[2]=da2
buf[3]=da3
buf[4]=da4
buf[5]=crc
spi.write(buf)
return buf
# response
def spi_res(num=1):
response=spi.read(num)
#print(response)
time.sleep_us(100)
return response
# dummy clock
def spi_ff():
spi.write(b'\xff')
# spi end
def spi_end():
spi_ff()
CS(1)
spi_ff()
# SET SPI MODE
def SPI_MODE():
print('SPI_MODE')
CS(1) #CS=Hでダミークロックを80個送信
for i in range(10):
spi_ff()
# CS select
CS(0) #カード選択
spi_ff()
## SDC, SDHC---Erase(CMD38)、Write(CMD24)、Read(CMD17)
# CMD32 BLK_START_ADD
def ERASE_START(s=[0x00,0x00,0x00,0x00]):
# CS select
CS(0)
spi_ff()
spi_cmd(cmd32,s[0],s[1],s[2],s[3],0x01)
spi_ff()
res=spi_res(1)
if res != b'\x00':
print('CMD32 ERROR',res)
sys.exit()
#end
spi_end()
# CMD33 BLK_END_ADD
def ERASE_END(e=[0x00,0x00,0x02,0x00]):
CS(0)
spi_ff()
spi_cmd(cmd33,e[0],e[1],e[2],e[3],0x01)
spi_ff()
res=spi_res(1)
if res != b'\x00':
print('CMD33 ERROR',res)
sys.exit()
#end
spi_end()
# CMD38 ERASE Erases
def ERASE_GO():
CS(0)
spi_ff()
spi_cmd(cmd38,0x00,0x00,0x00,0x00,0x01)
spi_ff()
res=spi_res(1)
if res != b'\x00':
print('CMD38 ERROR',res)
sys.exit()
#end
spi_end()
## ERASE (2)
def SD_ERASE(es,ee):
ERASE_START(es)
ERASE_END(ee)
ERASE_GO()
print('ERASE')
print('START ADD',['{:#04x}'.format(i) for i in es])
print(' END ADD',['{:#04x}'.format(i) for i in ee])
#end
spi_end()
# CMD17 READ_SINGLE_BLOCK(3)
def SD_Read(r=[0x00,0x00,0x00,0x00]):
# READ_SINGLE_ BLOCK
# CS select
CS(0)
time.sleep_ms(100)
spi_ff()
radr=spi_cmd(cmd17,r[0],r[1],r[2],r[3],0x01)
# 0x00>>0xfeが返るまで受信
cnt=0
while True:
cnt=cnt+1
spi_ff() # CMD>>0xFF out >>read response
res=spi_res()
if res== b'\x00':
print('CMD17 Read')
break
if cnt>10:
print('CMD17-NG',cnt)
sys.exit()
cnt=0
while True:
cnt=cnt+1
spi_ff()
res=spi_res()
if res== b'\xfe':
print('token 0xfe')
break
if cnt>10:
print('token-NG',cnt)
sys.exit()
#read data 512bytes
block_buf=spi.read(512)
#CRC16 2byte
crc_buf=spi.read(2)
spi_end()
return block_buf,crc_buf
# CMD24 WRITE_BLOCK(4)
def SD_Write(w=[0x00,0x00,0x00,0x00]):
# CS select
CS(0)
spi_ff()
# WRITE_BLOCK
spi_cmd(cmd24,w[0],w[1],w[2],w[3],0x01)
# SDHC R1確認(標準SDはCMD24の応答を確認せずに書込む)
if Card=='HC':
#Card='HC'
cnt=0
while True:
cnt=cnt+1
res=spi_res()
if res== b'\x00':
print('CMD24 Write')
break
if cnt>10:
print('CMD24 NG',cnt)
sys.exit()
#スタートバイト 0xFE をまず送ってからWriteデータを送ります
spi_ff() #One byte gap
spi.write(b'\xfe') #Start Block Token //Send Data token
#512バイトデータを送ります
spi.write(block_buf)
# data response check xxx00101
cnt=0
while True:
cnt=cnt+1
res=spi_res()
re=res[0] & 0x1f # datares XXX1???1
if re==0x05:
print('data response=',res,bin(res[0]))
print('')
break
if cnt>10:
print('WRITE-NG',cnt)
sys.exit()
#end
spi_end()
time.sleep_ms(200) ###
# main ---------------------動作確認
block_buf=bytearray(512) # Write Data 512byte 同じ値
for i in range(512):
block_buf[i]=0xff
Card='HC' #Card='SD' Card='HC'
# write --------- CMD24
SPI_MODE() # SPI MODE ---##### 必要
w_adr=[0x00,0x00,0x02,0x00] #BLK_WRITE_ADD
SD_Write(w_adr)
print('')
# read ---------------------CMD17
#SPI_MODE() NG
#32bit address SDcard bytes,SDHCcard_Block unit
r_adr=[0x00,0x00,0x02,0x00] #BLK_READ_ADD
r_dat,r_crc=SD_Read(r_adr)
print('Data=',r_dat)
print('CRC16=',r_crc, hex(r_crc[0]),hex(r_crc[1]))
print('')
# erase -------------- CMD32-33-38
SPI_MODE()
# 32bit address SDcard bytes,SDHCcard_Block unit
s_adr=[0x00,0x00,0x00,0x00] #BLK_END_ADD
e_adr=[0x00,0x00,0x04,0x00] #BLK_START_ADD
SD_ERASE(s_adr,e_adr)
関数・部分説明
■SPI_MODE()
SDカードをSPIモードに設定します。
■SD_Read(r=[0x00,0x00,0x00,0x00])
シングルブロックデータとCRC16をリストで返します。ブロックは512byte単位です。
Read開始アドレスを[0x00,0x00,0x00,0x00]で与えます。
SDHCは512byteのブロックアドレスになります。SDCは512byteの倍数で指定します。
■SD_Write(w=[0x00,0x00,0x00,0x00])
シングルブロックでデータを書き込みます。ブロックは512byte単位です。
Write開始アドレスを[0x00,0x00,0x00,0x00]で与えます。
SDHCは512byteのブロックアドレスになります。SDCは512byteの倍数で指定します。
■SD_ERASE(es,ee)
es:消去開始アドレスを[0x00,0x00,0x00,0x00]で与えます。
es:消去終了アドレスを[0x00,0x00,0x00,0x00]で与えます。
SDHCは512byteのブロックアドレスになります。SDCは512byteの倍数で指定します。
実行結果
初期化後に16GBのSDHCで実行してみました。初期化は前回の記事を参照ください。
結果は以下のようになりました。※Thonnyのshellに表示されます。
>>> %Run -c $EDITOR_CONTENT
SPI_MODE
CMD24 Write #[0x00,0x00,0x02,0x00] 0xffを512bytes書き込み
data response= b'\xe5' 0b11100101
CMD17 Read #[0x00,0x00,0x02,0x00]読み出し
token 0xfe #読み出し
Data= b'\xff\xff\xff\xff\xff\xff\xff\
..省略..
\xff\xff\xff\xff'#
CRC16= b'\x7f\xa1' 0x7f 0xa1 #正常です。下CRC16例を参照。
SPI_MODE
ERASE
START ADD ['0x00', '0x00', '0x00', '0x00']
END ADD ['0x00', '0x00', '0x04', '0x00']
>>>
CRC16の例がデータシートにあります。
512bytes すべて’0xff’のデータを読みだした際のCRC16の例です。値は0x7FA1です。
※「Physical Layer Simplified Specification Version 2.00」より抜粋
ちなみにCard=’SD’にすれば、SDカード(初期化後)のテストが出来ます。
その際のRead開始などのアドレスは
[0x00,0x00,0x02,0x00]、[0x00,0x00,0x04,0x00]・・などの512の倍数で指定しないとエラーになります。
まとめ
Raspberry Pi PicoでSDHCカードのWrite、Read、Erase動作を確認しました。