From d5db138d9aa6a4f25533b729a73635ce5074545e Mon Sep 17 00:00:00 2001 From: soypat Date: Sun, 14 Jan 2024 20:08:57 -0300 Subject: [PATCH] no crc errors in status; closing the gap? --- examples/sd/main.go | 3 +- sd/card.go | 80 +++-------- sd/definitions.go | 130 ----------------- sd/responses.go | 331 ++++++++++++++++++++++++++++++++++++++++++++ sd/status_string.go | 79 ----------- 5 files changed, 348 insertions(+), 275 deletions(-) create mode 100644 sd/responses.go delete mode 100644 sd/status_string.go diff --git a/examples/sd/main.go b/examples/sd/main.go index a314ac1..55a01d1 100644 --- a/examples/sd/main.go +++ b/examples/sd/main.go @@ -36,7 +36,7 @@ func main() { err = sdcard.Init() if err != nil { - panic(err.Error()) + panic("sd card init:" + err.Error()) } cid := sdcard.CID() pname := cid.ProductName() @@ -61,7 +61,6 @@ func main() { var buf [512]byte for i := 0; i < 11; i += 1 { - time.Sleep(100 * time.Millisecond) err = sdcard.ReadBlock(0, buf[:]) if err != nil { println("err reading block", i, ":", err.Error()) diff --git a/sd/card.go b/sd/card.go index 494d1ab..b0ff98f 100644 --- a/sd/card.go +++ b/sd/card.go @@ -4,7 +4,7 @@ import ( "encoding/binary" "errors" "math" - "strconv" + "runtime" "time" "tinygo.org/x/drivers" @@ -44,7 +44,8 @@ type SPICard struct { numblocks int64 timeout time.Duration // relative card address. - rca uint32 + rca uint32 + lastr1 r1 } func NewSPICard(spi drivers.SPI, cs digitalPinout) *SPICard { @@ -55,6 +56,7 @@ func (c *SPICard) csEnable(b bool) { c.cs(!b) } // LastReadCRC returns the CRC for the last ReadBlock operation. func (c *SPICard) LastReadCRC() uint16 { return c.lastCRC } +func (c *SPICard) LastR1() r1 { return c.lastr1 } func (d *SPICard) Init() error { dummy := d.buf[:] @@ -184,7 +186,7 @@ func (d *SPICard) Init() error { return errNoblocks } d.numblocks = int64(nb) - + return nil err = d.readRegister(cmdSendRelativeAddr, d.buf[:4]) if err != nil { return err @@ -386,12 +388,16 @@ func (d *SPICard) cmd(cmd command, arg uint32, precalculatedCRC byte) (response1 d.bus.Transfer(0xFF) } - for i := 0; i < 0xFFFF; i++ { + tm := d.timers[0].setTimeout(d.timeout) + for { tok, _ := d.bus.Transfer(0xff) response := response1(tok) if (response & 0x80) == 0 { return response, nil + } else if tm.expired() { + break } + runtime.Gosched() } d.csEnable(false) @@ -400,14 +406,14 @@ func (d *SPICard) cmd(cmd command, arg uint32, precalculatedCRC byte) (response1 } func (d *SPICard) waitNotBusy(timeout time.Duration) error { - if d.waitToken(timeout, 0xff) { + if _, ok := d.waitToken(timeout, 0xff); ok { return nil } return errBusyTimeout } func (d *SPICard) waitStartBlock() error { - if d.waitToken(d.timeout, tokSTART_BLOCK) { + if _, ok := d.waitToken(d.timeout, tokSTART_BLOCK); ok { return nil } d.csEnable(false) @@ -416,76 +422,22 @@ func (d *SPICard) waitStartBlock() error { // 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 { +func (d *SPICard) waitToken(timeout time.Duration, tok byte) (byte, bool) { tm := d.timers[1].setTimeout(timeout) for { received, err := d.bus.Transfer(0xFF) if err != nil { - return false + return received, false } matchTok := received == tok if matchTok || (!matchTok && tok == 0xff) { - return true + return received, true } else if tm.expired() { - return false + return received, false } } } -type response1Err struct { - context string - status response1 -} - -func (e response1Err) Error() string { - return e.status.Response() - if e.context != "" { - return "sd:" + e.context + " " + strconv.Itoa(int(e.status)) - } - return "sd:status " + strconv.Itoa(int(e.status)) -} - -func (e response1) Response() string { - b := make([]byte, 0, 8) - return string(e.appendf(b)) -} - -func (r response1) appendf(b []byte) []byte { - b = append(b, '[') - if r.IsIdle() { - b = append(b, "idle,"...) - } - if r.EraseReset() { - b = append(b, "erase-rst,"...) - } - if r.EraseSeqError() { - b = append(b, "erase-seq,"...) - } - if r.CRCError() { - b = append(b, "crc-err,"...) - } - if r.AddressError() { - b = append(b, "addr-err,"...) - } - if r.ParamError() { - b = append(b, "param-err,"...) - } - if r.IllegalCmdError() { - b = append(b, "illegal-cmd,"...) - } - if len(b) > 1 { - b = b[:len(b)-1] - } - b = append(b, ']') - return b -} - -func makeResponseError(status response1) error { - return response1Err{ - status: status, - } -} - var timeoutTimer [2]timer type timer struct { diff --git a/sd/definitions.go b/sd/definitions.go index 4ff3381..4ae58f7 100644 --- a/sd/definitions.go +++ b/sd/definitions.go @@ -456,136 +456,6 @@ func (t TAAC) AccessTime() (d time.Duration) { return time.Duration(log10table[t&0b111]) * time.Nanosecond } -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 -) - -// 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 status uint32 - -// First status bits. -// -//go:generate stringer -type=status -trimprefix=status -output=status_string.go -const ( - statusRsvd0 status = iota - statusRsvd1 - statusRsvd2 - statusAuthSeqError - statusRsvdSDIO - statusAppCmd - statusFXEvent - statusRsvd7 - statusReadyForData -) - -// Upper bound status bits. -const ( - statusEraseReset status = iota + 13 - statusECCDisabled - statusWPEraseSkip - statusCSDOverwrite - _ - _ - statusGenericError - statusControllerError // internal card controller error - statusECCFailed - statusIllegalCommand - statusComCRCError // CRC check of previous command failed - statusLockUnlockFailed - statusCardIsLocked // Signals that the card is locked by the host. - statusWPViolation // Write protected violation - statusEraseParamError // invalid write block selection for erase - statusEraseSeqError // error in erase sequence - statusBlockLenError // tx block length not allowed - statusAddrError // misaligned address - statusAddrOutOfRange // address out of range -) - -// r1 is the normal response to a command. -type r1 struct { - data [48 / 8]byte // 48 bits of response. -} - -func (r *r1) RawCopy() [6]byte { return r.data } -func (r *r1) startbit() bool { - return r.data[0]&(1<<7) != 0 -} -func (r *r1) txbit() bool { - return r.data[0]&(1<<6) != 0 -} -func (r *r1) cmdidx() uint8 { - return r.data[0] & 0b11_1111 -} -func (r *r1) cardstatus() status { - return status(binary.BigEndian.Uint32(r.data[1:5])) -} -func (r *r1) CRC7() uint8 { return r.data[5] >> 1 } -func (r *r1) endbit() bool { return r.data[5]&1 != 0 } - -func (r *r1) IsValid() bool { - return r.endbit() && CRC7(r.data[:5]) == r.CRC7() -} - -type r6 struct { - data [48 / 8]byte -} - -func (r *r6) RawCopy() [6]byte { return r.data } -func (r *r6) startbit() bool { - return r.data[0]&(1<<7) != 0 -} -func (r *r6) txbit() bool { - return r.data[0]&(1<<6) != 0 -} -func (r *r6) cmdidx() uint8 { - return r.data[0] & 0b11_1111 -} -func (r *r6) rca() uint16 { - return binary.BigEndian.Uint16(r.data[1:3]) -} -func (r *r6) cardstatus() status { - moveBit := func(b status, from, to uint) status { - return (b & (1 << from)) >> from << to - } - // See 4.9.5 R6 (Published RCA response) of the SD Simplified Specification. - s := status(binary.BigEndian.Uint16(r.data[1:5])) - s = moveBit(s, 13, 19) - s = moveBit(s, 14, 22) - s = moveBit(s, 15, 23) - return s -} -func (r *r6) CRC7() uint8 { return r.data[5] >> 1 } -func (r *r6) endbit() bool { return r.data[5]&1 != 0 } - -func (r *r6) IsValid() bool { - return r.endbit() && CRC7(r.data[:5]) == r.CRC7() -} - -type response1 uint8 - -func (r response1) IsIdle() bool { return r&_R1_IDLE_STATE != 0 } -func (r response1) IllegalCmdError() bool { return r&_R1_ILLEGAL_COMMAND != 0 } -func (r response1) CRCError() bool { return r&_R1_COM_CRC_ERROR != 0 } -func (r response1) EraseReset() bool { return r&_R1_ERASE_RESET != 0 } -func (r response1) EraseSeqError() bool { return r&_R1_ERASE_SEQUENCE_ERROR != 0 } -func (r response1) AddressError() bool { return r&_R1_ADDRESS_ERROR != 0 } -func (r response1) ParamError() bool { return r&_R1_PARAMETER_ERROR != 0 } - func b2u8(b bool) uint8 { if b { return 1 diff --git a/sd/responses.go b/sd/responses.go new file mode 100644 index 0000000..b1f4bc8 --- /dev/null +++ b/sd/responses.go @@ -0,0 +1,331 @@ +package sd + +import ( + "encoding/binary" + "strconv" +) + +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 +) + +type response1 uint8 + +func (r response1) IsIdle() bool { return r&_R1_IDLE_STATE != 0 } +func (r response1) IllegalCmdError() bool { return r&_R1_ILLEGAL_COMMAND != 0 } +func (r response1) CRCError() bool { return r&_R1_COM_CRC_ERROR != 0 } +func (r response1) EraseReset() bool { return r&_R1_ERASE_RESET != 0 } +func (r response1) EraseSeqError() bool { return r&_R1_ERASE_SEQUENCE_ERROR != 0 } +func (r response1) AddressError() bool { return r&_R1_ADDRESS_ERROR != 0 } +func (r response1) ParamError() bool { return r&_R1_PARAMETER_ERROR != 0 } + +type response1Err struct { + context string + status response1 +} + +func (e response1Err) Error() string { + return e.status.Response() + if e.context != "" { + return "sd:" + e.context + " " + strconv.Itoa(int(e.status)) + } + return "sd:status " + strconv.Itoa(int(e.status)) +} + +func (e response1) Response() string { + b := make([]byte, 0, 8) + return string(e.appendf(b)) +} + +func (r response1) appendf(b []byte) []byte { + b = append(b, '[') + if r.IsIdle() { + b = append(b, "idle,"...) + } + if r.EraseReset() { + b = append(b, "erase-rst,"...) + } + if r.EraseSeqError() { + b = append(b, "erase-seq,"...) + } + if r.CRCError() { + b = append(b, "crc-err,"...) + } + if r.AddressError() { + b = append(b, "addr-err,"...) + } + if r.ParamError() { + b = append(b, "param-err,"...) + } + if r.IllegalCmdError() { + b = append(b, "illegal-cmd,"...) + } + if len(b) > 1 { + b = b[:len(b)-1] + } + b = append(b, ']') + return b +} + +func makeResponseError(status response1) error { + return response1Err{ + status: status, + } +} + +// is part of specification but not used in every implementation out there... +func (d *SPICard) readR1() (resp r1, err error) { + first, ok := d.waitToken(d.timeout, 0xff) + if !ok { + return resp, errBusyTimeout + } + err = d.bus.Tx(nil, d.buf[1:6]) + if err != nil { + return resp, err + } + d.buf[0] = first + copy(resp.data[:], d.buf[:6]) + return resp, nil +} + +// Commands used to help generate this file: +// - stringer -type=state -trimprefix=state -output=state_string.go +// - stringer -type=status -trimprefix=status -output=status_string.go + +// 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 state uint8 + +const ( + stateIdle state = iota + stateReady + stateIdent + stateStby + stateTran + stateData + stateRcv + statePrg + stateDis +) + +// status represents the Card Status Register (R1), as per section 4.10.1. +type status uint32 + +func (s status) state() state { + return state(s >> 9 & 0xf) +} + +// First status bits. +const ( + statusRsvd0 status = iota + statusRsvd1 + statusRsvd2 + statusAuthSeqError + statusRsvdSDIO + statusAppCmd + statusFXEvent + statusRsvd7 + statusReadyForData +) + +// Upper bound status bits. +const ( + statusEraseReset status = iota + 13 + statusECCDisabled + statusWPEraseSkip + statusCSDOverwrite + _ + _ + statusGenericError + statusControllerError // internal card controller error + statusECCFailed + statusIllegalCommand + statusComCRCError // CRC check of previous command failed + statusLockUnlockFailed + statusCardIsLocked // Signals that the card is locked by the host. + statusWPViolation // Write protected violation + statusEraseParamError // invalid write block selection for erase + statusEraseSeqError // error in erase sequence + statusBlockLenError // tx block length not allowed + statusAddrError // misaligned address + statusAddrOutOfRange // address out of range +) + +// r1 is the normal response to a command. +type r1 struct { + data [48 / 8]byte // 48 bits of response. +} + +func (r *r1) RawCopy() [6]byte { return r.data } +func (r *r1) startbit() bool { + return r.data[0]&(1<<7) != 0 +} +func (r *r1) txbit() bool { + return r.data[0]&(1<<6) != 0 +} +func (r *r1) cmdidx() uint8 { + return r.data[0] & 0b11_1111 +} +func (r *r1) cardstatus() status { + return status(binary.BigEndian.Uint32(r.data[1:5])) +} +func (r *r1) CRC7() uint8 { return r.data[5] >> 1 } +func (r *r1) endbit() bool { return r.data[5]&1 != 0 } + +func (r *r1) IsValid() bool { + return r.endbit() && CRC7(r.data[:5]) == r.CRC7() +} + +type r6 struct { + data [48 / 8]byte +} + +func (r *r6) RawCopy() [6]byte { return r.data } +func (r *r6) startbit() bool { + return r.data[0]&(1<<7) != 0 +} +func (r *r6) txbit() bool { + return r.data[0]&(1<<6) != 0 +} +func (r *r6) cmdidx() uint8 { + return r.data[0] & 0b11_1111 +} +func (r *r6) rca() uint16 { + return binary.BigEndian.Uint16(r.data[1:3]) +} +func (r *r6) CardStatus() status { + moveBit := func(b status, from, to uint) status { + return (b & (1 << from)) >> from << to + } + // See 4.9.5 R6 (Published RCA response) of the SD Simplified Specification. + s := status(binary.BigEndian.Uint16(r.data[1:5])) + s = moveBit(s, 13, 19) + s = moveBit(s, 14, 22) + s = moveBit(s, 15, 23) + return s +} +func (r *r6) CRC7() uint8 { return r.data[5] >> 1 } +func (r *r6) endbit() bool { return r.data[5]&1 != 0 } + +func (r *r6) IsValid() bool { + return r.endbit() && CRC7(r.data[:5]) == r.CRC7() +} + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[statusRsvd0-0] + _ = x[statusRsvd1-1] + _ = x[statusRsvd2-2] + _ = x[statusAuthSeqError-3] + _ = x[statusRsvdSDIO-4] + _ = x[statusAppCmd-5] + _ = x[statusFXEvent-6] + _ = x[statusRsvd7-7] + _ = x[statusReadyForData-8] + _ = x[statusEraseReset-13] + _ = x[statusECCDisabled-14] + _ = x[statusWPEraseSkip-15] + _ = x[statusCSDOverwrite-16] + _ = x[statusGenericError-19] + _ = x[statusControllerError-20] + _ = x[statusECCFailed-21] + _ = x[statusIllegalCommand-22] + _ = x[statusComCRCError-23] + _ = x[statusLockUnlockFailed-24] + _ = x[statusCardIsLocked-25] + _ = x[statusWPViolation-26] + _ = x[statusEraseParamError-27] + _ = x[statusEraseSeqError-28] + _ = x[statusBlockLenError-29] + _ = x[statusAddrError-30] + _ = x[statusAddrOutOfRange-31] +} + +const ( + _status_name_0 = "Rsvd0Rsvd1Rsvd2AuthSeqErrorRsvdSDIOAppCmdFXEventRsvd7ReadyForData" + _status_name_1 = "EraseResetECCDisabledWPEraseSkipCSDOverwrite" + _status_name_2 = "GenericErrorControllerErrorECCFailedIllegalCommandComCRCErrorLockUnlockFailedCardIsLockedWPViolationEraseParamErrorEraseSeqErrorBlockLenErrorAddrErrorAddrOutOfRange" +) + +var ( + _status_index_0 = [...]uint8{0, 5, 10, 15, 27, 35, 41, 48, 53, 65} + _status_index_1 = [...]uint8{0, 10, 21, 32, 44} + _status_index_2 = [...]uint8{0, 12, 27, 36, 50, 61, 77, 89, 100, 115, 128, 141, 150, 164} +) + +func (i status) string() string { + switch { + case i <= 8: + return _status_name_0[_status_index_0[i]:_status_index_0[i+1]] + case 13 <= i && i <= 16: + i -= 13 + return _status_name_1[_status_index_1[i]:_status_index_1[i+1]] + case 19 <= i && i <= 31: + i -= 19 + return _status_name_2[_status_index_2[i]:_status_index_2[i+1]] + default: + return "" + } +} + +func (s status) String() string { + return string(s.appendf(nil, ',')) +} + +func (s status) appendf(b []byte, delim byte) []byte { + b = append(b, s.state().String()...) + b = append(b, '[') + if s == 0 { + return append(b, ']') + } + for bit := 0; bit < 32; bit++ { + if s&(1<= state(len(_state_index)-1) { + return "" + } + return _state_name[_state_index[i]:_state_index[i+1]] +} diff --git a/sd/status_string.go b/sd/status_string.go deleted file mode 100644 index 82b77de..0000000 --- a/sd/status_string.go +++ /dev/null @@ -1,79 +0,0 @@ -package sd - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[statusRsvd0-0] - _ = x[statusRsvd1-1] - _ = x[statusRsvd2-2] - _ = x[statusAuthSeqError-3] - _ = x[statusRsvdSDIO-4] - _ = x[statusAppCmd-5] - _ = x[statusFXEvent-6] - _ = x[statusRsvd7-7] - _ = x[statusReadyForData-8] - _ = x[statusEraseReset-13] - _ = x[statusECCDisabled-14] - _ = x[statusWPEraseSkip-15] - _ = x[statusCSDOverwrite-16] - _ = x[statusGenericError-19] - _ = x[statusControllerError-20] - _ = x[statusECCFailed-21] - _ = x[statusIllegalCommand-22] - _ = x[statusComCRCError-23] - _ = x[statusLockUnlockFailed-24] - _ = x[statusCardIsLocked-25] - _ = x[statusWPViolation-26] - _ = x[statusEraseParamError-27] - _ = x[statusEraseSeqError-28] - _ = x[statusBlockLenError-29] - _ = x[statusAddrError-30] - _ = x[statusAddrOutOfRange-31] -} - -const ( - _status_name_0 = "Rsvd0Rsvd1Rsvd2AuthSeqErrorRsvdSDIOAppCmdFXEventRsvd7ReadyForData" - _status_name_1 = "EraseResetECCDisabledWPEraseSkipCSDOverwrite" - _status_name_2 = "GenericErrorControllerErrorECCFailedIllegalCommandComCRCErrorLockUnlockFailedCardIsLockedWPViolationEraseParamErrorEraseSeqErrorBlockLenErrorAddrErrorAddrOutOfRange" -) - -var ( - _status_index_0 = [...]uint8{0, 5, 10, 15, 27, 35, 41, 48, 53, 65} - _status_index_1 = [...]uint8{0, 10, 21, 32, 44} - _status_index_2 = [...]uint8{0, 12, 27, 36, 50, 61, 77, 89, 100, 115, 128, 141, 150, 164} -) - -func (i status) string() string { - switch { - case i <= 8: - return _status_name_0[_status_index_0[i]:_status_index_0[i+1]] - case 13 <= i && i <= 16: - i -= 13 - return _status_name_1[_status_index_1[i]:_status_index_1[i+1]] - case 19 <= i && i <= 31: - i -= 19 - return _status_name_2[_status_index_2[i]:_status_index_2[i+1]] - default: - return "" - } -} - -func (s status) String() string { - return string(s.appendf(nil, ',')) -} - -func (s status) appendf(b []byte, delim byte) []byte { - b = append(b, '[') - if s == 0 { - return append(b, ']') - } - for bit := 0; bit < 32; bit++ { - if s&(1<