Strange behaviour 25LC1025 SPI EEprom routine attempt

General discussion on mikroPascal for dsPIC30/33 and PIC24.
Post Reply
Author
Message
OT
Posts: 581
Joined: 19 May 2005 05:08
Location: Fairbanks, Alaska

Strange behaviour 25LC1025 SPI EEprom routine attempt

#1 Post by OT » 02 Dec 2007 13:26

This started as a discussion in the MBasic dsPascal forum:
http://www.mikroe.com/forum/viewtopic.php?t=12525
I have problems to read single bytes from 25LC1025 SPI EEprom. As far as I can see if I follow the Microchip instructions, cycling CS pin for separate reads, I cannot read back reliably, it could seem as the 24 bit address get messed up somehow:
Image

If I comment out the two marked lines below in the read procedure and include the other marked lines outside the read loop the bottom line of the display which is the string read from memory is identical to the top line:

Code: Select all

program EEPROM_SPI_25LC1024;

//******************************************************************************
// Program writes start of alphabet to a 25LC1024 eeprom, displays on line #1
//of LCD, then reads back the same bytes, displaying them on line #2 and
// EasydsPIC2 at FRC 7.37 MHz, dsPIC30F4013.

const
  CS_PIN_EEPROM           = 4;     //Chip enable pin portF
  //Commands
  EE_ReadCmd            = $03;     //read data from selected address
  EE_WriteCmd           = $02;     //write data to selected address
  EE_WriteEnable        = $06;     //set write enable latch (enable write ops)
  EE_WriteDisable       = $04;     //reset the write enable latch (disable write ops)
  EE_ReadStatusReg      = $05;     //read status register
  EE_WriteStatusReg     = $01;     //write status register
  EE_PageErase          = $42;     //page erase
  EE_SectorErase        = $D8;     //sector erase
  EE_ChipErase          = $C7;     //chip erase
  EE_RelDeepPowerDwn    = $AB;     //release from deep power down & read ID
  EE_DeepPowerDwn       = $B9;     //enter deep power down mode


var
   dataByte,dataReadByte : Word;
   Pos                  : Word;
   EEdataAddr           : LongInt;


procedure DeepPowerDwnEE;
begin
  while SPI1STAT.1 = 1 do                // wait for SPI module to finish, if doing something
   nop;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_DeepPowerDwn);           // Deep power down
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 1;
end;

procedure RelDeepPowerDwnEE;
begin
  while SPI1STAT.1 = 1 do                // wait for SPI module to finish, if doing something
   nop;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_RelDeepPowerDwn);        // Deep power down
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 1;
  Delay_us(200);
end;



procedure WriteInProgressPollEE;
//This routine loops until WIP:= 0
var
  Status: Byte;
begin

  while (Status and $01) do              //The WIP bit is bit 0.
    begin
      LATF.CS_PIN_EEPROM:= 0;             //Select Device
      SPI1_Write(EE_ReadStatusReg);       //Read Status Reg OpCode
      Status:= SPI1_Read(0);              //Read Status Reg
      LATF.CS_PIN_EEPROM:= 1;             //Deselect Device
    end;                                  //Check for WIP bit Set
end;


procedure WriteEnableEE;
begin
  while SPI1STAT.1 = 1 do                // wait for SPI module to finish, if doing something
   nop;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_WriteEnable);            //enable writes
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 1;
end;


procedure WriteDisableEE;
begin
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_WriteDisable);           //disable writes to eeprom
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 1;
end;


procedure WriteEEByte(databyte: Word; EEdataAddr:LongInt);
begin
  while SPI1STAT.1 = 1 do             // wait for SPI module to finish, if doing something
   nop;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_WriteCmd);             //write command
  Spi1_Write(Higher(EEdataAddr));       //send 24 bit address
  Spi1_Write(Hi(EEdataAddr));
  Spi1_Write(Lo(EEdataAddr));
  Spi1_Write(databyte);               //write data
  LatF.CS_PIN_EEPROM := 1;
  WriteInProgressPollEE;
