From 7db9e9d6dbc2a284051cca4eddd17fc9b71b1d92 Mon Sep 17 00:00:00 2001 From: soypat Date: Mon, 15 Jan 2024 01:04:39 -0300 Subject: [PATCH] still working on consolidation of init --- examples/sd/main.go | 23 +-- sd/card.go | 196 ++++++++--------------- sd/definitions.go | 7 +- sd/responses.go | 5 + sd/rustref.go | 368 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 442 insertions(+), 157 deletions(-) create mode 100644 sd/rustref.go diff --git a/examples/sd/main.go b/examples/sd/main.go index 9df0cc2..228fbbb 100644 --- a/examples/sd/main.go +++ b/examples/sd/main.go @@ -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 diff --git a/sd/card.go b/sd/card.go index 59e1848..d3fd23e 100644 --- a/sd/card.go +++ b/sd/card.go @@ -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 } diff --git a/sd/definitions.go b/sd/definitions.go index 66976de..0c4e319 100644 --- a/sd/definitions.go +++ b/sd/definitions.go @@ -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 } diff --git a/sd/responses.go b/sd/responses.go index b1f4bc8..7c50763 100644 --- a/sd/responses.go +++ b/sd/responses.go @@ -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) diff --git a/sd/rustref.go b/sd/rustref.go new file mode 100644 index 0000000..96b1291 --- /dev/null +++ b/sd/rustref.go @@ -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 +}