稼働中

Raspberry Pi Pico(s_19)SDカード 用モジュール

SD Card SDc.py

SDカードモジュールはデジタル温湿度センサーです。※詳細はSDカードのデータシート等を参照ください。
Raspberry Pi PicoでSDカードモジュールを使う自作の’SDc.py’を作成しました。記事d_24を元に作成しています。
‘SDc.py’は末尾にあります。使い方だけを記載します。

使い方

■ ファイル転送
Raspberry Pi Picoへ’SDc.py’を送った後でimportして使います。
※SDc.pyの末尾に、使用例(SDc_ex.py)の## example以降をコピペ追加しても動作確認できます。
■ メソッド
‘SDc’をimportすると「SD_Init、SD_Read、SD_Write、SD_Waddr、SD_ERASE、SPI_MODE、CMD_58」のメソッドが使えるようになります。
SDC(spi_num=1,cs_num=13)で初期化します。
spi_num:使用するSPI0、SPI1の番号(0,1)です。
cs_num:CSに使うGPIO番号です。
spi_numとcs_numを設定すれば、RX, SCK, TXは指定されます。
SPI0、SPI1の[RX, Csn, SCK, TX]のGPIO番号リストは以下になります。
#SPI0 [ 0, 1, 2, 3] [ 4, 5, 6, 7] [16,17,18,19]
#SPI1 [ 8, 9,10,11] [12,13,14,15]
※RX(MISO), TX(MOSI)
(01)SD_Init()
SDC、SDHCを初期化します。
(02)SD_Read(r=[0x00,0x00,0x00,0x00])
32bit[0x00,0x00,0x00,0x00]で与えたアドレスから読み出します。
SDHCは512bytesブロックのアドレスになります。(※以下も同様)
(03)SD_Waddr(w=[0x00,0x00,0x00,0x00])
32bit[0x00,0x00,0x00,0x00]で書き込むアドレスを指定します。
(04)SD_Write(buf)
指定されたアドレスのバイトアレイデータ(512bytes)を書き込みます。
(05)SD_ERASE(es,ee)
指定したアドレスのデータを消去します。
es=[0x00,0x00,0x00,0x00]の形式で消去開始アドレスを指定します。
ee=[0x00,0x00,0x00,0x00]の形式で消去終了アドレスを指定します。
(06)ERASE_END(e=[0x00,0x00,0x02,0x00])
32bit[0x00,0x00,0x00,0x00]で消去終了アドレスを指定します。
(07)CS(dat)
CSピンの状態(0,1)を与えます。CS(0)でSPIデバイス指定されます。
(08)SPI_MODE()
SDカードをSPIモードにします。実行後はCS(0)になります。
(09)CMD_58()
OCR(Operation conditions register)の値が返ります。
(10)CMD_16()
SET_BLOCKLEN(CMD16)を実行します。ブロック長を512bytesにします。

使用例

MCP4725で温湿度を測定します。


SDc_ex.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from SDCc import SDC

## exapmle
a=SDC(1,13)    #SDC(spi_num=1,cs_num=13) CS='1'

# SD SDHC card init SPI-MODE
a.SD_Init()

# 書込むデータ512byte 全部同じ 0x02
block_buf=bytearray(512)
for i in range(512):
    block_buf[i]=0x02

# write ---------------------CMD24
# 32bit address
w_adr=[0x00,0x00,0x02,0x00]     #BLK_WRITE_ADD 
a.SD_Waddr(w_adr)               #アドレスセット
a.SD_Write(block_buf)           #書込み

# read ---------------------CMD17
r_adr=[0x00,0x00,0x02,0x00]     #BLK_READ_ADD 
r_dat,r_crc=a.SD_Read(r_adr)    #読出し
print(r_dat)
print(r_crc)

# erase ---------------------CMD32-33-38
s_adr=[0x00,0x00,0x00,0x00]     #BLK_END_ADD
e_adr=[0x00,0x00,0x04,0x00]     #BLK_START_ADD
a.SD_ERASE(s_adr,e_adr)         #消去

実行結果

実行すると以下のようになりました。※Thonnyのshellに表示されます。


>>> %Run -c $EDITOR_CONTENT
INIT SDHC                           #SDHCを初期化
WRITE_BLOCK                         #0x02を512書込み
READ _BLOCK
b'\x02\x02\x02\x02\x02\x02\x02\     #0x02を512を読出し
省略
\x02\x02\x02\x02'
b'\xd7}'                            #CRC16 読出し
ERASE START ['0x00', '0x00', '0x00', '0x00']    #消去区間を表示
ERASE END   ['0x00', '0x00', '0x04', '0x00']
>>> 

実行後にThonnyのshellでデータを確認すると消去されているのが分かります。
Thonny Shell

SDカードモジュール用


