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 CCS Technical Support

16F1789 with 23LC1024 SRAM over SPI

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



Joined: 02 Jun 2009
Posts: 123
Location: The Netherlands

View user's profile Send private message

16F1789 with 23LC1024 SRAM over SPI
PostPosted: Mon Feb 05, 2018 5:27 am     Reply with quote

I'm trying to communicate with a 16F1789 with an Sram 23LC1024, but it will not be working as expected. Maybe one of you know what is wrong.

Compiler: V5.062

Simplified code:

Code:

#include <16F1789.H>


#Fuses HS,NOWDT
#Fuses NOPUT,MCLR,NOPROTECT,NOCPD,BROWNOUT
#Fuses NOCLKOUT,NOIESO
#Fuses NOFCMEN,NOWRT,NOVCAP
#Fuses PLL_SW,NOSTVREN,NOLPBOR,NODEBUG
#Fuses NOLVP

#Device ADC=8
#use delay(clock=18432000)

#use spi(Master, BITS=8, SPI1)

#include "LCD_MIDAS_4x20.c"
#include "IOpins_IC2.c"

delay_ms(500);
lcd_init();
delay_ms(500);

output_high(CS1);

printf(lcd_putc, "\f");

output_low(CS1);
SPI_write(0x01); //mode register
SPI_write(0x00); //use "byte" mode
output_high(CS1);


while(1)
{
//write SRAM
         output_low(CS1);
         SPI_write(0x02);  //write modus
         SPI_write(0x00);  //24 bits address
         SPI_write(0x00);  //24 bits address
         SPI_write(0x01);  //24 bits address
         SPI_write(0x11);  //Data
         output_high(CS1);


//Read SRAM
         output_low(CS1);
         SPI_write(0x03);
         SPI_write(0x00);
         SPI_write(0x00);
         SPI_write(0x01);

         int SPI_var = 10;
         SPI_var = SPI_read(0);
         
         output_high(CS1);
         
         printf(lcd_putc,"\f%u",SPI_var);
}


When I expect to write 0x11 to address 0x01 I read 255 on my LCD.
temtronic



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

View user's profile Send private message

PostPosted: Mon Feb 05, 2018 6:39 am     Reply with quote

quickly...
you need to select which 'mode' of SPI in the USE SPI(...options...)

there are 4. The device datasheet will either say or show what is required.

//spi modes
#define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_1 (SPI_L_TO_H)
#define SPI_MODE_2 (SPI_H_TO_L)
#define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H)

If you press F11 when your project is open the CCS manual appears, locate USE SPI and dbl click. The options will be displayed and what they mean.
I'm sure someone will reply with the correct answer and the SPI 'cheat' ( CCS to mode defines) . I'm not on the eng PC today.

edited: I've added the defines to help you.

Jay


Last edited by temtronic on Mon Feb 05, 2018 8:48 am; edited 1 time in total
mvanvliet



Joined: 02 Jun 2009
Posts: 123
Location: The Netherlands

View user's profile Send private message

PostPosted: Mon Feb 05, 2018 6:47 am     Reply with quote

Quote:
The MODE option is more or less a quick way to specify how the
stream is going to sample data. MODE=0 sets IDLE=0 and
SAMPLE_RISE. MODE=1 sets IDLE=0 and SAMPLE_FALL.
MODE=2 sets IDLE=1 and SAMPLE_FALL. MODE=3 sets IDLE=1
and SAMPLE_RISE. There are only these 4 MODEs.


Which pin is decribed for MODE? SDI/SDO?
Ttelmah



Joined: 11 Mar 2010
Posts: 19499

View user's profile Send private message

PostPosted: Mon Feb 05, 2018 9:40 am     Reply with quote

Both.

The #USE SPI will accept mode numbers, so you don't need the old defines posted.

Mode 0 & 1 have the clock idling low, 2 & 3 have the clock idling high. Then 0 & 2 have the data sampled on the rising edge, while 2 & 3 have it sampled on the falling edge.

