8051 Keil c51 Unable to properly use the INTERNAL EEPROM, on 89c52 or STC12C5A60S2

50 Views Asked by At

Difficulty Reading/Writing Multiple Bytes from Internal EEPROM (STC12C5A60S2 / 89C52)
Body:

I'm encountering a persistent challenge in reading and writing multiple bytes from the internal EEPROM of an STC12C5A60S2 microcontroller, both in a Proteus simulation and in a real-world application.

When I write and then read one byte, everything works fine. But when I try to write 2 bytes, (one after the other), and then read them, the result is returns both readed bytes with the value of the last one that has been written.

Lets say that When I write 0xABCD It gets written 0xCDCD

To provide context, I've simulated the STC12C5A60S2 using an 89c02 as a substitute due to the unavailability of the exact model in Proteus.

  • IDE : Using uVision Keil C51
  • Simulation : Proteus 8 (MCU 89C52)
  • Real life: Custom board (MCU STC12C5A60S2)

I've explored three different approaches:

Attempt 0: Using Demo Code from Datasheet: I initially attempted to implement the code provided in the datasheet (https://www.stcmicro.com/datasheet/STC12C5A60S2-en.pdf) on page 374. Exact copy the code provided by the producer, it is so long so i wont post it here, see the repo, I added some prints to ease the interpretation. It is supposed a bunch of demo operations It doesnt work how it should.

Attempt 0: See the full simulable repo here.

Attempt 1: Using Demo Code from Datasheet Simplified Read/Write for Single and Multiple Bytes: I then simplified the code to read and write one byte, as well as two bytes. Surprisingly, everything works fine when performing single-byte operations. However, when attempting consecutive read and write operations for multiple bytes, both bytes end up with the value of the last one.

 
 /*-------------------------------------------------------------------------------*/
/* --- STC MCU International Limited ----------------------------------*/
/* --- STC 1T Series MCU ISP/IAP/EEPROM Demo ----------------*/
/* If you want to use the program or the program referenced in the */
/* article, please specify in which data and procedures from STC */
/*-------------------------------------------------------------------------------*/
#include "reg51.h"
#include "intrins.h"
#include "uart.h"

typedef unsigned char BYTE;
typedef unsigned int WORD;
/*Declare SFR associated with the IAP */
sfr IAP_DATA = 0xC2; //Flash data register
sfr IAP_ADDRH = 0xC3; //Flash address HIGH
sfr IAP_ADDRL = 0xC4; //Flash address LOW
sfr IAP_CMD = 0xC5; //Flash command register
sfr IAP_TRIG = 0xC6; //Flash command trigger
sfr IAP_CONTR = 0xC7; //Flash control register
/*Define ISP/IAP/EEPROM command*/
#define CMD_IDLE 0 //Stand-By
#define CMD_READ 1 //Byte-Read
#define CMD_PROGRAM 2 //Byte-Program
#define CMD_ERASE 3 //Sector-Erase
/*Define ISP/IAP/EEPROM operation const for IAP_CONTR*/
//#define ENABLE_IAP 0x80 //if SYSCLK<30MHz
  #define ENABLE_IAP 0x81 //if SYSCLK<24MHz
//#define ENABLE_IAP 0x82 //if SYSCLK<20MHz
//#define ENABLE_IAP 0x83 //if SYSCLK<12MHz
//#define ENABLE_IAP 0x84 //if SYSCLK<6MHz
//#define ENABLE_IAP 0x85 //if SYSCLK<3MHz
//#define ENABLE_IAP 0x86 //if SYSCLK<2MHz
//#define ENABLE_IAP 0x87 //if SYSCLK<1MHz
//Start address for STC12C5A60S2 EEPROM
#define IAP_ADDRESS 0x0000 
void Delay(BYTE n);
void IapIdle();
BYTE IapReadByte(WORD addr);
void IapProgramByte(WORD addr, BYTE dat);
void IapEraseSector(WORD addr);
void main()
{
 
 UART_init(9600);
 

 UART_printStr("System Reset OK\r");
 Delay(10); //Delay (this is an eternal test delay )


 

/*
>>> WORKS FINE! Writing and reading a single byte 

// Write a byte
 UART_printStr("Writing 0xAB\r");
  IapProgramByte(0, (BYTE)0xAB); // Write byte 
  Delay(10); //Delay  

  // Read and print a byte
 UART_printStr("Readed \r");
 UART_printHex( IapReadByte(0),1);
 UART_printStr("\r");
   
*/



/*
>>>  WORKS NOT  FINE! Writing and reading a multiple bytes 
*/
  
// Write one byte
 UART_printStr("Writing 0xAB\r");
  IapProgramByte(0, (BYTE)0xAB); // Write byte 
  Delay(10); //Delay  
  // Write other byte
   UART_printStr("Writing 0xCD\r");
  IapProgramByte(1, (BYTE)0xCD); // Write byte 
  Delay(10); //Delay   

  // Read and print one byte
 UART_printStr("Readed \r");
 UART_printHex( IapReadByte(0),1);
 UART_printStr("\r");
  
    // Read and print the other byte
 UART_printStr("Readed \r");
 UART_printHex( IapReadByte(1),1);
 UART_printStr("\r");

 

 while (1);
}
/*----------------------------
Software delay function
----------------------------*/ 
void Delay(BYTE n)
{
 WORD x;
 while (n--)
 {
 x = 0;
 while (++x);
 }
}
/*----------------------------
Disable ISP/IAP/EEPROM function
Make MCU in a safe state
----------------------------*/
void IapIdle()
{
 IAP_CONTR = 0; //Close IAP function
 IAP_CMD = 0; //Clear command to standby
 IAP_TRIG = 0; //Clear trigger register
 IAP_ADDRH = 0x80; //Data ptr point to non-EEPROM area
 IAP_ADDRL = 0; //Clear IAP address to prevent misuse
}
/*----------------------------
Read one byte from ISP/IAP/EEPROM area
Input: addr (ISP/IAP/EEPROM address)
Output:Flash data
----------------------------*/
BYTE IapReadByte(WORD addr)
{
 BYTE dat; //Data buffer
 IAP_CONTR = ENABLE_IAP; //Open IAP function, and set wait time
 IAP_CMD = CMD_READ; //Set ISP/IAP/EEPROM READ command
 IAP_ADDRL = addr; //Set ISP/IAP/EEPROM address low
 IAP_ADDRH = addr >> 8; //Set ISP/IAP/EEPROM address high
 IAP_TRIG = 0x5a; //Send trigger command1 (0x5a)
 IAP_TRIG = 0xa5; //Send trigger command2 (0xa5)
 _nop_(); //MCU will hold here until ISP/IAP/EEPROM
 //operation complete
 dat = IAP_DATA; //Read ISP/IAP/EEPROM data
 IapIdle(); //Close ISP/IAP/EEPROM function
 return dat; //Return Flash data
}
 
/*----------------------------
Program one byte to ISP/IAP/EEPROM area
Input: addr (ISP/IAP/EEPROM address)
 dat (ISP/IAP/EEPROM data)
Output:-
----------------------------*/
void IapProgramByte(WORD addr, BYTE dat)
{
 IAP_CONTR = ENABLE_IAP; //Open IAP function, and set wait time
 IAP_CMD = CMD_PROGRAM; //Set ISP/IAP/EEPROM PROGRAM command
 IAP_ADDRL = addr; //Set ISP/IAP/EEPROM address low
 IAP_ADDRH = addr >> 8; //Set ISP/IAP/EEPROM address high
 IAP_DATA = dat; //Write ISP/IAP/EEPROM data
 IAP_TRIG = 0x5a; //Send trigger command1 (0x5a)
 IAP_TRIG = 0xa5; //Send trigger command2 (0xa5)
 _nop_(); //MCU will hold here until ISP/IAP/EEPROM
 //operation complete
 IapIdle();
}
/*----------------------------
Erase one sector area
Input: addr (ISP/IAP/EEPROM address)
Output:-
----------------------------*/
void IapEraseSector(WORD addr)
{
 IAP_CONTR = ENABLE_IAP; //Open IAP function, and set wait time
 IAP_CMD = CMD_ERASE; //Set ISP/IAP/EEPROM ERASE command
 IAP_ADDRL = addr; //Set ISP/IAP/EEPROM address low
 IAP_ADDRH = addr >> 8; //Set ISP/IAP/EEPROM address high
 IAP_TRIG = 0x5a; //Send trigger command1 (0x5a)
 IAP_TRIG = 0xa5; //Send trigger command2 (0xa5)
 _nop_(); //MCU will hold here until ISP/IAP/EEPROM
 //operation complete
 IapIdle();
}
 

Attempt 1: See the full simulable repo here.

Attempt 2: Adapted Code from Other Sources: As a third approach, I adapted code snippets from various sources to address the issue. While this approach shows promise, I'm still encountering the same problem of values not persisting across multiple-byte operations.

#include "header/stc12.h"
#include <intrins.h>
#include "uart.h"
 
 
 
// Define the EEPROM base address
#define EEPROM_BASE_ADDRESS 0x0000

// Function to write a byte to EEPROM
void EEPROM_writeByte(unsigned int address, unsigned char byteToWrite) {
    EA = 0; // Disable interrupts during write operation
    IAP_CONTR = 0x82; // Enable IAP and set programming time

    IAP_DATA = byteToWrite; // Load data to be written
    IAP_ADDRH = (address >> 8) & 0xFF; // Load high byte of address
    IAP_ADDRL = address & 0xFF; // Load low byte of address

    IAP_CMD = 0x02; // Command to erase and write one byte
    IAP_TRIG = 0x5A; // Trigger IAP
   _nop_();// Wait for IAP to complete

    EA = 1; // Re-enable interrupts
}

// Function to read a byte from EEPROM
unsigned char EEPROM_readByte(unsigned int address) {
    unsigned char readData;

    EA = 0; // Disable interrupts during read operation
    IAP_CONTR = 0x80; // Enable IAP

    IAP_ADDRH = (address >> 8) & 0xFF; // Load high byte of address
    IAP_ADDRL = address & 0xFF; // Load low byte of address

    IAP_CMD = 0x01; // Command to read one byte
    IAP_TRIG = 0x5A; // Trigger IAP

     
        _nop_();// Wait for IAP to complete
     

    readData = IAP_DATA; // Read the data from EEPROM

    EA = 1; // Re-enable interrupts

    return readData;
}


 
void main() {
     

    unsigned char byteToWrite = 0xAA;
    
    UART_init(9600);
    
    UART_printStr("Hello, this is working with one byte ... \r"); 
    // Write a byte to EEPROM
    EEPROM_writeByte(0, 0xAA); 
    // Read the byte from EEPROM 
    UART_printHex(EEPROM_readByte(0),1);// Print readed

 


    
        UART_printStr("\r... but not with 2 \r"); 
    // Write a byte to EEPROM
    EEPROM_writeByte(1, 0xBB); 
        _nop_(); // no matter how long I wait here 
    EEPROM_writeByte(2, 0xCC); 
    // Read the byte from EEPROM 
    UART_printHex(EEPROM_readByte(1),1);// Print readed
        _nop_(); // no matter how long I wait here
    UART_printHex(EEPROM_readByte(2),1);// Print readed
    

    while(1) {
        // Main loop, do nothing
    }
}



 

Attempt 2: See the full simulable repo here.

I'd greatly appreciate any insights or suggestions on what might be causing this issue, especially considering it persists in both simulation and real-world applications. If there are alternative approaches or corrections to the code, I'm open to exploring them. Thank you for your assistance!

I have attempted to run, among others, the examples shown on the post, available on github. I also have attempthed almost eternal delays between operations. Also a lot of annoying chatGPT.

0

There are 0 best solutions below