Micropython学习交流群 学习QQ群:786510434 提供多种固件下载和学习交流。

Micropython-扇贝物联 QQ群:31324057 扇贝物联是一个让你与智能设备沟通更方便的物联网云平台

Micropython学习交流群 学习QQ群:468985481 学习交流ESP8266、ESP32、ESP8285、wifi模块开发交流、物联网。

Micropython老哥俩的IT农场分享QQ群:929132891 为喜欢科创制作的小白们分享一些自制的计算机软硬件免费公益课程,由两位多年从事IT研发的中年大叔发起。

Micropython ESP频道

micropython esp32 sd卡操作类


esp_sdcard.py

"""
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.

# Examples of how to use
# ESP32 - mount sd card
import uos
import esp_sdcard
sd = esp_sdcard.Esp_sdcard(slot=2)
uos.mount(sd, '/sd')

Notes:
Format SD FAT32 not FAT or other
When sd_init=True sd bus/slot will not get initialized if SD card is not inserted/available

# If card was removed you can remount
uos.umount('/sd')
sd.init()
uos.mount(sd, '/sd')

# List file on sd card
uos.listdir('/sd')

# Try to creat and write to a file
f = open('/sd/data.txt', 'w')
f.write('some data')
f.close()

# Try to read from a file
f = open('/sd/data.txt')
f.read()
f.close()
"""

from micropython import const
from machine import SPI, Pin

