マイクロビット(e_22)SDカード モジュール(1)初期化 ACMD41
SDカードの初期化 ACMD41 、OCR確認 CMD58
SPI通信で制御できるSDカードモジュールを使ってみました。
5Vで動作させるので別途電源が必要です。レベル変換回路を搭載しているのでmicro:bitとのSPI通信は直接接続できます。5V電源とmicro:bitとSPI接続するだけです。そのため、接続図などは省きました。
※thonny-microbitのMicroPythonを使っています。
外観・接続
使用したSDカードモジュールの外観写真です。
実体接続した写真です。
テストに使用したSDカードです。以下のSD 1GB(以降では標準SD、MMCと記載することがあります)、SDHC 16GBを使いました。
SDカードの初期化
素人なので詳しくは分かりません。そのため、分かった範囲で記載します。※詳細は記事末に列挙した参考サイトなどを参照して下さい。
SDCカードはMMC(Multi-Media Card)の上位の兌換品のようです。
SDCカードの初期化は①CMD0>CMD8>CMD55>ADCMD41で行います。CMD8でエラーになれば②CMD0>CMD1で行います。②は2GB以下の標準SDカード(MMC)の初期化のような感じです。
初期化で使うコマンド
コマンド 概略
CMD0 0x40 #GO_IDLE_STATE CRC必要 リセット
CMD8 0x48 #SEND_IF_COND CRC必要 カードの世代、動作電圧確認
CMD55 0x77 #APP_CMD 次コマンドはAPP_CMD
ACMD41 0x69 #SD_APP_OP_COND 初期化 SDHCのサポート
CMD1 0x41 #SEND_OP_COND 初期化 MMC
コマンドの送信
送信は、コマンド(1) + データ(4) + CRC|0x01(1)の6バイトで送ります。
CRCは循環冗長チェックと言うそうです。計算式がありますが素人なので省略します。
CMD0とCMD8には’CRC|0x01’の正値が必要です。他は適当(奇数)で良いようです。
コマンドの形式は8bitで上位2ビット01(0b01000000)+コマンド番号になっています。
例えばCMD55なら
>>> hex(0x40|55)
'0x77'
になります。
CMD(1) + data(4) + CRC|0x01(1)の送信例として、
コマンドがcmd55、データが0x00 0x00 0x01 0xAA,「CRC|0x01」が0x87の場合、
spi_cmd(0x77,0x00,0x00,0x01,0xAA,0x87)
の形式で送信できるように以下の関数を作成しました。
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
レスポンス
コマンドを送信すると状態が返ります。大半はR1形式で1バイトが戻ります。正常な場合は0x01か0x00です。
※Physical Layer Simplified Specification Version 2.00から引用
CMD8のレスポンスはR7形式の5バイトが返ります。エラーの確認は上位部のR1で判別します。
※Physical Layer Simplified Specification Version 2.00から引用
CMD8でエラーの場合はCMD1で初期化します。
一連の処理で初期化できれば良かったのですがmicro:bitでメモリー関連のエラーが出てしまうので、諦めてACMD41を使う場合とCMD1を使う場合を個別処理にしました。
具体的にはSDHC 16GBの初期化はACMD41を使って行い、標準SD 1GBの初期化はCMD1を別々に行うことにしました。
ACMD41 SD_APP_OP_COND
ACMD41でSDHCカードの初期化を行います。
CMD0>CMD8>CMD55>ADCMD41の順番でコマンドを送ります。
CMD0を送る前にシーケンスの初期化のためにダミークロックを74回以上送る必要があるそうです。実際には80回送るようにしました。ダミークロック8回は「spi.write(b’\xff’)」で良いようです。
micro:bitのpin8をSPIのCS(チップセレクト)用にしました。
スクリプト
以下にスクリプトを作成してみました。※動作を確認するための実働には関係しない表示(print)が多く入ってます。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from microbit import *
# ------------------------INIT (SDC 2GB<)---- CMD0>>CMD8>>CMD55>>ACMD41
import sys
# micro:bit pin8 for CS active(0)
CS_pin=pin8 # micro:bitの8pinでCSを制御
# command
cmd0=0x40 #GO_IDLE_STATE (コマンド番号|0x40)の値
cmd8=0x48 #SEND_IF_COND
cmd55=0x77 #APP_CMD
acmd41=0x69 #SD_APP_OP_COND
# card send data コマンド送信 6バイト
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)
return response
# dummy 8clk ダミークロック
def spi_ff():
spi.write(b'\xff')
# chip select active(0) チップセレクト(0)がアクティブ
def CS(dat):
CS_pin.write_digital(dat)
# SPI init bps=100kHz 100~400KHz
spi.init(baudrate=100000, bits=8, mode=0, sclk=pin13, mosi=pin15, miso=pin14)
sleep(10)
## SDC INIT START
print('CLK>80')
CS(1) # CS=H ダミークロックを80個送信
for i in range(10):
spi_ff()
CS(0) #カード選択
spi_ff()
# ---------------------------------------------------------- CMD00
print('CMD0')
spi_cmd(cmd0,0x00,0x00,0x00,0x00,0x95)
while True:
spi_ff() # CMD>>0xFF out>>read response
res=spi_res()
if res== b'\x01': # 正確には0x1fならダミークロック80回へ戻る
print(res,'cmd0 OK x01') # 0x01 返りでOK
break
# ---------------------------------------------------------- CMD08
print('CMD8')
spi_ff()
# 3.3V動作、推奨チェックパターン'0xaa'なら(CRC0x86|0x01)の0x87 らしい
spi_cmd(cmd8,0x00,0x00,0x01,0xAA,0x87)
spi_ff()
# R7(5bytes)=R1+4bytes
res=spi_res(5) # R7形式で5バイトの応答
print('R7-5bytes >>',res)
if (res[0]== 0x01) or (res[0]== 0x00):
print('CMD8 0x01 or 0x00 >>> ') # エラーなし CMD55へ続行
else:
print('SDC-CARD ERROR >>> MMC ') # MMC(CMD01での初期化へ)
sys.exit()
while True:
# ---------------------------------------------------------- CMD55 APP_CMD
print('CMD55')
spi_ff()
spi_cmd(cmd55,0x00,0x00,0x00,0x00,0x01) # CMD55を送信(次CMDはAPP)
spi_ff()
res=spi_res()
if res == b'\x01': # エラーなし ACMD41へ続行
print('CMD55 0x01 >>>')
else:
print('CMD55 ERROR EXIT') # エラー中止
sys.exit()
# ---------------------------------------------------------- acmd41
print('ACMD41')
spi_ff()
# SDHCをサポートしていることを示すためにビット30を1に設定 >0x40
spi_cmd(acmd41,0x40,0x00,0x00,0x00,0x01)
spi_ff()
res=spi_res(1)
if res == b'\x01':
print('ACMD41 0x01 RETRY', res) # IDLE > CMD55から再試行
if res == b'\x00':
print('ACMD41 0x00 OK', res) # エラーなし 初期化 了
break
#CS(1)
#spi_ff()
#END
spi_ff()
CS(1)
spi_ff()
実行結果
実行結果です。※Thonnyのshell枠に表示されます。
SDカードモジュールのソケットにSDHC 16GBをさして実行しました。
>>> %Run 220113_sd_b01_acmd41_init.py
CLK>80 # ダミークロック80回開始
CMD0
b'\x01' # CMD 返り0x01OK
b'\x01' cmd0 OK x01
CMD8
b'\x01\x00\x00\x01\xaa' # CM8 返り5バイト OK
R7-5bytes >> b'\x01\x00\x00\x01\xaa' # CM8のR1 0x01でOK
CMD8 0x01 or 0x00 >>>
CMD55
b'\x01' # CM55の返り0x01でOK
CMD55 0x01 >>>
ACMD41
b'\x01' # ACMD41の返り0x01 リトライ
ACMD41 0x01 RETRY b'\x01'
CMD55
b'\x01' # CM55の返り0x01でOK
CMD55 0x01 >>>
ACMD41
b'\x00' # ACMD41の返り0x00でOK 初期化了
ACMD41 0x00 OK b'\x00'
>>>
ちなみに標準SD 1GBにして実行すると、以下のようにCMD8でエラーになります。別途、CDM1で初期化します。
>>> %Run 220113_sd_b01_acmd41_init.py
CLK>80
CMD0
b'\x01'
b'\x01' cmd0 OK x01
CMD8
b'\xff\xff\xff\xff\xfe'
R7-5bytes >> b'\xff\xff\xff\xff\xfe'
SDC-CARD ERROR >>> MMC
CDM58 READ_OCR
CMD58はOCR(動作条件レジスタ)を読み取ります。レスポンスは5バイトです。下のR3フォーマットになります。
※Physical Layer Simplified Specification Version 2.00から引用
OCRレジスタのビット15~31の内容は以下になります。
※Physical Layer Simplified Specification Version 2.00から引用
ビット15~23はサポートされている電圧です。2.7~3.6Vなら0xff/0x80にあたります。
ビット31はカードが電源投入ルーチン(初期化)を終了なら’1’になります。
ビット30はカード容量を示し、初期化後のみ有効でありSDHCやSSCXCなら’1’になります。
先のSDHC 16GBカードの場合、初期化後はOCRレジスタの4バイトは 0xC0/0x00/0xff/0x80 になります。
スクリプト
CMD58、OCR(動作条件レジスタ)をだけ実行するスクリプトを以下のようにしました。
※先と同じ関数部分は省略して記載しています。
# SPI init bps=100kHz
spi.init(baudrate=100000, bits=8, mode=0, sclk=pin13, mosi=pin15, miso=pin14)
sleep(10)
# command
cmd58=0x7A # READ_OCR
# SDCの初期化
print('START CLK>80')
CS(1) # CS=Hでダミークロックを80個送信
for i in range(10):
spi_ff()
CS(0) #カード選択
spi_ff()
# --------------------------------------------- CMD58 OCR
print('CMD58 ***')
spi_ff()
spi_cmd(cmd58,0x00,0x00,0x00,0x00,0x01)
spi_ff()
# R3 5bytes (R1+OCR_4bytes) # R3 5バイト戻る
res=spi_res(5)
if (res[0]== 0x01) or (res[0]== 0x00): # R1 エラーチェック
print('cmd58 >>>')
print('R1(1byte)+OCR(4bytes)=',res) # R3 5バイト戻値の表示
else:
print('CMD58 ERROR EXIT') # エラー 終了
sys.exit()
#END
spi_ff()
CS(1)
実行結果
R3の値はb’\x00\xc0\xff\x80\x00’になりました。エラーなし、初期化済(power up=’1’)、SDHCカード(CCS=’1’)、2.7~3.6Vの電源を示しています。
>>> %Run 220114_sd_b01_cmd58_ocr.py
START CLK>80
CMD58 ***
cmd58 >>>
R1(1byte)+OCR(4bytes)= b'\x00\xc0\xff\x80\x00'
>>>
ちなみにACMD41で初期化していない(パソコンでフォーマットした後)の場合、
CMD58を実行するとR3の値はb’\x00\x00\x00\x00\x00’になりました。
>>> %Run 220114_sd_b01_cmd58_ocr.py
START CLK>80
CMD58 ***
cmd58 >>>
R1(1byte)+OCR(4bytes)= b'\x00\x00\x00\x00\x00'
また、ACMD41で初期化していないカードでCMD0を実行するとのR3の値は
b’\x01\x00\xff\x80\x00’になりました。アイドル状態、サポート電圧のビットだけが変化しました。
初期化が終われば、カード種類(容量)のビット30と初期化済みのビット31が’1’になるようです。
>>> %Run 220113_sd_b01_test01.py
CLK>80
CMD0
b'\x01'
b'\x01' cmd0 OK x01
>>> %Run 220114_sd_b01_cmd58_ocr.py
START CLK>80
CMD58 ***
cmd58 >>>
R1(1byte)+OCR(4bytes)= b'\x01\x00\xff\x80\x00'
>>>
まとめ
micro:bitでSDカードモジュールを使って、SDHCカードのACMD41コマンドによる初期化ができたと思います。また、CMD58でOCRレジスタ情報を確認できました。次回はCMD1を使った通常SDカードの初期化を記載したいと思います。
※参考にしたサイト
AVRCチュートリアル「http://www.rjhcoding.com/avrc-tutorials-home.php」
PICを使ってSDカードを操作「http://bitcraft.web.fc2.com/embedded/microchip/microchip.html#sdhcinit」
A33FでMicroSDカードにアクセスしてみる「http://www.robotsfx.com/robot/robohow/RoboHow91/RoboHow91.html」