How to use KSDK 1.1.0 SPI Master driver to read/write Microwire(tm) EEPROM?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to use KSDK 1.1.0 SPI Master driver to read/write Microwire(tm) EEPROM?

Jump to solution
1,546 Views
surrealist14
Contributor III

Hello folks,

 

I am using a K60 tower, KSDK 1.1.0, and IAR Embedded Workbench for Cortex-M 7.40.2.  I am trying to figure out how to use the provided DSPI Master Driver code to read from/write to an ST Micro M93C46, which is a 1-Kbit EEPROM (128 bytes or 64 16-bit words) using a Microwire(tm) interface.

 

The M93C46 is configured to be organized as "by word" (x16 bits), so the ORG line on the part is pulled high.  The data sheet for the part indicates the following:

 

- Each instruction is preceded by a rising edge on Chip Select Input (S) with Serial Clock (C) being held low.

- A start bit, which is the first '1' read on Serial Data Input (D) during the rising edge of Serial Clock (C).

- Two opcode bits, read on the Serial Data Input (D) during the rising edge of Serial Clock (C)

- The address bits of the byte or word that is to be accessed (6 bits for x16 organization)

 

For example, a Read Data from Memory would have a Start Bit (1b), and Opcode (10b), the starting address (A5-A0, 6 bits), then 16 bits of data (D15-D0), for a total of 25 required clock cycles.  I just can't figure out how to set up and use the DSPI Master Driver to perform this transfer or transfers, and need some help.

 

I have PTD0, 1, 2, and 3 configured as Alt2 to configure them for SPI0 use.  This assigns the following pins:

PTD0 -> SPI0_PCS0, PTD1 ->SPI0_SCK, PTD2 ->SPI0_SOUT, and PTD3 -> SPI0_SIN.  This is accomplished via some code I found in an example pin_mux.c using HW_SPI0 as the instance:

 

void configure_spi_pins(uint32_t instance)

