物联网开发笔记(96)- Micropython ESP32开发之SPI接口控制Micro SD卡TF卡模块挂载内存卡_esp32 sd卡(1)

137 阅读5分钟
        self.init_card_v2()
    elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
        self.init_card_v1()
    else:
        raise OSError("couldn't determine SD card version")

    # get the number of sectors
    # CMD9: response R2 (R1 byte + 16-byte block read)
    if self.cmd(9, 0, 0, 0, False) != 0:
        raise OSError("no response from SD card")
    csd = bytearray(16)
    self.readinto(csd)
    if csd[0] & 0xC0 == 0x40:  # CSD version 2.0
        self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024
    elif csd[0] & 0xC0 == 0x00:  # CSD version 1.0 (old, <=2GB)
        c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6
        c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7
        read_bl_len = csd[5] & 0b1111
        capacity = (c_size + 1) * (2 ** (c_size_mult + 2)) * (2**read_bl_len)
        self.sectors = capacity // 512
    else:
        raise OSError("SD card CSD format not supported")
    # print('sectors', self.sectors)

    # CMD16: set block length to 512 bytes
    if self.cmd(16, 512, 0) != 0:
        raise OSError("can't set 512 block size")

    # set to high data rate now that it's initialised
    self.init_spi(baudrate)

def init_card_v1(self):
    for i in range(_CMD_TIMEOUT):
        self.cmd(55, 0, 0)
        if self.cmd(41, 0, 0) == 0:
            # SDSC card, uses byte addressing in read/write/erase commands
            self.cdv = 512
            # print("[SDCard] v1 card")
            return
    raise OSError("timeout waiting for v1 card")

def init_card_v2(self):
    for i in range(_CMD_TIMEOUT):
        time.sleep_ms(50)
        self.cmd(58, 0, 0, 4)
        self.cmd(55, 0, 0)
        if self.cmd(41, 0x40000000, 0) == 0:
            self.cmd(58, 0, 0, -4)  # 4-byte response, negative means keep the first byte
            ocr = self.tokenbuf[0]  # get first byte of response, which is OCR
            if not ocr & 0x40:
                # SDSC card, uses byte addressing in read/write/erase commands
                self.cdv = 512
            else:
                # SDHC/SDXC card, uses block addressing in read/write/erase commands
                self.cdv = 1
            # print("[SDCard] v2 card")
            return
    raise OSError("timeout waiting for v2 card")

def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
    self.cs(0)

    # create and send the command
    buf = self.cmdbuf
    buf[0] = 0x40 | cmd
    buf[1] = arg >> 24
    buf[2] = arg >> 16
    buf[3] = arg >> 8
    buf[4] = arg
    buf[5] = crc
    self.spi.write(buf)

    if skip1:
        self.spi.readinto(self.tokenbuf, 0xFF)

    # wait for the response (response[7] == 0)
    for i in range(_CMD_TIMEOUT):
        self.spi.readinto(self.tokenbuf, 0xFF)
        response = self.tokenbuf[0]
        if not (response & 0x80):
            # this could be a big-endian integer that we are getting here
            # if final<0 then store the first byte to tokenbuf and discard the rest
            if final < 0:
                self.spi.readinto(self.tokenbuf, 0xFF)
                final = -1 - final
            for j in range(final):
                self.spi.write(b"\xff")
            if release:
                self.cs(1)
                self.spi.write(b"\xff")
            return response

    # timeout
    self.cs(1)
    self.spi.write(b"\xff")
    return -1

def readinto(self, buf):
    self.cs(0)

    # read until start byte (0xff)
    for i in range(_CMD_TIMEOUT):
        self.spi.readinto(self.tokenbuf, 0xFF)
        if self.tokenbuf[0] == _TOKEN_DATA:
            break
        time.sleep_ms(1)
    else:
        self.cs(1)
        raise OSError("timeout waiting for response")

    # read data
    mv = self.dummybuf_memoryview
    if len(buf) != len(mv):
        mv = mv[: len(buf)]
    self.spi.write_readinto(mv, buf)

    # read checksum
    self.spi.write(b"\xff")
    self.spi.write(b"\xff")

    self.cs(1)
    self.spi.write(b"\xff")

def write(self, token, buf):
    self.cs(0)

    # send: start of block, data, checksum
    self.spi.read(1, token)
    self.spi.write(buf)
    self.spi.write(b"\xff")
    self.spi.write(b"\xff")

    # check the response
    if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05:
        self.cs(1)
        self.spi.write(b"\xff")
        return

    # wait for write to finish
    while self.spi.read(1, 0xFF)[0] == 0:
        pass

    self.cs(1)
    self.spi.write(b"\xff")

def write_token(self, token):
    self.cs(0)
    self.spi.read(1, token)
    self.spi.write(b"\xff")
    # wait for write to finish
    while self.spi.read(1, 0xFF)[0] == 0x00:
        pass

    self.cs(1)
    self.spi.write(b"\xff")

def readblocks(self, block_num, buf):
    nblocks = len(buf) // 512
    assert nblocks and not len(buf) % 512, "Buffer length is invalid"
    if nblocks == 1:
        # CMD17: set read address for single block
        if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
            # release the card
            self.cs(1)
            raise OSError(5)  # EIO
        # receive the data and release card
        self.readinto(buf)
    else:
        # CMD18: set read address for multiple blocks
        if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
            # release the card
            self.cs(1)
            raise OSError(5)  # EIO
        offset = 0
        mv = memoryview(buf)
        while nblocks:
            # receive the data and release card
            self.readinto(mv[offset : offset + 512])
            offset += 512
            nblocks -= 1
        if self.cmd(12, 0, 0xFF, skip1=True):
            raise OSError(5)  # EIO

