weAut_01 / weAutSys    R 2.2.1
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines
Files | Data Structures | Defines | Functions | Variables
Communication with a small memory card (via SPI)
+ + System services (threading, time keeping, communication) + +

Overview

These are the basic functions to communicate serially with a small memory card. Higher level capabilities as e.g. a file system are build upon those functions.

Files

file  smc.h
 

weAutSys' (low level) system calls, services and types for communication with a small memory card


Data Structures

struct  smcThr_data_t
 The organisational data for a small memory card (SMC) handling thread. More...

Defines

#define APP_CMD   55
 SMC command: next command is application specific.
#define GO_IDLE_STATE   0
 SMC command: soft reset.
#define LOCK_UNLOCK   42
 SMC command: lock / unlock the card.
#define NCR_EXTR_WAIT   8
 Maximum extra waits for command response.
#define othersAskPrio()
 Other devices asks for priority.
#define READ_OCR   58
 SMC command: read the card's OCR register (R3)
#define READ_SINGLE_BLOCK   17
 SMC command: read one block.
#define SEND_CID
 SMC command: get CID.
#define SEND_CID
 SMC command: get CID.
#define SEND_CSD   9
 SMC command: send card specific data.
#define SEND_IF_COND   8
 SMC command: send interface condition.
#define SEND_OP_COND   1
 SMC command: init. process.
#define SEND_OP_COND_APP
 SMC application specific command: initialisation process.
#define SEND_STATUS   13
 SMC command: get status (R2)
#define SET_BLOCKLEN   16
 SMC command: set block length for LOCK_UNLOCK.
#define smcInsPow()
 Inserted card is powered up.
#define smcReceive()
 Receive a single byte from the small memory card.
#define smcReceiveN(receiveB, skip, n)
 Receive n bytes from the SMC to a buffer with optional skip.
#define smcTypeD()
 The SMC's type is determined and OCR is known.
#define smcXmit(datByte)
 Transmit a single byte to the small memory card.
#define smcXmit2(sendB1, sendB2)
 Send two bytes and receive one byte to/from the small memory card.
#define WRITE_BLOCK   24
 SMC command: write one block.

Functions

uint8_t checkBusy (uint8_t tries)
 Check if card is busy.
void clk80 (void)
 Have 80 dummy SPI clocks.
uint8_t crc7stp (uint8_t crcIn, uint8_t datByte) __attribute__((always_inline))
 Calculate CRC7 (one step)
void deSelectSMC (void) __attribute__((always_inline))
 De-select the small memory card.
uint8_t doSectorRead (uint32_t sector)
 Order sector read (as background task)
uint8_t doSectorSync (void)
 Order sector synchronisation (as background task)
uint8_t getSMCtype (void)
 Determine the card type.
void initSMCthreadState (uint8_t actionFlag)
 Initialise the small memory card (smc) handling thread.
uint8_t readDataBlock (uint32_t sector, uint8_t *buff)
 Read single data block (512 byte)
uint8_t sendAppCmd (uint8_t appCmdNum, uint32_t cmdArg)
 Send an application specific command to the small memory card.
uint8_t sendCmd (uint8_t cmdNum, uint32_t cmdArg)
 Send a command to the small memory card.
uint8_t sendCmd0arg (uint8_t cmdNum)
 Send a command with 0 argument to the small memory card.
void setSectorModified (uint32_t sector)
 Mark sector buffer data as modified (start)
uint32_t smcGetSectCount (void)
 Get the sector count.
uint8_t smcInsertSwitch (void)
 Hardware card detect.
uint8_t smcReadCID (uint8_t *buff)
 Read the SMC's CID.
uint8_t smcReadCSD (void)
 Read the SMC's CSD.
uint8_t smcReadOCR (void)
 Read the SMC's ORC.
void smcSetFrq (uint8_t frqUse) __attribute__((always_inline))
 Set the SPI clock frequency for the small memory card.
uint8_t smcSetIdle (uint8_t mode)
 Put card to idle state.
ptfnct_t smcThreadF (void)
 The small memory card (smc) handling thread.
uint8_t writeDataBlock (uint32_t sector, const uint8_t *buff)
 Write single data block (512 byte)

