still working on consolidation of init

This commit is contained in:
soypat
2024-01-15 01:04:39 -03:00
parent 845bd6fe93
commit 7db9e9d6db
5 changed files with 442 additions and 157 deletions
+3 -20
View File
@@ -34,7 +34,7 @@ func main() {
panic(err.Error())
}
sdcard := sd.NewSPICard(spibus, SPI_CS_PIN.Set)
println("start init")
err = sdcard.Init()
if err != nil {
panic("sd card init:" + err.Error())
@@ -46,28 +46,11 @@ func main() {
err = spibus.Configure(spicfg)
cid := sdcard.CID()
pname := cid.ProductName()
if !cid.IsValid() {
copy := cid.RawCopy()
println("CID not valid: theirCRC=", cid.CRC7(), "ourCRC=", sd.CRC7(copy[:15]))
}
valid := csd.IsValid()
if !valid {
data := csd.RawCopy()
crc := sd.CRC7(data[:15])
always1 := data[15]&(1<<7) != 0
fmt.Printf("ourCRC7=%#b theirCRC7=%#b for data %d\n", crc, csd.CRC7(), data[:15])
println("CSD not valid got", crc, "want", csd.CRC7(), "always1:", always1)
return
} else {
println("CSD valid!")
}
fmt.Printf("name=%s\ncsd=\n%s\n", pname, csd.String())
fmt.Printf("name=%s\ncsd=\n%s\n", cid.ProductName(), csd.String())
var buf [512]byte
for i := 0; i < 11; i += 1 {
err = sdcard.ReadBlock(0, buf[:])
err = sdcard.ReadBlocks(buf[:], 0)
if err != nil {
println("err reading block", i, ":", err.Error())
continue
+60 -136
View File
@@ -9,40 +9,45 @@ import (
"tinygo.org/x/drivers"
)
// See rustref.go for the new implementation.
var (
errBadCSDCID = errors.New("sd:bad CSD/CID in CRC or always1")
errNoSDCard = errors.New("sd:no card")
errCardNotSupported = errors.New("sd:card not supported")
errCmd8 = errors.New("sd:cmd8")
errCmdOCR = errors.New("sd:cmd_ocr")
errCmdBlkLen = errors.New("sd:cmd_blklen")
errAcmdAppCond = errors.New("sd:acmd_appOrCond")
errWaitStartBlock = errors.New("sd:did not find start block token")
errNeed512 = errors.New("sd:need 512 bytes for I/O")
errWrite = errors.New("sd:write")
errWriteTimeout = errors.New("sd:write timeout")
errBusyTimeout = errors.New("sd:busy card timeout")
errOOB = errors.New("sd:oob block access")
errNoblocks = errors.New("sd:no readable blocks")
errCmdGeneric = errors.New("sd:command error")
errBadCSDCID = errors.New("sd:bad CSD/CID in CRC or always1")
errNoSDCard = errors.New("sd:no card")
errCardNotSupported = errors.New("sd:card not supported")
errCmd8 = errors.New("sd:cmd8")
errCmdOCR = errors.New("sd:cmd_ocr")
errCmdBlkLen = errors.New("sd:cmd_blklen")
errAcmdAppCond = errors.New("sd:acmd_appOrCond")
errWaitStartBlock = errors.New("sd:did not find start block token")
errNeedBlockLenMultiple = errors.New("sd:need blocksize multiple for I/O")
errWrite = errors.New("sd:write")
errWriteTimeout = errors.New("sd:write timeout")
errReadTimeout = errors.New("sd:read timeout")
errBusyTimeout = errors.New("sd:busy card timeout")
errOOB = errors.New("sd:oob block access")
errNoblocks = errors.New("sd:no readable blocks")
errCmdGeneric = errors.New("sd:command error")
)
type digitalPinout func(b bool)
type SPICard struct {
bus drivers.SPI
cs digitalPinout
bufcmd [6]byte
buf [512]byte
bufTok [1]byte
kind CardKind
cid CID
csd CSD
lastCRC uint16
timers [2]timer
numblocks int64
timeout time.Duration
wait time.Duration
bus drivers.SPI
cs digitalPinout
bufcmd [6]byte
buf [512]byte
bufTok [1]byte
kind CardKind
cid CID
csd CSD
lastCRC uint16
// shift to calculate blocksize, taken from CSD.
blockshift uint8
timers [2]timer
numblocks int64
timeout time.Duration
wait time.Duration
// relative card address.
rca uint32
lastr1 r1
@@ -67,11 +72,12 @@ func (c *SPICard) setTimeout(timeout time.Duration) {
c.wait = timeout / 512
}
func (c *SPICard) csEnable(b bool) { c.cs(!b) }
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 }
// 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.
@@ -90,6 +96,7 @@ func (d *SPICard) Init() error {
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() {
@@ -148,9 +155,11 @@ func (d *SPICard) Init() error {
}
// 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
@@ -162,7 +171,7 @@ func (d *SPICard) Init() error {
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)
@@ -182,11 +191,16 @@ func (d *SPICard) Init() error {
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 {
@@ -196,113 +210,23 @@ func (d *SPICard) Init() error {
if err != nil {
return err
}
nb := d.csd.NumberOfBlocks()
if nb > math.MaxUint32 {
return errCardNotSupported
} else if nb == 0 {
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
err = d.readRegister(cmdSendRelativeAddr, d.buf[:4])
if err != nil {
return err
}
d.rca = binary.BigEndian.Uint32(d.buf[:4])
return nil
}
func (d *SPICard) NumberOfBlocks() uint64 {
return uint64(d.numblocks)
}
// ReadBlock reads 512 bytes from sdcard into dst.
func (d *SPICard) ReadBlock(block int64, dst []byte) error {
if len(dst) != 512 {
return errNeed512
} else if block >= d.numblocks {
return errOOB
}
// use address if not SDHC card
if d.kind != TypeSDHC {
block <<= 9
}
err := d.cmdEnsure0Status(cmdReadSingleBlock, uint32(block), 0xff)
if err != nil {
return err
}
defer d.csEnable(false)
if err := d.waitStartBlock(); err != nil {
return err
}
buf := d.buf[:]
err = d.bus.Tx(buf, dst)
if err != nil {
return err
}
// skip CRC (2byte)
hi, _ := d.bus.Transfer(0xFF)
lo, _ := d.bus.Transfer(0xFF)
d.lastCRC = uint16(hi)<<8 | uint16(lo)
return nil
}
// WriteBlock writes 512 bytes from dst to sdcard.
func (d *SPICard) WriteBlock(block int64, src []byte) error {
if len(src) != 512 {
return errNeed512
} else if block >= d.numblocks {
return errOOB
}
// use address if not SDHC card
if d.kind != TypeSDHC {
block <<= 9
}
err := d.cmdEnsure0Status(cmdWriteBlock, uint32(block), 0xFF)
if err != nil {
return err
}
defer d.csEnable(false)
// wait 1 byte?
token := byte(0xFE)
d.bus.Transfer(token)
err = d.bus.Tx(src[:512], nil)
if err != nil {
return err
}
// send dummy CRC (2 byte)
d.bus.Transfer(0xFF)
d.bus.Transfer(0xFF)
// Data Resp.
r, err := d.bus.Transfer(0xFF)
if err != nil {
return err
}
if (r & 0x1F) != 0x05 {
return errWrite
}
err = d.waitNotBusy(2 * d.timeout)
if err != nil {
return errWriteTimeout
}
return nil
}
func (d *SPICard) ReadStatus() (response1, error) {
if err := d.readRegister(cmdSendStatus, d.buf[:4]); err != nil {
return 0, err
}
return response1(binary.BigEndian.Uint32(d.buf[:4])), nil
func (d *SPICard) NumberOfBlocks() int64 {
return d.numblocks
}
// CID returns a copy of the Card Identification Register value last read.
@@ -400,23 +324,24 @@ func (d *SPICard) cmd(cmd command, arg uint32, precalcCRC byte) (response1, erro
if err != nil {
return 0, err
}
if cmd == 12 {
if cmd == cmdStopTransmission {
// skip 1 byte
d.bus.Transfer(0xFF)
}
tm := d.timers[0].setTimeout(d.timeout)
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
@@ -435,7 +360,6 @@ func (d *SPICard) waitStartBlock() error {
if _, ok := d.waitToken(d.timeout, tokSTART_BLOCK); ok {
return nil
}
d.csEnable(false)
return errWaitStartBlock
}
+6 -1
View File
@@ -13,6 +13,10 @@ import (
type CardKind uint8
func isTimeout(err error) bool {
return err == errReadTimeout || err == errWriteTimeout || err == errBusyTimeout
}
const (
// card types
TypeSD1 CardKind = 1 // Standard capacity V1 SD card
@@ -150,7 +154,8 @@ func (c *CSD) CommandClasses() CommandClasses {
}
// ReadBlockLen returns the Max Read Data Block Length in bytes.
func (c *CSD) ReadBlockLen() uint16 { return 1 << (c.data[5] & 0x0F) }
func (c *CSD) ReadBlockLen() uint16 { return 1 << c.ReadBlockLenShift() }
func (c *CSD) ReadBlockLenShift() uint8 { return c.data[5] & 0x0F }
// AllowsReadBlockPartial should always return true. Indicates that
func (c *CSD) AllowsReadBlockPartial() bool { return c.data[6]&(1<<7) != 0 }
+5
View File
@@ -15,6 +15,9 @@ const (
_R1_ERASE_SEQUENCE_ERROR = 1 << 4
_R1_ADDRESS_ERROR = 1 << 5
_R1_PARAMETER_ERROR = 1 << 6
_DATA_RES_MASK = 0x1F
_DATA_RES_ACCEPTED = 0x05
)
type response1 uint8
@@ -81,6 +84,8 @@ func makeResponseError(status response1) error {
}
}
// func (c *SPICard) lastR1() r1 { return c.lastr1 }
// 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)
+368
View File
@@ -0,0 +1,368 @@
package sd
import (
"encoding/binary"
"errors"
"io"
"math"
"math/bits"
"time"
)
// Reference for this implementation:
// https://github.com/embassy-rs/embedded-sdmmc-rs/blob/master/src/sdmmc.rs
// Not used currently. We'd want to switch over to one way of doing things, Rust way.
func (d *SPICard) initRs() error {
// Supply minimum of 74 clock cycles with CS high.
d.csEnable(true)
for i := 0; i < 10; i++ {
d.send(0xff)
}
d.csEnable(false)
d.csEnable(true)
defer d.csEnable(false)
// Enter SPI mode
const maxRetries = 32
retries := maxRetries
tm := d.timers[0].setTimeout(2 * time.Second)
for retries > 0 {
stat, err := d.card_command(cmdGoIdleState, 0)
if err != nil {
if isTimeout(err) {
retries--
continue // Try again!
}
return err
}
if stat == _R1_IDLE_STATE {
break
} else if tm.expired() {
retries = 0
break
}
retries--
}
if retries <= 0 {
return errNoSDCard
}
const enableCRC = false
if enableCRC {
stat, err := d.card_command(cmdCRCOnOff, 1)
if err != nil {
return err
} else if stat != _R1_IDLE_STATE {
return errors.New("sd:cant enable CRC")
}
}
tm.setTimeout(d.timeout)
for {
stat, err := d.card_command(cmdSendIfCond, 0x1aa)
if err != nil {
return err
} else if stat == _R1_IDLE_STATE || stat == _R1_ILLEGAL_COMMAND {
d.kind = TypeSD1
break
}
d.receive()
d.receive()
d.receive()
status, err := d.receive()
if err != nil {
return err
}
if status == 0xaa {
d.kind = TypeSD2
break
}
d.yield()
}
var arg uint32
if d.kind != TypeSD1 {
arg = 0x4000_0000
}
for {
stat, err := d.card_acmd(acmdSD_APP_OP_COND, arg)
if err != nil {
return err
} else if stat == 0 { // READY state.
break
}
d.yield()
}
err := d.updateCSDCID()
if err != nil {
return err
}
if d.kind != TypeSD2 {
return nil // Done if not SD2.
}
// Discover if card is high capacity.
stat, err := d.card_command(cmdReadOCR, 0)
if err != nil {
return err
} else if stat != 0 {
return makeResponseError(response1(stat))
}
ocr, err := d.receive()
if err != nil {
return err
} else if ocr&0xc0 == 0xc0 {
d.kind = TypeSDHC
}
// Discard next 3 bytes.
d.receive()
d.receive()
d.receive()
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))
if err != nil {
return err
}
if d.kind != TypeSDHC {
startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards.
}
d.csEnable(true)
defer d.csEnable(false)
if numblocks == 1 {
_, err = d.card_command(cmdReadSingleBlock, uint32(startBlockIdx))
if err != nil {
return err
}
return d.read_data(dst)
} else if numblocks > 1 {
blocksize := 1 << d.blockshift
_, err = d.card_command(cmdReadMultipleBlock, uint32(startBlockIdx))
if err != nil {
return err
}
for i := 0; i < numblocks; i++ {
offset := i * blocksize
err = d.read_data(dst[offset : offset+blocksize])
if err != nil {
return err
}
}
_, err = d.card_command(cmdStopTransmission, 0)
return err
}
panic("unreachable numblocks<=0")
}
// WriteBlocks writes to sdcard from a buffer multiple of 512 bytes from src starting at block `startBlockIdx`.
func (d *SPICard) WriteBlocks(data []byte, startBlockIdx int64) error {
numblocks, err := d.checkBounds(startBlockIdx, len(data))
if err != nil {
return err
}
if d.kind != TypeSDHC {
startBlockIdx <<= 9 // Multiply by 512 for non high capacity SD cards.
}
d.csEnable(true)
defer d.csEnable(false)
if numblocks == 1 {
_, err = d.card_command(cmdWriteBlock, uint32(startBlockIdx))
if err != nil {
return err
}
err = d.write_data(tokSTART_BLOCK, data)
if err != nil {
return err
}
err = d.wait_not_busy()
if err != nil {
return err
}
status, err := d.card_command(cmdSendStatus, 0)
if err != nil {
return err
} else if status != 0 {
return makeResponseError(response1(status))
}
status, err = d.receive()
if err != nil {
return err
} else if status != 0 {
return errWrite
}
return nil
} else if numblocks > 1 {
// Start multi block write.
blocksize := 1 << d.blockshift
_, err = d.card_command(cmdWriteMultipleBlock, uint32(startBlockIdx))
if err != nil {
return err
}
for i := 0; i < numblocks; i++ {
offset := i * blocksize
err = d.waitNotBusy(d.timeout)
if err != nil {
return err
}
err = d.write_data(tokWRITE_MULT, data[offset:offset+blocksize])
if err != nil {
return err
}
}
// Stop the multi write operation.
err = d.waitNotBusy(d.timeout)
if err != nil {
return err
}
return d.send(tokSTOP_TRAN)
}
panic("unreachable numblocks<=0")
}
func (d *SPICard) checkBounds(startBlockIdx int64, datalen int) (numblocks int, err error) {
if startBlockIdx >= d.numblocks {
return 0, errOOB
} else if startBlockIdx > math.MaxUint32 {
return 0, errCardNotSupported
}
tz := bits.TrailingZeros(uint(datalen))
if tz < int(d.blockshift) {
return 0, errNeedBlockLenMultiple
}
numblocks = datalen >> d.blockshift
if numblocks == 0 {
return 0, io.ErrShortBuffer
}
return numblocks, nil
}
func (d *SPICard) card_acmd(acmd appcommand, args uint32) (uint8, error) {
_, err := d.card_command(cmdAppCmd, 0)
if err != nil {
return 0, err
}
return d.card_command(command(acmd), args)
}
func (d *SPICard) card_command(cmd command, args uint32) (uint8, error) {
const transmitterBit = 1 << 6
err := d.wait_not_busy()
if err != nil {
return 0, err
}
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], args)
buf[5] = crc7noshift(buf[:5]) | 1 // CRC and end bit which is always 1.
err = d.bus.Tx(buf, nil)
if cmd == cmdStopTransmission {
d.receive() // skip stuff byte for stop read.
}
for i := 0; i < 512; i++ {
result, err := d.receive()
if err != nil {
return 0, err
}
if result&0x80 == 0 {
return result, nil
}
}
return 0, errReadTimeout
}
func (d *SPICard) read_data(data []byte) (err error) {
var status uint8
for {
status, err = d.receive()
if err != nil {
return err
}
if status != 0xff {
break
}
d.yield()
}
if status != tokSTART_BLOCK {
return errWaitStartBlock
}
err = d.bus.Tx(nil, data)
if err != nil {
return err
}
// CRC16 is always sent on a data block.
crchi, _ := d.receive()
crclo, _ := d.receive()
d.lastCRC = uint16(crclo) | uint16(crchi)<<8
return nil
}
func (s *SPICard) wait_not_busy() error {
tm := s.timers[0].setTimeout(s.timeout)
for {
tok, err := s.receive()
if err != nil {
return err
} else if tok == 0xff {
break
} else if tm.expired() {
return errBusyTimeout
}
s.yield()
}
return nil
}
func (s *SPICard) write_data(tok byte, data []byte) error {
if len(data) > 512 {
return errors.New("data too long for write_data")
}
crc := CRC16(data)
err := s.send(tok)
if err != nil {
return err
}
err = s.bus.Tx(data, nil)
if err != nil {
return err
}
err = s.send(byte(crc >> 8))
if err != nil {
return err
}
err = s.send(byte(crc))
if err != nil {
return err
}
status, err := s.receive()
if err != nil {
return err
}
if status&_DATA_RES_MASK != _DATA_RES_ACCEPTED {
return makeResponseError(response1(status))
}
return nil
}
func (s *SPICard) receive() (byte, error) {
return s.bus.Transfer(0xFF)
}
func (s *SPICard) send(b byte) error {
_, err := s.bus.Transfer(b)
return err
}