{

    switch(instance)

    {

    case HW_SPI0:                       /* SPI0 */

        /* Affects PORTD_PCR0 register */

        PORT_HAL_SetMuxMode(PORTD_BASE,0u,kPortMuxAlt2);

        /* Affects PORTD_PCR3 register */

        PORT_HAL_SetMuxMode(PORTD_BASE,3u,kPortMuxAlt2);

        /* Affects PORTD_PCR1 register */

        PORT_HAL_SetMuxMode(PORTD_BASE,1u,kPortMuxAlt2);

        /* Affects PORTD_PCR2 register */

        PORT_HAL_SetMuxMode(PORTD_BASE,2u,kPortMuxAlt2);

        break;

 

We wired up a sample M93C46 on one of the TWR-PROTO boards, and it seems to be connected to the correct pins.

 

I read the documentation for the DSPI Master Driver, and it appears that I need to make calls to DSPI_DRV_MasterInit() and DSPI_DRV_MasterConfigureBus() to set up the user configuration and the bus configuration respectively, but some of the parameters are not clear to me. 

 

Initially I need to set a single Start bit (1b), then the 2-bit Read opcode (10b), then the starting address (let's assume we start at 0 to make it easy, so A5-A0 is (000000b).  Then I need to clock another 16 times to transfer the actual data. 

 

To make things a bit more difficult, the data sheet indicates that for the first word, a dummy 0 bit is output first, followed by the 16-bit word, MSB first.  Output data changes are triggered by the rising edge of Serial Clock (C).  As long as Chip Select (S) is held high, we can continue to clock out the next words, and a dummy 0 bit is NOT output between words, so you can read a continuous stream of data.

 

Here's where I get lost.  The examples show that I need to call DSPI_DRV_MasterInit() first, so I tried the following:

 

    dspi_master_state_t dspiMasterState;

    uint32_t calculatedBaudRate;

    dspi_master_user_config_t userConfig;

   

    // Set up the user configuration

    userConfig.isChipSelectContinuous = false; 

    userConfig.isSckContinuous        = false;

    userConfig.pcsPolarity            = kDspiPcs_ActiveHigh;

    userConfig.whichCtar              = kDspiCtar0;

    userConfig.whichPcs               = kDspiPcs0;

 

    // Initialize the DSPI module

    DSPI_DRV_MasterInit(HW_SPI0, &dspiMasterState, &userConfig);

 

I have a feeling that the .isChipSelectContinuous field needs to be true, but set that aside for now.  I believe that I next have to configure the bus, which is done in the following example:

 

    dspi_device_t spiDevice;

    spiDevice.dataBusConfig.bitsPerFrame = ???; // What to put here?

    spiDevice.dataBusConfig.clkPhase = kDspiClockPhase_FirstEdge;   // What does this do?

    spiDevice.dataBusConfig.clkPolarity = kDspiClockPolarity_ActiveHigh;

    spiDevice.dataBusConfig.direction = kDspiMsbFirst;

    spiDevice.bitsPerSec = 50000;   // Can go faster, up to 2 MHz

    DSPI_DRV_MasterConfigureBus(instance, &spiDevice, &calculatedBaudRate);

 

Since I need to transfer a 1-bit Start bit, followed by 2 bits of opcode and 6 bits of address, then somehow get 17 bits of data for the first word (because of the dummy 0 bit and then as many other 16-bit words as I need (without the dummy 0 bit), what should the bitsPerFrame be set to?

 

Further, how should I set up the actual data blocks/frames to be transferred and received?  Eventually I will probably want to use a non-blocking Transfer call to do this, but an example with a blocking call would be fine for now.

 

Has anyone else run into using the DSPI interface on the K60 with variable-length transfers?  What's the best way to handle them, preferably using the KSDK drivers?

 

Many thanks, and please let me know if you need any clarification.  It's a hard question to write briefly!

 

--Scott

Labels (1)
0 Kudos
1 Solution
848 Views
surrealist14
Contributor III

I made more progress on this, and think I have arrived at a solution.  I wanted to share this with the community in case someone else ran into a similar issue.

The "trick" hinges on the fact that this particular EEPROM doesn't do anything until it sees a START bit (1b) with the chip select asserted.  I found an application note about this family of parts that stated:

  • "Non-byte wide instruction and address issues can be dealt with by shifting in leading 0’s before the required start bit. The leading 0’s will not be recognized and can be used to fill out an instruction to byte length. The MICROWIRE word wide architecture can be made to appear byte accessible with clever software."

For example, to do a read, the part needs a start bit (1b), a two-bit opcode (10b), six address bits in word-wide organization with ORG held high (i.e., reading from address 0 would be a pattern of 000000b), and then the first bit that is read out of the part is a “dummy” 0 bit.  The next 16 clocks will then shift out the 16-bits of data for word address 0.  This means that my initial command/setup info requires 10 bits (start, opcode, address, and dummy), so I pre-pad the pattern with six 0 bits to give me an even 16 bits of data.

Desired pattern:

     START READ A5:A0     DUMMY Total

     1b         10b     000000b 0b          10 bits of data

Pre-padded pattern with six 0’s at the beginning:

     PAD        START   READ A5:A0     DUMMY Total

     000000b 1b           10b     000000b 0b           16 bits of data

Once this pattern (0000 0011 0000 0000 binary) is shifted out as two 8-bit transfers, the next 16 clocks give me the first word of real data on the “Q” line, and if I keep sending clocks, I get the next word (address 1), and so on.

To write data to the EEPROM, assuming that I have already sent the correct pattern for a Write Enable command, the Desired pattern for a word write is:

     START WRITE A5:A0     Total

     1b         01b      000000b 9 bits of data

Pre-padded pattern with seven 0’s at the beginning:

     PAD          START WRITE A5:A0      Total

     0000000b 1b         01b       000000b 16 bits of data

Once this pattern (0000 0001 0100 0000 binary) is shifted out as two 8-bit transfers, the next 16 clocks shift out the word data to be written to address 0.

This lets me do byte-aligned transfers instead of the strange lengths that would otherwise be needed.  I should also note that during these transfers, the SPI interface is set up to keep the chip select S asserted until all of the transfers are done (i.e., all words have been read, or the word of data to be written has been shifted out).  Then the SPI interface de-asserts the chip select.  This requires:

     userConfig.isChipSelectContinuous = true;

This method works, and the manufacturer that makes the Microwire EEPROM confirmed that the part will ignore 0 bits until it sees a START bit (1b), so this nifty trick lets me do byte aligned transfers.  Presumably it would also work on 16-bit transfers.

Hope this helps someone else.  I think we have a solution!

Best regards,

Scott

View solution in original post

0 Kudos
3 Replies
848 Views
surrealist14
Contributor III

Some follow-up info:

It appears that the chip select must be held in it's active state for the entire duration of a read or write operation on the M93C46.  This means that I probably need:

      userConfig.isChipSelectContinuous = true;


Further, it looks like the SPI driver doesn't really support changing the frame length in the middle of a transfer.  Doing so appears to stop the current transfer, which de-asserts the chip select.  Not a valid transfer as far as the M93C46 is concerned, so no data for me.

Therefore, it looks like I need to pick a single bitsPerFrame and blast everything out at that rate.  For example, if I choose 8 bits per frame, then my first byte to transmit consists of a start bit (1b), a read opcode (10b), and A5:A1 of the word address (5 bits).  The next byte has to have A0 (1 bit) in the MSB position, followed by 7 more "don't care" bits - 0's would work.  By "word address", I mean the address number of the word I want to start reading.  Then I need to keep sending "don't care" bytes to cause the data starting at that word address to be clocked out of the M93C46 so I can read it in.  The driver is set up so that the size of the data transmitted is the same as the size of the data received.

As a specific example, here is the interleaved write and read data for 8-bit transfers, assuming an address of 0:

            //      START  READOP1 READOP2 A5      A4      A3      A2      A1

            // WR:  1      1       0       0       0       0       0       0

            // RD:  X      X       X       X       X       X       X       X

            //      A0     <don't care> x 7 bits

            // WR:  0      X       X       X       X       X       X       X

            // RD:  X      DUMMY0  W0:Q15  W0:Q14  W0:Q13  W0:Q12  W0:Q11  W0:Q10

            // WR:  X      X       X       X       X       X       X       X

            // RD:  W0:Q9  W0:Q8   W0:Q7   W0:Q6   W0:Q5   W0:Q4   W0:Q3   W0:Q2

            // WR:  X      X       X       X       X       X       X       X

            // RD:  W0:Q1  W0:Q0   W1:Q15  W1:Q14  W1:Q13  W1:Q12  W1:Q11  W1:Q10

            // WR:  X      X       X       X       X       X       X       X

            // RD:  W1:Q9  W1:Q8   W1:Q7   W1:Q6   W1:Q5   W1:Q4   W1:Q3   W1:Q2

            // WR:  X      X       X       X       X       X       X       X

            // RD:  W1:Q1  W1:Q0   W2:Q15  W2:Q14  W2:Q13  W2:Q12  W2:Q11  W2:Q10

            // WR:  X      X       X       X       X       X       X       X

            // RD:  W2:Q9  W2:Q8   W2:Q7   W2:Q6   W2:Q5   W2:Q4   W2:Q3   W2:Q2

            // WR:  X      X       X       X       X       X       X       X

            // RD:  W2:Q1  W2:Q0   W3:Q15  W3:Q14  W3:Q13  W3:Q12  W3:Q11  W3:Q10

            // ...

The first write has the start bit, two bits of read opcode, and 5 of the address bits (A5:A1).  The data read back is a "don't care".  The next write has the MSB set to the value of address A0, followed by 7 other bits of "don't care".  As that is being clocked out, we start to receive real data from the M93C46.  The first bit is don't care, then there's a dummy value of 0, then the MSB (Q15) of the first word W0, followed by less significant bits down to bit 10 (Q10) of word W0.  Then I continue to write bytes of "don't care" data, reading back successively less significant bits of word W0, until the EEPROM auto-increments its address pointer and starts clocking out the MSB of the next word, W1:Q15.

If I continue this enough times to clock out all of the bits in the memory, I'll end up with an array of bytes that will have all of the data in the memory.  It's just going to be "skewed" and not byte-aligned, due to the 9 bits of start/opcode/address and 1 dummy zero bit.  A loop with some shifting and masking should let me reconstruct the data in a more usable byte or word format.

At least that's the theory.  So, my basic questions are:

1) To keep the chip select active, the entire transfer needs to have the same bits per frame, correct?

2) Since the SPI is full-duplex, I have to transmit the same number of bytes that I read back, correct?

3) Would it speed things up to do 16-bit transfers, or will that just rearrange the skewing of the data pattern?

4) Any clever ideas to re-align the data?

