mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-04 06:57:48 +00:00
fully comply
This commit is contained in:
+1
-255
@@ -1,9 +1,7 @@
|
||||
package sd
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
@@ -82,147 +80,7 @@ func (c *SPICard) LastReadCRC() uint16 { return c.lastCRC }
|
||||
// Init initializes the SD card. This routine should be performed with a SPI clock
|
||||
// speed of around 100..400kHz. One may increase the clock speed after initialization.
|
||||
func (d *SPICard) Init() error {
|
||||
dummy := d.buf[:512]
|
||||
for i := range dummy {
|
||||
dummy[i] = 0xFF
|
||||
}
|
||||
defer d.csEnable(false)
|
||||
|
||||
d.csEnable(true)
|
||||
// clock card at least 100 cycles with cs high
|
||||
d.bus.Tx(dummy[:10], nil)
|
||||
d.csEnable(false)
|
||||
|
||||
d.bus.Tx(dummy[:], nil)
|
||||
|
||||
// CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts)
|
||||
println("first timer")
|
||||
ok := false
|
||||
tm := d.timers[0].setTimeout(2 * time.Second)
|
||||
for !tm.expired() {
|
||||
// Wait up to 2 seconds to be the same as the Arduino
|
||||
result, err := d.cmd(cmdGoIdleState, 0, 0x95)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result == _R1_IDLE_STATE {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return errNoSDCard
|
||||
}
|
||||
|
||||
// CMD8: determine card version
|
||||
r1, err := d.cmd(cmdSendIfCond, 0x01AA, 0x87)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r1.IllegalCmdError() {
|
||||
d.kind = TypeSD1
|
||||
return errCardNotSupported
|
||||
}
|
||||
// r7 response
|
||||
status := byte(0)
|
||||
for i := 0; i < 3; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(0xFF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if (status & 0x0F) != 0x01 {
|
||||
return makeResponseError(response1(status))
|
||||
}
|
||||
|
||||
for i := 3; i < 4; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(0xFF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if status != 0xAA {
|
||||
return makeResponseError(response1(status))
|
||||
}
|
||||
d.kind = TypeSD2
|
||||
|
||||
// initialize card and send host supports SDHC if SD2
|
||||
arg := uint32(0)
|
||||
if d.kind == TypeSD2 {
|
||||
arg = 0x40000000
|
||||
}
|
||||
|
||||
// check for timeout
|
||||
println("app cmd")
|
||||
ok = false
|
||||
tm = tm.setTimeout(2 * time.Second)
|
||||
for !tm.expired() {
|
||||
println("timer")
|
||||
r1, err = d.appCmd(acmdSD_APP_OP_COND, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r1 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if r1 != 0 {
|
||||
return makeResponseError(r1)
|
||||
}
|
||||
println("preensure")
|
||||
// if SD2 read OCR register to check for SDHC card
|
||||
if d.kind == TypeSD2 {
|
||||
err := d.cmdEnsure0Status(cmdReadOCR, 0, 0xFF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
statusb, err := d.bus.Transfer(0xFF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (statusb & 0xC0) == 0xC0 {
|
||||
d.kind = TypeSDHC
|
||||
}
|
||||
// discard rest of ocr - contains allowed voltage range
|
||||
for i := 1; i < 4; i++ {
|
||||
d.bus.Transfer(0xFF)
|
||||
}
|
||||
}
|
||||
println("ensure")
|
||||
err = d.cmdEnsure0Status(cmdSetBlocklen, 0x0200, 0xff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println("get to update csdid")
|
||||
return d.updateCSDCID()
|
||||
}
|
||||
|
||||
func (d *SPICard) updateCSDCID() (err error) {
|
||||
// read CID
|
||||
d.cid, err = d.readCID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.csd, err = d.readCSD()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockshift := d.csd.ReadBlockLenShift()
|
||||
blocklen := uint16(1) << blockshift
|
||||
capacity := d.csd.DeviceCapacity()
|
||||
if blocklen == 0 || capacity < uint64(blocklen) {
|
||||
return errNoblocks
|
||||
}
|
||||
nb := capacity / uint64(blocklen)
|
||||
if nb > math.MaxUint32 {
|
||||
return errCardNotSupported
|
||||
}
|
||||
d.blockshift = blockshift
|
||||
d.numblocks = int64(nb)
|
||||
return nil
|
||||
return d.initRs()
|
||||
}
|
||||
|
||||
func (d *SPICard) NumberOfBlocks() int64 {
|
||||
@@ -235,118 +93,6 @@ func (d *SPICard) CID() CID { return d.cid }
|
||||
// CSD returns a copy of the Card Specific Data Register value last read.
|
||||
func (d *SPICard) CSD() CSD { return d.csd }
|
||||
|
||||
func (d *SPICard) readCID() (CID, error) {
|
||||
buf := d.buf[len(d.buf)-16:]
|
||||
if err := d.readRegister(cmdSendCID, buf); err != nil {
|
||||
return CID{}, err
|
||||
}
|
||||
return DecodeCID(buf)
|
||||
}
|
||||
|
||||
func (d *SPICard) readCSD() (CSD, error) {
|
||||
buf := d.buf[len(d.buf)-16:]
|
||||
if err := d.readRegister(cmdSendCSD, buf); err != nil {
|
||||
return CSD{}, err
|
||||
}
|
||||
return DecodeCSD(buf)
|
||||
}
|
||||
|
||||
func (d *SPICard) readRegister(cmd command, dst []byte) error {
|
||||
err := d.cmdEnsure0Status(cmd, 0, 0xFF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.waitStartBlock(); err != nil {
|
||||
return err
|
||||
}
|
||||
// transfer data
|
||||
for i := uint16(0); i < 16; i++ {
|
||||
r, err := d.bus.Transfer(0xFF)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst[i] = r
|
||||
}
|
||||
// skip CRC.
|
||||
d.bus.Transfer(0xFF)
|
||||
d.bus.Transfer(0xFF)
|
||||
d.csEnable(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SPICard) appCmd(cmd appcommand, arg uint32) (response1, error) {
|
||||
status, err := d.cmd(cmdAppCmd, 0, 0xFF)
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
return d.cmd(command(cmd), arg, 0xFF)
|
||||
}
|
||||
|
||||
func (d *SPICard) cmdEnsure0Status(cmd command, arg uint32, precalcCRC byte) error {
|
||||
status, err := d.cmd(cmd, arg, precalcCRC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status != 0 {
|
||||
return makeResponseError(status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SPICard) cmd(cmd command, arg uint32, precalcCRC byte) (response1, error) {
|
||||
const transmitterBit = 1 << 6
|
||||
if cmd >= transmitterBit {
|
||||
panic("invalid SD command")
|
||||
}
|
||||
d.csEnable(true)
|
||||
|
||||
if cmd != cmdStopTransmission {
|
||||
err := d.waitNotBusy(d.timeout)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// create and send the command
|
||||
buf := d.bufcmd[:6]
|
||||
// Start bit is always zero; transmitter bit is one since we are Host.
|
||||
|
||||
buf[0] = transmitterBit | byte(cmd)
|
||||
binary.BigEndian.PutUint32(buf[1:5], arg)
|
||||
if precalcCRC != 0 {
|
||||
buf[5] = precalcCRC
|
||||
} else {
|
||||
// CRC and end bit which is always 1.
|
||||
buf[5] = crc7noshift(buf[:5]) | 1
|
||||
}
|
||||
|
||||
err := d.bus.Tx(buf, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if cmd == cmdStopTransmission {
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(0xFF)
|
||||
}
|
||||
|
||||
tm := d.timers[1].setTimeout(d.timeout)
|
||||
for {
|
||||
tok, _ := d.bus.Transfer(0xff)
|
||||
response := response1(tok)
|
||||
if (response & 0x80) == 0 {
|
||||
// NOMINAL FUNCTION EXIT HERE
|
||||
return response, nil
|
||||
} else if tm.expired() {
|
||||
break
|
||||
}
|
||||
d.yield()
|
||||
}
|
||||
println("============== BAD EXIT ================")
|
||||
d.csEnable(false)
|
||||
d.bus.Transfer(0xFF)
|
||||
return 0xFF, errCmdGeneric
|
||||
}
|
||||
|
||||
func (d *SPICard) yield() { time.Sleep(d.wait) }
|
||||
|
||||
func (d *SPICard) waitNotBusy(timeout time.Duration) error {
|
||||
|
||||
+70
-10
@@ -20,18 +20,22 @@ func (d *SPICard) initRs() error {
|
||||
d.send(0xff)
|
||||
}
|
||||
d.csEnable(false)
|
||||
|
||||
for i := 0; i < 512; i++ {
|
||||
d.receive()
|
||||
}
|
||||
d.csEnable(true)
|
||||
defer d.csEnable(false)
|
||||
// Enter SPI mode
|
||||
const maxRetries = 32
|
||||
retries := maxRetries
|
||||
tm := d.timers[0].setTimeout(2 * time.Second)
|
||||
println("cmdGoIdle")
|
||||
for retries > 0 {
|
||||
stat, err := d.card_command(cmdGoIdleState, 0)
|
||||
stat, err := d.card_command(cmdGoIdleState, 0) // CMD0.
|
||||
if err != nil {
|
||||
if isTimeout(err) {
|
||||
retries--
|
||||
println("cmdGoIdle timeout")
|
||||
continue // Try again!
|
||||
}
|
||||
return err
|
||||
@@ -47,9 +51,10 @@ func (d *SPICard) initRs() error {
|
||||
if retries <= 0 {
|
||||
return errNoSDCard
|
||||
}
|
||||
const enableCRC = false
|
||||
println("cmdGoIdle done; start cmdCRCON/OFF")
|
||||
const enableCRC = true
|
||||
if enableCRC {
|
||||
stat, err := d.card_command(cmdCRCOnOff, 1)
|
||||
stat, err := d.card_command(cmdCRCOnOff, 1) // CMD59.
|
||||
if err != nil {
|
||||
return err
|
||||
} else if stat != _R1_IDLE_STATE {
|
||||
@@ -57,12 +62,13 @@ func (d *SPICard) initRs() error {
|
||||
}
|
||||
}
|
||||
|
||||
tm.setTimeout(d.timeout)
|
||||
println("cmdCRCON/OFF done; start cmdSendIfCond")
|
||||
tm.setTimeout(time.Second)
|
||||
for {
|
||||
stat, err := d.card_command(cmdSendIfCond, 0x1aa)
|
||||
stat, err := d.card_command(cmdSendIfCond, 0x1AA) // CMD8.
|
||||
if err != nil {
|
||||
return err
|
||||
} else if stat == _R1_IDLE_STATE || stat == _R1_ILLEGAL_COMMAND {
|
||||
} else if stat == (_R1_ILLEGAL_COMMAND | _R1_IDLE_STATE) {
|
||||
d.kind = TypeSD1
|
||||
break
|
||||
}
|
||||
@@ -84,7 +90,9 @@ func (d *SPICard) initRs() error {
|
||||
if d.kind != TypeSD1 {
|
||||
arg = 0x4000_0000
|
||||
}
|
||||
for {
|
||||
println("cmdSendIfCond done; start cmdAppCmd", arg)
|
||||
tm.setTimeout(time.Second)
|
||||
for !tm.expired() {
|
||||
stat, err := d.card_acmd(acmdSD_APP_OP_COND, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -93,7 +101,7 @@ func (d *SPICard) initRs() error {
|
||||
}
|
||||
d.yield()
|
||||
}
|
||||
|
||||
println("cmdAppCmd done; start cmdReadOCR")
|
||||
err := d.updateCSDCID()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -103,6 +111,7 @@ func (d *SPICard) initRs() error {
|
||||
return nil // Done if not SD2.
|
||||
}
|
||||
|
||||
println("discover high capacity")
|
||||
// Discover if card is high capacity.
|
||||
stat, err := d.card_command(cmdReadOCR, 0)
|
||||
if err != nil {
|
||||
@@ -123,6 +132,31 @@ func (d *SPICard) initRs() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *SPICard) updateCSDCID() (err error) {
|
||||
// read CID
|
||||
d.cid, err = d.read_cid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.csd, err = d.read_csd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockshift := d.csd.ReadBlockLenShift()
|
||||
blocklen := uint16(1) << blockshift
|
||||
capacity := d.csd.DeviceCapacity()
|
||||
if blocklen == 0 || capacity < uint64(blocklen) {
|
||||
return errNoblocks
|
||||
}
|
||||
nb := capacity / uint64(blocklen)
|
||||
if nb > math.MaxUint32 {
|
||||
return errCardNotSupported
|
||||
}
|
||||
d.blockshift = blockshift
|
||||
d.numblocks = int64(nb)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadBlock reads to a buffer multiple of 512 bytes from sdcard into dst starting at block `startBlockIdx`.
|
||||
func (d *SPICard) ReadBlocks(dst []byte, startBlockIdx int64) error {
|
||||
numblocks, err := d.checkBounds(startBlockIdx, len(dst))
|
||||
@@ -247,6 +281,32 @@ func (d *SPICard) checkBounds(startBlockIdx int64, datalen int) (numblocks int,
|
||||
return numblocks, nil
|
||||
}
|
||||
|
||||
func (d *SPICard) read_cid() (csd CID, err error) {
|
||||
err = d.cmd_read(cmdSendCID, 0, d.buf[:16]) // CMD10.
|
||||
if err != nil {
|
||||
return csd, err
|
||||
}
|
||||
return DecodeCID(d.buf[:16])
|
||||
}
|
||||
|
||||
func (d *SPICard) read_csd() (csd CSD, err error) {
|
||||
err = d.cmd_read(cmdSendCSD, 0, d.buf[:16]) // CMD9.
|
||||
if err != nil {
|
||||
return csd, err
|
||||
}
|
||||
return DecodeCSD(d.buf[:16])
|
||||
}
|
||||
|
||||
func (d *SPICard) cmd_read(cmd command, args uint32, buf []byte) error {
|
||||
status, err := d.card_command(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if status != 0 {
|
||||
return makeResponseError(response1(status))
|
||||
}
|
||||
return d.read_data(buf)
|
||||
}
|
||||
|
||||
func (d *SPICard) card_acmd(acmd appcommand, args uint32) (uint8, error) {
|
||||
_, err := d.card_command(cmdAppCmd, 0)
|
||||
if err != nil {
|
||||
@@ -312,7 +372,7 @@ func (d *SPICard) read_data(data []byte) (err error) {
|
||||
}
|
||||
|
||||
func (s *SPICard) wait_not_busy() error {
|
||||
tm := s.timers[0].setTimeout(s.timeout)
|
||||
tm := s.timers[1].setTimeout(s.timeout)
|
||||
for {
|
||||
tok, err := s.receive()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user