end;


function ReadEEByte(EEdataAddr: LongInt): Word;
begin
  while SPI1STAT.1 = 1 do             // wait for SPI module to finish, if doing something
    nop;
  LatF.CS_PIN_EEPROM := 0;                  //Comment out this to make it "work"
  Spi1_Write(EE_ReadCmd);            //issue the READ command
  Spi1_Write(Higher(EEdataAddr));     //send 24 bit address
  Spi1_Write(Hi(EEdataAddr));
  Spi1_Write(Lo(EEdataAddr));

  dataReadByte := SPI1_read(0);       //read the eeprom
  LatF.CS_PIN_EEPROM := 1;                  //Comment out this to make it "work"
  ReadEEByte:=dataReadByte;
end;




begin
  Delay_ms(500);
  ADPCFG := $FFFF;
  TRISD  := $FFF0;
  TrisB  := $000F;
  LatB:=0;
  TRISF.2 := 1;     //input
  TRISF.3 := 0;     //output
  TRISF.CS_PIN_EEPROM := 0;          // CS pin
  LatF.CS_PIN_EEPROM  := 1;          // Set CS to inactive

  //CS pins for other connected SPI devices, keep them quiet
  TRISF.0 := 0;     //input
  LatF.0  := 1;     //inactive
  TRISF.5 := 0;     //input
  LatF.5  := 1;     //inactive

 // SPI setup
  Spi1_Init_Advanced(_SPI_MASTER, _SPI_8_BIT, _SPI_PRESCALE_SEC_1, _SPI_PRESCALE_PRI_1,
                     _SPI_SS_DISABLE, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_HIGH,
                     _SPI_ACTIVE_2_IDLE);
  // Spi1_Init;


  Lcd_Init(LATB, 7, 6, 5, 4, LATD, 0, 1, 2);
  Lcd_Cmd(LCD_CURSOR_OFF);
  Lcd_Cmd(LCD_CLEAR);

  RelDeepPowerDwnEE; //Just in case..

  Delay_ms(1000);


  //Write single bytes to external eeprom and display written chars on LCD
  EEdataAddr := 0;
  databyte := 65;
  WriteEnableEE;
  while EEdataAddr < 16 do begin
     WriteEEByte(databyte,EEdataAddr);
     Pos:=EEdataAddr+1;
    lcd_Chr(1,Pos,databyte);
    Inc(EEdataAddr);
    Inc(databyte);
  end;
  WriteDisableEE;

  Delay_ms(1000);
 //Read single bytes from external eeprom and display the chars on LCD
  EEdataAddr := 0;
  //LatF.CS_PIN_EEPROM := 0;            //Remove comment to make it "work"
  while EEdataAddr < 16 do begin
    dataReadByte:=ReadEEByte(EEdataAddr);
    delay_ms(10);
    Pos:=EEdataAddr+1;
    lcd_Chr(2,Pos,dataReadByte);
    Inc(EEdataAddr);
  end;
  //LatF.CS_PIN_EEPROM := 1;            //Remove comment to make it "work"

  //finished, do nothing more
  While true do begin
    nop;
  end;
end.
If you do not have this chip you could try a similar smaller version which would require adjustment of the length of the data address to 16 or 8 bit.

From the data sheet of 25LC1024:
Read Sequence
The device is selected by pulling CS low. The 8-bit
READ instruction is transmitted to the 25LC1024
followed by the 24-bit address, with seven MSBs of the
address being “don’t care” bits. After the correct READ
instruction and address are sent, the data stored in the
memory at the selected address is shifted out on the
SO pin.
The data stored in the memory at the next address can
be read sequentially by continuing to provide clock
pulses. The internal Address Pointer is automatically
incremented to the next higher address after each byte
of data is shifted out. When the highest address is
reached (1FFFFh), the address counter rolls over to
address, 00000h, allowing the read cycle to be continued
indefinitely. The read operation is terminated by
raising the CS pin (Figure 2-1).
In other words, it should be possible to terminate read after a single byte. And if multiple reads are to be done sequentially the code should not resend the address as is the case in the commented out version that works.