Variables

struct smcThr_data_t smcState
 The (one) small memory card (SMC) state.

Define Documentation

#define othersAskPrio ( )

Other devices asks for priority.

This expression is true if other software respectively devices need resources used by SMC operations in critical situations.

As some SMC operations need SPI communication or thread time uninterrupted for quite a long time they should refrain from doing so as long as this request is pending.

For SMC processing controlled by this module's functions this time may be up to 2ms as documented in each case. File system implementations implemented upon these (driver) functions would increase this value quite considerably if programmed unaware of the (Protothread) threading model.

As of Revision 422++ this request comes from (seldom) NTP synchronisation actions only.

#define NCR_EXTR_WAIT   8

Maximum extra waits for command response.

The maximum number of (8 SPI clock) cycles to receive a valid command response is this value + 1. The specification says the number (NCR) of those waiting cycles is in the range of 1 .. 8 (depending on card type and command). Hence 7 would be the minimum for NCR_EXTR_WAIT; anything larger than 10 would just waste time in case of a (command) failure.

#define SEND_CID

SMC command: get CID.

SMC command: send card idendification.

#define SEND_CID

SMC command: get CID.

SMC command: send card idendification.

#define LOCK_UNLOCK   42

SMC command: lock / unlock the card.

See also:
SET_BLOCKLEN

SMC application specific command: initialisation process.

Like SEND_OP_COND this command activates the card's initialisation process. But as an application specific command it has to be prepended by APP_CMD() or simply used in sendAppCmd instead of sendCmd().

Note: In SPI mode SEND_OP_COND_APP and SEND_OP_COND should have the same behaviour, though most implementors prefer the application specific variant. This tradition is respected here.

#define smcXmit (   datByte)

Transmit a single byte to the small memory card.

Parameters:
datBytethe byte to be sent to the SMC
Returns:
the byte received (on MiSo); can be disregarded for SMCs (0 may indicate an error)
#define smcXmit2 (   sendB1,
  sendB2 
)

Send two bytes and receive one byte to/from the small memory card.

Parameters:
sendB1the byte to be sent first
sendB2the second byte to be sent
Returns:
the byte read from SPI while transmitting; can be disregarded for SMCs sendB2 (0 may indicate an error)
#define smcReceive ( )

Receive a single byte from the small memory card.

Returns:
the byte received
See also:
smcReceiveN
#define smcReceiveN (   receiveB,
  skip,
 
)

Receive n bytes from the SMC to a buffer with optional skip.

This function does skip + n receptions. The (last) n bytes received are put into the buffer receiveB. It returns nothing (void).

Compared to doing this by skip + n times smcReceive() this function is at least 20 % faster.

Parameters:
receiveBpointer to the buffer (type uint8_t *) to receive n bytes to (must not be NULL if n != 0)
skipif > 0 skip bytes are received and forgotten before receiveB will be filled
nnumber of bytes to be received and filled into receiveB
#define smcInsPow ( )

Inserted card is powered up.

This expression is true if a SMC is inserted and powered up according to smcState.

#define smcTypeD ( )

The SMC's type is determined and OCR is known.

This expression is true if a (inserted and powered up) SMC's type has been determined. In the process also the OCR has been read to smcState.ocr.


Function Documentation

uint8_t smcInsertSwitch ( void  )

Hardware card detect.

On the weAut_01 module the card switch must be "jumpered" to port D6 for this function to work properly.

Note:
To see a card that was correctly powered up and put out of idle state has vanished is easily done by every command. So in this respect the card switch is hardly needed. On the other hand the detection of a potentially inserted SMC by pure (SPI) access requires the complete power up and type recognition process.

Remark: It might well be considered a misconception in weAut_01 not to tie the respective port (D5) directly to the card insert switch and have a lot of jumper options instead. This can be mended by an (1K) resistor soldered in 2 via holes near the jumper.

Returns:
true as 'i': switch closed = card inserted, true as 'm': may be inserted, switch not configured; false (0): no card inserted switch open.
void clk80 ( void  )

Have 80 dummy SPI clocks.

Some small memory cards need at least 74 (dummy) SPI clocks with their chip select inactive (high) and data in (DI, MoSi) all ones after power up.

