Raspberry Pi Pico(d_24)SDカード モジュール 初期化
SD Card INIT (SPI)
Raspberry Pi PicoでSDカードモジュールを使ってみました。SPI通信で制御できます。
SDカード(Secure Digital Card)の初期化について記載します。
SPIのクロック周波数は100kHz~400kHzのようです。100kHzで動作を確認しました。
※micro:bitの記事e_22などからRaspberry Pi Picoで使えるように追記、修正等をしました。
外観
SDカード モジュールの外観写真です。(以後はSDカード、SDC、SDHCと記載します)
テストに使用したSDカードです。SD 1GBとSDHC 16GBを使いました。
接続例
SDカードの電源電圧は4.5~5.5Vなので、5Vの別電源を使用しました。
レベル変換回路を搭載しているので、SPI通信はRaspberry Pi Picoと直接接続できます。
SPI通信にはSPI1のRX(GP12)、CSn(GP13)、SCK(GP14)、RX(GP15)を使いました。それぞれMISO、CS、SCK、MISOと接続しました。
SDカードの説明
データシートから抜粋してSDカード(Secure Digital Card)の一部を説明します。
※素人レベルなので詳しくは分かりません。間違っているかも知れません。詳細はデータシートなどを参照ください。
使用できるSDカードは、SDC(Standard Capacity SD Memory Card ~2GB)とSDHC(High Capacity SD Memory Card 2~32GB?)があります。カードの種類で初期化やアドレス指定などが変わります。
■SPIモード
SDカードの制御をSPIモードにするには、CS(チップセレクトピン)を’1’にした状態で74クロック以上を送り、CSを’0’にしてデバイスを指定します。
■SDカードの初期化
SDHCの場合はCMD0>CMD8>CMD55>ADCMD41で行います。SDCの場合にはCMD8でエラーになります。
この記事ではSDCの初期化はCMD0>CMD1で行います。
初期化で使うコマンド
コマンド Resp 概略
CMD0 0x40 R1 #GO_IDLE_STATE CRC必要 リセット
CMD8 0x48 R7 #SEND_IF_COND CRC必要 カードの世代、動作電圧確認
CMD55 0x77 R1 #APP_CMD 次コマンドはAPP_CMD
ACMD41 0x69 R1 #SD_APP_OP_COND 初期化 SDHCのサポート
CMD1 0x41 R1 #SEND_OP_COND 初期化
■コマンド
Command Formatは下表のようになっています。
Comand(1byte) + data(4bytes) + |CRC|0x01|(1byte)の6バイトを送ります。
※「Physical Layer Simplified Specification Version 2.00」より抜粋
Comand(1byte)は‘0’+’1’+’comand index xxxxxx'(0x40|comand index)です。
例えばCMD55なら0x40|55
>>> hex(0x40|55)
'0x77'
になります。
コマンドがcmd55、データが0x00 0x00 0x01 0xAA,「CRC|0x01」が0x87の場合、
0x77,0x00,0x00,0x01,0xAA,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」より抜粋
■CRC
SPIモードではCRC値は適当でよい場合が大半らしいです。詳しくは分かりません。
CRC値が必要なコマンドはCMD0、CMD8らしいです。値は以下になります。
CMD0 CRC|0x01 = 0x95
CMD8 CRC|0x01 = 0x87
ちなみに記事(d_21)HTU21D 温湿度センサーで作成したCRC計算のスクリプトを修正した「SD_CRC_07b.py」で計算してみました。
コマンド送信時のCRC7の多項式は以下です。
SD_CRC_07b.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# polynomial X7+X3++1 ---
poly=[7,3,0]
div=2**poly[0]+2**poly[1]+2**poly[2]
zisu=poly[0] #次数
#message data
msg_dat =0x4000000000 #0x4000000000(CMD0) ,0x48000001aa(CMD8)
msg_len=len(bin(msg_dat))-2
msg =msg_dat << zisu
div = div<<(msg_len-1)
for i in range((msg_len+zisu-1), (zisu-1), -1):
if msg & (1<>1
print('result=',msg, hex(msg))
CMD0は (0x40|comand index 0),00,00,00,00 なので
msg_dat(message) =0x4000000000で計算します。
結果は
result= 74(0x4a)になります。
CRC|0x01を計算すると
>>> hex(74<<1 |1)
'0x95'
になります。
したがって、CMD0は
0x40,0x00 0x00 0x00 0x00,0x95
を送ることになります。
CMD8の場合は下表でチェックパターンを推奨の1010_1010にすると
0b_0100_1000_0000_0000_0000_0000_0000_0001_1010_1010(‘0x48000001aa’)でCRC7を計算します。結果は
result= 67 (0x43)になります。
CRC|0x01を計算すると
>>> hex(67<<1 |1)
'0x87'
になります。
したがって、CMD8は
0x48,0x00 0x00 0x00 0x00,0x87
を送ることになります。
※「Physical Layer Simplified Specification Version 2.00」より抜粋
CMD58
CDM58 READ_OCR(Operation conditions register)
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ならxff\x80\にあたります。
ビット31はカードが電源投入ルーチン(初期化)を終了なら’1’になります。
ビット30はカード容量を示し、初期化後のみ有効でありSDHCやSSCXCなら’1’になります。
電源投入後の値はSDならb’\x01\x80\xff\x80\x00’、SDHCはb’\x00\xc0\xff\x80\x00′
スクリプト
SDカードを初期化するスクリプトは以下のようにしました。
※開発環境はThonnyです。ThonnyでMicroPythonをRaspberry Pi Pico with RP2040にインストールして使っています。
※Raspberry Pi Pico単独で動作させるには’main.py’としてRaspberry Pi Picoにuploadして使います。
pico_sd_test_05b_01.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from machine import SPI, Pin
import sys
import time
# CMD INIT
cmd0=0x40 #GO_IDLE_STATE
cmd1=0x41 #SEND_OP_CND
cmd8=0x48 #SEND_IF_COND
cmd16=0x50 #SET_BLOCKLEN
cmd55=0x77 #APP_CMD
cmd58=0x7A #READ_OCR
acmd41=0x69 #SD_APP_OP_COND
#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_pin 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)
return response
# Dummy Clock
def spi_ff():
spi.write(b'\xff')
# spi end
def spi_end():
spi_ff()
CS(1)
spi_ff()
# 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()
## SD-SDHC INIT (1)
# CMD0 GO_IDLE_STATE
def CMD_00():
CS(0)
spi_ff()
spi_cmd(cmd0,0x00,0x00,0x00,0x00,0x95)
while True:
spi_ff()
res=spi_res()
if res== b'\x01':
print('CMD0 pass')
break
# CMD08 SEND_IF_COND
def CMD_08():
#print('CMD8')
spi_ff()
spi_cmd(cmd8,0x00,0x00,0x01,0xAA,0x87)
spi_ff()
# R7(5bytes)=R1+4bytes
res=spi_res(5)
#print('R7-5bytes >>',res)
if (res[0]== 0x01) or (res[0]== 0x00):
print('CMD8 pass')
return 1 #Card='HC'
else:
print('CMD8 ERROR >>> SD ')
return 0 #Card='SD'
# CMD55 APP_CMD
def CMD_55():
#print('CMD55')
spi_ff()
spi_cmd(cmd55,0x00,0x00,0x00,0x00,0x01)
spi_ff()
res=spi_res()
if res == b'\x01':
print('CMD55 Pass')
else:
print('CMD55 ERROR EXIT')
sys.exit()
# ACMD4 SD_SEND_OP_COND
def CMD_ACMD41():
#print('ACMD41')
spi_ff()
spi_cmd(acmd41,0x40,0x00,0x00,0x00,0x01)
spi_ff()
res=spi_res(1)
if res == b'\x01':
print('ACMD41 Retry')
if res == b'\x00':
print('ACMD41 Pass')
return 0
# CMD01 SEND_OP_CND
def CMD_01():
while True:
spi_ff()
spi_cmd(cmd1,0x00,0x00,0x00,0x00,0x01) # CMD1
spi_ff()
res=spi_res()
if res== b'\x00':
print('CMD1 Pass')
break
# CMD58 READ_OCR Operation conditions register
def CMD_58():
#print('CMD58')
spi_ff()
spi_cmd(cmd58,0x00,0x00,0x00,0x00,0x01)
spi_ff()
# R3 5bytes (R1+OCR_4bytes)
res=spi_res(5)
if (res[0]== 0x01) or (res[0]== 0x00):
print('CMD58')
print('R1(1byte)+OCR(4bytes)=',res)
else:
print('CMD58 ERROR EXIT')
sys.exit()
## SDC SDHC Init CMD0>CMD8>CMD55>ACMD41, CMD0>CMD1
def SD_Init():
global Card
SPI_MODE()
CMD_00()
SEND_IF_COND=CMD_08()
# CMD8 NG SD<=2GB Initialize
if SEND_IF_COND == 0:
Card='SD'
CMD_00()
CMD_01()
print('SD-INIT')
# CMD8 Pass SDHC>2GB Initialize
elif SEND_IF_COND == 1:
Card='HC'
while True:
CMD_55()
if CMD_ACMD41()==0:
#CMD_58()
print('SDHC-INIT')
break
else:
print('CARD ERROR')
sys.exit()
spi_end()
## SD SDHC card init SPI-MODE
SD_Init() # SDカード初期化
CS(0)
CMD_58() # OCR読み出し
関数 部分説明
■SD_Init()
SDC、SDHCを初期化します。
■CMD_58()
OCR(Operation conditions register)の値を返します。
実行結果
16GBのSDHCカードの初期化を実行しました。
結果は以下のようになりました。初期化出来たようです。※Thonnyのshellに表示されます。
>>> %Run -c $EDITOR_CONTENT
SPI_MODE
CMD0 pass
CMD8 pass
CMD55 Pass
ACMD41 Retry # 再確認
CMD55 Pass
ACMD41 Pass
SDHC-INIT
CMD58
R1(1byte)+OCR(4bytes)= b'\x00\xc0\xff\x80\x00'# SDHC,2.7~3.6V
次に、1GBのSDカードを実行してみました。初期化出来たようです。
>>> %Run -c $EDITOR_CONTENT
SPI_MODE
CMD0 pass
CMD8 ERROR >>> SD #CMD8エラーのためCMD1初期化へ
CMD0 pass
CMD1 Pass
SD-INIT
CMD58
R1(1byte)+OCR(4bytes)= b'\x00\x80\xff\x80\x00' # SD,2.7~3.6V
>>>
まとめ
Raspberry Pi PicoでSDHC、SDカードの初期化ができました。