implement waitToken

This commit is contained in:
soypat
2024-01-14 17:38:20 -03:00
parent 222f368681
commit 66da4422dc
2 changed files with 48 additions and 46 deletions
+40 -46
View File
@@ -4,7 +4,6 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"math" "math"
"runtime"
"strconv" "strconv"
"time" "time"
@@ -19,12 +18,14 @@ var (
errCmdOCR = errors.New("sd:cmd_ocr") errCmdOCR = errors.New("sd:cmd_ocr")
errCmdBlkLen = errors.New("sd:cmd_blklen") errCmdBlkLen = errors.New("sd:cmd_blklen")
errAcmdAppCond = errors.New("sd:acmd_appOrCond") errAcmdAppCond = errors.New("sd:acmd_appOrCond")
errWaitStartBlock = errors.New("sd:wait start block") errWaitStartBlock = errors.New("sd:did not find start block token")
errNeed512 = errors.New("sd:need 512 bytes for I/O") errNeed512 = errors.New("sd:need 512 bytes for I/O")
errWrite = errors.New("sd:write") errWrite = errors.New("sd:write")
errWriteTimeout = errors.New("sd:write timeout") errWriteTimeout = errors.New("sd:write timeout")
errBusyTimeout = errors.New("sd:busy card timeout")
errOOB = errors.New("sd:oob block access") errOOB = errors.New("sd:oob block access")
errNoblocks = errors.New("sd:no readable blocks") errNoblocks = errors.New("sd:no readable blocks")
errCmdGeneric = errors.New("sd:command error")
) )
type digitalPinout func(b bool) type digitalPinout func(b bool)
@@ -41,10 +42,11 @@ type SPICard struct {
lastCRC uint16 lastCRC uint16
timers [2]timer timers [2]timer
numblocks int64 numblocks int64
timeout time.Duration
} }
func NewSPICard(spi drivers.SPI, cs digitalPinout) *SPICard { func NewSPICard(spi drivers.SPI, cs digitalPinout) *SPICard {
return &SPICard{bus: spi, cs: cs} return &SPICard{bus: spi, cs: cs, timeout: 300 * time.Millisecond}
} }
func (c *SPICard) csEnable(b bool) { c.cs(!b) } func (c *SPICard) csEnable(b bool) { c.cs(!b) }
@@ -200,7 +202,7 @@ func (d *SPICard) ReadBlock(block int64, dst []byte) error {
block <<= 9 block <<= 9
} }
err := d.cmdEnsure0Status(cmdReadSingleBlock, uint32(block), 0) err := d.cmdEnsure0Status(cmdReadSingleBlock, uint32(block), 0xff)
if err != nil { if err != nil {
return err return err
} }
@@ -261,8 +263,7 @@ func (d *SPICard) WriteBlock(block int64, src []byte) error {
return errWrite return errWrite
} }
// wait no busy err = d.waitNotBusy(2 * d.timeout)
err = d.waitNotBusy(600 * time.Millisecond)
if err != nil { if err != nil {
return errWriteTimeout return errWriteTimeout
} }
@@ -335,19 +336,23 @@ func (d *SPICard) cmdEnsure0Status(cmd command, arg uint32, crc byte) error {
} }
func (d *SPICard) cmd(cmd command, arg uint32, precalculatedCRC byte) (response1, error) { func (d *SPICard) cmd(cmd command, arg uint32, precalculatedCRC byte) (response1, error) {
if cmd >= 1<<6 { const transmitterBit = 1 << 6
if cmd >= transmitterBit {
panic("invalid SD command") panic("invalid SD command")
} }
d.csEnable(true) d.csEnable(true)
if cmd != 12 { if cmd != cmdStopTransmission {
d.waitNotBusy(300 * time.Millisecond) err := d.waitNotBusy(d.timeout)
if err != nil {
return 0, err
}
} }
// create and send the command // create and send the command
buf := d.bufcmd[:6] buf := d.bufcmd[:6]
// Start bit is always zero; transmitter bit is one since we are Host. // Start bit is always zero; transmitter bit is one since we are Host.
const transmitterBit = 1 << 6
buf[0] = transmitterBit | byte(cmd) buf[0] = transmitterBit | byte(cmd)
binary.BigEndian.PutUint32(buf[1:5], arg) binary.BigEndian.PutUint32(buf[1:5], arg)
if precalculatedCRC != 0 { if precalculatedCRC != 0 {
@@ -366,61 +371,50 @@ func (d *SPICard) cmd(cmd command, arg uint32, precalculatedCRC byte) (response1
d.bus.Transfer(0xFF) d.bus.Transfer(0xFF)
} }
// wait for the response (response[7] == 0)
buf[0] = 0xFF
for i := 0; i < 0xFFFF; i++ { for i := 0; i < 0xFFFF; i++ {
d.bus.Tx(buf[:1], d.bufTok[:]) tok, _ := d.bus.Transfer(0xff)
response := response1(d.bufTok[0]) response := response1(tok)
if (response & 0x80) == 0 { if (response & 0x80) == 0 {
return response, nil return response, nil
} }
} }
// TODO
//// timeout
d.csEnable(false) d.csEnable(false)
d.bus.Transfer(0xFF) d.bus.Transfer(0xFF)
return 0xFF, errCmdGeneric
return 0xFF, nil // -1
} }
func (d *SPICard) waitNotBusy(timeout time.Duration) error { func (d *SPICard) waitNotBusy(timeout time.Duration) error {
tm := d.timers[1].setTimeout(timeout) if d.waitToken(timeout, 0xff) {
for !tm.expired() { return nil
r, err := d.bus.Transfer(0xFF)
if err != nil {
return err
}
if r == 0xFF {
return nil
}
runtime.Gosched()
} }
return nil return errBusyTimeout
} }
func (d *SPICard) waitStartBlock() error { func (d *SPICard) waitStartBlock() error {
status := byte(0xFF) if d.waitToken(d.timeout, tokSTART_BLOCK) {
tm := d.timers[0].setTimeout(300 * time.Millisecond) return nil
for !tm.expired() { }
var err error d.csEnable(false)
status, err = d.bus.Transfer(0xFF) return errWaitStartBlock
}
// waitToken transmits over SPI waiting to read a given byte token. If argument tok
// is 0xff then waitToken will wait for a token that does NOT match 0xff.
func (d *SPICard) waitToken(timeout time.Duration, tok byte) bool {
tm := d.timers[1].setTimeout(timeout)
for {
received, err := d.bus.Transfer(0xFF)
if err != nil { if err != nil {
d.csEnable(false) return false
return err
} }
if status != 0xFF { matchTok := received == tok
break if matchTok || (!matchTok && tok == 0xff) {
return true
} else if tm.expired() {
return false
} }
runtime.Gosched()
} }
if status != 254 {
d.csEnable(false)
return errWaitStartBlock
}
return nil
} }
type response1Err struct { type response1Err struct {
+8
View File
@@ -468,6 +468,14 @@ const (
_R1_PARAMETER_ERROR = 1 << 6 _R1_PARAMETER_ERROR = 1 << 6
) )
// Tokens that are sent by card during polling.
// https://github.com/arduino-libraries/SD/blob/master/src/utility/SdInfo.h
const (
tokSTART_BLOCK = 0xfe
tokSTOP_TRAN = 0xfd
tokWRITE_MULT = 0xfc
)
type response1 uint8 type response1 uint8
func (r response1) IsIdle() bool { return r&_R1_IDLE_STATE != 0 } func (r response1) IsIdle() bool { return r&_R1_IDLE_STATE != 0 }