This function sends 80 clocks (sClk) on the SPI (2) interface used for small memory cards with all (SPI2) devices de-selected. The stream of 80 clocks delivered by this function has no gaps.

At SPI clock frequency of 2 MHz the whole thing would take 40,4 µs. At the 400 kHz usually required for power up it will take 200 µs.

See also:
smcSetIdle()
void smcSetFrq ( uint8_t  frqUse)

Set the SPI clock frequency for the small memory card.

Some (older) small memory cards require SPI clock frequencies of 400 kHz (or lower) during initialisation. Afterwards higher SPI baud-rates are to be used. This function sets the latter / normal mode frequency.

A 20 MHz clocked micro-controller (like ATmega1284P) has a maximum 10 MHz SPI baud rate. There are few SMC types with a maximum SPI clock frequency of 6.5 MHz around. Most other types can handle much higher clock rates. So the principally possible 10 MHz would hardly give them a wet shirt.

The next possible lower frequency is 5 MHz. It would fit all card types, anyway. Additionally in case of resistor divide level shifters — from ATmega's 5V to SMC's 3.3V — the waveforms provided for some card types dictate 5 MHz as maximum SPI clock frequency.

Parameters:
frqUsecontrol value to set SPI 2 clock for the memory card (use the predefined values or the UBRR1 formula)
See also:
spi2EtherChipBaud
spi2MemCardBaud
uint8_t crc7stp ( uint8_t  crcIn,
uint8_t  datByte 
)

Calculate CRC7 (one step)

This function calculates the CRC7 (used in some small memory cards for command transmission). It is a 7 bit CRC with polynomial x7 + x3 + 1. This function implements just the step for next 8 bits given by datByte. The result returned would have to be fed as crcIn for the next step. Use 0 for crcIn in the first step.

To make the final 7 bit CRC sendable to the small memory card via SPI it must be put in the upper seven bit of the CRC byte padded with an one in bit 0 by:

    crc = (crc<<1) | 1; // make so sendable after the last step

The function uses 10 CPU cycles if inlined as intended or 18 including call and return. Hence the algorithm is "faster than one byte" for all SPI clocks not faster than 1/2 CPU clock. Sending one byte at 5 MHz takes 48 CPU clocks with a 20 MHz clocked ATmega.

Parameters:
crcInthe crc before; use 0 in first step
datBytethe next 8 bits for the CRC
Returns:
the new CRC
void deSelectSMC ( void  )

De-select the small memory card.

This function (chip-) de-selects the SMC. It has to be used when all commands and write / reads are done. An explicit select is not needed as all command functions do so automatically.

Hint: Most operations in most SMCs (probably all) fail if de-selected and re-selected. Keep in mind that small memory cards are not very friendly to sharing a SPI bus with other devices.

uint8_t sendCmd ( uint8_t  cmdNum,
uint32_t  cmdArg 
)

Send a command to the small memory card.

This function sends the command, the arguments and the CRC7 to the memory card. Returned is the card's answer if any. Several (i.e. NCR_EXTR_WAIT + 1) attempts are made to get the cards command response recognised by bit 7 zero. If the response returned is 0xFF the card may be gone.

In this (worst) case this function takes 74 µs at a SPI clock frequency of 2 MHz compared to 32.5 µs for a card answering fast.

Hint: To send an application specific command use the function sendAppCmd().

Hint2: If the response is 0 (all OK) and if the command's specified answer is not of type R1 (or if it is a block read command), its up to the caller of this or similar functions to receive all coming response bytes, using e.g. smcReceive() or smcReceiveN().

Hint3: The returned response is also stored in smcState.lastCmdResp[0].

Parameters:
cmdNumcommand number, only the lower 6 bits are relevant
cmdArgthe 4 byte (32 bits) command arguments
Returns:
the first (and in case of R1 only) response byte
See also:
GO_IDLE_STATE
deselectSCM()
sendCmd0arg
sendAppCmd
uint8_t sendCmd0arg ( uint8_t  cmdNum)

Send a command with 0 argument to the small memory card.

This function is equivalent to
      sendCmd(cmdNr, 0);
with some improvements for this frequent case.

  See also:   The hints at sendCmd().