Hoping someone can clarify these assumptions.  The chapter on the SPI for the K60 is about 50 pages of light reading, so a real-world example using the KSDK master driver would be very helpful!

Thanks for your time and ideas,

Scott

0 Kudos
849 Views
surrealist14
Contributor III

I made more progress on this, and think I have arrived at a solution.  I wanted to share this with the community in case someone else ran into a similar issue.

The "trick" hinges on the fact that this particular EEPROM doesn't do anything until it sees a START bit (1b) with the chip select asserted.  I found an application note about this family of parts that stated:

  • "Non-byte wide instruction and address issues can be dealt with by shifting in leading 0’s before the required start bit. The leading 0’s will not be recognized and can be used to fill out an instruction to byte length. The MICROWIRE word wide architecture can be made to appear byte accessible with clever software."

For example, to do a read, the part needs a start bit (1b), a two-bit opcode (10b), six address bits in word-wide organization with ORG held high (i.e., reading from address 0 would be a pattern of 000000b), and then the first bit that is read out of the part is a “dummy” 0 bit.  The next 16 clocks will then shift out the 16-bits of data for word address 0.  This means that my initial command/setup info requires 10 bits (start, opcode, address, and dummy), so I pre-pad the pattern with six 0 bits to give me an even 16 bits of data.