class Esp_sdcard:

    # Class constants
    _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)

    def __init__(self, slot=2, baudrate=1000000, polarity=0, phase=0, bits=8, firstbit=0, sck=None, mosi=None, miso=None, cs=None, sd_init=True):

        # Instance variables
        self.spi = None
        self.baudrate = baudrate # just storing value to initialize with slower baud
        self.cs = None
        
        self.cdv = 1
        self.sectors = None
        
        # SPI Type
        if slot == 1:
            # Use default pins if not entered
            if sck is None:
                sck = 14
            if mosi is None:
                mosi = 13
            if miso is None:
                miso = 12
            if cs is None:
                cs = 15

        elif slot == 2:
            # Use default pins if not entered
            if sck is None:
                sck = 18
            if mosi is None:
                mosi = 23
            if miso is None:
                miso = 19
            if cs is None:
                cs = 5
        else:
            raise OSError("Invalid SPI slot")
        
        # Set baud floor value? (should probably remove)
        if self.baudrate < 100000:
            self.baudrate = 100000

        # Initialize chip select pin
        try:
            self.cs = Pin(cs)
            self.cs.init(self.cs.OUT, value=1)
        except:
            raise OSError("Failed to initialize CS")

        # Initialize SPI bus
        try:
            self.spi = SPI(slot)
            self.spi.init(baudrate=self.baudrate, polarity=polarity, phase=phase, bits=bits, firstbit=firstbit, sck=Pin(sck), mosi=Pin(mosi), miso=Pin(miso))
        except:
            raise OSError("Failed to initialize SPI bus")

        # SD read buffers
        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 SD card
        if sd_init:
            self.init() # May want to put in try block so atleast 

    # De-initialize SPI bus
    def deinit(self):
        self.spi.deinit()
    
    # Initialize SD card
    def init(self):
        
        # Initialise the SPI bus with slow baudrate by default
        self.spi.init(baudrate=100000)

        # Set CS pin high
        self.cs(1)

        # clock card at least 100 cycles with cs high
        for i in range(_CMD_TIMEOUT):#16):
            self.spi.write(b"\xFF")
        
        # CMD0: Reset card; should return _R1_IDLE_STATE
        # Response: 0xFF 0x01
        for i in range(_CMD_TIMEOUT):#5):
            self.init_cmd(0, 0, 0x95, 2) # Read Two bytes first being garbage
            #check sd card is in idle mode
            if (self.dummybuf_memoryview[1] == _R1_IDLE_STATE): # only care about the second byte
                break

            if i== (_CMD_TIMEOUT-1):
                print(bytes(self.dummybuf_memoryview[0:2])) # all bytes read
                raise OSError("no SD card")
            
            # Clean borrowed buffers
            self.dummybuf_memoryview[0] = 0xFF 
            self.dummybuf_memoryview[1] = 0xFF
        
        # Clean borrowed buffers
        self.dummybuf_memoryview[0] = 0xFF 
        self.dummybuf_memoryview[1] = 0xFF


        # CMD8: determine card version
        # Response: 0xFF 0x01 0x00 0x00 0x01 0xAA
        self.init_cmd(8, 0x01AA, 0x87, 6)
        if self.dummybuf_memoryview[1] == _R1_IDLE_STATE: # Only care about the second byte
            for i in range(_CMD_TIMEOUT):
                # CMD55 Indicates to the card that the next command is an application specific command
                # Response: 0xFF 0x01 (Don't care)
                #self.init_cmd(55, 0, 0, 2)
                self.init_cmd(55, 0, 0x87, 2)
                
                # Clean borrowed buffers
                self.dummybuf_memoryview[0] = 0xFF 
                self.dummybuf_memoryview[1] = 0xFF


                #ACM41 Sends HCS, asks OCR content in the response
                # Response: 0xFF 0x00
                self.init_cmd(41, 0x40000000, 0x87, 2)
                if (self.dummybuf_memoryview[1] == 0): # Only care about the second byte
                    #if the response is 0x00 your good
                    # and set cdv = 1
                    #self.cdv = 1
                    break

                if i == (_CMD_TIMEOUT - 1):
                    print(bytes(self.dummybuf_memoryview[0:2]))
                    raise OSError("timeout waiting for v2 card")
            
            # Clean borrowed buffers
            self.dummybuf_memoryview[0] = 0xFF 
            self.dummybuf_memoryview[1] = 0xFF
            self.dummybuf_memoryview[2] = 0xFF
            self.dummybuf_memoryview[3] = 0xFF
            self.dummybuf_memoryview[4] = 0xFF
            self.dummybuf_memoryview[5] = 0xFF
                
            #ACM58 Check card version
            # Response V2 HC: 0xFF 0x00 0xC0 0xFF 0x80 0x00
            # Response V1 SC: 0xFF 0x00 0x80 0xFF 0x80 0x00
            self.init_cmd(58, 0, 0xFF, 6)
            if self.dummybuf_memoryview[2] == 0x80:
                # CMD55 Indicates to the card that the next command is an application specific command
                # Response: 0xFF 0x01 (Don't care)
                self.init_cmd(55, 0, 0x87, 2)
                
                # CMD42: ...
                # Response: ... (Don't care)
                self.init_cmd(42, 0, 0x87, 2)

                self.cdv = 512
            else:
                self.cdv = 1
            
            # Clean borrowed buffers
            self.dummybuf_memoryview[0] = 0xFF 
            self.dummybuf_memoryview[1] = 0xFF
            self.dummybuf_memoryview[2] = 0xFF
            self.dummybuf_memoryview[3] = 0xFF
            self.dummybuf_memoryview[4] = 0xFF
            self.dummybuf_memoryview[5] = 0xFF
            
            # CMD16: set block length to 512 bytes
            # Response: 0xFF 0x00
            self.init_cmd(16, 0x00000200, 0x87, 2)
            if self.dummybuf_memoryview[1] != 0: # Only care about the second byte
                print(bytes(self.dummybuf_memoryview[0:2]))
                raise OSError("Can't set 512 block size")
            
            # Clean borrowed buffers
            self.dummybuf_memoryview[0] = 0xFF 
            self.dummybuf_memoryview[1] = 0xFF

        else:
            print(bytes(self.dummybuf_memoryview[0:6])) # all bytes read
            raise OSError("Couldn't determine SD card version")


        # CMD9: response R2 (R1 byte + 16-byte block read)
        # Response: 0xFF 0x01 0x... 0xFE
        if self.cmd(9, 0, 0xAF, 0, False) != 0:
            raise OSError("no response from SD card")
        
        # Get sector size
        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 | csd[7] << 2 | (csd[8] & 0b11000000) << 4
            c_size_mult = ((csd[9] & 0b11) << 1) | csd[10] >> 7
            self.sectors = (c_size + 1) * (2 ** (c_size_mult + 2))
        else:
            raise OSError("SD card CSD format not supported")

        # Clean whole buffer
        for i in range(512):
            self.dummybuf[i] = 0xFF
        self.dummybuf_memoryview = memoryview(self.dummybuf)
        
        # Set back to requested baud rate
        self.spi.init(baudrate=self.baudrate)

        # Send dummy byte for clock sync
        self.cs(0)
        self.spi.write(b"\xFF")
        self.cs(1)
        

    def init_cmd(self, cmd=None, arg=0, crc=0, resp_size=2):

        # send dummy byte before every command
        self.cs(0)
        self.spi.write(b"\xFF")
        self.cs(1)


        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)

        # Get response from SD and save to buffer
        self.spi.readinto(self.dummybuf_memoryview[0:resp_size], 0xFF)

        # For debugging sd card init
        #print(bytes(buf)) # Command
        #print(bytes(self.dummybuf_memoryview[0:resp_size])) # Response

        self.cs(1)

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

        # send dummy byte before every command
        self.cs(0)
        self.spi.write(b"\xFF")
        self.cs(1)

        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]
            # For debugging sd card  commands
            #print(bytes(buf)) # Command
            #print(bytes(response)) # Response

            if not (response & 0x80):
                # this could be a big-endian integer that we are getting here
                for j in range(final):
                    self.spi.write(b"\xff")
                if release:
                    self.cs(1)
                    self.spi.write(b"\xff")
                #print(bytes(response)) # Debug Response
                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
        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, 1, 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, 1, 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, 1) != 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, 1) != 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