SDc.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from machine import SPI, Pin
import sys
import time
class SDC:
    def __init__(self,spi_num=0,cs_num=1):
        self.spi = SPI(spi_num, baudrate=1000_000,sck=Pin(cs_num+1), 
                       mosi=Pin(cs_num+2), miso=Pin(cs_num-1))
        #Chip Select Pin
        self.CS_pin=Pin(cs_num, mode=Pin.OUT, value=1)
        # CMD INIT
        self.cmd0=0x40     #GO_IDLE_STATE 
        self.cmd1=0x41     #SEND_OP_CND 
        self.cmd8=0x48     #SEND_IF_COND
        self.cmd16=0x50    #SET_BLOCKLEN
        self.cmd55=0x77    #APP_CMD 
        self.cmd58=0x7A    #READ_OCR
        self.acmd41=0x69   #SD_APP_OP_COND 
        # CMD ERASE
        self.cmd32=0x60    #ERASE_WR_BLK_START_ADDR R1
        self.cmd33=0x61    #ERASE_WR_BLK_END_ADDR   R1
        self.cmd38=0x66    #ERASE  Erases           R1b
        # CMD READ
        self.cmd17=0x51    #READ_SINGLE_BLOCK
        # CMD WRITE
        self.cmd24=0x58    #WRITE_BLOCK
        
    # Chip Select CS(0)
    def CS(self,dat):
        self.CS_pin.value(dat)
    # Card send data
    def spi_cmd(self,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
        self.spi.write(buf)
        return buf
    # response
    def spi_res(self,num=1):   
        response=self.spi.read(num)
        return response
    # dummy clock
    def spi_ff(self):
        self.spi.write(b'\xff')

    # spi end
    def spi_end(self):
        self.spi_ff()
        self.CS(1)
        self.spi_ff()

    ## SDC INIT START
    def SPI_MODE(self):
        #print('SPI MODE')
        self.CS(1)               #CS=Hでダミークロックを80個送信
        for i in range(10):
            self.spi_ff()
        # CS select
        self.CS(0)                #カード選択
        self.spi_ff()

    ##(1)SD-SDHC INIT
    # GO_IDLE_STATE CMD00
    def CMD_00(self):    
        self.CS(0)
        self.spi_ff()
        self.spi_cmd(self.cmd0,0x00,0x00,0x00,0x00,0x95)
        cnt=0
        while True:
            cnt=cnt+1
            self.spi_ff() 
            res=self.spi_res()
            if res== b'\x01':
                #print('CMD0 OK')
                #pass
                #return
                break
            if cnt>10:
                print('CMD0-NG')
                sys.exit()

    # SEND_IF_COND  CMD08
    def CMD_08(self):
        self.spi_ff()
        self.spi_cmd(self.cmd8,0x00,0x00,0x01,0xAA,0x87)
        self.spi_ff() 
        #R7(5bytes)=R1+4bytes
        res=self.spi_res(5)
        #print('R7=',res)
        if (res[0]== 0x01) or (res[0]== 0x00):
            return 1        #Card='HC'
        else:
            #print('CMD8 ERROR')
            return 0        #Card='SD'

    # APP_CMD  CMD55
    def CMD_55(self):
        #print('CMD55')
        self.spi_ff()
        self.spi_cmd(self.cmd55,0x00,0x00,0x00,0x00,0x01)
        self.spi_ff()
        res=self.spi_res()

        if res == b'\x01':
            #print('CMD55 Pass)
            pass
        else:
            print('CMD55 ERROR')
            sys.exit()
        
    # SD_SEND_OP_COND    ACMD41
    def CMD_ACMD41(self):
        #print('ACMD41')
        self.spi_ff()
        self.spi_cmd(self.acmd41,0x40,0x00,0x00,0x00,0x01)
        self.spi_ff()
        res=self.spi_res(1)
        if res == b'\x00':
            return (0)

    # SEND_OP_CND    CMD01
    def CMD_01(self):
        cnt=0
        while True:
            cnt=cnt+1
            self.spi_ff()
            self.spi_cmd(self.cmd1,0x00,0x00,0x00,0x00,0x01)
            self.spi_ff()
            res=self.spi_res()
            if res== b'\x00':
                break
            if cnt>10:
                print('CMD01 ERROR')
                sys.exit()

    ## SDC SDHC Initialize 
    # CMD0>CMD8>CMD55>ACMD41, CMD0>CMD1
    def SD_Init(self):
        global Card
        #self.Card
        self.SPI_MODE()
        self.CMD_00()
        SEND_IF_COND=self.CMD_08()

        # CMD8 NG SD <=2GB Init if SEND_IF_COND == 0: Card='SD' self.CMD_00() self.CMD_01() print('INIT SDC') # CMD8 Pass SDHC elif SEND_IF_COND == 1: Card='HC' while True: self.CMD_55() if self.CMD_ACMD41()==0: print('INIT SDHC') break else: print('CARD ERROR') sys.exit() #end self.spi_end() self.CS(0) ##(2)ERASE #BLK_START_ADD CMD32 def ERASE_START(self,s=[0x00,0x00,0x00,0x00]): # CS select self.CS(0) self.spi_ff() self.spi_cmd(self.cmd32,s[0],s[1],s[2],s[3],0x01) self.spi_ff() res=self.spi_res(1) if res != b'\x00': print('CMD32 ERROR') sys.exit() #end self.spi_end() #BLK_END_ADD CMD33 def ERASE_END(self,e=[0x00,0x00,0x02,0x00]): self.CS(0) self.spi_ff() self.spi_cmd(self.cmd33,e[0],e[1],e[2],e[3],0x01) self.spi_ff() res=self.spi_res(1) if res != b'\x00': print('CMD33 ERROR') sys.exit() #end self.spi_end() # ERASE CMD38 def ERASE_GO(self): self.CS(0) self.spi_ff() self.spi_cmd(self.cmd38,0x00,0x00,0x00,0x00,0x01) self.spi_ff() res=self.spi_res(1) if res != b'\x00': print('CMD38 ERROR') sys.exit() #end self.spi_end() def SD_ERASE(self,es,ee): self.SPI_MODE() self.ERASE_START(es) self.ERASE_END(ee) self.ERASE_GO() print('ERASE START',['{:#04x}'.format(i) for i in es]) print('ERASE END ',['{:#04x}'.format(i) for i in ee]) #end self.spi_end() time.sleep_ms(250) ##(3)READ # READ_SINGLE_ BLOCK CMD17 def SD_Read(self,r=[0x00,0x00,0x00,0x00]): #print('cmd17 READ_SINGLE_BLOCK START') # CS select self.CS(0) # time.sleep_ms(5) # self.spi_ff() # radr=self.spi_cmd(self.cmd17,r[0],r[1],r[2],r[3],0x01) # 0x00>0xfeが返るまで受信
        cnt=0
        while True:
            cnt=cnt+1
            self.spi_ff()    # CMD>>0xFF out >>read response
            res=self.spi_res()
            
            if res== b'\x00':
                time.sleep_ms(5)     #
                break
            if cnt>10:
                print('CMD17 ERROR')
                sys.exit()
        cnt=0
        while True:
            cnt=cnt+1
            res=self.spi_res()
            if res== b'\xfe':
                break
            if cnt>10:
                print('token ERROR')
                sys.exit()

        print('READ _BLOCK')
        block_buf=self.spi.read(512)
        #CRC16 2byte read
        crc_buf=self.spi.read(2)
        #end
        self.spi_end()
        time.sleep_ms(300)
        return block_buf,crc_buf

    ##(4)WRITE
    # WRITE_BLOCK    CMD24
    def SD_Waddr(self,w=[0x00,0x00,0x00,0x00]):
        # CS select
        self.CS(0)
        self.spi_ff()
        self.spi_cmd(self.cmd24,w[0],w[1],w[2],w[3],0x01)
        # 標準SDはCMD24の応答を確認せずに書込む
        if Card=='HC':
            cnt=0
            while True:
                cnt=cnt+1
                res=self.spi_res()
                if res== b'\x00':
                    break
                if cnt>10:
                    print('CMD24 ERROR')
                    sys.exit()
                    
    def SD_Write(self, buf):       #
        #スタートバイト 0xFE を送ってからWriteデータを送る
        self.spi_ff()            #One byte gap
        self.spi.write(b'\xfe')  #Start Block Token
        #512バイトデータを送ります
        self.spi.write(buf)

        cnt=0
        while True:
            cnt=cnt+1
            res=self.spi_res()
            re=res[0] & 0x1f        # data response XXX1???1 
            if re==0x05:
                print('WRITE_BLOCK')
                break
            if cnt>10:
                print('WRITE ERROR')
                sys.exit()
        #end
        self.spi_end()

    ##(5) READ_OCR    CMD58
    def CMD_58(self):
        self.spi_ff()
        self.spi_cmd(self.cmd58,0x00,0x00,0x00,0x00,0x01)
        self.spi_ff() 
        res=self.spi_res(5)
        if (res[0]== 0x01) or (res[0]== 0x00):
            print('CMD58')
            print('R1(1byte)+OCR(4bytes)=',res)
        else:
            print('CMD58 ERROR')
            sys.exit()

    ##(6)SET_BLOCKLEN    CMD16
    def CMD_16(self):
        cap='512bytes'
        cnt=0
        self.CS(0)
        self.spi_ff()
        self.spi_cmd(self.cmd16,0x00,0x00,0x02,0x00,01)
        while True:
            cnt=cnt+1
            res=self.spi_res()
            if res== b'\x00':
                print(res,'CMD16',cap)
                break
            if cnt>10:
                print('CMD16 ERROR')
                sys.exit()
        # spi end
        self.spi_end()