稼働中

マイクロビット(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カードモジュール外観

実体接続した写真です。

実体図

テストに使用したSDカードです。以下のSD 1GB(以降では標準SD、MMCと記載することがあります)、SDHC 16GBを使いました。

使用したSDカード2種類

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です。
R1フォーマット
※Physical Layer Simplified Specification Version 2.00から引用

CMD8のレスポンスはR7形式の5バイトが返ります。エラーの確認は上位部のR1で判別します。

R7フォーマット
※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フォーマットになります。
R3フォーマット
※Physical Layer Simplified Specification Version 2.00から引用

OCRレジスタのビット15~31の内容は以下になります。
OCR
※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」