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

Porting SPI Eeprom Driver from Microchip

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



Joined: 30 Jun 2012
Posts: 45

View user's profile Send private message

Porting SPI Eeprom Driver from Microchip
PostPosted: Tue Nov 13, 2012 1:10 pm     Reply with quote

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: 9172
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Nov 13, 2012 1:24 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Nov 13, 2012 4:08 pm     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Tue Nov 13, 2012 11:27 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Nov 14, 2012 2:38 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Wed Nov 14, 2012 3:11 am     Reply with quote

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: 19365

View user's profile Send private message

PostPosted: Wed Nov 14, 2012 3:55 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Nov 14, 2012 4:56 am     Reply with quote

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: 19365

View user's profile Send private message

PostPosted: Wed Nov 14, 2012 5:28 am     Reply with quote

#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

View user's profile Send private message

PostPosted: Wed Nov 14, 2012 5:48 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Nov 14, 2012 6:10 am     Reply with quote

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: 19365

View user's profile Send private message

PostPosted: Wed Nov 14, 2012 6:12 am     Reply with quote

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
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