CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug Reports on this forum. Send them to support@ccsinfo.com

** SOLVED ** Problems writing/reading external EEPROM

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
tgilbert



Joined: 14 Aug 2020
Posts: 8

View user's profile Send private message

** SOLVED ** Problems writing/reading external EEPROM
PostPosted: Wed Feb 10, 2021 3:38 pm     Reply with quote

The external EEPROM is - Microchip 25AA160B EEPROM
The compiler is CCS v5.075
The IDE is MPLAB X IDE v5.30

Hi,

I've been struggling with writing/reading with external EEPROM. In this test I attempt to write data to the external EEPROM, then read it back. There are no errors.

I've used the setup_spi command in the past doing chip-to-chip and wireless communications with the NRF24 transceiver, but didn't have any luck with the external EEPROM. In the example code provided I've set the SSP1STAT and SSPCON1 registers directly. Please note that only FOSC/4 seems to not hang up. All other faster timings cause the program to hang.

The end result is that the receive data array is populated entirely with 0's.

I'm pretty sure I'm doing something stupid because it seems pretty much straight forward. Any help will be appreciated.

Thanks - Tim

Code:

/*
 * File:   ExternalEEPROM.c
 * Author: tim
 *
 * Created on February 10, 2021, 3:10 PM
 */
#include <16F1788.h>

/***********************    define functions    *******************************/
void externalSPIwrite(int addmsbIN, int16 addressIN, int numBytesIN, char *dataIN);
void externalSPIread(int addmsbIN, int16 addressIN, int16 numBytesIN, char *dataIN);

/***********************    define constants    *******************************/
#define DUMMY_VALUE             0                                               // SPI throwaway value - used as a 2nd SPI transmit value that gets ignored with some SPI commands

/***********************    define #USE settings    ***************************/
#USE FAST_IO(ALL)
#USE DELAY(CLOCK = 20Mhz)
#USE PWM(OUTPUT = PIN_C1, FREQUENCY = 10kHz)

/***********************    external EEPROM defines    ************************/
    // define external EEPROM commands
#define EXT_EEPROM_WRSR         1                                               // 0000 0001 - write status register
#define EXT_EEPROM_WRITE        2                                               // 0000 0010 - write data to memory beginning at selected address
#define EXT_EEPROM_READ         3                                               // 0000 0011 - read data to memory array beginning at selected address
#define EXT_EEPROM_WRDI         4                                               // 0000 0100 - reset the write enable latch (disable write operations)
#define EXT_EEPROM_RDSR         5                                               // 0000 0101 - read status register
#define EXT_EEPROM_WREN         6                                               // 0000 0110 - set the write enable latch (enable write operations)
#define EXT_EEPROM_CE           199                                             // 1100 0111 - erase external EEPROM memory (chip erase)

    // define external EEPROM pins
#define EXT_EEPROM_CS           PIN_A6                                          // external EEPROM CS pin

    //SPI registers - SSP1STAT & SSPCON1
#byte   MCU_SSP1STAT    =   0x214                                               // SSP1STAT register address
#bit    MCU_CKE         =   MCU_SSP1STAT.6                                      // SSP1STAT register bit 6
#bit    MCU_SMP         =   MCU_SSP1STAT.7                                      // SSP1STAT register bit 7
#byte   MCU_SSPCON1     =   0x215                                               // SSPCON1 register address
#bit    MCU_SSMP0       =   MCU_SSPCON1.0                                       // SSPCON1 register bit 0
#bit    MCU_SSMP1       =   MCU_SSPCON1.1                                       // SSPCON1 register bit 1
#bit    MCU_SSPM2       =   MCU_SSPCON1.2                                       // SSPCON1 register bit 2
#bit    MCU_SSPM3       =   MCU_SSPCON1.3                                       // SSPCON1 register bit 3
#bit    MCU_CKP         =   MCU_SSPCON1.4                                       // SSPCON1 register bit 4
#bit    MCU_SSPEN       =   MCU_SSPCON1.5                                       // SSPCON1 register bit 5

/*
 * Program to test writing to and reading from external EEPROM.
 * This code was copied and slightly modified from an example @:
 *      http://hades.mech.northwestern.edu/index.php/Interfacing_PIC_with_SPI_memory
 *
 * The external EEPROM is - Microchip 25AA160B EEPROM
 * The compiler is CCS v5.075
 * The IDE is MPLAB X IDE v5.30
 *
 */