It would be very useful if someone with an in-ciruit debugger could try to reproduce it and find out what is going on...

OT
Posts: 581
Joined: 19 May 2005 05:08
Location: Fairbanks, Alaska

Code for 25LC1024/25LC256 Solved! (I think...)

#2 Post by OT » 03 Dec 2007 13:44

I have not seen this in the documentation anywhere, however it seemed that it was necessary to have a write unprotect before every write followed by a write protect when writing was finished. This also solved the problem on the read side for unknown reason. Here is the current version of the code which works both on 25LC256 and 25LC1024 by setting the proper define/undefine. There are some further write tests too:

Code: Select all

program EEPROM_SPI_25LC1024;
//******************************************************************************
// Program To test 25LC1024 or 25LC256 eeprom
// EasydsPIC2 at FRC 7.37 MHz, dsPIC30F4013.
// CS for EEPROM is connected to port F pin 4
// O. T. 2007-12-03

{$UNDEFINE 25LC1024}     //setting EEPROM type

const
  CS_PIN_EEPROM           = 4;     //Chip enable pin portF
  //Commands
  EE_ReadCmd            = $03;     //read data from selected address
  EE_WriteCmd           = $02;     //write data to selected address
  EE_WriteEnable        = $06;     //set write enable latch (enable write ops)
  EE_WriteDisable       = $04;     //reset the write enable latch (disable write ops)
  EE_ReadStatusReg      = $05;     //read status register
  EE_WriteStatusReg     = $01;     //write status register
  EE_PageErase          = $42;     //page erase
  EE_SectorErase        = $D8;     //sector erase
  EE_ChipErase          = $C7;     //chip erase
  EE_RelDeepPowerDwn    = $AB;     //release from deep power down & read ID
  EE_DeepPowerDwn       = $B9;     //enter deep power down mode


var
   DatWord,dataReadWord  : Word;
   Pos                   : Word;
   EEdataAddr            : LongInt;
   txt                   : string[16];


procedure DeepPowerDwnEE;
begin
  while SPI1STAT.1 = 1 do                // wait for SPI module to finish, if doing something
   nop;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_DeepPowerDwn);           // Deep power down
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 1;
end;

procedure RelDeepPowerDwnEE;
begin
  while SPI1STAT.1 = 1 do                // wait for SPI module to finish, if doing something
   nop;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_RelDeepPowerDwn);        // Deep power down
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 1;
  Delay_us(200);
end;



procedure WriteInProgressPollEE;
//This routine loops until WIP:= 0
var
  Status: Byte;
begin
  while (Status and $01) do               //The WIP bit is bit 0.
    begin
      LatF.CS_PIN_EEPROM:= 0;             //Select Device
      SPI1_Write(EE_ReadStatusReg);       //Read Status Reg OpCode
      Status:= SPI1_Read(0);              //Read Status Reg
      LatF.CS_PIN_EEPROM:= 1;             //Deselect Device
    end;                                  //Check for WIP bit Set
end;


procedure WriteEnableEE;
begin
  while SPI1STAT.1 = 1 do                // wait for SPI module to finish, if doing something
   nop;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_WriteEnable);            //enable writes
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 1;
end;


procedure WriteDisableEE;
begin
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
   LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_WriteDisable);           //disable writes to eeprom
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 1;
end;


procedure ChipEraseEE;
begin
  while SPI1STAT.1 = 1 do                // wait for SPI module to finish, if doing something
   nop;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_ChipErase);              // Chip erase command
  while SPI1STAT.1 = 1 do                // Wait for SPI module to finish write
   nop;
  LatF.CS_PIN_EEPROM := 1;
  WriteInProgressPollEE;
  WriteDisableEE;
