mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-22 07:29:01 +00:00
implement waitToken
This commit is contained in:
+39
-45
@@ -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() {
|
|
||||||
r, err := d.bus.Transfer(0xFF)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if r == 0xFF {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
runtime.Gosched()
|
return errBusyTimeout
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
status, err = d.bus.Transfer(0xFF)
|
|
||||||
if err != nil {
|
|
||||||
d.csEnable(false)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
if status != 0xFF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
runtime.Gosched()
|
|
||||||
}
|
|
||||||
|
|
||||||
if status != 254 {
|
|
||||||
d.csEnable(false)
|
d.csEnable(false)
|
||||||
return errWaitStartBlock
|
return errWaitStartBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
// 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 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
matchTok := received == tok
|
||||||
|
if matchTok || (!matchTok && tok == 0xff) {
|
||||||
|
return true
|
||||||
|
} else if tm.expired() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type response1Err struct {
|
type response1Err struct {
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
Reference in New Issue
Block a user