Go Down

Topic: EEPROM **Hardware Level** programming ( 2560 Mega) (Read 5212 times) previous topic - next topic

TheArchitect22



It seems to me there are 4 instructions between the two (st)ore instuctions:  ldr, ldr, ld, ori...   I myself do not know how to interpret assembly language.  So if you don't mind me asking?  What is the significance of these in-between instructions?  And how do we know how long these instructions are taking in cycles.  I tried looking for it on the 2560 ATmel manual but no look so far.  I digress and will continue to search though..

http://www.atmel.com/Images/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf
Instruction set summary, page 404


Thanks for the help AWOL.  If I was understanding the datasheet correctly it seems we are taking 7 clock cycles to complete the instructions in between the two "st(ore)" instructions, which is over the required limit of within 4 clock cycles.  But then how can we correct for this?  What do we need to do to get it to within the specified limit of 4 clock cycles.  Any help would be appreciated, thank you all.  By the way my background is not in CS or computer engineering, i'm just trying to learn.  Thank you all for helping.

TheArchitect22


Quote
Quote
Quote
It looks like an examle from datasheet. Try EEARH 0x22; EEARL 0x21; EEDR 0x20; EECR 0x1F instead.
...are you suggestion I try the other addresses given in the data sheet, instead of where I declared their homes already?
The compiler has enough intelligence to use the lower addresses when appropriate.

Yes, that is exactly true. It does'n matter which addresses are used.

I 've tried to compile the code from datasheet and Capt_Rhizz code also in AtmelStudio and both worked.
Code: [Select]
while(EECR & (1 << EEPE));
000001AF  SBIC 0x1F,1 Skip if bit in I/O register cleared
000001B0  RJMP PC-0x0001 Relative jump
EEAR = 1;
000001B1  LDI R24,0x01 Load immediate
000001B2  LDI R25,0x00 Load immediate
000001B3  OUT 0x22,R25 Out to I/O location
000001B4  OUT 0x21,R24 Out to I/O location
EEDR = 0x55;
000001B5  LDI R24,0x55 Load immediate
000001B6  OUT 0x20,R24 Out to I/O location
EECR |= (1 << EEMPE);
000001B7  SBI 0x1F,2 Set bit in I/O register
EECR |= (1 << EEPE);
000001B8  SBI 0x1F,1 Set bit in I/O register


while(*myEECR & 0x02) {}
000001B9  SBIC 0x1F,1 Skip if bit in I/O register cleared
000001BA  RJMP PC-0x0001 Relative jump
*myEEARhigh = 0;
000001BB  OUT 0x22,R1 Out to I/O location
*myEEARlow  = 1;
000001BC  LDI R24,0x01 Load immediate
000001BD  OUT 0x21,R24 Out to I/O location
*myEEDR     = 0xAA;
000001BE  LDI R24,0xAA Load immediate
000001BF  OUT 0x20,R24 Out to I/O location
*myEECR    |= 0x04;
000001C0  SBI 0x1F,2 Set bit in I/O register
*myEECR    |= 0x02;
000001C1  SBI 0x1F,1 Set bit in I/O register




Yes, the code compiles for me as well (using Arduino Compiler) but EEPROM_write function does not write to an EEPROM address like it is suppose to.  And I believe it is because of the reason AWOL and CodingBadly are pointing out:  that the logic 1 flipping of the EEMPE bit and EEPE bit are not occuring within the specified time limit (4 clock cycles).  If i read the datasheet correctly, it is taking 7 clock cycles.  By the way, my custom EEPROM_read function works, tested it using the built-in EEPROM.write() function from the EEPROM.h, it is just that my custom EEPORM_write() function is not working properly.

TheArchitect22

By the way here is my code:  The Read function works just not the Write function..

void EEPROM_write(unsigned char eeData)
{
 
  while (*myEECR & EPE); //Wait for completion of previous write
 
  //while (*mySPMCSR & SPM){};
 
  *myEEARH = 0x00;
  *myEEARL = 0x01; //set up address and data registers

  *myEEDR = eeData;
 
  *myEECR |= MPE; //write logical one to EEMPE
  *myEECR |= EPE; //Start eeprom by writing logic one to EEPE
 
}


unsigned char EEPROM_read()
{
 
  while (*myEECR & EPE){}; //wait for completion of previous write
 
  *myEEARH = 0x00;
  *myEEARL = 0x01; //set up address register
 
  *myEECR |= ERE; //Start EEPROM Read by writing logic one to EERE
 
  return *myEEDR;
 
}


My # defines are as follows:

#define EPE 0x02
#define MPE 0x04
#define ERE 0x01


I narrowed down my problem to my EEPROM_write() function..

Coding Badly

From some searching around on the internets. I saw that each instruction should be added together...


The number of clock cycles for each instruction should be added together for a total number of clock cycles.  Correct.

Quote
...then multiplied by 2.5.


Why on Earth would you do that?

Quote
Can I just do this:
FROM:
*myEECR |= 0x04;// writing 1 to EEMPE bit
*myEECR |= 0x02;// now writing to EEPE bit which starts write
TO:
*myEECR |=06;


No.  The datasheet is very clear.  The bits must be set separately, in sequence, within four clock cycles.

Quote
Then it would be done it one instruction?


Correct.  Which is why it will not work.

Quote
I feel a bronze star for effort coming.. :smiley-roll-sweat:


