|
|
View previous topic :: View next topic |
Author |
Message |
Bill24
Joined: 30 Jun 2012 Posts: 45
|
Porting SPI Eeprom Driver from Microchip |
Posted: Tue Nov 13, 2012 1:10 pm |
|
|
I have been trying to port some code from a C18 compiler to read and write to an eeprom via SPI.
I can write data out and see the data and the clock on an oscilloscope. However I do not get data being clocked into the SSP.
My theory is that the SI pin from the eeprom is not being connected to the SSP in the PIC.
I have searched through many examples but have so far not found out how to do this.
Could someone please show me how to make the PIC SI pin (RC4) connect to the SSP rather that be a general IO pin.
I am not using the CCS SPI library as I need to port some old legacy code.
Thanks. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9229 Location: Greensville,Ontario
|
|
Posted: Tue Nov 13, 2012 1:24 pm |
|
|
It'd really help us help you if you said which PIC you're using as well as compiler version, might as well say which eeprom too.....
The more information you supply the faster someone here can reply with a solution.
hth
jay |
|
|
Bill24
Joined: 30 Jun 2012 Posts: 45
|
|
Posted: Tue Nov 13, 2012 4:08 pm |
|
|
temtronic wrote: | It'd really help us help you if you said which PIC you're using as well as compiler version, might as well say which eeprom too.....
The more information you supply the faster someone here can reply with a solution.
hth
jay |
Oops, I am a bit too wrapped up in the problem.
The eeprom is a 25LC256 on a PIC18 explorer board. The PIC is a PIC18F66K80 although I would also like the software to support a PIC18F66K22. I believe the MSSP is the same in both devices.
I have previously ported a Microchip SPI application to drive an SPI parallel port expander chip which just transmits SPI data. For the eeprom, I can see SPI data being transmitted by the PIC. I am struggling with getting an SPI reply back. The code is based on Microchip C18 compiler and I am trying to convert it to use the CCS library.
Compiler version is 4.135. |
|
|
n-squared
Joined: 03 Oct 2006 Posts: 99
|
|
Posted: Tue Nov 13, 2012 11:27 pm |
|
|
Hi
Which pins of the MCU are connected to the EEPROM's SS,SCK,DO and DI?
Are they the SPI hardware pins (except the SS which is a regular I/O pin)?
Do you actually "see" the data coming back to RC4 using an oscilloscope?
Do you control the SS (chip select)?
It would be much better if you posted the code concerned, including the header file (#use SPI).
BR
NN _________________ Every solution has a problem. |
|
|
Bill24
Joined: 30 Jun 2012 Posts: 45
|
|
Posted: Wed Nov 14, 2012 2:38 am |
|
|
n-squared wrote: | Hi
Which pins of the MCU are connected to the EEPROM's SS,SCK,DO and DI?
Are they the SPI hardware pins (except the SS which is a regular I/O pin)?
Do you actually "see" the data coming back to RC4 using an oscilloscope?
Do you control the SS (chip select)?
It would be much better if you posted the code concerned, including the header file (#use SPI).
BR
NN |
Please see the code below. It is all in one file for simplicity. The pins on the demo board are SO - RC4, SI - RC5, CLK - RC3 and CS - RA3.
I am not sure if data is coming back from the eeprom. The line of code 'while(! bit_test(SPI1STAT,BUF_FULL_BIT));' never completes so it seems the receive buffer fuul bit is not set.
I have changed this to 'while(! bit_test(PIR1,SSP1IF_BIT));'
However the line of code 'while (ReadSR() & 0x1); // check WIP'
does complete.
Any help appreciated.
Code: | #include <18F66K80.h>
#device ICD=TRUE
#device adc=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES VREGSLEEP_SW //Ultra low-power regulator is enabled
#FUSES INTRC_LP //LF-INTOSC in Low-Power mode during Sleep
#FUSES SOSC_DIG //Digital mode, I/O port functionality of RC0 and RC1
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES PUT //Power Up Timer
#FUSES BORM_LOW //Low-power BOR
#FUSES WDT_NOSLEEP //Watch Dog Timer, disabled during SLEEP
#FUSES DEBUG //Debug mode for use with ICD
#use delay(int=4000000,RESTART_WDT)
#USE FAST_IO(a)
#USE FAST_IO(c)
#USE FAST_IO(d)
// SPI registers
#word SPI1BUF = 0x0FC9 // SPI1BUF
#word SPI1CON1 = 0x0FC6 // SPI1CON1
#word SPI1STAT = 0x0FC7 // SPI1STAT
#word PIR1 = 0x0F9E // PIR1
#define SSP1IF_BIT 0x03
#define BUF_FULL_BIT 0x01
// peripheral configurations
#define CSEE PIN_A3 // select line for Serial EEPROM
// 25LC256 Serial EEPROM commands
#define SEE_WRSR 1 // write status register
#define SEE_WRITE 2 // write command
#define SEE_READ 3 // read command
#define SEE_WDI 4 // write disable
#define SEE_STAT 5 // read status register
#define SEE_WEN 6 // write enable
#define EE_PAGE_SIZE 64
// Serial EEPROM read/write functions
int write_spiee(unsigned int eeaddr, void *src, unsigned int count);
int read_spiee(unsigned int eeaddr, void *target, unsigned int count);
// test data
char dng[256]; // array of bytes
unsigned int ui;
void main(void)
{
int i;
// Set PIN_A3 (EEPROM chip sel) to output.
set_tris_a(0b11110011);
// Set Pin_C3 (EEPROM clk ) and pin_C5 (EEPROM si) to output.
set_tris_c(0b11010000);
// Set PIN_D0 to output for LED's on PIC18 demo.
set_tris_d(0x0FE);
// Setup SPI -----------------------------------
// Set SSPM1 bit and SSPEN bit
SPI1CON1 = 0x22;
// Set CKE bit
SPI1STAT= 0x40;
// Set Master Synchronous Serial Port Interrupt Flag bit bit in PIR1 to 0
bit_clear(PIR1,SSP1IF_BIT);
output_high(CSEE);
// preload some data
for (i=0; i<255;i++)
dng[i] = i;
ui = 0x1234;
// write various objects to the Serial EEPROM
write_spiee(2, &ui, sizeof(ui));
write_spiee(101, &dng, sizeof(dng));
// clear all data..
for (i=0; i<255;i++)
dng[i] = 0;
ui = 0;
// read various objects from the Serial EEPROM
read_spiee(2, &ui, sizeof(ui));
read_spiee(101, &dng, sizeof(dng));
}
//////////////////////////////////////////////////////////////////////////////
// write 8-bit data to Serial EEPROM
int WriteSPI(int data)
{
SPI1BUF = data; // write to buffer for TX
while(! bit_test(PIR1,SSP1IF_BIT));
//while(! bit_test(SPI1STAT,BUF_FULL_BIT)); // wait for the transfer to complete
return SPI1BUF; // return the value received.
}
// Check the Serial EEPROM status register
int ReadSR(void) {
int i;
output_low(CSEE); // select the Serial EEPROM
WriteSPI(SEE_STAT); // send Read Status command
i = WriteSPI(0); // send dummy, read status
output_high(CSEE); // deselect Serial EEPROM
return i; // return status
}
// send a Write Enable command
void WriteEnable(void) {
output_low(CSEE); // select the Serial EEPROM
WriteSPI(SEE_WEN); // send Write Enabled command
output_high(CSEE); // deselect Serial EEPROM
}
//
// this routine supports spi eeproms up to 32k bytes in size that use 2-byte addresses
// to support a 64k device, change count, num_bytes, and bytes_remaining to 'unsigned long'
// to support larger devices, change eeaddr to 'unsigned long', and change the routine to send
// out the address bytes to the device accordingly.
//
int write_spiee(unsigned int eeaddr, void *src, unsigned int count)
{
unsigned char *saddr; // pointer to data to write
unsigned int num_bytes; // bytes to write per iteration
unsigned int bytes_remaining; // total bytes to write
int i;
// get ptr to the data (not really needed... could just cast src, but makes it easier to read)
saddr = (unsigned char *)src;
bytes_remaining = count;
while (bytes_remaining)
{
// limit number of bytes written per loop iteration to either
// - numer of bytes to end of page
// - a full page
// - remaining partial page
// figure out how many bytes to end of current page
num_bytes = EE_PAGE_SIZE - (eeaddr % EE_PAGE_SIZE);
// check to see if we want to limit bytes written this iteration
if (bytes_remaining < num_bytes)
num_bytes = bytes_remaining;
// send data to eeprom
// wait until any work in progress is completed
while (ReadSR() & 0x1); // check WIP
// set the write enable latch
WriteEnable();
// perform write
output_low(CSEE); // select the Serial EEPROM
WriteSPI(SEE_WRITE); // write command
WriteSPI(eeaddr >> 8); // address MSB first
WriteSPI(eeaddr & 0xff); // address LSB
for (i=num_bytes; i; i--)
WriteSPI(*saddr++);
output_high(CSEE); // deselect the Serial EEPROM
bytes_remaining -= num_bytes;
eeaddr += num_bytes;
}
// return status
// 0=ok, others = timeout, size error, etc (not implemented)
return 0;
}
//
// this routine supports spi eeproms up to 32k bytes in size that use 2-byte addresses
// to support a 64k device, change count, num_bytes, and bytes_remaining to 'unsigned long'
// to support larger devices, change eeaddr to 'unsigned long', and change the routine to send
// out the address bytes to the device accordingly.
//
int read_spiee(unsigned int eeaddr, void *target, unsigned int count)
{
unsigned char *taddr; // pointer to data to write
// get ptr to the data (not really needed... could just cast target, but makes it easier to read)
taddr = (unsigned char *)target;
// read data from eeprom
// wait until any work in progress is completed
while (ReadSR() & 0x1); // check WIP
// select the Serial EEPROM and send address
output_low(CSEE); // select the Serial EEPROM
WriteSPI(SEE_READ); // send Read command
WriteSPI(eeaddr>>8); // address MSB first
WriteSPI(eeaddr & 0xff); // address LSB (word aligned)
while (count--)
*taddr++ = WriteSPI(0); // send dummy, read data msb
output_high(CSEE); // deselect Serial EEPROM
// return status
// 0=ok, others = timeout, size error, etc (not implemented)
return 0;
} |
|
|
|
n-squared
Joined: 03 Oct 2006 Posts: 99
|
|
Posted: Wed Nov 14, 2012 3:11 am |
|
|
Hi
Why don't you use #USE SPI with spi_xfer()?
This alleviates the need to deal with the registers.
BR
NN _________________ Every solution has a problem. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Wed Nov 14, 2012 3:55 am |
|
|
Seriously, just use the CCS functions.....
However comments:
1) Use the CCS automatic register locating. Much less likely to go wrong.
So:
#word SPI1BUF = getenv("SSPBUF") // SPI1BUF
etc..
Same with the bits. Use them as bit variables, easier, and less likely again to go wrong.
Then there is a problem with your buffer full detection. You are using the SSPIF bit, but never clear it. Unlike the buffer full bit that will automatically clear when you read the register, the interrupt remains set till you clear it.....
Data coming back from the EEPROM, has nothing to do with the buffer full bit becoming set. It _will_ set whan the master device generates eight clocks, whatever is attached to the data in line. The fact the buffer will then contain garbage, doesn't prevent the bit being set.
Then there are problems with your variable sizes. 'int' in CCS, is an int8. Your 'address', and 'count' values _must_ be int16. Currently you call:
write_spiee(101, &dng, sizeof(dng));
'sizeof(dng)', is 256, which translates to 0 in an int8.......
The last is probably the 'killer'.
Best Wishes |
|
|
Bill24
Joined: 30 Jun 2012 Posts: 45
|
|
Posted: Wed Nov 14, 2012 4:56 am |
|
|
Ttelmah wrote: | Seriously, just use the CCS functions.....
However comments:
1) Use the CCS automatic register locating. Much less likely to go wrong.
So:
#word SPI1BUF = getenv("SSPBUF") // SPI1BUF
etc..
<SNIP>The last is probably the 'killer'.
Best Wishes |
Thanks for you suggestions but I am still having no success.
Some example code on this forum has
#pin_select SDI1 = PIN_C4
Could this be my problem ?
According to the help manual only 'SDI2' is a valid option but either way I get a compilation error:
#pin_select SDI1 = PIN_C4
Error 7 "main.c" Line 24(12,28): Invalid Pre-Processor directive Invalid Pin ID
Or
#pin_select SDI2 = PIN_C4
*** Error 44 "main.c" Line 24(12,22): Internal Error - Contact CCS.
Any suggestions ? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Wed Nov 14, 2012 5:28 am |
|
|
#PIN_SELECT _is only used on peripherals that are re-mappable_. Your chip does not offer this on the SPI, so what on earth are you doing trying to use it?. Of course it won't work, the hardware is not there to support it.... |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Nov 14, 2012 5:48 am |
|
|
Code: | read_spiee(101, &dng, sizeof(dng)); | The &dng is wrong too. When you want the address of an array you should use &dng[0], or just 'dng' by itself. Now you are creating a pointer to a pointer. |
|
|
Bill24
Joined: 30 Jun 2012 Posts: 45
|
|
Posted: Wed Nov 14, 2012 6:10 am |
|
|
Ttelmah wrote: | #PIN_SELECT _is only used on peripherals that are re-mappable_. Your chip does not offer this on the SPI, so what on earth are you doing trying to use it?. Of course it won't work, the hardware is not there to support it.... |
?? But the data sheet says e.g pin c4 defaults to I/O or can be selected for SDI1.
However I have taken your previous advice and can talk to the eeprom with the code below. This code was found on this forum for a PIC24. It still would not work for me unitil I set the ports with
set_tris_c(0b11010000);
set_tris_a(0b11110011);
Seems to work OK. Thanks for all of you assistence.
Code: | #include <18F8722.h>
#device ICD=TRUE
#device adc=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES PUT //Power Up Timer
#FUSES DEBUG //Debug mode for use with ICD
#use delay(int=4000000,RESTART_WDT)
#USE FAST_IO(a)
#USE FAST_IO(c)
#USE FAST_IO(d)
#USE SPI (MASTER, CLK=PIN_C3, DI=PIN_C4, DO=PIN_C5, ENABLE=PIN_A3, MODE=0, BITS=8, STREAM=SPI_1)
#define EEPROM_SELECT PIN_A3
#define EEPROM_ADDRESS long int
#define EEPROM_SIZE 32768
void init_ext_eeprom()
{
output_high(EEPROM_SELECT);
setup_spi(SPI_MASTER | SPI_XMIT_L_TO_H | SPI_CLK_DIV_4 );
}
//--------------------------------
unsigned int8 ext_eeprom_ready(void)
{
int8 data;
output_low(EEPROM_SELECT);
spi_write(0x05);
data = spi_read(0);
output_high(EEPROM_SELECT);
return(!bit_test(data, 0));
}
//--------------------------------
void write_ext_eeprom(EEPROM_ADDRESS address, BYTE data)
{
while(!ext_eeprom_ready());
output_low(EEPROM_SELECT);
spi_write(0x06);
output_high(EEPROM_SELECT);
output_low(EEPROM_SELECT);
spi_write(0x02);
spi_write(address >> 8);
spi_write(address);
spi_write(data);
output_high(EEPROM_SELECT);
}
//--------------------------------
BYTE read_ext_eeprom(EEPROM_ADDRESS address)
{
int8 data;
while(!ext_eeprom_ready());
output_low(EEPROM_SELECT);
spi_write(0x03);
spi_write(address >> 8);
spi_write(address);
data = spi_read(0);
output_high(EEPROM_SELECT);
return(data);
}
void main(void)
{
int16 SPI_Temp = 0;
// Set PIN_A3 (EEPROM chip sel) to output.
set_tris_a(0b11110011);
// Set Pin_C3 (EEPROM clk ) and pin_C5 (EEPROM si) to output.
set_tris_c(0b11010000);
// Set PIN_D0 to output for LED's on PIC18 demo.
set_tris_d(0x0FE);
init_ext_eeprom();
write_ext_eeprom(5, 4);
SPI_Temp = read_ext_eeprom(5);
while(1);
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Wed Nov 14, 2012 6:12 am |
|
|
Just stuffed together a version using the CCS functions, with added single byte read/writes:
Code: |
#include <18F66K80.h>
#device ICD=TRUE
#device adc=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES VREGSLEEP_SW //Ultra low-power regulator is enabled
#FUSES INTRC_LP //LF-INTOSC in Low-Power mode during Sleep
#FUSES SOSC_DIG //Digital mode, I/O port functionality of RC0 and RC1
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES PUT //Power Up Timer
#FUSES BORM_LOW //Low-power BOR
#FUSES WDT_NOSLEEP //Watch Dog Timer, disabled during SLEEP
#FUSES DEBUG //Debug mode for use with ICD
//Adjust to suit chip
#define SPI_MODE_0_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_0_1 (SPI_L_TO_H)
#define SPI_MODE_1_0 (SPI_H_TO_L)
#define SPI_MODE_1_1 (SPI_H_TO_L | SPI_XMIT_L_TO_H)
#define EE_CS PIN_A3
#define SELECT_EE() output_low(EE_CS)
#define DESELECT_EE() output_high(EE_CS);
//SPI configuration
// 25LC256 Serial EEPROM commands
#define SEE_WRSR 1 // write status register
#define SEE_WRITE 2 // write command
#define SEE_READ 3 // read command
#define SEE_WDI 4 // write disable
#define SEE_STAT 5 // read status register
#define SEE_WEN 6 // write enable
#define EE_PAGE_SIZE 64
void enable_ee_write(void){
SELECT_EE();
spi_write(SEE_WEN);
DESELECT_EE();
}
int1 ext_eeprom_ready(void) {
BYTE data;
SELECT_EE();
spi_write(SEE_STAT);
data = spi_read(0);
DESELECT_EE();
return(!bit_test(data, 0));
}
//Single byte write to EE
void ext_eeprom_writebyte(unsigned int16 address, BYTE data) {
while(!ext_eeprom_ready()); //wait for chip
enable_ee_write();
SELECT_EE();
spi_write(SEE_WRITE);
spi_write(make8(address,1)); //MSB of address
spi_write(make8(address,0)); //LSB
spi_write(data);
DESELECT_EE();
}
//singgle byte write to EE
BYTE ext_eeprom_readbyte(unsigned int16 address) {
BYTE data;
while(!ext_eeprom_ready());
SELECT_EE();
spi_write(SEE_READ);
spi_write(make8(address,1));
spi_write(make8(address,0));
data = spi_read(0);
DESELECT_EE();
return(data);
}
//Block write to EE
void ext_eeprom_writeblock(unsigned int16 eeaddr, char *src, unsigned int16 count) {
unsigned int num_bytes; // bytes to write per iteration
unsigned int16 bytes_remaining; // total bytes to write
int i;
bytes_remaining = count;
while (bytes_remaining > 0) { //Beware of doing a logic test for non zero
//The behaviour of this changes with ANSI compatible compilation.....
// limit number of bytes written per loop iteration to either
// - numer of bytes to end of page
// - a full page
// - remaining partial page
// figure out how many bytes to end of current page
num_bytes = EE_PAGE_SIZE - (eeaddr % EE_PAGE_SIZE);
// check to see if we want to limit bytes written this iteration
if (bytes_remaining < num_bytes)
num_bytes = bytes_remaining;
// send data to eeprom
// wait until any work in progress is completed
while (!ext_eeprom_ready());
// set the write enable latch
enable_ee_write();
// perform write
SELECT_EE();
spi_write(SEE_WRITE); // write command
spi_write(make8(eeaddr,1)); // address MSB first
spi_write(make8(eeaddr,0)); // address LSB
for (i=num_bytes; i; i--)
spi_write(*src++);
DESELECT_EE(); // deselect the Serial EEPROM
bytes_remaining -= num_bytes;
eeaddr += num_bytes;
}
}
//As above for read
void ext_eeprom_readblock(unsigned int16 eeaddr, char *target, unsigned int16 count) {
// read data from eeprom
// wait until any work in progress is completed
while (!ext_eeprom_ready());
// select the Serial EEPROM and send address
SELECT_EE(); // select the Serial EEPROM
spi_write(SEE_READ); // send Read command
spi_write(make8(eeaddr,1)); // address MSB first
spi_write(make8(eeaddr,0)); //LSB
while (count--)
*target++ = spi_read(0); // send dummy, read data
DESELECT_EE(); // deselect Serial EEPROM
}
void main() {
// test data
char dng[256]; // array of bytes
unsigned int16 ui; //Two byte value to send
unsigned int16 i;
setup_spi(SPI_MASTER | SPI_MODE_0_0 | SPI_CLK_DIV_4);
setup_comparator(NC_NC_NC_NC);
DESELECT_EE();
// preload some data
for (i=0; i<256;i++)
dng[i] = i;
ui = 0x1234;
//Block write
ext_eeprom_writeblock(101, dng, sizeof(dng));
//byte write
ext_eeprom_writebyte(2,make8(ui,0));
ext_eeprom_writebyte(3,make8(ui,1));
memset(dng,0,256); //clear the array
ui=0;
//and read back
ext_eeprom_readblock(101, dng, sizeof(dng));
//do the readback using block read for the second value
ext_eeprom_readblock(2, &ui, sizeof(ui));
//stop running off end
do {
} while(TRUE);
}
|
No guarantees, probably fault ridden, but shows how to substitute the CCS code for the Microchip code.
Best Wishes |
|
|
|
|
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
|