• 【MicroPython ESP32】通过sdcard模块读取SD卡实例


    【MicroPython ESP32】通过sdcard模块读取SD卡实例


    • 本实验基于Thonny平台开发。esp32固件版本MicroPython v1.19.1 on 2022-06-18; ESP32 module with ESP32
      在这里插入图片描述

    • Micro SD卡模块(TF卡读写卡器 SPI 带电平转换芯片)
      在这里插入图片描述

    • sdcard模块

    • MicroPython源码:https://github.com/micropython/micropython
      在这里插入图片描述

    sdcard模块可以在MicroPython源码中找到。

    在这里插入图片描述

    """
    MicroPython driver for SD cards using SPI bus.
    
    Requires an SPI bus and a CS pin.  Provides readblocks and writeblocks
    methods so the device can be mounted as a filesystem.
    
    Example usage on pyboard:
    
        import pyb, sdcard, os
        sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5)
        pyb.mount(sd, '/sd2')
        os.listdir('/')
    
    Example usage on ESP8266:
    
        import machine, sdcard, os
        sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15))
        os.mount(sd, '/sd')
        os.listdir('/')
    
    """
    
    from micropython import const
    import time
    
    
    _CMD_TIMEOUT = const(100)
    
    _R1_IDLE_STATE = const(1 << 0)
    # R1_ERASE_RESET = const(1 << 1)
    _R1_ILLEGAL_COMMAND = const(1 << 2)
    # R1_COM_CRC_ERROR = const(1 << 3)
    # R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
    # R1_ADDRESS_ERROR = const(1 << 5)
    # R1_PARAMETER_ERROR = const(1 << 6)
    _TOKEN_CMD25 = const(0xFC)
    _TOKEN_STOP_TRAN = const(0xFD)
    _TOKEN_DATA = const(0xFE)
    
    
    class SDCard:
        def __init__(self, spi, cs, baudrate=1320000):
            self.spi = spi
            self.cs = cs
    
            self.cmdbuf = bytearray(6)
            self.dummybuf = bytearray(512)
            self.tokenbuf = bytearray(1)
            for i in range(512):
                self.dummybuf[i] = 0xFF
            self.dummybuf_memoryview = memoryview(self.dummybuf)
    
            # initialise the card
            self.init_card(baudrate)
    
        def init_spi(self, baudrate):
            try:
                master = self.spi.MASTER
            except AttributeError:
                # on ESP8266
                self.spi.init(baudrate=baudrate, phase=0, polarity=0)
            else:
                # on pyboard
                self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)
    
        def init_card(self, baudrate):
    
            # init CS pin
            self.cs.init(self.cs.OUT, value=1)
    
            # init SPI bus; use low data rate for initialisation
            self.init_spi(100000)
    
            # clock card at least 100 cycles with cs high
            for i in range(16):
                self.spi.write(b"\xff")
    
            # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
            for _ in range(5):
                if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
                    break
            else:
                raise OSError("no SD card")
    
            # CMD8: determine card version
            r = self.cmd(8, 0x01AA, 0x87, 4)
            if r == _R1_IDLE_STATE:
                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
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300

    引脚接线说明

    • 使用的是VSPI总线
    SPIMOSIMISOCLKCS
    VSPI2319185

    • SPI通讯
    1.GND-for the ground pins.
    2.VCC-for the supply voltage.
    3.MISO-for the SPI Master Input Slave Output pin.
    4.MOSI-for the SPI Master Output Slave Input pin.
    5.SCK-for the SPI Serial Clock pin.
    6.CS-for the SPI Chip Select pin.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. gnd -用于接地插脚。
    2. vcc- 电源电压。
    3. miso-用于SPI主输入从输出引脚。
    4. mosi -用于SPI主输出从输入引脚。
    5. sck -用于SPI串行时钟引脚。
    6. cs -用于SPI芯片选择引脚。

    • esp32 Devkeit
      在这里插入图片描述
    sck=Pin(17), 
    mosi=Pin(23),
    miso=Pin(19)
    SD_CS= Pin(5)
    
    • 1
    • 2
    • 3
    • 4
    • 本实例需要引入sdcard模块

    运行代码前,需要先将sdcard模块保存到MicroPython设备当中。
    在这里插入图片描述

    在这里插入图片描述

    程序实例一代码

    import machine, sdcard, os
    from machine import SPI
    from machine import Pin
    SD_CS = Pin(5)
    sd = sdcard.SDCard(SPI(2,sck=Pin(17), mosi=Pin(23),miso=Pin(19)), SD_CS)
    # 初始化⽂件系统
    vfs = os.VfsFat(sd)# fat挂载卡到⽬录下
    os.mount(sd,"/sd")# SD/sd
    dirs=os.listdir('/sd')
    for file in dirs:   
        print(file)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    程序实例二代码

    import os, sdcard, machine
    from machine import SPI
    from machine import Pin
    
    def sdtest():
        SD_CS = Pin(5)
        sd = sdcard.SDCard(SPI(2,sck=Pin(17), mosi=Pin(23),miso=Pin(19)), SD_CS)  
        vfs = os.VfsFat(sd)
        os.mount(vfs, "/fc")
        print("Filesystem check")
        print(os.listdir("/fc"))
    
        line = "abcdefghijklmnopqrstuvwxyz\n"
        lines = line * 200  # 5400 chars
        short = "1234567890\n"
    
        fn = "/fc/rats.txt"
        print()
        print("Multiple block read/write")
        with open(fn, "w") as f:
            n = f.write(lines)
            print(n, "bytes written")
            n = f.write(short)
            print(n, "bytes written")
            n = f.write(lines)
            print(n, "bytes written")
    
        with open(fn, "r") as f:
            result1 = f.read()
            print(len(result1), "bytes read")
    
        fn = "/fc/rats1.txt"
        print()
        print("Single block read/write")
        with open(fn, "w") as f:
            n = f.write(short)  # one block
            print(n, "bytes written")
    
        with open(fn, "r") as f:
            result2 = f.read()
            print(len(result2), "bytes read")
    
        os.umount("/fc")
    
        print()
        print("Verifying data read back")
        success = True
        if result1 == "".join((lines, short, lines)):
            print("Large file Pass")
        else:
            print("Large file Fail")
            success = False
        if result2 == short:
            print("Small file Pass")
        else:
            print("Small file Fail")
            success = False
        print()
        print("Tests", "passed" if success else "failed")
        
    if __name__ == '__main__':
        sdtest()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    在这里插入图片描述

  • 相关阅读:
    editor验证必填项不能为空
    uniapp中实现H5录音和上传、实时语音识别(兼容App小程序)和波形可视化
    数值分析基础应用线性代数
    输入和输出处理
    Docker--harbor私有仓库部署与管理
    【AUTOSAR】【CAN通信】CanSyn
    如何使用 Javascript 将图标字体渲染为图片
    本地编译openpose遇到“set_gpu_data“ error
    ECMA各版本特性
    PMI-ACP练习题(30)
  • 原文地址:https://blog.csdn.net/weixin_42880082/article/details/126519543