sd_card.py

# Import libraries
import uos
import esp_sdcard

class SD_card: 
    def __init__(self, sd_slot=2, sd_loc='/sd'):
        # Load class variables
        
        # change slot if using different spi or mmc
        self.sd_slot = sd_slot
        self.sd_loc = sd_loc
        self.sd = None
        try:
            self.sd = esp_sdcard.Esp_sdcard(slot=self.sd_slot, sd_init=False) # SD gets initialized
        except:
            raise OSError("Failed to set SD bus")
    
    # Initialize SD card
    def initialize(self):
        initialized = True
        
        # Attempt to initialize SD card
        try:
            self.sd.init()
        except:
            initialized = False
        
        return initialized
        
    # Mount SD card
    def mount(self):
        mounted = True
        file = self.sd_loc + '/.avail'
        
        # Maybe should check if its already available before remounting?
        # First try to unmount prevous sd mount
        try:
            uos.umount(self.sd_loc)
        except:
            pass
        
        
        #try to mount
        try:
            uos.mount(self.sd, self.sd_loc)
            
            ## Create file to check SD is writable
            f = open(file, 'w')
            f.write('1')
            f.close()
            
        except:
            mounted = False
        
        return mounted
    
    # Check if SD card is available
    def available(self):
        available = True
        file = self.sd_loc + '/.avail'
            
        # Try to write to a file
        try:
            f = open(file, 'w')
            f.write('1')
            f.close()
            
        # If not accessible to to remount
        except:
            # Re-initialize card
            self.initialize()
            
            # Remount SD card 
            if self.mount():
                
                # # Try to write to a file
                try:
                    f = open(file, 'w')
                    f.write('1')
                    f.close()
                    
                except:
                    available = False
            else:
                available = False
        
        return available



推荐分享
图文皆来源于网络,内容仅做公益性分享,版权归原作者所有,如有侵权请告知删除!
 

Copyright © 2014 ESP56.com All Rights Reserved

执行时间: 0.0093598365783691 seconds