EEPROM's have generally standardised on SPI mode 0.


#use spi(Master, MODE=0, BITS=8, SPI1)
mvanvliet



Joined: 02 Jun 2009
Posts: 123
Location: The Netherlands

View user's profile Send private message

PostPosted: Mon Feb 05, 2018 9:44 am     Reply with quote

Yes you were both right. Mode=0 solved my problem. Thanks!
mvanvliet



Joined: 02 Jun 2009
Posts: 123
Location: The Netherlands

View user's profile Send private message

PostPosted: Wed Feb 07, 2018 8:06 am     Reply with quote

Ok, this part is working, but now the second step, use the sequential mode of the sram to write a lot of bytes into the sram.

So I first changed the "byte" to "sequential" mode:

Code:
output_low(CS1);
SPI_write(0x01);        //mode register
//SPI_write(0x00);      //put SRAM in "byte" mode
SPI_write(0b01000000);  //put SRAM in "sequential" mode, bit 7 and 6 to 01
output_high(CS1);


Changed the write function:
Code:
         output_low(CS1);
         SPI_write(0x02);  //write modus
         SPI_write(0x00);  //24 bits address
         SPI_write(0x00);  //24 bits address
         SPI_write(0x00);  //24 bits address
         
         while(i = 0, i<250, i++)
            {
            SPI_write(i);  //8 bits Data
            }
           
         output_high(CS1);


And changed the read function:
Code:
         output_low(CS1);
         SPI_write(0x03); //Read instruction
         SPI_write(0x00); //24 bits address
         SPI_write(0x00); //24 bits address
         SPI_write(0x0A); //24 bits address
         
         while(i = 0, i<250, i++)
            {
            SPI_var = SPI_read(0);
            printf(lcd_putc,"\f%u",SPI_var);
            }
        output_high(CS1);


But I receive no usable data on my LCD, does someone see my fault?
Ttelmah



Joined: 11 Mar 2010
Posts: 19499

View user's profile Send private message

PostPosted: Wed Feb 07, 2018 8:37 am     Reply with quote

First, as a general comment, you should be using spi_xfer, not spi_write.

There are two separate 'families' of SPI coperation commands. The original set that had setup_spi, spi_read and spi_write. Then the 'newer' set that use #use spi, and spi_xfer. If you look at the manual, you will find that #use does not refer to spi_read/write, and setup_spi does not refer to spi_xfer.
You are 'getting away with it' in this case, because you are specifying the hardware SPI port, but it is an 'accident waiting to happen'....

Now using #USE, I wouldn't be setting the number of bits. If you leave this out, you can specify the bits in the instruction. So sending the address becomes:

Code:

#define WRITE_CMD 0x2


    int32 address=1;
    int8 dummy;
    while(1)
    {
//write SRAM
         output_low(CS1);
         SPI_xfer(WRITE_CMD,8);  //select write 8bit command
         SPI_xfer(address,24);       //send 24bit address
         dummy=SPI_xfer(0x11,8);  //send 8 bit Data
         output_high(CS1);


Now there is a critical thing to note (applies to spi_write as well). Note how I wait for the _return_ on the last write. Problem is that if you don't do this, given you are using the hardware, the spi_xfer or spi_write will exit as soon as the byte is loaded into the transmit shift register, and you will raise the CS before the byte has actually sent. This can easily result in the chip not accepting the data correctly.
Now it appears the chip is accepting the byte write correctly, but it is possible that this is resulting in it not accepting the register change correctly. Though it says the default is sequential, this may be the problem.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Feb 07, 2018 11:19 am     Reply with quote

mvanvliet wrote:

while(i = 0, i<250, i++)
{
SPI_var = SPI_read(0);
printf(lcd_putc,"\f%u",SPI_var);
}


But I receive no usable data on my LCD, does someone see my fault?

This is a high speed loop. It has no delay statement after the printf.
Example: delay_ms(500);
Also, you are clearing the screen each time with "\f".
You might see a high speed "buzzing" of characters in the upper left cell,
or you might see nothing.
mvanvliet



Joined: 02 Jun 2009
Posts: 123
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu Feb 08, 2018 3:03 am     Reply with quote

Ttelmah wrote:

Note how I wait for the _return_ on the last write.


Thank you for your reaction, can you explain your 'return' a bit more, is it
Code:
dummy=SPI_xfer(0x11,8);  //send 8 bit Data
this part what you mean? Are you reading the byte you've written to the sram so you're sure it is inside the SRAM or is it a different approach?

With the SPI_XMIT function I receive an error that I haven't used a STREAM, this is not an problem to use, but you haven't got a STREAM name, is is a different in compiler version or am I doing something wrong?

To PCM programmer: I receive an value (e.g. 95 or 233) which is not the one I expect, not an buzzing character. But I also have deleted the \f for testing.
Ttelmah



Joined: 11 Mar 2010
Posts: 19499

View user's profile Send private message

PostPosted: Thu Feb 08, 2018 4:32 am     Reply with quote

_Every_ SPI transaction involves a byte being sent on the DO line and another being received on the DI line. This may be meaningful or garbage.
Now if you send a byte, and don't ask for the return, the code loads the data to send into the output shift register, and _returns immediately_.
At this point, the data starts clocking out, but the code is already 'back'. If you then raise CS immediately, the result becomes 'chip dependant' on what it'll actually do (accept the byte, ignore the byte etc...).
Now if you ask the code to 'give you' the return value, the code works differently. It loads the byte to send, and _waits_ for this to send, then returns what came back. Now the value itself is normally garbage, but it is the act of 'asking for it', that changes how the code behaves, forcing the byte transmit to not return, till the byte has finished transmitting.
If you look at the chip data sheet, they only show CS being raised _after_ the last clock. Making it 'read' the dummy byte back, ensures this happens.

Now you only have to do this on the last transaction before you raise CS. If you call two or more write/xfers, the code waits on each till the transmit buffer is empty before loading the next value. To ensure the correct timing, you should always 'read back' the last return value before raising CS.
mvanvliet



Joined: 02 Jun 2009
Posts: 123
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu Feb 08, 2018 8:58 am     Reply with quote

Code:

#include <16F1789.H>

#Fuses HS,NOWDT
#Fuses NOPUT,MCLR,NOPROTECT,NOCPD,BROWNOUT
#Fuses NOCLKOUT,NOIESO
#Fuses NOFCMEN,NOWRT,NOVCAP
#Fuses PLL_SW,NOSTVREN,NOLPBOR,NODEBUG
#Fuses NOLVP

#Device ADC=8
#use delay(clock=18432000)
#use spi(Master, MODE=0, SPI1, STREAM=SRAM)

#include "LCD_MIDAS_4x20.c"
#include "IOpins_IC2.c"

int8  i;
int   SPI_var = 0;

int32 address=1;
int8 dummy = 0x11;

#define MODE_CMD 0x01
#define WRITE_CMD 0x02
#define READ_CMD 0x03

printf(lcd_putc, "\f");

output_low(CS1);
SPI_xfer(SRAM,MODE_CMD,8);   //select write 8bit command
//SPI_xfer(SRAM,0x00,8);  //Byte mode
SPI_xfer(SRAM,64,8); //Sequential mode
dummy=SPI_xfer(SRAM,0x11,8);  //send 8 bit Data
output_high(CS1);

while(1)
   {
         //write SRAM
         output_low(CS1);
         SPI_xfer(SRAM,WRITE_CMD,8);   //select write 8bit command
         SPI_xfer(SRAM,address,24);    //send 24bit address
        while(i=0,i<250,i++)
         {
         SPI_xfer(SRAM,i,8);
         }
         dummy=SPI_xfer(SRAM,0x11,8);  //send 8 bit Data
         output_high(CS1);

        //read SRAM
        output_low(CS1);
        SPI_xfer(SRAM,READ_CMD,8);   //select read 8bit command
        SPI_xfer(SRAM,address,24);    //send 24bit address
        spi_var = SPI_xfer(SRAM,i,8); //send 8 bit Data
        output_high(CS1);
       
        printf(lcd_putc,"%u",spi_var);
}


I'm trying to get it working with this code at this moment, but on my LCD I receive 17 (0x11), the value of the dummy variable instead of the 250 I expect.

The SRAM should be in sequential mode, so after the address function the data should be clocked in after each other.

This code is a bit simplified, so probably you can't compile it.
Ttelmah



Joined: 11 Mar 2010
Posts: 19499

View user's profile Send private message

PostPosted: Thu Feb 08, 2018 12:16 pm     Reply with quote

First, you never need to change mode.
The chip wakes in sequential mode, and this is all you ever need to use.
If you want to write one byte, you just do a one byte sequential mode transfer. You are making things much more complex than they need to be by fiddling with the mode setting.
Then use some structure. On the read, you can just read each byte as you send. Does no harm, since you have to wait for each transmission anyway.
Code:

#define MODE_CMD 0x01
#define WRITE_CMD 0x02
#define READ_CMD 0x03

void write_byte(unsigned int32 address, byte val)
{
    int8 dummy;
    output_low(CS1); //select chip
    spi_xfer(SRAM, WRITE_CMD, 8); //send write command
    spi_xfer(SRAM, address, 24); //send address
    dummy=spi_xfer(SRAM,val,8); //send byte and wait for it to send
    output_high(CS1); //deselect
}

byte read_byte(unsigned int32 address)
{
    int8 rval;
    output_low(CS1); //select
    spi_xfer(SRAM, READ_CMD, 8); //send read command
    spi_xfer(SRAM, address, 24); //and address
    rval=spi_xfer(SRAM, 0, 8); //clock dummy byte to get reply
    output_high(CS1); //and deselect
    return rval;
}

void write_block(unsigned int32 address, byte * block, int16 number)
{
    //send a block of data stored at 'block', to address.
    //number is how many bytes to send
    int8 dummy;
    if (number<1) return; //do nothing if no count
    output_low(CS1); //select chip
    spi_xfer(SRAM, WRITE_CMD, 8); //send write command
    spi_xfer(SRAM, address, 24); //send address
    //now need to loop through the data
    while (--number) //do all except last byte
    {
        spi_xfer(SRAM,*(block++),8); //send each byte
    }
    dummy=spi_xfer(SRAM,*(block++),8); //send last byte
    output_high(CS1); //and deselect
}

void read_block(unsigned int32 address, byte * block, int16 number)
{
    //read a block from address.
    //number is how many bytes to retrieve
    int8 dummy;
    if (number<1) return; //do nothing if no count
    output_low(CS1); //select chip
    spi_xfer(SRAM, READ_CMD, 8); //send write command
    spi_xfer(SRAM, address, 24); //send address
    //now need to loop through the data
    while (number--)
    {
        *(block++) = spi_xfer(SRAM,0,8); //read bytes
    }
    output_high(CS1); //and deselect
}

//Now use a test routine like:
    char test_block[250];
    int ctr;
    //to write one byte to address 0
    write_byte(0L, 0x11);

    for (ctr=0;ctr<250;ctr++)
    {
        test_block[ctr]=ctr;
    }
    //now write the block at address 1
    write_block(1L, test_block, 250);

    //Then to test read the byte at address 250 (this should be byte 249)
    ctr=read_byte(250L)

    //This should be the '249'
mvanvliet



Joined: 02 Jun 2009
Posts: 123
Location: The Netherlands

View user's profile Send private message

PostPosted: Fri Feb 09, 2018 8:57 am     Reply with quote

It seems that your code is working correctly, thank you for your effort.
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