Parameters:
cmdNumcommand number, only the lower 6 bits are relevant
Returns:
the first (and in case of R1 only) response byte
uint8_t sendAppCmd ( uint8_t  appCmdNum,
uint32_t  cmdArg 
)

Send an application specific command to the small memory card.

This function is equivalent to
      sendCmd0arg(APP_CMD);
      sendCmd(cmdNr, cmdArg);
besides some slight improvements.

  See also:   The hints at sendCmd() and SEND_OP_COND_APP.

Parameters:
appCmdNumapplication specific command number (lower 6 bits)
cmdArgthe 4 byte (32 bits) command arguments
Returns:
the first (and in case of R1 only) response byte
uint8_t smcSetIdle ( uint8_t  mode)

Put card to idle state.

This function should run once a small memory card is inserted (or is believed to have been). Returned is the "R1" answer to the soft reset command. Only the value 1 is OK meaning card is present and (now) in idle state. All other values are faults. 0xFF (no valid R1 response) usually means no card inserted or card not responding at all.

Most specifications require 400 kHz for the initial ref clk80 "dummy clocks" and the command. Though this seems out-dated for most modern cards it can for compatibility reasons be done by setting bit 0 in mode. The whole thing takes 370µs then.

In smcState.cardType this function sets bits CT_POWERD_UP and CT_INSERTED on success; all other bits are cleared.

Parameters:
mode
Bit 0 set (1): force 400 kHz and restore spi2MemCardBaud to previous value afterwards
Bit 1 set (2): omit the 80 dummy clocks before the go idle command Bit 2 set (4): omit the go idle command (0 is returned in that case)
Returns:
answer (R1) for the command sent: 0x01 is OK (card in slot and in idle state)
uint8_t checkBusy ( uint8_t  tries)

Check if card is busy.

This function returns 0 (false not busy) if the SMC is inserted and responds 0xFF at least twice to two dummy reads. The two consecutive not busy answers are looked for 3 .. (3 + tries) times.

If the outcome is not busy the card is not de-selected.

Parameters:
tries2..255 maximum number of dummy check reads (byte clock cycles) used
Returns:
0: not busy; 0xFF: no card inserted and powered up; 1: busy
ptfnct_t smcThreadF ( void  )

The small memory card (smc) handling thread.

If some basic handling, like initialising, has to be done with a SMC this protothread thread will do it reasonable steps, dividing long running procedures by yielding.

Hint: As this is for small (petit) systems with just one SMC slot this thread uses the one smcState (without any choice by parameter).

void initSMCthreadState ( uint8_t  actionFlag)

Initialise the small memory card (smc) handling thread.

This function resets all SMC state. It should be called on reset / restart and on SMC removal, failures and insertion.

Parameters:
actionFlagstart value for the SMC thread's flag
See also:
smcState
uint8_t smcReadOCR ( void  )

Read the SMC's ORC.

This function reads the operation condition register (OCR, 4 byte) of an inserted and powered up small memory card. In case of success 0 is returned and the result is stored in smcState.ocr.

It is usually not necessary to call this function if smcTypeD() is true as the OCR and the CSD have been read during the card type determination process.

Returns:
0: OK or negative response of command READ_OCR or or 0x80 if SCM isn't powered up.
uint8_t smcReadCSD ( void  )

Read the SMC's CSD.

This function reads the card specific data register (CSD, 16 byte) of an inserted and powered up small memory card. In case of success 0 is returned and the result is stored in smcState.csd.

It is usually not necessary to call this function if smcTypeD() is true as the OCR and CSD have been read during the card type determination process.

Returns:
0: OK or negative response of command SEND_CSD or or 0x80 if SCM isn't powered up.
uint32_t smcGetSectCount ( void  )

Get the sector count.

If the card type is determined this function returns the number of (512 byte) sectors on the card and 0 otherwise.

uint8_t smcReadCID ( uint8_t *  buff)

Read the SMC's CID.

This function reads the card identification register (CID, 16 byte) of an inserted and powered up small memory card. In case of success 0 is returned and the result is stored in buff.

0: Manufacturer ID
1,2: OEM/Application ID
3..7: Product name
8: Product revision
9..12: Serial number
13 15: Manufaturing date