Sounds good to me.

Coding Badly

If I was understanding the datasheet correctly it seems we are taking 7 clock cycles to complete the instructions in between the two "st(ore)" instructions...


lds --> 2
lds --> 2
ld  --> 2
ori --> 1


You did not include one of the st(ore) instructions in the total.  I did mention twice that is necessary.

Quote
...which is over the required limit of within 4 clock cycles.  


In any case that is correct.  The code takes too long to set the second bit.

Coding Badly

Do we have to do something with the produced assembly language?? 


No.  Adding one C keyword (four times) to the original code corrects the problem.

Quote
Also if we are outside the given parameters (in this case 4 clock cycles), how can we correct for this error? 


I'll post the solution in the morning.  It's bedtime...

Coding Badly


Really?  No one has any idea on how to fix the problem?  Not even a guess?

TheArchitect22



Really?  No one has any idea on how to fix the problem?  Not even a guess?



Thanks for your help, CodingBadly.  I was busy all day Friday and haven't gotten to it since.  I'll have some time again tonight though and will look to try to crack this nut with your hints.  Thanks for the help.

TheArchitect22



Really?  No one has any idea on how to fix the problem?  Not even a guess?



Greetings CodingBadly,

Thanks for your help.  I fixed my EEPROM_write() problem by placing the KEYWORD "static" in front of my global pointer-variables as follows: 

static volatile unsigned char *myEEARH = (unsigned char *) 0x42; //Physical address for the EEPROM Address Register (High)
static volatile unsigned short *myEEARL = (unsigned short *) 0x41; //Physical address for the EEPROM Address Register (low)
static volatile unsigned char *myEEDR = (unsigned char *) 0x40; //Physical address for the EEPROM Data Register
static volatile unsigned char *myEECR = (unsigned char *) 0x3F; //Physical address for the EEPROM Control Register

I read this trick any many others for the topic of optimization from the following link:

http://www.atmel.com/images/doc8453.pdf


My program works now, my only other question is how were you able to produce the Assembly code so I could check how many clock cycles a certain part of my program is taking in the future(Perhaps even checking  the cycle count on my new modified code).  Thanks again for your help.

TheArchitect22

Also, someone give me a plus +1  on Karma.  lol

TheArchitect22

Never mind, i figured out how to produce the assembly code...  Very useful.

Coding Badly

#26
Sep 28, 2014, 09:48 pm Last Edit: Sep 28, 2014, 09:51 pm by Coding Badly Reason: 1
Greetings TheArchitect22,

I fixed my EEPROM_write() problem by placing the KEYWORD "static" in front of my global pointer-variables as follows:


const also works and is arguably more appropriate.  It certainly makes the complete type more accurate.

It helps me to treat type qualifiers as always right-to-left associative; which they are except to the far left.  In the following, the unsigned char / unsigned short is volatile and the pointer is a constant...

Code: [Select]
unsigned char volatile * const myEEARH = (unsigned char *) 0x42; //Physical address for the EEPROM Address Register (High)
unsigned short volatile * const myEEARL = (unsigned short *) 0x41; //Physical address for the EEPROM Address Register (low)
unsigned char volatile * const myEEDR = (unsigned char *) 0x40; //Physical address for the EEPROM Data Register
unsigned char volatile * const myEECR = (unsigned char *) 0x3F; //Physical address for the EEPROM Control Register


Quote
I read this trick any many others for the topic of optimization from the following link:


The trick works because of the optimizer.  It may not work at a different level of optimization.

Coding Badly


To round off the final rough edge, it is necessary to put the time sensitive code in a critical section...

Code: [Select]
  noInterrupts();
  *myEECR |= MPE; //write logical one to EEMPE
  *myEECR |= EPE; //Start eeprom by writing logic one to EEPE
  interrupts();


...or, like this for code that can be used from an interrupt service routine...

Code: [Select]
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
  *myEECR |= MPE; //write logical one to EEMPE
  *myEECR |= EPE; //Start eeprom by writing logic one to EEPE
}


TheArchitect22



To round off the final rough edge, it is necessary to put the time sensitive code in a critical section...

Code: [Select]
  noInterrupts();
  *myEECR |= MPE; //write logical one to EEMPE
  *myEECR |= EPE; //Start eeprom by writing logic one to EEPE
  interrupts();


...or, like this for code that can be used from an interrupt service routine...

Code: [Select]
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
  *myEECR |= MPE; //write logical one to EEMPE
  *myEECR |= EPE; //Start eeprom by writing logic one to EEPE
}




Yeah I did something similar in my setup() function by disabling all global interrupts, plus I wasn't using any in my code.

Code: [Select]

void setup()
{
// initialize the serial port on USART0:
U0init(9600);
       
        *myregSREG &= 0x7F; //Disable Global Interrupts
}


I noticed this was something important to do in the EEPROM section of the Atmel 2560 manual.  By the way do you work as a firmware or embedded systems engineer or something cause you really seem to know your embedded systems stuff?  Thanks again for your tips and advice.

Coding Badly

Quote
Thanks again for your tips and advice.


You are welcome.

By the way do you work as a firmware or embedded systems engineer or something...


In a previous life. 

Quote
...cause you really seem to know your embedded systems stuff? 


Thanks.  However, while the timed sequence is specific to microcontrollers, critical sections and optimizer hinting are relevant across the spectrum of computing.

Go Up