void main()
{
        // Setup SPI - example settings
//    MCU_SMP = 1;                                                                // data sampled at end of data output time
//    MCU_CKE = 1;                                                                // transmit occurs on transmission from active to idle clock state
//    MCU_CKP = 0;                                                                // idle state for clock is low level
//    MCU_SSPEN = 1;                                                              // enables serial port and enables SCK, SDO, SDI, and /SS as the source of the serial port pins
//    MCU_SSMP0 = 0;                                                              // clock = FOSC/4
//    MCU_SSMP1 = 0;
//    MCU_SSPM2 = 0;
//    MCU_SSPM3 = 0;
/******************************************************************************/

        // Setup SPI
        //      SSP1STAT register
    MCU_SMP = 0;                                                                // data sampled at middle of data output time
    MCU_CKE = 0;                                                                // transmit occurs on transmission from idle to active clock state

        // SSPCON1 register
    MCU_CKP = 1;                                                                // idle state for clock is a high level
    MCU_SSPEN = 1;                                                              // enables serial port and enables SCK, SDO, SDI, and /SS as the source of the serial port pins
                                                                                // SSMP (bits 0 - 3) 0000 - SPI Master mode, clock = FOSC/4
    MCU_SSMP0 = 0;                                                              //
    MCU_SSMP1 = 0;                                                              //
    MCU_SSPM2 = 0;                                                              //
    MCU_SSPM3 = 0;                                                              //

//    setup_spi(SPI_MASTER | SPI_XMIT_L_TO_H | SPI_SAMPLE_AT_MIDDLE | SPI_CLK_DIV_16);

        // define read/write buffers
    unsigned char eepromReadBuf[16] = {101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116};
    unsigned char eepromWriteBuf[16] = {201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216};

    while(TRUE)
    {
        externalSPIwrite(0, 0, 16, eepromWriteBuf);
        externalSPIread(0, 0, 16, eepromReadBuf);
    }
}

/* externalEEPRONMisWriting
 *
 * Checks to see if the chip is currently writing to memory
 *
 * Reads the external EEPROM status register to determine if the last write operation is still being processed
 *
 * No input parameters
 * Return value:
 *      returns int -   0 = writing
 *                      1 = not writing
 */
unsigned int externalEEPROMisWriting()
{
    int spiResult;                                                              // result value of SPI read status register.
    output_low(EXT_EEPROM_CS);                                                  // begin SPI session
    spi_write(EXT_EEPROM_RDSR);                                                 // issue read status register command
    spiResult = spi_read(DUMMY_VALUE);                                          // return status register and store in spiResult
    output_high(EXT_EEPROM_CS);                                                 // finish SPI Session
    return (spiResult & 0x1);                                                   // return result
}

/* externalSPIread
 *
 * Reads sequential bytes in memory, starting at addressIN.
 * Bytes are stored into *dataIN, read until numBytesIN is reached
 *
 * Input Parameters:
 *      addmsbIN    8 bit integer   -   address most significant byte
 *      addressIN   16 bit integer  -   address to read
 *      numBytesIN  16 bit integer  -   number of bytes to read
 *      *dataIN     char *          -   pointer to the location of data array to be stored
 *
 * No values are returned
 */
void externalSPIread(int addmsbIN, int16 addressIN, int16 numBytesIN, char *dataIN)
{
    char *bufptr;                                                               // char pointer to the input data array
    output_low(EXT_EEPROM_CS);                                                  // begin SPI session

    spi_write(EXT_EEPROM_READ);                                                 // issue external EEPROM read command. Follow with:
    spi_write(addmsbIN);                                                        //      address most significan byte
    spi_write(addressIN >> 8);                                                  //      16 bit address with the 5 MSBs being don't care bits
    spi_write(addressIN);                                                       // I don't understand this!!!

        // loop thru memory and continue reading data
    for(bufptr = dataIN; bufptr < dataIN + numBytesIN; bufptr++)
    {
       *bufptr = spi_read(DUMMY_VALUE);
    }

    output_high(EXT_EEPROM_CS);                                                 // end the SPI session
}

/* externalSPIwrite
 *
 * Writes sequential data starting at addressIN
 *      Note:   Caller must ensure page boundary is not passed!
 *              Page size is 32 bytes.
 *
 * Input Parameters:
 *      addmsbIN    8 bit integer   -   address most significant byte
 *      addressIN   16 bit integer  -   address to write
 *      numBytesIN  16 bit integer  -   number of bytes to write
 *      *dataIN     char *          -   pointer to the location of data array to be read
 *
 * No values are returned
 */
