Created
December 15, 2023 02:39
-
-
Save Vbitz/c5590ea2343dc88bbfc08c7d81530f51 to your computer and use it in GitHub Desktop.
Glasgow script capable of reading and writing to SD Cards.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# SS_EEPROM (Chip Select) Pin 2 | |
# 3.3v - | |
# MISO (CIPO) Pin 0 | |
# SCK (SCK) Pin 3 | |
# GND - | |
# MOSI (COPI) Pin 1 | |
set -x | |
glasgow script sdcard.py spi-controller \ | |
--port B \ | |
-V 3.3 \ | |
--pin-sck 3 \ | |
--pin-cs 2 \ | |
--pin-copi 1 \ | |
--pin-cipo 0 \ | |
-f 100 | |
glasgow safe |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Based on: https://github.com/micropython/micropython-lib/blob/master/micropython/drivers/storage/sdcard/sdcard.py | |
# My first time talking to SD cards so expect lots of problems with the code. | |
# Tested with MKDV1GIL-AS | |
_CMD_TIMEOUT = 100 | |
_R1_IDLE_STATE = 1 << 0 | |
# R1_ERASE_RESET = const(1 << 1) | |
_R1_ILLEGAL_COMMAND = 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 = 0xFC | |
_TOKEN_STOP_TRAN = 0xFD | |
_TOKEN_DATA = 0xFE | |
# cmdbuf = bytearray(6) | |
# dummybuf = bytearray(512) | |
# tokenbuf = bytearray(1) | |
spi = iface | |
async def readinto(size: int, byte: int) -> bytearray: | |
buf = await spi.transfer(bytearray([byte] * size)) | |
return buf | |
async def full_readinto(size: int) -> bytearray: | |
# read until start byte (0xff) | |
for i in range(_CMD_TIMEOUT): | |
result = await readinto(1, 0xFF) | |
if result[0] == _TOKEN_DATA: | |
break | |
await spi.delay_ms(1) | |
else: | |
raise OSError("timeout waiting for response") | |
# read data | |
result = await readinto(size, 0xFF) | |
# read checksum | |
await spi.write(b"\xff") | |
await spi.write(b"\xff") | |
await spi.write(b"\xff") | |
return result | |
async def write(token, buf): | |
# send: start of block, data, checksum | |
await readinto(1, token) | |
await spi.write(buf) | |
await spi.write(b"\xff") | |
await spi.write(b"\xff") | |
# check the response | |
if ((await readinto(1, 0xFF))[0] & 0x1F) != 0x05: | |
await spi.write(b"\xff") | |
return | |
# wait for write to finish | |
while (await readinto(1, 0xFF))[0] == 0: | |
pass | |
await spi.write(b"\xff") | |
async def cmd(cmd, arg, crc, final=0, release=True, skip1=False): | |
# create and send the command | |
buf = bytearray(6) | |
buf[0] = 0x40 | cmd | |
buf[1] = (arg >> 24) % 256 | |
buf[2] = (arg >> 16) % 256 | |
buf[3] = (arg >> 8) % 256 | |
buf[4] = arg % 256 | |
buf[5] = crc | |
await spi.write(buf) | |
if skip1: | |
tokenbuf = await readinto(1, 0xFF) | |
# wait for the response (response[7] == 0) | |
for i in range(_CMD_TIMEOUT): | |
tokenbuf = await readinto(1, 0xFF) | |
response = 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: | |
tokenbuf = await readinto(1, 0xFF) | |
final = -1 - final | |
for j in range(final): | |
await spi.write(b"\xff") | |
if release: | |
await spi.write(b"\xff") | |
return response | |
# timeout | |
await spi.write(b"\xff") | |
return -1 | |
async def init_card_v2(): | |
for i in range(_CMD_TIMEOUT): | |
await spi.delay_ms(50) | |
await cmd(58, 0, 0, 4) | |
await cmd(55, 0, 0) | |
if await cmd(41, 0x40000000, 0) == 0: | |
ocr = await cmd( | |
58, 0, 0, -4 | |
) # 4-byte response, negative means keep the first byte | |
cdv = 0 | |
if not ocr & 0x40: | |
# SDSC card, uses byte addressing in read/write/erase commands | |
cdv = 512 | |
else: | |
# SDHC/SDXC card, uses block addressing in read/write/erase commands | |
cdv = 1 | |
# print("[SDCard] v2 card") | |
return cdv | |
raise OSError("timeout waiting for v2 card") | |
async def read_blocks(block_num, num_blocks, block_size): | |
if num_blocks == 1: | |
if await cmd(17, block_num * block_size, 0, release=False) != 0: | |
raise OSError(5) # EIO | |
return await full_readinto(block_size) | |
else: | |
# Just read block after block. | |
ret = bytes([]) | |
for i in range(num_blocks): | |
ret += await read_blocks(block_num + i, 1, block_size) | |
return ret | |
async def write_blocks(block_num, buf, block_size): | |
num_blocks = len(buf) // 512 | |
if num_blocks == 1: | |
# CMD24: set write address for single block | |
if await cmd(24, block_num * block_size, 0) != 0: | |
raise OSError(5) # EIO | |
# send the data | |
await write(_TOKEN_DATA, buf) | |
else: | |
raise NotImplementedError() | |
async def main(): | |
# clock card at least 100 cycles with cs high | |
for i in range(16): | |
await spi.write(b"\xff") | |
for _ in range(5): | |
result = await cmd(0, 0, 0x95) | |
if result == _R1_IDLE_STATE: | |
break | |
else: | |
raise OSError("no SD card") | |
print("got SD card!") | |
r = await cmd(8, 0x01AA, 0x87, 4) | |
if r != _R1_IDLE_STATE: | |
raise OSError("couldn't determine SD card version") | |
block_size = await init_card_v2() | |
print("block_size = ", block_size) | |
if await cmd(9, 0, 0, 0, False) != 0: | |
raise OSError("no response from SD card") | |
csd = await full_readinto(16) | |
sectors = 0 | |
if csd[0] & 0xC0 == 0x40: # CSD version 2.0 | |
print("csd v2", csd) | |
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) | |
sectors = capacity // 512 | |
else: | |
raise OSError("SD card CSD format not supported") | |
print("sectors", sectors) | |
if await cmd(16, 512, 0) != 0: | |
raise OSError("can't set 512 block size") | |
# data = open("sdcard.py", "rb").read()[:512] | |
# block_0 = await read_blocks(0, 1, block_size) | |
# await write_blocks(0, data, block_size) | |
# print(block_0) | |
await main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment