|
|
View previous topic :: View next topic |
Author |
Message |
tgilbert
Joined: 14 Aug 2020 Posts: 8
|
** SOLVED ** Problems writing/reading external EEPROM |
Posted: Wed Feb 10, 2021 3:38 pm |
|
|
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: 9244 Location: Greensville,Ontario
|
|
Posted: Wed Feb 10, 2021 5:48 pm |
|
|
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: 19545
|
|
Posted: Thu Feb 11, 2021 2:06 am |
|
|
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....
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: 21708
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Thu Feb 11, 2021 5:26 am |
|
|
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... |
|
|
tgilbert
Joined: 14 Aug 2020 Posts: 8
|
|
Posted: Fri Feb 12, 2021 6:12 am |
|
|
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
|
|
Posted: Fri Feb 12, 2021 6:20 am |
|
|
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
|
|
Posted: Fri Feb 12, 2021 8:19 am |
|
|
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: 19545
|
|
Posted: Sat Feb 13, 2021 12:51 am |
|
|
Well done.
It is nice to have a thing that starts, is solved, and is then flagged as such.
Happy programming. |
|
|
|
|
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
|