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

SPI Flash memory and DSPIC33

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



Joined: 08 Sep 2008
Posts: 38

View user's profile Send private message

SPI Flash memory and DSPIC33
PostPosted: Tue Mar 03, 2020 10:44 am     Reply with quote

DSPIC33EP512MC806 and IS25LQ080B
Anybody know how to make this work?
Everything just returns 0x00. If I add set_pullup(SPI1_CS) everything returns 0xFF.

Code:

// SPI_FLASH.C
//
//
//
// Drivers for IS25LQ080B SERIAL FLASH MEMORY 1MB
// 256 Sectors, 4Kx8 bits each
// Sector Address: 0x000000 to 0x0FF000
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------

#ifndef SPI1_CS

   #define SPI1_CS          PIN_E7   // 3
   #define SPI1_DI          PIN_G8   // 6
   #define SPI1_DO          PIN_G7   // 5
   #define SPI1_CLK         PIN_G6   // 4
   #define SPI1_WP          PIN_E6   // 2

   #define READ         0x03 // Read Data From Memory
   #define WRITE        0x02 // Write Data Into Memory
   #define RDSR         0x05 // Read Status Register
   #define WREN         0x06 // Set Write Enable
   #define ERASE        0xD7 // Sector Erase 4KB (D7h/20h)
   #define RDMDID       0x90 // Read Manufacturer and Device ID
   #define RSTEN        0x66 // Reset-Enable
   #define RESET        0x99 // Chip Reset
   
   int8 gSpi_Buffer[256];
   
   #use SPI ( STREAM=SPI1, CLK=SPI1_CLK, DO=SPI1_DO, DI=SPI1_DI, BAUD=100000, BITS=8, MODE=0, MASTER )

#endif
   
// ---------------------------------------------------------------------------- 

int8 spi_Ready() {
   int8 data;
   output_low(SPI1_CS);       
   spi_xfer(SPI1, RDSR);     
   data = spi_xfer(SPI1, 0); 
   output_high(SPI1_CS);     
   return data&0x01;               
}

void spi_Init()
{
   input(SPI1_DO);
   output_high(SPI1_CS);
   output_high(SPI1_WP);
   output_low(SPI1_DI);
   output_low(SPI1_CLK);
   
   while (spi_Ready());
}

void spi_SendAddress(int32 address, int8 command)
{
   spi_xfer(SPI1, command);
   spi_xfer(SPI1, make8(address,2));
   spi_xfer(SPI1, make8(address,1));
   spi_xfer(SPI1, make8(address,0));
}

int8 spi_ReadByte(int32 address)
{
   int8 data;
   while(spi_Ready());
   
   output_low(SPI1_CS);     
   spi_SendAddress(SPI1, READ); 
   data=spi_xfer(SPI1, 0);     
   output_high(SPI1_CS);
   return data;
}
Ranfo



Joined: 08 Sep 2008
Posts: 38

View user's profile Send private message

PostPosted: Tue Mar 03, 2020 12:14 pm     Reply with quote

Made some changes. Still does not work.
Trial and Error not getting me anywhere.

Code:

#ifndef SPI2_CS

   #define SPI2_CS          PIN_E7   // 3
   #define SPI2_DI          PIN_G8   // 6
   #define SPI2_DO          PIN_G7   // 5
   #define SPI2_CLK         PIN_G6   // 4
   #define SPI2_WP          PIN_E6   // 2

   #define READ         0x03 // Read Data From Memory
   #define WRITE        0x02 // Write Data Into Memory
   #define RDSR         0x05 // Read Status Register
   #define WREN         0x06 // Set Write Enable
   #define ERASE        0xD7 // Sector Erase 4KB (D7h/20h)
   #define RDMDID       0x90 // Read Manufacturer and Device ID
   #define RSTEN        0x66 // Reset-Enable
   #define RESET        0x99 // Chip Reset
   
   int8 gSpi_Buffer[256];

   #use SPI ( STREAM=SPI2, SPI2, BAUD=100000, BITS=8, MODE=0, MASTER )

#endif
   
// ---------------------------------------------------------------------------- 

int8 spi_Ready() {
   int8 data;
   output_low(SPI2_CS);       
   spi_xfer(SPI2, RDSR);     
   data = spi_xfer(SPI2, 0); 
   output_high(SPI2_CS);     
   return data&0x01;               
}

void spi_Init()
{
   input(SPI2_DO);
   output_high(SPI2_CS);
   output_high(SPI2_WP);
   output_low(SPI2_DI);
   output_low(SPI2_CLK);
   while (spi_Ready());
}

void spi_SendAddress(int32 address, int8 command)
{
   spi_xfer(SPI2, command);
   spi_xfer(SPI2, make8(address,2));
   spi_xfer(SPI2, make8(address,1));
   spi_xfer(SPI2, make8(address,0));
}

int8 spi_ReadByte(int32 address)
{
   int8 data;
   while(spi_Ready());
   output_low(SPI2_CS);     
   spi_SendAddress(address, READ); 
   data=spi_xfer(SPI2, 0);     
   output_high(SPI2_CS);
   return data;
}

void spi_Erase(int8 sector)
{
   while(spi_Ready());
   int32 address = (int32) sector * 0x1000;
   output_low(SPI2_CS);
   spi_SendAddress(address, ERASE);
   output_high(SPI2_CS);
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19337

View user's profile Send private message

PostPosted: Tue Mar 03, 2020 1:07 pm     Reply with quote

Er. Your pin naming is wrong.
You say that SPI2_DO is G7, but this is the DI pin not the DO pin.
Same applies to the next define as well.
You do understand that the PIC's DO line goes to the chip's DI.
Then you are not write enabling he chip.
The hardware WR bit controls allowing access to the status register.
To physically write to the chip, this has to be turned off, but then the WEL
bit has to be set in software as well.
From the data sheet:
Quote:

Before the execution of any program, erase or Write Status/Function Register instruction, the Write Enable
Latch (WEL) bit must be enabled by executing a Write Enable (WREN) instruction. If the WEL bit is not enabled,
the program, erase or write register instruction will be ignored


This is common for all chips like this.

Hold pin also needs to be high.
Ranfo



Joined: 08 Sep 2008
Posts: 38

View user's profile Send private message

PostPosted: Tue Mar 03, 2020 1:51 pm     Reply with quote

Ttelmah,
Thank you. I did not design the circuit and will have someone double check the ins and outs. If they are backwards then there is not much I can do to make this work.

I see that I forgot to send the WREN command before ERASE, but as above, if DI and DO are reversed it won't work anyway.
Ranfo



Joined: 08 Sep 2008
Posts: 38

View user's profile Send private message

PostPosted: Tue Mar 03, 2020 4:00 pm     Reply with quote

The circuit is wired correctly. I changed the names to avoid confusion.
Added spi_xfer(SPI2, WREN); to the erase function.
Still, nothing works.

Is there a WORKING example out there some place?
Ranfo



Joined: 08 Sep 2008
Posts: 38

View user's profile Send private message

PostPosted: Tue Mar 03, 2020 4:02 pm     Reply with quote

Code:

#ifndef SPI2_CS

   #define SPI2_CS          PIN_E7   // 3
   #define SPI2_DO          PIN_G8   // 6 
   #define SPI2_DI          PIN_G7   // 5 
   #define SPI2_CLK         PIN_G6   // 4
   #define SPI2_WP          PIN_E6   // 2

   #define READ         0x03 // Read Data From Memory
   #define WRITE        0x02 // Write Data Into Memory
   #define RDSR         0x05 // Read Status Register
   #define WREN         0x06 // Set Write Enable
   #define ERASE        0xD7 // Sector Erase 4KB (D7h/20h)
   #define RDMDID       0x90 // Read Manufacturer and Device ID
   #define RSTEN        0x66 // Reset-Enable
   #define RESET        0x99 // Chip Reset
   
   int8 gSpi_Buffer[256];

   #use SPI ( STREAM=SPI2, SPI2, BAUD=100000, BITS=8, MODE=0, MASTER )

#endif
   
// ---------------------------------------------------------------------------- 

int8 spi_Ready() {
   int8 data;
   output_low(SPI2_CS);       
   spi_xfer(SPI2, RDSR);     
   data = spi_xfer(SPI2, 0); 
   output_high(SPI2_CS);     
   return data&0x01;               
}

void spi_Init()
{
   input(SPI2_DI);
   output_high(SPI2_CS);
   output_high(SPI2_WP);
   output_low(SPI2_DO);
   output_low(SPI2_CLK);
   while (spi_Ready());
}

void spi_SendAddress(int32 address, int8 command)
{
   // Is this right?
   spi_xfer(SPI2, command);
   spi_xfer(SPI2, make8(address,2));
   spi_xfer(SPI2, make8(address,1));
   spi_xfer(SPI2, make8(address,0));
   
   // Or this?
   // spi_xfer(SPI2, command);
   // spi_xfer(SPI2, address);
}

int8 spi_ReadByte(int32 address)
{
   int8 data;
   while(spi_Ready());
   output_low(SPI2_CS);     
   spi_SendAddress(address, READ); 
   data=spi_xfer(SPI2, 0);     
   output_high(SPI2_CS);
   return data;
}

void spi_Erase(int8 sector)
{
   while(spi_Ready());
   int32 address = (int32) sector * 0x1000;
   output_low(SPI2_CS);
   spi_xfer(SPI2, WREN);
   spi_SendAddress(address, ERASE);
   output_high(SPI2_CS);
}
dluu13



Joined: 28 Sep 2018
Posts: 395
Location: Toronto, ON

View user's profile Send private message Visit poster's website

PostPosted: Tue Mar 03, 2020 6:17 pm     Reply with quote

In another memory chip that I am using now, you must bring CS high and then back down between two instructions. In your erase function, you are doing write enable and another instruction on one single chip select event.

Check section 8 (p19) on your data sheet.
Ttelmah



Joined: 11 Mar 2010
Posts: 19337

View user's profile Send private message

PostPosted: Wed Mar 04, 2020 1:03 am     Reply with quote

You do realise that 'erased' gives 0xFF on flash memories.
Nowhere do you actually attempt to write to the chip.
I've got a library I use for a similar FLASH EEPROM. I'll dig it out and
post it later to show how you have to work.
Ttelmah



Joined: 11 Mar 2010
Posts: 19337

View user's profile Send private message

PostPosted: Wed Mar 04, 2020 2:43 am     Reply with quote

OK. I've modified it so the commands match what this chip uses and
added a basic test routine. No guarantees since it involved quite a bit of
'tweaking'....
Code:

#include <33EP512MC806.h>
#build (stack=512)
#use delay(internal=58.96MHz)

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES CKSFSM                   //Clock Switching is enabled, fail Safe clock monitor is enabled

//Now program the SPI port

#define SPI2_CS          PIN_E7   // 3
#define SPI2_DO          PIN_G8   // 6
#define SPI2_DI          PIN_G7   // 5
#define SPI2_CLK         PIN_G6   // 4
#define SPI2_WP          PIN_E6   // 2

#define MAX_ADDRESS 0xFFFFFUL //change this according to chip

#USE SPI ( STREAM=SPI2_EEPROM, SPI2, BAUD=4000000, BITS=8, MODE=0, MASTER )
//Don't use the same name for the stream as the hardware port.

//define some pins for UART
#PIN_SELECT U1TX=PIN_E5
#PIN_SELECT U1RX=PIN_E4
#USE RS232 (UART1, baud=9600, ERRORS) //default port for output

#define EE_READ         0x03 // Read Data From Memory
#define EE_PP           0x2 //Page program
#define EE_CER          0xc7 //chip erase
#define EE_WREN         0x6 //write enable
#define EE_WRDI         0x4 //write disable
#define EE_RDSR         0x5 //read status
#define EE_WRSR         0x1 //write status
#define EE_RDID         0xAB //read ID
#define EE_RSTEN        0x66 //software reset enable
#define EE_RST          0x99 //software reset
#define EE_IRP          0x62 //program information row
#define EE_IRRD         0x68 //read information row
#define EE_SECLOCK      0x24 //sector lock
#define EE_SEC_UNLOCK   0x26 //sector unlock
#define EE_SECERASE     0xD7 //erase sector

byte read_status(void)
{
   //read the status register
   byte rval;
   output_low(SPI2_CS);
   spi_xfer(SPI2_EEPROM,EE_RDSR); //read status register command
   rval=spi_xfer(SPI2_EEPROM,0); //read reply
   output_high(SPI2_CS); //deselect chip
   return rval; //return value
}

void read_block(unsigned int32 address, byte * data, int count)
{
   if (count==0)
      return; //do nothing if count==0
   if (address>MAX_ADDRESS)
      return; //do nothing if address above maximum supported.
   //Now need to read status register to know if chip is busy
   do {
      delay_cycles(10);
   } while (bit_test(read_status(),0)==1) ; //wait if chip busy - add timeout if needed here
     
   output_low(SPI2_CS);
   //read 'count' bytes from memory at 'address'
   spi_xfer(SPI2_EEPROM,EE_READ); //send READ command
   spi_xfer(SPI2_EEPROM,make8(address,2)); //msb of address
   spi_xfer(SPI2_EEPROM,make8(address,1)); //NMSB
   spi_xfer(SPI2_EEPROM,make8(address,0)); //LSB
   //Now need to read bytes
   do {
      *(data++)=spi_xfer(SPI2_EEPROM,0); //clock out null bytes to read data
   } while(--count); //for count bytes
   output_high(SPI2_CS);
}

void enable_write(void)
{
   //set the WEL required before any write operation
   output_high(SPI2_WP); //disable hardware WP
   output_low(SPI2_CS);
   //set write enable
   spi_xfer(SPI2_EEPROM,EE_WREN); //set write enable bit
   output_high(SPI2_CS); //terminate command
}

void write_block(unsigned int32 address, byte * data, int count)
{
   if (count==0)
      return; //do nothing if count==0
   if (address>MAX_ADDRESS)
      return; //do nothing if address above maximum supported.
   if (count>256)
      return; //do nothing if attempt to write > 256 bytes
   //Now need to read status register to know if chip is busy
   do {
      delay_cycles(10);
   } while (bit_test(read_status(),0)==1) ; //wait if chip busy - add timeout if needed here
   //Now before I can write need to enable write operation
   enable_write();
   //Now single CS and send the command with address
   output_low(SPI2_CS);   
   spi_xfer(SPI2_EEPROM,EE_PP); //send page write command
   spi_xfer(SPI2_EEPROM,make8(address,2)); //msb of address
   spi_xfer(SPI2_EEPROM,make8(address,1)); //NMSB
   spi_xfer(SPI2_EEPROM,make8(address,0)); //LSB   
   do {
      spi_xfer(SPI2_EEPROM,*(data++)); //clock out bytes to write
   } while(--count); //for count bytes
   output_high(SPI2_CS); //deselect
}

void erase_sector(unsigned int32 address)
{
   //erase 4K sector
   if (address>MAX_ADDRESS)
      return; //do nothing if address above maximum supported.
   do {
      delay_cycles(10);
   } while (bit_test(read_status(),0)==1) ; //wait if chip busy - add timeout if needed here   
   enable_write();
   output_low(SPI2_CS); //select   
   spi_xfer(SPI2_EEPROM,EE_SECERASE); //send erase sector command
   address &=0x7FFF000UL; //page to erase
   spi_xfer(SPI2_EEPROM,make8(address,2)); //msb of address
   spi_xfer(SPI2_EEPROM,make8(address,1)); //NMSB
   spi_xfer(SPI2_EEPROM,make8(address,0)); //LSB       
   output_high(SPI2_CS); //deselect
}

BYTE read_id(void)
{
   //read the product ID
   BYTE value;
   do {
      delay_cycles(10);
   } while (bit_test(read_status(),0)==1) ; //wait if chip busy - add timeout if needed here     
   output_low(SPI2_CS); //select 
   spi_xfer(SPI2_EEPROM,EE_RDID); //send read ID command
   spi_xfer(SPI2_EEPROM,0);
   spi_xfer(SPI2_EEPROM,0);
   spi_xfer(SPI2_EEPROM,0); //three dummy bytes required

   value=spi_xfer(SPI2_EEPROM,0); //read reply
   output_high(SPI2_CS); //deselect     
   return value; //return reply
}
   
void main(void)
{
   BYTE ID;
   delay_us(500);
   BYTE buffer[16];
   BYTE new[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
   int count;
   //Now test reading ID and see if chip value is reasonable
   ID=read_ID(); //note this also wakes chip from power down.
   //This should be 0x13 for IS25LQ080B
   if (ID==0x13)
   {
      //OK we are talking to the chip
      //Read start of memory. If erased, write some new test data
      read_block(0UL, buffer, 16);
      for (count=0;count<16;count++)
      {
         if (buffer[count]!=0xFF)
         {
            printf("Data found\n");
            break; //there is some data in the block
         }
      }
      if (count==16)
      {
         //here block was empty so write something
         write_block(0UL,new,16);
      }
      read_block(0UL, buffer, 16); //read again
     
      //Now compare with 'new' as test.
      for (count=0;count<16;count++)
      {
         if (buffer[count]!=new[count])
         {
            printf("Data ERROR\n");
            break; //
         }
      }
      if (count==16)
      {
         printf("Data matches\n");
      }
   }

   while(TRUE)
   {
      //something
   }
}
Ranfo



Joined: 08 Sep 2008
Posts: 38

View user's profile Send private message

PostPosted: Wed Mar 04, 2020 2:46 pm     Reply with quote

Wonderful Ttelmah! Using your example I have it all working and apparently much faster than a bitbang solution I was using before. It should all be down hill from here. Thank you much.



Code:

#ifndef SPI_FLASH2
   #define SPI_FLASH2
   #define MAX_ADDRESS     0x0FFFFF
   
   #define SPI2_CS         PIN_E7   // 3
   #define SPI2_DO         PIN_G8   // 6
   #define SPI2_DI         PIN_G7   // 5
   #define SPI2_CLK        PIN_G6   // 4
   #define SPI2_WP         PIN_E6   // 2
   
   #define EE_READ         0x03 // Read Data From Memory
   #define EE_WRITE        0x02 // Write Data Into Memory
   #define EE_RDSR         0x05 // Read Status Register
   #define EE_WREN         0x06 // Set Write Enable
   #define EE_ERASE        0xD7 // Sector Erase 4KB (D7h/20h)
   #define EE_RDMDID       0x90 // Read Manufacturer and Device ID
   #define EE_RSTEN        0x66 // Reset-Enable
   #define EE_RESET        0x99 // Chip Reset
   #define EE_IRP          0x62 // program information row
   #define EE_IRRD         0x68 // read information row
   #define EE_SECLOCK      0x24 // sector lock
   #define EE_SEC_UNLOCK   0x26 // sector unlock
   #define EE_SECERASE     0xD7 // erase sector
   #define EE_RDID         0xAB // Read ID

#endif

#USE SPI ( STREAM=SPI2EE, SPI2, BAUD=4000000, BITS=8, MODE=0, MASTER )

// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------

// Read the status register
// ----------------------------------------------------------------------------
byte spi2_read_status()
{
   byte rval;
   output_low(SPI2_CS);
   spi_xfer(SPI2EE, EE_RDSR);    // Read status register command
   rval=spi_xfer(SPI2EE, 0);     // Read reply
   output_high(SPI2_CS);         // Deselect chip
   return rval;                  // Return value
}

// Read EEPROM product ID (IS25LQ080B=0x13)
// ----------------------------------------------------------------------------
byte spi2_read_id()
{
   byte value;
   do {                          // Wait if chip busy
      delay_cycles(10);
   } while (bit_test(spi2_read_status(), 0)==1);     
   output_low(SPI2_CS);          // Select
   spi_xfer(SPI2EE, EE_RDID);    // Send read ID command
   spi_xfer(SPI2EE, 0);
   spi_xfer(SPI2EE, 0);
   spi_xfer(SPI2EE, 0);          // Three dummy bytes required

   value=spi_xfer(SPI2EE, 0);    // Read reply
   output_high(SPI2_CS);         // Deselect     
   return value;                 // Return reply
}

// Read Block
// ----------------------------------------------------------------------------
void spi2_read_block(unsigned int32 address, byte * data, int16 count)
{
   if (count==0) return;
   if (address>MAX_ADDRESS) return;

   do {                                   // Wait while chip busy
      delay_cycles(10);
   } while (bit_test(spi2_read_status(),0)==1); 
     
   output_low(SPI2_CS);
   spi_xfer(SPI2EE, EE_READ);             // Send READ command
   spi_xfer(SPI2EE, make8(address,2));    // MSB of address
   spi_xfer(SPI2EE, make8(address,1));    // NMSB
   spi_xfer(SPI2EE, make8(address,0));    // LSB

   do {
      *(data++)=spi_xfer(SPI2EE, 0);      // clock out null bytes to read data
   } while(--count);                      // for count bytes
   output_high(SPI2_CS);
}

// Write Enable
// ----------------------------------------------------------------------------
void spi2_enable_write()
{
   output_high(SPI2_WP);            // Disable hardware WP
   output_low(SPI2_CS);
   spi_xfer(SPI2EE, EE_WREN);       // Set write enable bit
   output_high(SPI2_CS);            // Deselect
}

// Erase Sector (0-255)
// ----------------------------------------------------------------------------
void spi2_erase_sector(int8 sector)
{
   int32 address = (int32) sector * 0x1000;
   if (address>MAX_ADDRESS) return;
   
   do {                                   // Wait while chip busy
      delay_cycles(10);
   } while (bit_test(spi2_read_status(),0)==1);
   
   spi2_enable_write();                   // Enable Write
   output_low(SPI2_CS);                   // Select   
   spi_xfer(SPI2EE, EE_SECERASE);         // Send erase sector command
   spi_xfer(SPI2EE, make8(address,2));    // MSB
   spi_xfer(SPI2EE, make8(address,1));    // NMSB
   spi_xfer(SPI2EE, make8(address,0));    // LSB       
   output_high(SPI2_CS);                  // Deselect
}

// Write Block
// ----------------------------------------------------------------------------
void spi2_write_block(unsigned int32 address, byte * data, int count)
{
   if (count==0) return;
   if (address>MAX_ADDRESS) return;
   if (count>256) return;
   
   do {                                   // Wait while chip busy
      delay_cycles(10);
   } while (bit_test(spi2_read_status(),0)==1) ;

   spi2_enable_write();                   // Enable Write
   output_low(SPI2_CS);                   // Select
   spi_xfer(SPI2EE, EE_WRITE);            // Send page write command
   spi_xfer(SPI2EE, make8(address,2));    // MSB
   spi_xfer(SPI2EE, make8(address,1));    // NMSB
   spi_xfer(SPI2EE, make8(address,0));    // LSB   
   do {
      spi_xfer(SPI2EE, *(data++));        // Clock out bytes
   } while(--count); 
   output_high(SPI2_CS);                  // Deselect
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19337

View user's profile Send private message

PostPosted: Thu Mar 05, 2020 1:11 am     Reply with quote

Good. Glad I got close. Smile
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