|
|
View previous topic :: View next topic |
Author |
Message |
mvanvliet
Joined: 02 Jun 2009 Posts: 123 Location: The Netherlands
|
16F1789 with 23LC1024 SRAM over SPI |
Posted: Mon Feb 05, 2018 5:27 am |
|
|
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
|
|
Posted: Mon Feb 05, 2018 6:39 am |
|
|
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
|
|
Posted: Mon Feb 05, 2018 6:47 am |
|
|
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
|
|
Posted: Mon Feb 05, 2018 9:40 am |
|
|
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
|
|
Posted: Mon Feb 05, 2018 9:44 am |
|
|
Yes you were both right. Mode=0 solved my problem. Thanks! |
|
|
mvanvliet
Joined: 02 Jun 2009 Posts: 123 Location: The Netherlands
|
|
Posted: Wed Feb 07, 2018 8:06 am |
|
|
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
|
|
Posted: Wed Feb 07, 2018 8:37 am |
|
|
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
|
|
Posted: Wed Feb 07, 2018 11:19 am |
|
|
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
|
|
Posted: Thu Feb 08, 2018 3:03 am |
|
|
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
|
|
Posted: Thu Feb 08, 2018 4:32 am |
|
|
_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
|
|
Posted: Thu Feb 08, 2018 8:58 am |
|
|
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
|
|
Posted: Thu Feb 08, 2018 12:16 pm |
|
|
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
|
|
Posted: Fri Feb 09, 2018 8:57 am |
|
|
It seems that your code is working correctly, thank you for your effort. |
|
|
|
|
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
|