end;


procedure WriteEEByte(databyte: Word; EEdataAddr:LongInt);
begin
  while SPI1STAT.1 = 1 do             // wait for SPI module to finish, if doing something
   nop;
  WriteEnableEE;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_WriteCmd);             //write command
 {$IFDEF 25LC1024}
   Spi1_Write(Higher(EEdataAddr));     //send Upper byte of 24 bit address
 {$ENDIF}
  Spi1_Write(Hi(EEdataAddr));          //send lower  16 bits of address
  Spi1_Write(Lo(EEdataAddr));
  Spi1_Write(databyte);               //write data
  LatF.CS_PIN_EEPROM := 1;
  WriteDisableEE;
end;


function ReadEEByte(EEdataAddr: LongInt): Word;
var
  datareadbyte: Word;
begin
  while SPI1STAT.1 = 1 do             // wait for SPI module to finish, if doing something
    nop;
  WriteInProgressPollEE;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_ReadCmd);            //issue the READ command
 {$IFDEF 25LC1024}
   Spi1_Write(Higher(EEdataAddr));   //send Upper byte of 24 bit address
 {$ENDIF}
  Spi1_Write(Hi(EEdataAddr));        //send lower  16 bits of address
  Spi1_Write(Lo(EEdataAddr));
  dataReadByte := SPI1_read(0);      //read the eeprom
  LatF.CS_PIN_EEPROM := 1;
  ReadEEByte:=dataReadByte;
end;


procedure WriteEEWord(dataWord: Word; EEWordAddr:LongInt);
//EEword address is now the count of words, not bytes
var
  BAddr: LongInt;
begin
  BAddr:=2*EEWordAddr;
  while SPI1STAT.1 = 1 do             // wait for SPI module to finish, if doing something
   nop;
  WriteEnableEE;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_WriteCmd);             //write command
 {$IFDEF 25LC1024}
   Spi1_Write(Higher(BAddr));         //send Upper byte of 24 bit address
 {$ENDIF}
  Spi1_Write(Hi(BAddr));              //send lower  16 bits of address
  Spi1_Write(Lo(BAddr));
  Spi1_Write(Hi(dataWord));           //write data
  Spi1_Write(Lo(dataWord));           //write data
  LatF.CS_PIN_EEPROM := 1;
  WriteDisableEE;
end;


function ReadEEWord(EEWordAddr: LongInt): Word;
//EEword address is now the count of words, not bytes
var
  BAddr: LongInt;
  Bread: Byte;
  Wread: Word;
begin
  BAddr:=2*EEWordAddr;
  while SPI1STAT.1 = 1 do             // wait for SPI module to finish, if doing something
    nop;
  WriteInProgressPollEE;
  LatF.CS_PIN_EEPROM := 0;
  Spi1_Write(EE_ReadCmd);             //issue the READ command
 {$IFDEF 25LC1024}
   Spi1_Write(Higher(BAddr));         //send Upper byte of 24 bit address
 {$ENDIF}
  Spi1_Write(Hi(BAddr));              //send lower  16 bits of address
  Spi1_Write(Lo(BAddr));
  Bread := SPI1_read(0);              //read the eeprom
  Wread:=Bread shl 8;
  Bread := SPI1_read(0);              //read the eeprom
  Wread:=Wread or Bread;
  LatF.CS_PIN_EEPROM := 1;
  ReadEEWord:=Wread;
end;