def writeblocks(self, block_num, buf):
    nblocks, err = divmod(len(buf), 512)
    assert nblocks and not err, "Buffer length is invalid"
    if nblocks == 1:
        # CMD24: set write address for single block
        if self.cmd(24, block_num * self.cdv, 0) != 0:
            raise OSError(5)  # EIO

        # send the data
        self.write(_TOKEN_DATA, buf)
    else:
        # CMD25: set write address for first block
        if self.cmd(25, block_num * self.cdv, 0) != 0:
            raise OSError(5)  # EIO
        # send the data
        offset = 0
        mv = memoryview(buf)
        while nblocks:
            self.write(_TOKEN_CMD25, mv[offset : offset + 512])
            offset += 512
            nblocks -= 1
        self.write_token(_TOKEN_STOP_TRAN)

def ioctl(self, op, arg):
    if op == 4:  # get number of blocks
        return self.sectors
    if op == 5:  # get block size in bytes
        return 512

**五、检查SD卡是否挂载成功代码**



from machine import Pin,SPI from sdcard import SDCard import time,os,esp

cs = Pin(5,Pin.OUT) spi = SPI(2,sck = Pin(18),mosi = Pin(23),miso = Pin(19)) sd = SDCard(spi,cs)

def main(): os.VfsFat(sd) os.mount(sd,"/sd") # 挂载SD卡

fb = os.statvfs('/sd')
print("SD capacity  = %d B %d M"%(fb[0] * fb[2],fb[0] * fb[2]/1024/1024))
print("SD Remaining = %d B %d M"%(fb[0] * fb[3],fb[0] * fb[3]/1024/1024))

print("esp32 Flash容量: %d M"%(esp.flash_size()/1024/1024))

while True:
    pass

if name == "main": main()


演示效果:


![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/72e74d94b20e49f3be6bcc347e59bfcb~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771856585&x-signature=HagAuPTonvspUuSPy%2FyfJnuhUrM%3D)


**六、示例代码**



from machine import Pin,SPI from sdcard import SDCard import time,os,esp

cs = Pin(5,Pin.OUT) spi = SPI(2,sck = Pin(18),mosi = Pin(23),miso = Pin(19)) sd = SDCard(spi,cs)

def main(): os.VfsFat(sd) # 创建一个使用 FAT 文件系统格式的文件系统对象。 os.mount(sd,"/sd") # 挂载SD卡

fb = os.statvfs('/sd')  # 获取文件系统的状态。
print("SD capacity  = %d B %d M"%(fb[0] * fb[2],fb[0] * fb[2]/1024/1024))
print("SD Remaining = %d B %d M"%(fb[0] * fb[3],fb[0] * fb[3]/1024/1024))

print("esp32 Flash容量: %d M"%(esp.flash_size()/1024/1024))

# 获取文件或目录的状态,若不存就创建
try:
    os.stat("/sd/Hi")  # 获取文件或目录的状态。
except:
    os.mkdir("/sd/Hi")  # 创建目录
#os.rmdir("/sd/Hi")  # 删除目录
print(os.listdir("/sd"))  # os.listdir不带参数,列出当前目录。否则列出给定的目录

# 写操作
w = open("/sd/Hi/text.txt",'w',encoding="utf-8")
w.write("Welcome to China!")
w.close()
print(os.listdir("/sd/Hi"))

# 读操作
r =  open("/sd/Hi/text.txt",'r',encoding="utf-8")
text = r.read()  # f.read()直接读取全部文件
r.close()
print(text)

r =  open("/sd/Hi/text.txt",'r',encoding="utf-8")
text = r.read().split()  # r.read().split()把文件内容当成一个列表返回
r.close()
print(text)

for chine in text:
    print(chine)

for number in range(len(text)):
    print(text[number])

for i in range(10):
    w = open("/sd/Hi/number.txt",'w',encoding="utf-8")
    w.write("%.3d"%i)
    w.close()
    
    r =  open("/sd/Hi/number.txt",'r',encoding="utf-8")
    num = r.read()
    r.close()
    
    #print("number = %s"%num)
    print("number = %.3d"%(int(num)))
    
    time.sleep(0.01)

os.umount("/sd")  # 卸载SD卡

while True:
    pass

if name == "main": main()


示例效果:


![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/9197250ed27b42a9b9189b417f659598~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771856585&x-signature=UrgTFftN5fBS%2BuplDwmSmXOtoSU%3D)


 ![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/023b4be75b864101adc3d959e38e6a0d~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771856585&x-signature=Y42%2Bcourf064xyQv1y40CdxPnA0%3D)



![img](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6045d9ae4fa14681a7a06ee77ce8bef0~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771856585&x-signature=dQ4YYb%2FtSjY2os%2BSUYr%2FYs7aNw0%3D)
![img](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/3d8628a3387d4477bfc9acb186aa5485~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5py65Zmo5a2m5Lmg5LmL5b-DQUk=:q75.awebp?rk3s=f64ab15b&x-expires=1771856585&x-signature=RQtj%2FcZY51ZyfaQBCgU9ZgQd%2Fao%3D)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://gitee.com/vip204888)**