sdcard: add support for fatfs

This commit is contained in:
sago35
2021-05-14 19:03:12 +09:00
committed by Ron Evans
parent 5a02fe068b
commit 6b914f7af7
25 changed files with 870 additions and 67 deletions
+20
View File
@@ -0,0 +1,20 @@
# SPI sdcard/mmc driver
This package provides the driver for sdcard/mmc with SPI connection.
`examples/sdcard/console` shows a low-level access example.
`examples/sdcard/tinyfs` shows an example of using fatfs to read FAT32.
If you use this package, you need to set `default-stack-size` in `targets/*.json`.
For example, `targets/wioterminal.json` has the following configuration.
```
{
"inherits": ["atsamd51p19a"],
"build-tags": ["wioterminal"],
"flash-1200-bps-reset": "true",
"flash-method": "msd",
"msd-volume-name": "Arduino",
"msd-firmware-name": "firmware.uf2",
"default-stack-size": 2048
}
```
+61 -26
View File
@@ -23,8 +23,15 @@ const (
SD_CARD_TYPE_SDHC = 3 // High Capacity SD card
)
var (
dummy [512]byte
)
type Device struct {
bus machine.SPI
sck machine.Pin
sdo machine.Pin
sdi machine.Pin
cs machine.Pin
cmdbuf []byte
dummybuf []byte
@@ -34,10 +41,13 @@ type Device struct {
CSD *CSD
}
func New(b machine.SPI, cs machine.Pin) Device {
func New(b machine.SPI, sck, sdo, sdi, cs machine.Pin) Device {
return Device{
bus: b,
cs: cs,
sck: sck,
sdo: sdo,
sdi: sdi,
cmdbuf: make([]byte, 6),
dummybuf: make([]byte, 512),
tokenbuf: make([]byte, 1),
@@ -50,26 +60,38 @@ func (d *Device) Configure() error {
}
func (d *Device) initCard() error {
d.bus.Configure(machine.SPIConfig{
SCK: d.sck,
SDO: d.sdo,
SDI: d.sdi,
Frequency: 250000,
LSBFirst: false,
Mode: 0, // phase=0, polarity=0
})
// 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))
for i := range dummy {
dummy[i] = 0xFF
}
// clock card at least 100 cycles with cs high
d.bus.Tx(dummy[:10], nil)
d.cs.Low()
d.bus.Tx(dummy[:], nil)
// CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts)
ok := false
for i := 0; i < 2000; i++ {
tm := setTimeout(0, 2*time.Second)
for !tm.expired() {
// 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")
@@ -115,20 +137,21 @@ func (d *Device) initCard() error {
// check for timeout
ok = false
for i := 0; i < 2000; i++ {
tm = setTimeout(0, 2*time.Second)
for !tm.expired() {
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 {
if d.cmd(CMD58_READ_OCR, 0, 0xFF) != 0 {
return fmt.Errorf("SD_CARD_ERROR_CMD58")
}
@@ -145,6 +168,10 @@ func (d *Device) initCard() error {
}
}
if d.cmd(CMD16_SET_BLOCKLEN, 0x0200, 0xFF) != 0 {
return fmt.Errorf("SD_CARD_ERROR_CMD16")
}
var buf [16]byte
// read CID
err := d.ReadCID(buf[:])
@@ -162,19 +189,28 @@ func (d *Device) initCard() error {
d.cs.High()
d.bus.Configure(machine.SPIConfig{
SCK: d.sck,
SDO: d.sdo,
SDI: d.sdi,
Frequency: 4000000,
LSBFirst: false,
Mode: 0, // phase=0, polarity=0
})
return nil
}
func (d Device) acmd(cmd byte, arg uint32) byte {
d.cmd(CMD55_APP_CMD, 0, 0)
return d.cmd(cmd, arg, 0)
d.cmd(CMD55_APP_CMD, 0, 0xFF)
return d.cmd(cmd, arg, 0xFF)
}
func (d Device) cmd(cmd byte, arg uint32, crc byte) byte {
d.cs.Low()
if cmd != 12 {
d.waitNotBusy(300)
d.waitNotBusy(300 * time.Millisecond)
}
// create and send the command
@@ -209,8 +245,9 @@ func (d Device) cmd(cmd byte, arg uint32, crc byte) byte {
return 0xFF // -1
}
func (d Device) waitNotBusy(timeoutMs int) error {
for i := 0; i < timeoutMs; i++ {
func (d Device) waitNotBusy(timeout time.Duration) error {
tm := setTimeout(1, timeout)
for !tm.expired() {
r, err := d.bus.Transfer(byte(0xFF))
if err != nil {
return err
@@ -218,8 +255,6 @@ func (d Device) waitNotBusy(timeoutMs int) error {
if r == 0xFF {
return nil
}
time.Sleep(1 * time.Millisecond)
}
return nil
}
@@ -227,7 +262,8 @@ func (d Device) waitNotBusy(timeoutMs int) error {
func (d Device) waitStartBlock() error {
status := byte(0xFF)
for i := 0; i < 3000; i++ {
tm := setTimeout(0, 300*time.Millisecond)
for !tm.expired() {
var err error
status, err = d.bus.Transfer(byte(0xFF))
if err != nil {
@@ -237,7 +273,6 @@ func (d Device) waitStartBlock() error {
if status != 0xFF {
break
}
time.Sleep(100 * time.Microsecond)
}
if status != 254 {
@@ -259,7 +294,7 @@ func (d Device) ReadCID(csd []byte) error {
}
func (d Device) readRegister(cmd uint8, dst []byte) error {
if d.cmd(cmd, 0, 0) != 0 {
if d.cmd(cmd, 0, 0xFF) != 0 {
return fmt.Errorf("SD_CARD_ERROR_READ_REG")
}
if err := d.waitStartBlock(); err != nil {
@@ -290,14 +325,14 @@ func (d Device) ReadData(block uint32, dst []byte) error {
if d.sdCardType != SD_CARD_TYPE_SDHC {
block <<= 9
}
if d.cmd(CMD17_READ_SINGLE_BLOCK, block, 0) != 0 {
if d.cmd(CMD17_READ_SINGLE_BLOCK, block, 0xFF) != 0 {
return fmt.Errorf("CMD17 error")
}
if err := d.waitStartBlock(); err != nil {
return fmt.Errorf("waitStartBlock()")
}
err := d.bus.Tx(nil, dst)
err := d.bus.Tx(dummy[:512], dst)
if err != nil {
return err
}
@@ -318,7 +353,7 @@ func (d Device) WriteMultiStart(block uint32) error {
if d.sdCardType != SD_CARD_TYPE_SDHC {
block <<= 9
}
if d.cmd(CMD25_WRITE_MULTIPLE_BLOCK, block, 0) != 0 {
if d.cmd(CMD25_WRITE_MULTIPLE_BLOCK, block, 0xFF) != 0 {
return fmt.Errorf("CMD25 error")
}
@@ -355,7 +390,7 @@ func (d Device) WriteMulti(buf []byte) error {
}
// wait no busy
err = d.waitNotBusy(600)
err = d.waitNotBusy(600 * time.Millisecond)
if err != nil {
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
}
@@ -373,7 +408,7 @@ func (d Device) WriteMultiStop() error {
// skip 1 byte
d.bus.Transfer(byte(0xFF))
err := d.waitNotBusy(600)
err := d.waitNotBusy(600 * time.Millisecond)
if err != nil {
return nil
}
@@ -391,7 +426,7 @@ func (d Device) WriteData(block uint32, src []byte) error {
if d.sdCardType != SD_CARD_TYPE_SDHC {
block <<= 9
}
if d.cmd(CMD24_WRITE_BLOCK, block, 0) != 0 {
if d.cmd(CMD24_WRITE_BLOCK, block, 0xFF) != 0 {
return fmt.Errorf("CMD24 error")
}
@@ -418,7 +453,7 @@ func (d Device) WriteData(block uint32, src []byte) error {
}
// wait no busy
err = d.waitNotBusy(600)
err = d.waitNotBusy(600 * time.Millisecond)
if err != nil {
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
}
+22
View File
@@ -0,0 +1,22 @@
package sdcard
import (
"time"
)
var timeoutTimer [2]timer
type timer struct {
start int64
timeout int64
}
func setTimeout(timerID int, timeout time.Duration) *timer {
timeoutTimer[timerID].start = time.Now().UnixNano()
timeoutTimer[timerID].timeout = timeout.Nanoseconds()
return &timeoutTimer[timerID]
}
func (t timer) expired() bool {
return time.Now().UnixNano() > (t.start + t.timeout)
}