begin
  Delay_ms(500);
  ADPCFG := $FFFF;
  TRISD  := $FFF0;
  TrisB  := $000F;
  LatB:=0;
  TRISF.2 := 1;     //input
  TRISF.3 := 0;     //output
  TRISF.CS_PIN_EEPROM := 0;          // CS pin
  LatF.CS_PIN_EEPROM  := 1;          // Set CS to inactive
  
 // SPI setup
  Spi1_Init_Advanced(_SPI_MASTER, _SPI_8_BIT, _SPI_PRESCALE_SEC_1, _SPI_PRESCALE_PRI_1,
                   _SPI_SS_DISABLE, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_HIGH, _SPI_ACTIVE_2_IDLE);
  //Spi1_Init;

  DeepPowerDwnEE; //have not been able to test if this really drops power cons.
  Lcd_Init(LATB, 7, 6, 5, 4, LATD, 0, 1, 2);
  Lcd_Cmd(LCD_CURSOR_OFF);
  Lcd_Cmd(LCD_CLEAR);
  LCD_out(1,1,'Sleeping for 3s.');
  Delay_ms(3000);
  LCD_out(1,1,'Wake-up         ');
  RelDeepPowerDwnEE;
  Delay_ms(500);
  Lcd_Cmd(LCD_CLEAR);

 //Write single bytes to external eeprom and display written chars on LCD line 1
  EEdataAddr :=  0;
  DatWord    := 65;
  WriteInProgressPollEE;
  while EEdataAddr < 16 do begin
    WriteEEByte(datWord,EEdataAddr);
    Pos:=EEdataAddr+1;
    lcd_Chr(1,Pos,DatWord);
    Inc(EEdataAddr);
    Inc(DatWord);
  end;
  WriteInProgressPollEE;
  Delay_ms(1000);

 //Read single bytes from external eeprom and display the chars on LCD line 2
  EEdataAddr := 0;
  while EEdataAddr < 16 do begin
    dataReadWord:=ReadEEByte(EEdataAddr);
    delay_ms(10);
    Pos:=EEdataAddr+1;
    lcd_Chr(2,Pos,dataReadWord);
    Inc(EEdataAddr);
  end;
  Delay_ms(3000);
  Lcd_Cmd(LCD_CLEAR);

  //Write single word to external eeprom and display written chars on LCD
  //note that address now is the number of word counts. As long as address starts
  //at 0 or an even address, there should not be problems with page boundaries
   EEdataAddr := 10000;
  DatWord    := 55000;
  While EEdataAddr<10101 do begin
   //Write single word to external eeprom and display the value on LCD line 1
    WriteInProgressPollEE;
    WriteEEWOrd(datWord,EEdataAddr);
    WordToStr(datWord, txt);
    lcd_out(1,1,txt);
    WriteInProgressPollEE;
    Delay_ms(10);
   //Read single word from external eeprom and display value LCD on LCD line 2
    dataReadWord:=ReadEEWord(EEdataAddr);
     WordToStr(dataReadWord, txt);
    lcd_out(2,1,txt);
    Inc(EEdataAddr);
    Inc(DatWord);
  end;
  lcd_out(2,11,'done!');
  //finished, do nothing more
  While true do begin
    nop;
  end;
end.
I have not been able to check the deep power down yet.

OT
Posts: 581
Joined: 19 May 2005 05:08
Location: Fairbanks, Alaska

#3 Post by OT » 04 Dec 2007 11:41

This explains the need to set the write enable latch for each write (from the data sheet):
"A write enable instruction must be issued to set the write enable latch. After a byte write, page write or STATUS register write, the write enable latch is reset."
BTW, the deep power down mode works.

FRM
Posts: 381
Joined: 20 May 2005 18:58
Location: UK

#4 Post by FRM » 04 Dec 2007 15:48

Looks like good work and useful code, OT.

Thanks for sharing.

:wink:

OT
Posts: 581
Joined: 19 May 2005 05:08
Location: Fairbanks, Alaska

#5 Post by OT » 05 Dec 2007 07:35

Thanks for the comment. I have now organized it into a unit which can be found here:
http://www.mikroe.com/forum/viewtopic.php?t=12629

Post Reply

Return to “mikroPascal for dsPIC30/33 and PIC24 General”