sdcard: add support for spi sdcard driver

This commit is contained in:
sago35
2021-05-14 18:40:29 +09:00
committed by Ron Evans
parent aa17ffd1cb
commit d07ad40373
11 changed files with 1319 additions and 1 deletions
+3 -1
View File
@@ -193,12 +193,14 @@ endif
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
@md5sum ./build/test.hex
DRIVERS = $(wildcard */)
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
pcf8563 mcp2515 servo
pcf8563 mcp2515 servo sdcard
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
unit-test:
+363
View File
@@ -0,0 +1,363 @@
package main
import (
"fmt"
"io"
"machine"
"os"
"strconv"
"strings"
"time"
"tinygo.org/x/drivers/sdcard"
)
const consoleBufLen = 64
const storageBufLen = 1024
var (
debug = false
input [consoleBufLen]byte
store [storageBufLen]byte
console = machine.Serial
dev *sdcard.Device
commands map[string]cmdfunc = map[string]cmdfunc{
"": cmdfunc(noop),
"help": cmdfunc(help),
"dbg": cmdfunc(dbg),
"erase": cmdfunc(erase),
"lsblk": cmdfunc(lsblk),
"write": cmdfunc(write),
"xxd": cmdfunc(xxd),
}
his history
)
type history struct {
buf [32]string
wp int
idx int
}
func (h *history) Add(cmd string) {
if len(cmd) == 0 {
h.idx = h.wp
return
}
if h.wp == len(h.buf)-1 {
for i := 1; i < len(h.buf); i++ {
h.buf[i-1] = h.buf[i]
}
h.wp--
}
h.buf[h.wp] = cmd
h.wp++
h.idx = h.wp
}
func (h *history) PeekPrev() string {
if h.idx > 0 {
h.idx--
}
return h.buf[h.idx]
}
func (h *history) PeekNext() string {
if h.idx < h.wp {
h.idx++
}
return h.buf[h.idx]
}
type cmdfunc func(argv []string)
const (
StateInput = iota
StateEscape
StateEscBrc
StateCSI
)
func RunFor(device *sdcard.Device) {
dev = device
prompt()
var state = StateInput
for i := 0; ; {
if console.Buffered() > 0 {
data, _ := console.ReadByte()
if debug {
fmt.Printf("\rdata: %x, his.idx: %d\r\n\r", data, his.idx)
prompt()
console.Write(input[:i])
}
switch state {
case StateInput:
switch data {
case 0x8:
fallthrough
case 0x7f: // this is probably wrong... works on my machine tho :)
// backspace
if i > 0 {
i -= 1
console.Write([]byte{0x8, 0x20, 0x8})
}
case 13:
// return key
console.Write([]byte("\r\n"))
runCommand(string(input[:i]))
his.Add(string(input[:i]))
prompt()
i = 0
continue
case 27:
// escape
state = StateEscape
default:
// anything else, just echo the character if it is printable
if strconv.IsPrint(rune(data)) {
if i < (consoleBufLen - 1) {
console.WriteByte(data)
input[i] = data
i++
}
}
}
case StateEscape:
switch data {
case 0x5b:
state = StateEscBrc
default:
state = StateInput
}
case StateEscBrc:
switch data {
case 0x41:
// up
println()
prompt()
cmd := his.PeekPrev()
i = len(cmd)
copy(input[:i], []byte(cmd))
console.Write(input[:i])
state = StateInput
case 0x42:
//down
println()
prompt()
cmd := his.PeekNext()
i = len(cmd)
copy(input[:i], []byte(cmd))
console.Write(input[:i])
state = StateInput
default:
// TODO: handle escape sequences
state = StateInput
}
default:
// TODO: handle escape sequences
state = StateInput
}
} else {
time.Sleep(1 * time.Millisecond)
}
}
}
func runCommand(line string) {
argv := strings.SplitN(strings.TrimSpace(line), " ", -1)
cmd := argv[0]
cmdfn, ok := commands[cmd]
if !ok {
println("unknown command: " + line)
return
}
cmdfn(argv)
}
func noop(argv []string) {}
func help(argv []string) {
fmt.Printf("help\r\n")
fmt.Printf("dbg\r\n")
fmt.Printf("erase\r\n")
fmt.Printf("lsblk\r\n")
fmt.Printf("write <hex offset> <bytes>\r\n")
fmt.Printf("xxd <start address> <length>\r\n")
}
func dbg(argv []string) {
if debug {
debug = false
println("Console debbuging off")
} else {
debug = true
println("Console debbuging on")
}
}
func lsblk(argv []string) {
csd := dev.CSD
sectors, err := csd.Sectors()
if err != nil {
fmt.Printf("%s\r\n", err.Error())
return
}
cid := dev.CID
fmt.Printf(
"\r\n-------------------------------------\r\n"+
" Device Information: \r\n"+
"-------------------------------------\r\n"+
" JEDEC ID: %v\r\n"+
" Serial: %v\r\n"+
" Status 1: %02x\r\n"+
" Status 2: %02x\r\n"+
" \r\n"+
" Max clock speed (MHz): %d\r\n"+
" Has Sector Protection: %t\r\n"+
" Supports Fast Reads: %t\r\n"+
" Supports QSPI Reads: %t\r\n"+
" Supports QSPI Write: %t\r\n"+
" Write Status Split: %t\r\n"+
" Single Status Byte: %t\r\n"+
"-Sectors: %d\r\n"+
"-Bytes (Sectors * 512) %d\r\n"+
"-ManufacturerID %02X\r\n"+
"-OEMApplicationID %04X\r\n"+
"-ProductName %s\r\n"+
"-ProductVersion %s\r\n"+
"-ProductSerialNumber %08X\r\n"+
"-ManufacturingYear %02X\r\n"+
"-ManufacturingMonth %02X\r\n"+
"-Always1 %d\r\n"+
"-CRC %02X\r\n"+
"-------------------------------------\r\n\r\n",
"attrs.JedecID", // attrs.JedecID,
cid.ProductSerialNumber, // serialNumber1,
0, // status1,
0, // status2,
csd.TRAN_SPEED, // attrs.MaxClockSpeedMHz,
false, // attrs.HasSectorProtection,
false, // attrs.SupportsFastRead,
false, // attrs.SupportsQSPI,
false, // attrs.SupportsQSPIWrites,
false, // attrs.WriteStatusSplit,
false, // attrs.SingleStatusByte,
sectors,
csd.Size(),
cid.ManufacturerID,
cid.OEMApplicationID,
cid.ProductName,
cid.ProductVersion,
cid.ProductSerialNumber,
cid.ManufacturingYear,
cid.ManufacturingMonth,
cid.Always1,
cid.CRC,
)
}
func erase(argv []string) {
fmt.Printf("erase - not impl\r\n")
}
var writeBuf [256]byte
func write(argv []string) {
if len(argv) < 3 {
println("usage: write <hex offset> <bytes>")
return
}
var err error
var addr uint64 = 0x0
if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil {
println("Invalid address: " + err.Error() + "\r\n")
return
}
buf := writeBuf[:0]
for i := 0; i < len(argv[2]); i += 2 {
var b uint64
if b, err = strconv.ParseUint(argv[2][i:i+2], 16, 8); err != nil {
println("Invalid bytes: " + err.Error() + "\r\n")
return
}
buf = append(buf, byte(b))
}
if _, err = dev.WriteAt(buf, int64(addr)); err != nil {
println("Write error: " + err.Error() + "\r\n")
}
}
func xxd(argv []string) {
var err error
var addr uint64 = 0x0
var size int = 64
switch len(argv) {
case 3:
if size, err = strconv.Atoi(argv[2]); err != nil {
println("Invalid size argument: " + err.Error() + "\r\n")
return
}
if size > storageBufLen || size < 1 {
fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", storageBufLen)
return
}
fallthrough
case 2:
if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil {
println("Invalid address: " + err.Error() + "\r\n")
return
}
fallthrough
case 1:
// no args supplied, so nothing to do here, just use the defaults
default:
println("usage: xxd <hex address, ex: 0xA0> <size of hexdump in bytes>\r\n")
return
}
buf := store[0:size]
_, err = dev.ReadAt(buf, int64(addr))
if err != nil {
fmt.Printf("xxd err : %s\r\n", err.Error())
}
xxdfprint(os.Stdout, uint32(addr), buf)
}
func xxdfprint(w io.Writer, offset uint32, b []byte) {
var l int
var buf16 = make([]byte, 16)
for i, c := 0, len(b); i < c; i += 16 {
l = i + 16
if l >= c {
l = c
}
fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l])
for j, n := 0, l-i; j < 16; j++ {
if j >= n || !strconv.IsPrint(rune(b[i+j])) || b[i+j] >= 0xFE {
buf16[j] = '.'
} else {
buf16[j] = b[i+j]
}
}
console.Write(buf16)
println()
}
}
func prompt() {
print("==> ")
}
+23
View File
@@ -0,0 +1,23 @@
// +build feather_m4 feather_m4_can
package main
import (
"machine"
)
func init() {
spi = machine.SPI0
spi.Configure(machine.SPIConfig{
SCK: machine.SPI0_SCK_PIN,
SDO: machine.SPI0_SDO_PIN,
SDI: machine.SPI0_SDI_PIN,
Frequency: 24000000,
LSBFirst: false,
Mode: 0, // phase=0, polarity=0
})
csPin = machine.D10
ledPin = machine.LED
}
+23
View File
@@ -0,0 +1,23 @@
// +build atsamd21
package main
import (
"machine"
)
func init() {
spi = machine.SPI0
spi.Configure(machine.SPIConfig{
SCK: machine.SPI0_SCK_PIN,
SDO: machine.SPI0_SDO_PIN,
SDI: machine.SPI0_SDI_PIN,
Frequency: 24000000,
LSBFirst: false,
Mode: 0, // phase=0, polarity=0
})
csPin = machine.D2
ledPin = machine.LED
}
+48
View File
@@ -0,0 +1,48 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/sdcard"
)
var (
spi machine.SPI
csPin machine.Pin
ledPin machine.Pin
)
func main() {
waitSerial()
fmt.Printf("sdcard console\r\n")
led := ledPin
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
sd := sdcard.New(spi, csPin)
err := sd.Configure()
if err != nil {
fmt.Printf("%s\r\n", err.Error())
for {
time.Sleep(time.Hour)
}
}
go RunFor(&sd)
for {
led.High()
time.Sleep(200 * time.Millisecond)
led.Low()
time.Sleep(200 * time.Millisecond)
}
}
// Wait for user to open serial console
func waitSerial() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+23
View File
@@ -0,0 +1,23 @@
// +build pyportal
package main
import (
"machine"
)
func init() {
spi = machine.SPI0
spi.Configure(machine.SPIConfig{
SCK: machine.SPI0_SCK_PIN,
SDO: machine.SPI0_SDO_PIN,
SDI: machine.SPI0_SDI_PIN,
Frequency: 24000000,
LSBFirst: false,
Mode: 0, // phase=0, polarity=0
})
csPin = machine.D32 // SD_CS
ledPin = machine.LED
}
+23
View File
@@ -0,0 +1,23 @@
// +build wioterminal
package main
import (
"machine"
)
func init() {
spi = machine.SPI2
spi.Configure(machine.SPIConfig{
SCK: machine.SCK2,
SDO: machine.SDO2,
SDI: machine.SDI2,
Frequency: 24000000,
LSBFirst: false,
Mode: 0, // phase=0, polarity=0
})
csPin = machine.SS2
ledPin = machine.LED
}
+49
View File
@@ -0,0 +1,49 @@
package sdcard
import "fmt"
type CID struct {
//// byte 0
ManufacturerID byte
//uint8_t mid; // Manufacturer ID
//// byte 1-2
OEMApplicationID uint16
//char oid[2]; // OEM/Application ID
//// byte 3-7
ProductName string
//char pnm[5]; // Product name
//// byte 8
ProductVersion string
//unsigned prv_m : 4; // Product revision n.m
//unsigned prv_n : 4;
//// byte 9-12
ProductSerialNumber uint32
//uint32_t psn; // Product serial number
//// byte 13
ManufacturingYear byte
ManufacturingMonth byte
//unsigned mdt_year_high : 4; // Manufacturing date
//unsigned reserved : 4;
//// byte 14
//unsigned mdt_month : 4;
//unsigned mdt_year_low : 4;
//// byte 15
Always1 byte
//unsigned always1 : 1;
CRC byte
//unsigned crc : 7;
}
func NewCID(buf []byte) *CID {
return &CID{
ManufacturerID: buf[0],
OEMApplicationID: (uint16(buf[0]) << 8) | uint16(buf[1]),
ProductName: string(buf[3:8]),
ProductVersion: fmt.Sprintf("%d.%d", (buf[8]&0xF0)>>4, buf[8]&0x0F),
ProductSerialNumber: (uint32(buf[9]) << 24) | (uint32(buf[10]) << 16) | (uint32(buf[11]) << 8) | uint32(buf[12]),
ManufacturingYear: (buf[13] & 0xF0) | (buf[14] & 0x0F),
ManufacturingMonth: (buf[14] & 0xF0) >> 4,
Always1: (buf[15] & 0x80) >> 7,
CRC: buf[15] & 0x7F,
}
}
+52
View File
@@ -0,0 +1,52 @@
package sdcard
const (
CMD0_GO_IDLE_STATE = 0
CMD1_SEND_OP_CND = 1
CMD2_ALL_SEND_CID = 2
CMD3_SEND_RELATIVE_ADDR = 3
CMD4_SET_DSR = 4
CMD6_SWITCH_FUNC = 6
CMD7_SELECT_DESELECT_CARD = 7
CMD8_SEND_IF_COND = 8
CMD9_SEND_CSD = 9
CMD10_SEND_CID = 10
CMD12_STOP_TRANSMISSION = 12
CMD13_SEND_STATUS = 13
CMD15_GO_INACTIVE_STATE = 15
CMD16_SET_BLOCKLEN = 16
CMD17_READ_SINGLE_BLOCK = 17
CMD18_READ_MULTIPLE_BLOCK = 18
CMD24_WRITE_BLOCK = 24
CMD25_WRITE_MULTIPLE_BLOCK = 25
CMD27_PROGRAM_CSD = 27
CMD28_SET_WRITE_PROT = 28
CMD29_CLR_WRITE_PROT = 29
CMD30_SEND_WRITE_PROT = 30
CMD32_ERASE_WR_BLK_START_ADDR = 32
CMD33_ERASE_WR_BLK_END_ADDR = 33
CMD38_ERASE = 38
CMD42_LOCK_UNLOCK = 42
CMD55_APP_CMD = 55
CMD56_GEN_CMD = 56
CMD58_READ_OCR = 58
CMD59_CRC_ON_OFF = 59
ACMD6_SET_BUS_WIDTH = 6
ACMD13_SD_STATUS = 13
ACMD22_SEND_NUM_WR_BLOCKS = 22
ACMD23_SET_WR_BLK_ERASE_COUNT = 23
ACMD41_SD_APP_OP_COND = 41
ACMD42_SET_CLR_CARD_DETECT = 42
ACMD51_SEND_SCR = 51
ACMD18_SECURE_READ_MULTI_BLOCK = 18
ACMD25_SECURE_WRITE_MULTI_BLOCK = 25
ACMD26_SECURE_WRITE_MKB = 26
ACMD38_SECURE_ERASE = 38
ACMD43_GET_MKB = 43
ACMD44_GET_MID = 44
ACMD45_SET_CER_RN1 = 45
ACMD46_SET_CER_RN2 = 46
ACMD47_SET_CER_RES2 = 47
ACMD48_SET_CER_RES1 = 48
ACMD49_CHANGE_SECURE_AREA = 49
)
+106
View File
@@ -0,0 +1,106 @@
package sdcard
import (
"fmt"
)
type CSD struct {
CSD_STRUCTURE byte // 2 R [127:126] 0x01 : CSD Structure
TAAC byte // 8 R [119:112] 0x0E : Data Read Access-Time-1
NSAC byte // 8 R [111:104] 0x00 : Data Read Access-Time-2 in CLK Cycles (NSAC*100)
TRAN_SPEED byte // 8 R [103:96] 0x5A : Max. Data Transfer Rate
CCC uint16 // 12 R [95:84] 0x5B5 : Card Command Classes
READ_BL_LEN byte // 4 R [83:80] 0x09 : Max. Read Data Block Length
READ_BL_PARTIAL byte // 1 R [79:79] 0x00 : Partial Blocks for Read Allowed
WRITE_BLK_MISALIGN byte // 1 R [78:78] 0x00 : Write Block Misalignment
READ_BLK_MISALIGN byte // 1 R [77:77] 0x00 : Read Block Misalignment
DSR_IMP byte // 1 R [76:76] 0x00 : DSR Implemented
C_SIZE uint32 // 22 R [69:48] 0xXXXXXX : Device Size
ERASE_BLK_EN byte // 1 R [46:46] 0x01 : Erase Single Block Enable
SECTOR_SIZE byte // 7 R [45:39] 0x7F : Erase Sector Size
WP_GRP_SIZE byte // 7 R [38:32] 0x00 : Write Protect Group Size
WP_GRP_ENABLE byte // 1 R [31:31] 0x00 : Write Protect Group Enable
R2W_FACTOR byte // 3 R [28:26] 0x02 : Write Speed Factor
WRITE_BL_LEN byte // 4 R [25:22] 0x09 : Max. Write data Block Length
WRITE_BL_PARTIAL byte // 1 R [21:21] 0x00 : Partial Blocks for Write Allowed
FILE_FORMAT_GRP byte // 1 R [15:15] 0x00 : File Format Group
COPY byte // 1 RW [14:14] 0x00 :Copy Flag
PERM_WRITE_PROTECT byte // 1 RW [13:13] 0x00 : Permanent Write Protection
TMP_WRITE_PROTECT byte // 1 RW [12:12] 0x00 : Temporary Write Protection
FILE_FORMAT byte // 2 R [11:10] 0x00 : File Format
CRC byte // 7 RW [7:1] 0xXX : CRC
}
func NewCSD(buf []byte) *CSD {
return &CSD{
CSD_STRUCTURE: (buf[0] & 0xC0) >> 6,
TAAC: buf[1],
NSAC: buf[2],
TRAN_SPEED: buf[3],
CCC: uint16(buf[4])<<4 | uint16(buf[5])>>4,
READ_BL_LEN: buf[5] & 0x0F,
READ_BL_PARTIAL: (buf[6] & 0x80) >> 7,
WRITE_BLK_MISALIGN: (buf[6] & 0x40) >> 6,
READ_BLK_MISALIGN: (buf[6] & 0x20) >> 5,
DSR_IMP: (buf[6] & 0x10) >> 4,
C_SIZE: uint32(buf[7]&0x3F)<<16 | uint32(buf[8])<<8 | uint32(buf[9]),
ERASE_BLK_EN: (buf[10] & 0x40) >> 6,
SECTOR_SIZE: (buf[10]&0x3F)<<1 | (buf[11]&0x80)>>7,
WP_GRP_SIZE: buf[11] & 0x7F,
WP_GRP_ENABLE: (buf[12] & 0x80) >> 7,
R2W_FACTOR: (buf[12] & 0x1C) >> 2,
WRITE_BL_LEN: (buf[12]&0x03)<<2 | (buf[13]&0xC0)>>6,
WRITE_BL_PARTIAL: (buf[13] & 0x20) >> 5,
FILE_FORMAT_GRP: (buf[14] & 0x80) >> 7,
COPY: (buf[14] & 0x40) >> 6,
PERM_WRITE_PROTECT: (buf[14] & 0x20) >> 5,
TMP_WRITE_PROTECT: (buf[14] & 0x10) >> 4,
FILE_FORMAT: (buf[14] & 0x0C) >> 2,
CRC: (buf[15] & 0xFE) >> 1,
}
}
func (c *CSD) Dump() {
fmt.Printf("CSD_STRUCTURE: %X\r\n", c.CSD_STRUCTURE)
fmt.Printf("TAAC: %X\r\n", c.TAAC)
fmt.Printf("NSAC: %X\r\n", c.NSAC)
fmt.Printf("TRAN_SPEED: %X\r\n", c.TRAN_SPEED)
fmt.Printf("CCC: %X\r\n", c.CCC)
fmt.Printf("READ_BL_LEN: %X\r\n", c.READ_BL_LEN)
fmt.Printf("READ_BL_PARTIAL: %X\r\n", c.READ_BL_PARTIAL)
fmt.Printf("WRITE_BLK_MISALIGN: %X\r\n", c.WRITE_BLK_MISALIGN)
fmt.Printf("READ_BLK_MISALIGN: %X\r\n", c.READ_BLK_MISALIGN)
fmt.Printf("DSR_IMP: %X\r\n", c.DSR_IMP)
fmt.Printf("C_SIZE: %X\r\n", c.C_SIZE)
fmt.Printf("ERASE_BLK_EN: %X\r\n", c.ERASE_BLK_EN)
fmt.Printf("SECTOR_SIZE: %X\r\n", c.SECTOR_SIZE)
fmt.Printf("WP_GRP_SIZE: %X\r\n", c.WP_GRP_SIZE)
fmt.Printf("WP_GRP_ENABLE: %X\r\n", c.WP_GRP_ENABLE)
fmt.Printf("R2W_FACTOR: %X\r\n", c.R2W_FACTOR)
fmt.Printf("WRITE_BL_LEN: %X\r\n", c.WRITE_BL_LEN)
fmt.Printf("WRITE_BL_PARTIAL: %X\r\n", c.WRITE_BL_PARTIAL)
fmt.Printf("FILE_FORMAT_GRP: %X\r\n", c.FILE_FORMAT_GRP)
fmt.Printf("COPY: %X\r\n", c.COPY)
fmt.Printf("PERM_WRITE_PROTECT: %X\r\n", c.PERM_WRITE_PROTECT)
fmt.Printf("TMP_WRITE_PROTECT: %X\r\n", c.TMP_WRITE_PROTECT)
fmt.Printf("FILE_FORMAT: %X\r\n", c.FILE_FORMAT)
fmt.Printf("CRC: %X\r\n", c.CRC)
}
func (c *CSD) Sectors() (int64, error) {
sectors := int64(0)
if c.CSD_STRUCTURE == 0x01 {
// CSD version 2.0
sectors = (int64(c.C_SIZE) + 1) * 1024
} else if c.CSD_STRUCTURE == 0x00 {
// CSD version 1.0 (old, <=2GB)
return 0, fmt.Errorf("CSD format version 1.0 is not supported")
} else {
return 0, fmt.Errorf("unknown CSD format")
}
return sectors, nil
}
func (c *CSD) Size() uint64 {
return uint64(c.C_SIZE) * 512 * 1024
}
+606
View File
@@ -0,0 +1,606 @@
package sdcard
import (
"fmt"
"machine"
"time"
)
const (
_CMD_TIMEOUT = 100
_R1_IDLE_STATE = 1 << 0
_R1_ERASE_RESET = 1 << 1
_R1_ILLEGAL_COMMAND = 1 << 2
_R1_COM_CRC_ERROR = 1 << 3
_R1_ERASE_SEQUENCE_ERROR = 1 << 4
_R1_ADDRESS_ERROR = 1 << 5
_R1_PARAMETER_ERROR = 1 << 6
// card types
SD_CARD_TYPE_SD1 = 1 // Standard capacity V1 SD card
SD_CARD_TYPE_SD2 = 2 // Standard capacity V2 SD card
SD_CARD_TYPE_SDHC = 3 // High Capacity SD card
)
type Device struct {
bus machine.SPI
cs machine.Pin
cmdbuf []byte
dummybuf []byte
tokenbuf []byte
sdCardType byte
CID *CID
CSD *CSD
}
func New(b machine.SPI, cs machine.Pin) Device {
return Device{
bus: b,
cs: cs,
cmdbuf: make([]byte, 6),
dummybuf: make([]byte, 512),
tokenbuf: make([]byte, 1),
sdCardType: 0,
}
}
func (d *Device) Configure() error {
return d.initCard()
}
func (d *Device) initCard() error {
// set pin modes
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.cs.High()
// clock card at least 100 cycles with cs high
for i := 0; i < 16; i++ {
d.bus.Transfer(byte(0xFF))
}
d.cs.Low()
// CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts)
ok := false
for i := 0; i < 2000; i++ {
// Wait up to 2 seconds to be the same as the Arduino
if d.cmd(CMD0_GO_IDLE_STATE, 0, 0x95) == _R1_IDLE_STATE {
ok = true
break
}
time.Sleep(1 * time.Millisecond)
}
if !ok {
return fmt.Errorf("no SD card")
}
// CMD8: determine card version
r := d.cmd(CMD8_SEND_IF_COND, 0x01AA, 0x87)
if (r & _R1_ILLEGAL_COMMAND) == _R1_ILLEGAL_COMMAND {
d.sdCardType = SD_CARD_TYPE_SD1
return fmt.Errorf("init_card_v1 not impl\r\n")
} else {
// r7 response
status := byte(0)
for i := 0; i < 3; i++ {
var err error
status, err = d.bus.Transfer(byte(0xFF))
if err != nil {
return err
}
}
if (status & 0x0F) != 0x01 {
return fmt.Errorf("SD_CARD_ERROR_CMD8 %02X", status)
}
for i := 3; i < 4; i++ {
var err error
status, err = d.bus.Transfer(byte(0xFF))
if err != nil {
return err
}
}
if status != 0xAA {
return fmt.Errorf("SD_CARD_ERROR_CMD8 %02X", status)
}
d.sdCardType = SD_CARD_TYPE_SD2
}
// initialize card and send host supports SDHC if SD2
arg := uint32(0)
if d.sdCardType == SD_CARD_TYPE_SD2 {
arg = 0x40000000
}
// check for timeout
ok = false
for i := 0; i < 2000; i++ {
if d.acmd(ACMD41_SD_APP_OP_COND, arg) == 0 {
ok = true
break
}
time.Sleep(1 * time.Millisecond)
}
if !ok {
return fmt.Errorf("SD_CARD_ERROR_ACMD41")
}
// if SD2 read OCR register to check for SDHC card
if d.sdCardType == SD_CARD_TYPE_SD2 {
if d.cmd(CMD58_READ_OCR, 0, 0) != 0 {
return fmt.Errorf("SD_CARD_ERROR_CMD58")
}
status, err := d.bus.Transfer(byte(0xFF))
if err != nil {
return err
}
if (status & 0xC0) == 0xC0 {
d.sdCardType = SD_CARD_TYPE_SDHC
}
// discard rest of ocr - contains allowed voltage range
for i := 1; i < 4; i++ {
d.bus.Transfer(byte(0xFF))
}
}
var buf [16]byte
// read CID
err := d.ReadCID(buf[:])
if err != nil {
return err
}
d.CID = NewCID(buf[:])
// read CSD
err = d.ReadCSD(buf[:])
if err != nil {
return err
}
d.CSD = NewCSD(buf[:])
d.cs.High()
return nil
}
func (d Device) acmd(cmd byte, arg uint32) byte {
d.cmd(CMD55_APP_CMD, 0, 0)
return d.cmd(cmd, arg, 0)
}
func (d Device) cmd(cmd byte, arg uint32, crc byte) byte {
d.cs.Low()
if cmd != 12 {
d.waitNotBusy(300)
}
// create and send the command
buf := d.cmdbuf
buf[0] = 0x40 | cmd
buf[1] = byte(arg >> 24)
buf[2] = byte(arg >> 16)
buf[3] = byte(arg >> 8)
buf[4] = byte(arg)
buf[5] = crc
d.bus.Tx(buf, nil)
if cmd == 12 {
// skip 1 byte
d.bus.Transfer(byte(0xFF))
}
// wait for the response (response[7] == 0)
for i := 0; i < 0xFFFF; i++ {
d.bus.Tx([]byte{0xFF}, d.tokenbuf)
response := d.tokenbuf[0]
if (response & 0x80) == 0 {
return response
}
}
// TODO
//// timeout
d.cs.High()
d.bus.Transfer(byte(0xFF))
return 0xFF // -1
}
func (d Device) waitNotBusy(timeoutMs int) error {
for i := 0; i < timeoutMs; i++ {
r, err := d.bus.Transfer(byte(0xFF))
if err != nil {
return err
}
if r == 0xFF {
return nil
}
time.Sleep(1 * time.Millisecond)
}
return nil
}
func (d Device) waitStartBlock() error {
status := byte(0xFF)
for i := 0; i < 3000; i++ {
var err error
status, err = d.bus.Transfer(byte(0xFF))
if err != nil {
d.cs.High()
return err
}
if status != 0xFF {
break
}
time.Sleep(100 * time.Microsecond)
}
if status != 254 {
d.cs.High()
return fmt.Errorf("SD_CARD_START_BLOCK")
}
return nil
}
// ReadCSD reads the CSD using CMD9.
func (d Device) ReadCSD(csd []byte) error {
return d.readRegister(CMD9_SEND_CSD, csd)
}
// ReadCID reads the CID using CMD10
func (d Device) ReadCID(csd []byte) error {
return d.readRegister(CMD10_SEND_CID, csd)
}
func (d Device) readRegister(cmd uint8, dst []byte) error {
if d.cmd(cmd, 0, 0) != 0 {
return fmt.Errorf("SD_CARD_ERROR_READ_REG")
}
if err := d.waitStartBlock(); err != nil {
return err
}
// transfer data
for i := uint16(0); i < 16; i++ {
r, err := d.bus.Transfer(byte(0xFF))
if err != nil {
return err
}
dst[i] = r
}
d.bus.Transfer(byte(0xFF))
d.bus.Transfer(byte(0xFF))
d.cs.High()
return nil
}
// ReadData reads 512 bytes from sdcard into dst.
func (d Device) ReadData(block uint32, dst []byte) error {
if len(dst) < 512 {
return fmt.Errorf("len(dst) must be greater than or equal to 512")
}
// use address if not SDHC card
if d.sdCardType != SD_CARD_TYPE_SDHC {
block <<= 9
}
if d.cmd(CMD17_READ_SINGLE_BLOCK, block, 0) != 0 {
return fmt.Errorf("CMD17 error")
}
if err := d.waitStartBlock(); err != nil {
return fmt.Errorf("waitStartBlock()")
}
err := d.bus.Tx(nil, dst)
if err != nil {
return err
}
// skip CRC (2byte)
d.bus.Transfer(byte(0xFF))
d.bus.Transfer(byte(0xFF))
// TODO: probably not necessary
d.cs.High()
return nil
}
// WriteMultiStart starts the continuous write mode using CMD25.
func (d Device) WriteMultiStart(block uint32) error {
// use address if not SDHC card
if d.sdCardType != SD_CARD_TYPE_SDHC {
block <<= 9
}
if d.cmd(CMD25_WRITE_MULTIPLE_BLOCK, block, 0) != 0 {
return fmt.Errorf("CMD25 error")
}
// skip 1 byte
d.bus.Transfer(byte(0xFF))
return nil
}
// WriteMulti performs continuous writing. It is necessary to call
// WriteMultiStart() in prior.
func (d Device) WriteMulti(buf []byte) error {
// send Data Token for CMD25
d.bus.Transfer(byte(0xFC))
for i := 0; i < 512; i++ {
_, err := d.bus.Transfer(buf[i])
if err != nil {
return err
}
}
// send dummy CRC (2 byte)
d.bus.Transfer(byte(0xFF))
d.bus.Transfer(byte(0xFF))
// Data Resp.
r, err := d.bus.Transfer(byte(0xFF))
if err != nil {
return err
}
if (r & 0x1F) != 0x05 {
return fmt.Errorf("SD_CARD_ERROR_WRITE")
}
// wait no busy
err = d.waitNotBusy(600)
if err != nil {
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
}
return nil
}
// WriteMultiStop exits the continuous write mode.
func (d Device) WriteMultiStop() error {
defer d.cs.High()
// Stop Tran token for CMD25
d.bus.Transfer(0xFD)
// skip 1 byte
d.bus.Transfer(byte(0xFF))
err := d.waitNotBusy(600)
if err != nil {
return nil
}
return nil
}
// WriteData writes 512 bytes from dst to sdcard.
func (d Device) WriteData(block uint32, src []byte) error {
if len(src) < 512 {
return fmt.Errorf("len(src) must be greater than or equal to 512")
}
// use address if not SDHC card
if d.sdCardType != SD_CARD_TYPE_SDHC {
block <<= 9
}
if d.cmd(CMD24_WRITE_BLOCK, block, 0) != 0 {
return fmt.Errorf("CMD24 error")
}
// wait 1 byte?
token := byte(0xFE)
d.bus.Transfer(token)
err := d.bus.Tx(src[:512], nil)
if err != nil {
return err
}
// send dummy CRC (2 byte)
d.bus.Transfer(byte(0xFF))
d.bus.Transfer(byte(0xFF))
// Data Resp.
r, err := d.bus.Transfer(byte(0xFF))
if err != nil {
return err
}
if (r & 0x1F) != 0x05 {
return fmt.Errorf("SD_CARD_ERROR_WRITE")
}
// wait no busy
err = d.waitNotBusy(600)
if err != nil {
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
}
// TODO: probably not necessary
d.cs.High()
return nil
}
// ReadAt reads the given number of bytes from the sdcard.
func (dev *Device) ReadAt(buf []byte, addr int64) (int, error) {
block := uint64(addr)
// use address if not SDHC card
if dev.sdCardType == SD_CARD_TYPE_SDHC {
block >>= 9
}
idx := uint32(0)
start := uint32(addr % 512)
end := uint32(0)
remain := uint32(len(buf))
// If data starts in the middle
if 0 < start {
if start+remain <= 512 {
end = start + remain
} else {
end = 512
}
err := dev.ReadData(uint32(block), dev.dummybuf)
if err != nil {
return 0, err
}
copy(buf[idx:], dev.dummybuf[start:end])
remain -= end - start
idx += end - start
block++
}
// If more than 512 bytes left
for 512 <= remain {
start = 0
end = 512
err := dev.ReadData(uint32(block), dev.dummybuf)
if err != nil {
return 0, err
}
copy(buf[idx:], dev.dummybuf[start:end])
remain -= end - start
idx += end - start
block++
}
// Read to the end
if 0 < remain {
start = 0
end = remain
err := dev.ReadData(uint32(block), dev.dummybuf)
if err != nil {
return 0, err
}
copy(buf[idx:], dev.dummybuf[start:end])
remain -= end - start
idx += end - start
block++
}
return int(idx), nil
}
// WriteAt writes the given number of bytes to sdcard.
func (dev *Device) WriteAt(buf []byte, addr int64) (n int, err error) {
block := uint64(addr)
// use address if not SDHC card
if dev.sdCardType == SD_CARD_TYPE_SDHC {
block >>= 9
}
idx := uint32(0)
start := uint32(addr % 512)
end := uint32(0)
remain := uint32(len(buf))
// If data starts in the middle
if 0 < start {
if start+remain <= 512 {
end = start + remain
} else {
end = 512
}
err := dev.ReadData(uint32(block), dev.dummybuf)
if err != nil {
return 0, err
}
copy(dev.dummybuf[start:end], buf[idx:])
err = dev.WriteData(uint32(block), dev.dummybuf)
if err != nil {
return 0, err
}
remain -= end - start
idx += end - start
block++
}
// If more than 512 bytes left
for 512 <= remain {
start = 0
end = 512
err := dev.WriteData(uint32(block), buf[idx:idx+512])
if err != nil {
return 0, err
}
remain -= end - start
idx += end - start
block++
}
// Write to the end
if 0 < remain {
start = 0
end = remain
err := dev.ReadData(uint32(block), dev.dummybuf)
if err != nil {
return 0, err
}
copy(dev.dummybuf[start:end], buf[idx:])
err = dev.WriteData(uint32(block), dev.dummybuf)
if err != nil {
return 0, err
}
remain -= end - start
idx += end - start
block++
}
return int(idx), nil
}
// Size returns the number of bytes in this sdcard.
func (dev *Device) Size() int64 {
return int64(dev.CSD.Size())
}
// WriteBlockSize returns the block size in which data can be written to
// memory.
func (dev *Device) WriteBlockSize() int64 {
return 512
}
// EraseBlockSize returns the smallest erasable area on this sdcard in bytes.
func (dev *Device) EraseBlockSize() int64 {
return 512
}
// EraseBlocks erases the given number of blocks.
func (dev *Device) EraseBlocks(start, len int64) error {
dev.WriteMultiStart(uint32(start))
for i := range dev.dummybuf {
dev.dummybuf[i] = 0
}
for i := 0; i < int(len); i++ {
dev.WriteMulti(dev.dummybuf)
}
dev.WriteMultiStop()
return nil
}