void externalSPIwrite(int addmsbIN, int16 addressIN, int numBytesIN, char *dataIN)
{
    char *bufptr;                                                               // char pointer to the input data array

        // Wait for last write to finish
    while(externalEEPROMisWriting())
    {
       delay_us(1);
    }

    output_low(EXT_EEPROM_CS);                                                  // begin SPI session
    spi_write(EXT_EEPROM_WREN);                                                 // write enable latch
    output_high(EXT_EEPROM_CS);                                                 //latch
    delay_us(5);

    output_low(EXT_EEPROM_CS);                                                  // begin SPI session
    spi_write(EXT_EEPROM_WRITE);                                                // Send write command. Follow with:
    spi_write(addmsbIN);                                                        //      address most significan byte

    spi_write(addressIN >> 16);                                                 // I don't understand this!!!
    spi_write(addressIN);                                                       // I don't understand this!!!

        // loop thru memory and continue writing data
    for (bufptr = dataIN; bufptr < dataIN + numBytesIN; bufptr ++)
    {
       spi_write(*bufptr);
    }
    spi_write(EXT_EEPROM_WRDI);                                                 // write enable latch
    output_high(EXT_EEPROM_CS);                                                 // end SPI session
}


Last edited by tgilbert on Fri Feb 12, 2021 1:30 pm; edited 1 time in total
temtronic



Joined: 01 Jul 2010
Posts: 8027
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Wed Feb 10, 2021 5:48 pm     Reply with quote

Ok....silly question....
gotta ask about the hardware

is the '_hold' pin pulled up ??

I downloaded the EEP PDF and there's a 'hold' pin.....that's a new one to my old eyes.....

I assume _WP is held high ?

...always look at hardware first.....
maybe 2 pins got reversed by accident ?

Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 17026

View user's profile Send private message

PostPosted: Thu Feb 11, 2021 2:06 am     Reply with quote

There are several issues with what you show. It looks as if you have
taken code from somewhere and are using it without looking at what it
actually 'does', or understanding it.

Now first thing. On the PIC, if you perform 'spi_write', this loads a byte
into the output register, _and returns immediately_. At this point
the byte has not sent. It is just starting to be clocked out.
So this code:
Code:

    output_low(EXT_EEPROM_CS);                                                  // begin SPI session
    spi_write(EXT_EEPROM_WREN);                                                 // write enable latch
    output_high(EXT_EEPROM_CS);

Has the CS being raised after just a couple of bits of the instruction have
been sent. Result, is that the instruction will not be accepted.
If you look at all the commands, the CS must not go up until after the
byte has actually been clocked out.....

This is why it is 'better', to use spi_read to send a byte, rather than
spi_write. If you instead have:
Code:

    int8 dummy;
    output_low(EXT_EEPROM_CS);                                                  // begin SPI session
    dummy=spi_read(EXT_EEPROM_WREN);                                                 // write enable latch
    output_high(EXT_EEPROM_CS);


Using the read forces the compiler to wait for the reply to actually be clocked
in from the chip (which will be garbage), but ensures the transfer completes
before raising the CS.

Now there are other issues.
In the externalSPIWrite function, instead of shifting the write address by
eight, you shift it by 16. Since this is a 16bit address, the result will be
0 being sent. Wrong.

Then you have the read and write functions being given a 16bit address,
and also an eight bit MSB. Why?. You are already using rotations to access
the MSB. In the write function you send a command, then 24bits of
address data. The chip only expects 16.... Sad

I've re-written the write function to show the changes. The rest need
similar updates:
Code:

void startWriteSession(int16 addressIN)
{
    //Start a write transaction
    int dummy;
    output_low(EXT_EEPROM_CS);     // begin SPI session
    dummy=spi_read(EXT_EEPROM_WREN);  // set write enable latch
    output_high(EXT_EEPROM_CS);   //latch
    //delay_us(5); No delay needed. The CS hold time is only 100nSec

    output_low(EXT_EEPROM_CS);    // begin SPI session
    spi_write(EXT_EEPROM_WRITE); // Send write command. Follow with:
    spi_write(addressIN >> 8;          // Send MSB of address first
    dummy=spi_read(addressIN);     // Now the LSB, and wait for this
    //to physically send.
}

void externalSPIwrite(int16 addressIN, int numBytesIN, char *dataIN)
{
    int8 counter;  //counter for the transfer. If this was to be a 'pointer'
    //you would need to add the dataIN value to this, not start at 0
    //as you were.                                                       
    int8 dummy;
    // Wait for last write to finish
    while(externalEEPROMisWriting())
    {
       delay_us(1);
    }
    startWriteSession(addressIN); //start the write session

    // loop thru memory and write data
    for (bufptr = 0; bufptr < numBytesIN; bufptr ++)
    {
         dummy=spi_read(dataIn[bufptr]);
         //send the byte - now need to check if we are crossing a page
         addressIN++; //next address
         if ((addressIN%32)==0)
         {
             //we have reached the end of a write page
             //need to trigger the write and load a new address
             output_high(EXT_EEPROM_CS);//trigger
             while(externalEEPROMisWriting())
             {
                 delay_us(1); //wait to complete
             }
             //Now trigger a new write sequence
             startWriteSession(addressIN);
             //can now continue
        }
    }
    output_high(EXT_EEPROM_CS);       // end SPI session
    //WRDI is automatically set whenever the write is triggered.
}


Now, since your 'write' supports sending more than one byte, you have
to cope with the situation where this crosses a boundary in the chip.
On the 'B' 32 bytes, while the 'A' chips only 16bytes.
PCM programmer



Joined: 06 Sep 2003
Posts: 21394

View user's profile Send private message

PostPosted: Thu Feb 11, 2021 5:03 am     Reply with quote

Ttelmah wrote:

It looks as if you have taken code from somewhere

Ttelmah,
He got the original code from here:
http://hades.mech.northwestern.edu/index.php/Interfacing_PIC_with_SPI_memory
Ttelmah



Joined: 11 Mar 2010
Posts: 17026

View user's profile Send private message

PostPosted: Thu Feb 11, 2021 5:26 am     Reply with quote

Problem is that is for a different memory, so larger page size,
and the functions are not the CCS ones, so probably do wait for the
transmit to complete on the write... Sad
tgilbert



Joined: 14 Aug 2020
Posts: 8

View user's profile Send private message

PostPosted: Fri Feb 12, 2021 6:12 am     Reply with quote

Quote:
is the '_hold' pin pulled up ??


Jay,

Yes, thanks for that. I should have posted that on the original message. The /HOLD pin is held high at 5v. It shares the power provided to the VCC pin.

Also, I should have posted the FUSES that I use:
Code:

/********************     fuses       *********************/
#FUSES NOWDT        // No Watch Dog Timer
#FUSES NOPUT        // No Power Up Timer
#FUSES NOPROTECT    // Code not protected from reading
#FUSES NOBROWNOUT   // brownout detection OFF
#FUSES NOMCLR
#FUSES INTRC_IO
tgilbert



Joined: 14 Aug 2020
Posts: 8

View user's profile Send private message

PostPosted: Fri Feb 12, 2021 6:20 am     Reply with quote

Also, I made an error on the original post. The clock is actually 32Mhz so the following change has to be made:

#USE DELAY(CLOCK = 32Mhz)

Sorry. The code compiles for me. Ttelmah is right, I copied it from a project I've been working on and just copied out the parts needed for the example.

I'll continue to read the rest of the replies and implement the suggestions. I'll post again with what I find.

Thanks to everyone for the help.

Tim
tgilbert



Joined: 14 Aug 2020
Posts: 8

View user's profile Send private message

PostPosted: Fri Feb 12, 2021 8:19 am     Reply with quote

It works!!!!! I've done as you've asked and also updated the read function and can now read the values that have been sent. I'm so excited!

Many thanks to Ttelmah, Jay, and also PCM_Programmer. Spending the time to make the example goes above and beyond in my view. I've spent so much time and tried multiple different drivers etc. with no success. This is a major burden off my shoulder...

You guys are great! Just a couple of days ago I found a solution to a different problem that PCM_Programmer had posted in 2016 (as many other problems as well). I'm really glad you guys are there. I could not do this alone...

Clearly I need to build a better understanding of C in general. I'm just an old uneducated hobbyist trying to do some work for a local business, so I'll really try not to bother you too much in the future.

Thank you so much!
Tim

P.S. - I'll see if I can figure out how to close this topic and mark it as successful.
Ttelmah



Joined: 11 Mar 2010
Posts: 17026

View user's profile Send private message

PostPosted: Sat Feb 13, 2021 12:51 am     Reply with quote

Well done.

It is nice to have a thing that starts, is solved, and is then flagged as such. Smile

Happy programming. Very Happy
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group