![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
thibow
Joined: 11 Apr 2012 Posts: 30
|
Trouble using SPI Flash memory SST25VF010A |
Posted: Mon May 28, 2012 2:51 pm |
|
|
Hello,
I've been looking around to find any sample code to compare with what I am doing, but I couldn't find anything that solved my problem. I am simply trying to read the status register of the memory (SST25VF010A datasheet). I am using a PIC18LF2550, here is my code and the output (I am using CCS compiler and I made my own SPI read/write function) :
Code: | #include <18F2550.H>
#include <STDLIBM.H>
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,PLL2,CPUDIV4,NOVREGEN,NOMCLR
#use delay(clock=16000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C5,bits=8,STOP=1)
#define SPI_SS pin_A5
#define LED1 pin_A1
#define nHOLD pin_A2
#define nWP pin_A3
#byte SSPSTAT = 0xFC7
#bit SMP = SSPSTAT.7
#bit CKE = SSPSTAT.6
#bit D_A = SSPSTAT.5
#bit P = SSPSTAT.4
#bit S = SSPSTAT.3
#bit R_W = SSPSTAT.2
#bit UA = SSPSTAT.1
#bit BF = SSPSTAT.0
#byte SSPBUF = 0xFC9
unsigned char do_spi(unsigned char txdata); // my own SPI fundtion
char *mychar=NULL;
int i=0;
int length;
void init(void){
setup_spi(spi_master | spi_l_to_h | spi_clk_div_16 | spi_xmit_l_to_h);
output_HIGH(LED1); //Led lit
output_HIGH(SPI_SS); // SS initialized
output_HIGH(nHOLD); // no hold
output_HIGH(nWP); // no Write protection
}
void main (void){
init();
printf("\nInit OK"); //tells me the init went all right
mychar = malloc(length * sizeof(char)+1); /* space for length chars */
if (mychar == NULL) {
printf("Memory not allocated.\n");
} else {
delay_us(500); //wait
//******** INITIALIZE THE EEPROM FOR READING STATUS **************************
output_LOW(SPI_SS); //clear the chip select
do_spi(0x05); //Say I want to Read Status
mychar[0]=do_spi(0); //read it
delay_us(1000);
output_HIGH(SPI_SS);
printf("\n\r data 1: %c",mychar[0]); //Show me what you got through UART
free(mychar);
output_toggle(LED1);
}
}
unsigned char do_spi(unsigned char txdata)
{
SSPBUF = txdata;
while (!BF); // SSPSTAT.BF (when the buffer is full)
return SSPBUF;
}
|
and here is the output :
First of all, I don't understand why my Uart does not want to write correctly "Init" and write "¡Ê+‹" instead.. but this is not so relevant, then of course, as you can see, I don't get anything from the memory..
If you have any clue.
Thank you |
|
![](templates/subSilver/images/spacer.gif) |
newguy
Joined: 24 Jun 2004 Posts: 1903
|
|
Posted: Mon May 28, 2012 3:43 pm |
|
|
Couple things - there could be more - but the two things that jump out at me are:
- Implement the PIC's power up timer fuse (add PUT to your #fuses line). That should the fix the problem of the PIC not properly printing your "Init OK" line.
- Your main() lacks an infinite loop. The compiler automatically places a "hidden" sleep() command at the end of main(). Your code is hitting this sleep command and the processor is going to sleep before the UART has a chance to transmit all remaining data. Simply add a while (TRUE) {} to the end of your code to avoid this hidden sleep() command. |
|
![](templates/subSilver/images/spacer.gif) |
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue May 29, 2012 12:39 am |
|
|
A few more notes:
1) The call to malloc uses a variable 'length' which is uninitialized.
2) Use of malloc in an embedded application is not recommended because memory fragmentation could cause problems in the long run (and embedded is supposed to run very long).
3) Use pin C7 for RS232 transmit instead of C5. Now the compiler will generate a software UART which is not as good as a hardware one.
4) Always add ERRORS to the #use RS232 definition for hardware UARTs, this causes the compiler to add code for clearing error flags. Otherwise the UART will stall after receive buffer overflows.
5) What is the level of the HOLD# pin? It should be HIGH.
6)
Code: | setup_spi(spi_master | spi_l_to_h | spi_clk_div_16 | spi_xmit_l_to_h); | This setup looks right to me, but CCS made it a bit difficult to read. Easier is to add defines for the 4 SPI modes: Code: | #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)
| Your Flash chip operates at either mode 0 or 3 and the configuration can then be setup like: Code: | setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_16); |
|
|
![](templates/subSilver/images/spacer.gif) |
thibow
Joined: 11 Apr 2012 Posts: 30
|
|
Posted: Tue May 29, 2012 7:24 am |
|
|
Hello,
@newguy: Thank you for your help, I have added "PUT" to the fuses and a while(1); at the end of my main, but I still got nothing as output (plus the microcontroller reset as it prints the "data1" twice..)
output:
Quote: | ¡Ê+‹OK
data 1:
¡Ê+‹OK
data 1: |
edit :
@ckielstra thank you for your help, I'll try this right now |
|
![](templates/subSilver/images/spacer.gif) |
thibow
Joined: 11 Apr 2012 Posts: 30
|
|
Posted: Tue May 29, 2012 7:51 am |
|
|
ckielstra wrote: | A few more notes:
1) The call to malloc uses a variable 'length' which is uninitialized.
2) Use of malloc in an embedded application is not recommended because memory fragmentation could cause problems in the long run (and embedded is supposed to run very long). |
You're right, I'll use a simple array.
Quote: |
3) Use pin C7 for RS232 transmit instead of C5. Now the compiler will generate a software UART which is not as good as a hardware one.
|
As I prefer to use SW USART than SW SPI, and both SPI and UART use the same pin, I had to make a choice ;)
Quote: |
4) Always add ERRORS to the #use RS232 definition for hardware UARTs, this causes the compiler to add code for clearing error flags. Otherwise the UART will stall after receive buffer overflows.
|
Ok this is done
Quote: |
5) What is the level of the HOLD# pin? It should be HIGH.
|
this is high as stated in the init() function "output_HIGH(nHOLD);"
Quote: |
6)
Code: | setup_spi(spi_master | spi_l_to_h | spi_clk_div_16 | spi_xmit_l_to_h); | This setup looks right to me, but CCS made it a bit difficult to read. Easier is to add defines for the 4 SPI modes: Code: | #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)
| Your Flash chip operates at either mode 0 or 3 and the configuration can then be setup like: Code: | setup_spi(SPI_MASTER | SPI_MODE_0 | SPI_CLK_DIV_16); |
|
Ok this is done too, thank you very much, but still.., it doesn't work
here is my code
Code: | #include <18F2550.H>
#include <STDLIBM.H>
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,PLL2,CPUDIV4,NOVREGEN,NOMCLR, PUT
#use delay(clock=16000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C5,bits=8,STOP=1,ERRORS)
#define SPI_SS pin_A5
#define LED1 pin_A1
#define nHOLD pin_A2
#define nWP pin_A3
#byte SSPSTAT = 0xFC7
#bit SMP = SSPSTAT.7
#bit CKE = SSPSTAT.6
#bit D_A = SSPSTAT.5
#bit P = SSPSTAT.4
#bit S = SSPSTAT.3
#bit R_W = SSPSTAT.2
#bit UA = SSPSTAT.1
#bit BF = SSPSTAT.0
#byte SSPBUF = 0xFC9
unsigned char do_spi(unsigned char txdata);
int length=2;
char mychar[length];
int i=0;
#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)
void init(void){
//configure the device to be a master, mode 0
setup_spi(spi_master | spi_clk_div_16 | SPI_MODE_0);
output_HIGH(LED1); //Led lit
output_HIGH(SPI_SS); // SS initialized
output_HIGH(nHOLD);
output_HIGH(nWP);
}
void main (void){
init();
printf("\nInit OK");
delay_us(500); //wait
//******** INITIALIZE THE EEPROM FOR READING ID **************************
output_LOW(SPI_SS); //clear the chip select
do_spi(0x05); //Read Status
for (i=0;i<length;i++)
{
mychar[i]=do_spi(0); //read it in data
}
delay_us(1000);
output_HIGH(SPI_SS);
printf("\n\r data 1: %c",mychar[0]); //Show me what you got through UART
output_toggle(LED1);
while(1);
}
unsigned char do_spi(unsigned char txdata)
{
SSPBUF = txdata;
while (!BF); // SSPSTAT.BF (when the buffer is full)
return SSPBUF;
}
|
and the output is the same
Quote: | ¡Ê+‹OK
data 1:
¡Ê+‹OK
data 1: |
|
|
![](templates/subSilver/images/spacer.gif) |
thibow
Joined: 11 Apr 2012 Posts: 30
|
|
Posted: Tue May 29, 2012 12:31 pm |
|
|
I have just checked with a scope where the problem could come from.
#HOLD is high
#WP is high
SCLK is ok
SI is ok
#CE goes down when I send or read something and high afterwards
SO does not send anything.
Which means that the problem comes from the SST Flash memory. I have tried to read the status register only, sending 0x05 and then reading 1 byte, and I have tried to read id, sending 0x90, then 0x00 3 times as the address and read 2 bytes. But the memory still doesn't send anything on SO.
Any idea ? |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9177 Location: Greensville,Ontario
|
|
Posted: Tue May 29, 2012 6:28 pm |
|
|
The 'garbage' before 'OK' concerns me....
should it not be 'Init OK' ?
If so,perhaps finding why that's not right(and fixing it) will help with the rest of the program ?? |
|
![](templates/subSilver/images/spacer.gif) |
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed May 30, 2012 5:12 am |
|
|
On first glance your program looks OK, so let me check a few basic things:
- Post your compiler version number. Some versions have known problems.
- What are the voltages used for your processor and for the flash memory? The Flash is 3.3V and you are running the same voltage for your LF version PIC?
- Post the pin numbers for the connection between PIC and Flash. PIN_C7 to ..., etc.
The garbage data in the RS232 output is indeed troubling. The first character is ok, then some garbage and then fine again. As you are using the software UART you would suspect such behaviour with an interrupt passing through, but that is not present in your code. The code as posted is exactly the same as you are testing with?
Code: | int length=2;
char mychar[length]; | This is not exactly legal C and as I don't have a compiler present here I can't test how CCS is handling it. Problem is that length is a variable and you can not declare an array with variable length. A better way of coding would have been: Code: | define LENGTH 2
char mychar[LENGTH]; | or: Code: | const int LENGTH=2; // note the 'const' keyword
char mychar[LENGTH]; |
thibow wrote: | ckielstra wrote: | 3) Use pin C7 for RS232 transmit instead of C5. Now the compiler will generate a software UART which is not as good as a hardware one.
|
As I prefer to use SW USART than SW SPI, and both SPI and UART use the same pin, I had to make a choice ;) | Of course the choice is up to you, but I would have chosen the other way around. RS232 is very sensitive to timing differences, for example from an interrupt being triggered during RS232 transmission. SPI doesn't care for timing, it is only sensitive to clock edges. Writing your own bit banging SPI routine is very easy, in fact you already wrote your own SPI routine and bit banging will be only slightly slower than the hardware implementation.
You can also use the CCS compiler to generate a software SPI for you, check the documentation on #use spi.
Another issue to keep in mind is mentioned in the chip hardware errata sheet. Issue 18 for the A3 hardware revision says that in SPI transmission you shouldn't bit_test the BF flag directly as you are doing. See the linked errata document for possible workarounds. |
|
![](templates/subSilver/images/spacer.gif) |
thibow
Joined: 11 Apr 2012 Posts: 30
|
|
Posted: Wed May 30, 2012 2:51 pm |
|
|
First of all, thank you very much for your help
ckielstra wrote: |
- Post your compiler version number. Some versions have known problems.
- What are the voltages used for your processor and for the flash memory? The Flash is 3.3V and you are running the same voltage for your LF version PIC?
- Post the pin numbers for the connection between PIC and Flash. PIN_C7 to ..., etc.
|
What I have in CCS help is v.4
I use a 3.3v power supply for both the flash and the pic.
for my µC connections, I am quite sure everything's all right
pin C7 (SDO) to Memory pin 5 (SI)
pin B0 (SDI) to Memory pin 2 (SO)
pin B1 (SCK) to Memory pin 6 (SCK)
pin A5 (SS) to Memory pin 1 (#CE)
pin A2 to Memory pin 7 (#HOLD)
pin A3to Memory pin 3 (#WP)
pin RC6 (TX) and RC5 (sw RX) to my Uart RS232 bridge
Quote: |
The garbage data in the RS232 output is indeed troubling. The first character is ok, then some garbage and then fine again. As you are using the software UART you would suspect such behaviour with an interrupt passing through, but that is not present in your code. The code as posted is exactly the same as you are testing with?
Code: | int length=2;
char mychar[length]; | This is not exactly legal C and as I don't have a compiler present here I can't test how CCS is handling it. Problem is that length is a variable and you can not declare an array with variable length. A better way of coding would have been: Code: | define LENGTH 2
char mychar[LENGTH]; | or: Code: | const int LENGTH=2; // note the 'const' keyword
char mychar[LENGTH]; |
|
Well in fact I did the change as it didn't compile without the "const" but I forgot to change it in the posted code, but it exists in my code, otherwise it is exactly the same yes.
Quote: |
thibow wrote: | ckielstra wrote: | 3) Use pin C7 for RS232 transmit instead of C5. Now the compiler will generate a software UART which is not as good as a hardware one.
|
As I prefer to use SW USART than SW SPI, and both SPI and UART use the same pin, I had to make a choice ;) | Of course the choice is up to you, but I would have chosen the other way around. RS232 is very sensitive to timing differences, for example from an interrupt being triggered during RS232 transmission. SPI doesn't care for timing, it is only sensitive to clock edges. Writing your own bit banging SPI routine is very easy, in fact you already wrote your own SPI routine and bit banging will be only slightly slower than the hardware implementation.
You can also use the CCS compiler to generate a software SPI for you, check the documentation on #use spi.
Another issue to keep in mind is mentioned in the chip hardware errata sheet. Issue 18 for the A3 hardware revision says that in SPI transmission you shouldn't bit_test the BF flag directly as you are doing. See the linked errata document for possible workarounds. | ok thank you, I guess this could be one thing to think about, I'll try to understand what's wrong and play with the workaround. I'll have a look at the SW SPI to change the SDO pin as well.
Thanks a lot again |
|
![](templates/subSilver/images/spacer.gif) |
thibow
Joined: 11 Apr 2012 Posts: 30
|
|
Posted: Wed May 30, 2012 3:02 pm |
|
|
Hello, I have switched to a PIC 18F2620 which has UART and SPI pins separated, and I am now on C18 compiler.. which doesn't change much.. I have exactly the same problem, (except that my UART is ok now) and it seems to come from the memory.
Here is what I have CH1 is my clock and CH2 is the SI (I am sending 0x05 to read the status register and reading once)
for this second CH1 is my clock and CH2 is the SO.. I can't figure out why I got these 2 spikes..
Here is my code
Code: | #include <p18F2620.H>
#include <stdio.h>
#include <usart.h>
#include <spi.h>
#include <delays.h>
/*
Pin configuration
---------------------- UART
RX RC7 pin 18
TX RC6 pin 17
---------------------- SPI
SDI RC4 pin 15
SDO RC5 pin 16
SCK RC3 pin 14
CE RB5 pin 7
HOLD
WP RA1 pin 3
-------------------- LED
LED0 RA0
*/
#pragma config OSC = HS//
#pragma config PWRT = ON //Power-up RESET Timer Enable A la mise sous tension du µC, lance une temporisation d'environ 72 ms durant laquelle est effectué un RESET interne.
#pragma config WDT = OFF
#pragma config MCLRE = ON //(ON)MCLR pin enabled; RE3 input pin disabled (OFF) RE3 input pin enabled; MCLR disabled
#pragma config STVREN = ON //(ON)Stack full/underflow will cause Reset
#pragma config LVP = OFF //Single-Supply ICSP disabled
#define CE LATAbits.LATA5
#define WP LATAbits.LATA1
#define LED0 LATAbits.LATA0
#define LENGTH 1
unsigned char do_spi(unsigned char byte);
char mychar[LENGTH];
char dummy;
int i=0;
//initialize IO pins
void InitializeIO(void)
{
ADCON1 = 0x0F; //disable AD converter functionality on PORTA
CMCON = 0x07; //disable comparators on PORTA
TRISAbits.TRISA0 = 0; //make PORTA.0 an output to control LED
LATAbits.LATA0 = 1; //turn on LED0
TRISAbits.TRISA1 = 0; //make PORTA.1 an output to control WP
LATAbits.LATA1 = 1; //Make WP High
TRISAbits.TRISA5 = 0; //make sure that PORTB.5 OUTPUT since it is CE pin
TRISCbits.TRISC3 = 0; //make sure that PORTC.3 OUTPUT since it is SCK pin
TRISCbits.TRISC4 = 1; //make sure that PORTC.4 INPUT since it is SDI pin
TRISCbits.TRISC5 = 0; //make sure that PORTC.5 OUTPUT since it is SDO pin
TRISCbits.TRISC6 = 0; //make sure that PORTC.6 OUTPUT since it is TX pin UART
TRISCbits.TRISC7 = 1; //make sure that PORTC.7 INPUT since it is RX pin UART
//TRISC = 0x91; //make CSN, CE, SCK, MOSI (SDO), and TX outputs
}
void Initialize(void)
{
InitializeIO(); //set up IO (directions and functions)
OpenUSART (USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE & USART_EIGHT_BIT & USART_CONT_RX & USART_BRGH_HIGH, 51); //open UART with spbrg = 8Mhz/(16*9600)-1
OpenSPI(SPI_FOSC_16, MODE_00, SMPMID); //open SPI1
printf("\n\r Init OK");
}
void main (void){
Initialize();
while(1){
//******** INITIALIZE THE EEPROM FOR READING ID **************************
CE=0; //clear the chip select
do_spi(0x05); //Prepare to Read Status
for (i=0;i<LENGTH;i++)
{
mychar[i]=do_spi(0x00); //read it in data
}
CE=1;
printf("\n\r data 1: %c",mychar[0]); //Show me what you got through UART
LED0=~LED0;
Delay10KTCYx(500);
}
}
unsigned char do_spi(unsigned char byte)
{
SSPBUF = byte;
while(!DataRdySPI());
return SSPBUF;
} |
Here is my output :
Quote: |
Init OK
data 1:
data 1:
|
|
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed May 30, 2012 5:12 pm |
|
|
1. In your CCS version of the program, why do you use your do_spi()
routine ? Why not use the CCS spi_read() routine, like this:
Code: |
mychar[i] = spi_read(0);
|
2. When you read the Status register in the memory chip, the power-on
reset default value for the register is 0x0C. But you are attempting to
display it in printf() with "%c". That's for an ASCII character. But you
should be using "%x" so you can properly see the hex value that is returned.
3. Regarding your scope waveforms, are you absolutely sure that you
don't accidentally have the SDO and SDI lines crossed ? If you had the
two SDI pins (on the PIC and Memory chips) connected together, you
might see that signal, due to capacitive pickup from the SCLK line. |
|
![](templates/subSilver/images/spacer.gif) |
thibow
Joined: 11 Apr 2012 Posts: 30
|
|
Posted: Wed May 30, 2012 7:59 pm |
|
|
Thank you for your suggestion.
I have used read_spi as well, but I remember from another project that there are troubles in CCS library while reading or writing (I don't remember which as there is also a write_spi function that is obviously useless).. so to make sure I wouldn't get any trouble with this I did the way I did in my old project: building my own routine.
About the %c, I was used to do so and then convert the ASCII character in bits to figure out.. I'll give a try tomorrow to %x.
about the SDI and SI (on µC and Memory) together as well as SO and SDO, I have tried to invert them without any luck.. I'll double check this but I am sure I tried both |
|
![](templates/subSilver/images/spacer.gif) |
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu May 31, 2012 2:12 am |
|
|
Quote: | What I have in CCS help is v.4 | The complete version number has 3 digits after the dot, so something like 4.xxx
You can find this number at the top of the list file (*.lst) in your project directory, or through a link in your Start menu -> PICC -> program version (or similar)
The scope pictures say a lot, the FLASH device is not responding at all and even shows some induced coupling signals.
I think we got bitten by one of the more common errors in PIC programming: on power up many pins are defined to have another function than we expect them to have.
In your original CCS program you were using A2, A3 and A5. These are all configured as analog input on power up. Try adding setup_adc_ports(NO_ANALOGS) to the start of your program.
In the C18 code you do disable the analog ports so this should have solved the problem existing in the CCS code. However.... you are testing this on other hardware. What did you do to the HOLD pin of the SST25VF010A ? It is not in the program any more, so I hope you tied it to 3.3V?
Also you confused me at first: Code: | CE RB5 pin 7
and
TRISAbits.TRISA5 = 0; //make sure that PORTB.5 OUTPUT since it is CE pin | The comments say B5 but the code is still for A5...
Also I miss the code for initializing the CE level to 1, but this should only have a possible effect on the first read.
I don't know the function DataRdySPI, but are you sure this is using one of the suggestions from the errata sheet?
Double check the voltage levels on _all_ pins of the Flash chip. When you measure them, are the levels what you expect them to be? |
|
![](templates/subSilver/images/spacer.gif) |
thibow
Joined: 11 Apr 2012 Posts: 30
|
|
Posted: Thu May 31, 2012 8:17 am |
|
|
Thank you for your advices. I did a mistake in my comment, but everything was right in the code. I have then implemented the Hold pin, that I didn't before !
I have switched to C18 as I already made a project with spi working fairly well, so I managed to get something trustful at first.
I then did a loop back to test my spi, and it worked well.
PCM Programmer found the problem, it comes from the printf function, I wanted to show a character on the screen, as I usually do, I then convert it into hex, but for an obscure reason it didn't want to convert the hex value into a character. now that I printf a %x instead of a %c I can see my status byte.
I have tried to printf the device id (device id is 49h and manufacturer id is bfh) but it then print me
Quote: | data 1: ffbf
data 2: I |
I'll have to worry about the code and not the connections.
Thanks a lot for your help
EDIT: I have created an int array to store the data I get back, and it works well :
int myarray[LENGTH];
printf("\n\r data %d: %x",i,myarray[i]);
output:
Quote: | data 0: bf
data 1: 49 |
Thank you again |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|