Desired pattern:

     START READ A5:A0     DUMMY Total

     1b         10b     000000b 0b          10 bits of data

Pre-padded pattern with six 0’s at the beginning:

     PAD        START   READ A5:A0     DUMMY Total

     000000b 1b           10b     000000b 0b           16 bits of data

Once this pattern (0000 0011 0000 0000 binary) is shifted out as two 8-bit transfers, the next 16 clocks give me the first word of real data on the “Q” line, and if I keep sending clocks, I get the next word (address 1), and so on.

To write data to the EEPROM, assuming that I have already sent the correct pattern for a Write Enable command, the Desired pattern for a word write is:

     START WRITE A5:A0     Total

     1b         01b      000000b 9 bits of data

Pre-padded pattern with seven 0’s at the beginning:

     PAD          START WRITE A5:A0      Total

     0000000b 1b         01b       000000b 16 bits of data

Once this pattern (0000 0001 0100 0000 binary) is shifted out as two 8-bit transfers, the next 16 clocks shift out the word data to be written to address 0.

This lets me do byte-aligned transfers instead of the strange lengths that would otherwise be needed.  I should also note that during these transfers, the SPI interface is set up to keep the chip select S asserted until all of the transfers are done (i.e., all words have been read, or the word of data to be written has been shifted out).  Then the SPI interface de-asserts the chip select.  This requires:

     userConfig.isChipSelectContinuous = true;

This method works, and the manufacturer that makes the Microwire EEPROM confirmed that the part will ignore 0 bits until it sees a START bit (1b), so this nifty trick lets me do byte aligned transfers.  Presumably it would also work on 16-bit transfers.

Hope this helps someone else.  I think we have a solution!

Best regards,

Scott

0 Kudos
848 Views
Jorge_Gonzalez
NXP Employee
NXP Employee

Hello Scott:

Thanks a lot for sharing your solution! :smileyhappy:

This will help others using a similar or the same EEPROM memory.

Just in case you or others reading this post are interested, there is an example with the KSDK SPI driver in the next document in community:

KSDK SPI Master-Slave with FRDM-K64F

Regards!

Jorge Gonzalez

0 Kudos