Parameters:
buff16 byte to write the CID to
Returns:
0: OK or negative response of command SEND_CSD or or 0x80 if SCM isn't powered up.
uint8_t readDataBlock ( uint32_t  sector,
uint8_t *  buff 
)

Read single data block (512 byte)

This function does one single block respectively sector read — with block size being fixed to 512 byte here. In case of success 0 is returned. If the given sector number has already been cached the SMC is not accessed.

In case of failure non 0 is returned. More information can be found in smcState.

In the normal case of SCC access the time needed by this function heavily depends on the SMC make. The (astonishing) differences are mainly due to different waits for the so called data token. The time needed for this With ~31 waits for data token (512MByte KData card) this takes about 1 ms. With 181 checks for the data token (8GByte Verbatim) we need 1,35 ms but this varies up to 250 waits and about 2 ms.

weAutSys is a non pre-emptive system — and besides that SMC operations (the /CS) could not be interrupted anyway. Hence it is clear and was said at other places this block read operation is the most critical for an automation modul / system with an 1ms cycle. No two such operations shall follow each other without yielding — and multi-block SMC operations are, of course, out of the question.

Hint: This function as well as writeDataBlock() handle the sector to byte address transformation for commands READ_SINGLE_BLOCK resp. WRITE_BLOCK needed with low capacity SMC types internally. Parameters sector (and smcState.rwSector) always reflect the sector number (0, 1 ...).

Parameters:
sectorthe sector number
buffpointer to where to write the received data (NULL not allowed)
Returns:
0: OK or negative response of command READ_SINGLE_BLOCK or or 0x80 if no data token came (time out; may try later) or no no type info available
See also:
writeDataBlock
uint8_t writeDataBlock ( uint32_t  sector,
const uint8_t *  buff 
)

Write single data block (512 byte)

Parameters:
sectorthe sector number
buffpointer to where to get the send data from (NULL not allowed)
Returns:
0: OK or negative response of command WRITE_BLOCK or or 0x80 if no type info available
See also:
readDataBlock
uint8_t getSMCtype ( void  )

Determine the card type.

If the an SMC is inserted and its type has been determined this function returns the type bits set in smcState.cardType.

Otherwise 0 is returned and the card type determination is triggered in the SMC handling thread.

Returns:
0: no type determined yet, CT_V_1, CT_V_2 [| CT_HC], CT_V_3,
uint8_t doSectorRead ( uint32_t  sector)

Order sector read (as background task)

This function sets smcState.rwSector by sector and initialises smcState.sectorBuff to be read from that sector.

Parameters:
sectorthe (new) sector number for smcState.rwSector/.sectorBuff
Returns:
0: OK already done (smcState.sectorBuff in sync.),
1: will be done ASAP (in background task), call again later,
0x80: action not possible (no card, no type or other error)
See also:
setSectorModified
void setSectorModified ( uint32_t  sector)

Mark sector buffer data as modified (start)

This function sets smcState.rwSector by sector and marks as smcState.sectorBuff as modified (new) data for the respective sector. This neither performs any read or write operations nor orders such as background task.

Hint: If this functions changes the current buffer's (smcState.sectorBuff) sector number and the buffer contained modified data for that previous sector those modifications will be lost. Call doSectorSync() before if that would not be intended.

Parameters:
sectorthe (new) sector number for smcState.rwSector/.sectorBuff
See also:
doSectorRead
uint8_t doSectorSync ( void  )

Order sector synchronisation (as background task)

This function checks if smcState.sectorBuff contains modified data (for sector smcState.rwSector) and if so initialises smcState.sectorBuff to be written to that sector.

Example sector copy:

  PT_WAIT_ASYIELD_WHILE(&myThread, doSectorRead(sourceSect) == 1);
  if (doSectorSync()) {  optional error handling / exit  }
  setSectorModified(destinationSect);
  PT_WAIT_ASYIELD_WHILE(&myThread, doSectorSync() == 1);
  if (doSectorSync()) {  optional error handling / exit  }
Returns:
0: OK already done (smcState.sectorBuff in sync.),
1: will be done ASAP (in background task), call again later,
0x80: action not possible (no card, no type or other error)