Calculating approx delay

Thread Starter

Tajiknomi

Joined Mar 18, 2010
34
I am writing data to EEPROM at 9600BR. I am using HyperTerminal to transfer characters. When i transfer 3 character at the same time, Its works but the problem arise when i try to transfer greater then 3 bytes at the same time. Therefore i've done calculation to see where the problem lies.
(The screen-shot of the Write Module in given below)
1) My MCU Freq = 4Mhz, Each instruction takes 4-cycles so write module have 13 instructions (see the screen-shot), instruction will take 13usec (13/1M) & Instruction time + EEPROM Write time(10ms) -->Approx 10.013ms.
2) 4 bytes should take around 40msec (13u*4).
3) I am using BR 9600 bits/sec; converting to byte will be 1200 bytes/sec(9600/8) means --> 1 byte will take approx 833usec (1/1200) to be on the bus.

I may be missing something...
Any help would be appreciated.
 
Last edited:

sailorjoe

Joined Jun 4, 2013
364
Tajiknomi, it would help us to know what MCU you are using, what EEPROM you're using, and how the two are connected. Then we will know what data sheets to examine to make sure we're not guessing.
 

sailorjoe

Joined Jun 4, 2013
364
OK, thanks. I'm not a PIC expert, but perhaps there is some help in this example:
void writeEEPROM(unsigned char address, unsigned char datas)
{
unsigned char INTCON_SAVE;//To save INTCON register value
EEADR = address; //Address to write
EEDATA = datas; //Data to write
EECON1.EEPGD = 0; //Selecting EEPROM Data Memory
EECON1.WREN = 1; //Enable writing of EEPROM
INTCON_SAVE=INTCON;//Backup INCON interupt register
INTCON=0; //Diables the interrupt
EECON2=0x55; //Required sequence for write to internal EEPROM
EECON2=0xAA; //Required sequence for write to internal EEPROM
EECON1.WR = 1; //Initialise write cycle
INTCON = INTCON_SAVE;//Enables Interrupt
EECON1.WREN = 0; //To disable write
while(PIR2.EEIF == 0)//Checking for complition of write operation
{
asm nop; //do nothing
}
PIR2.EEIF = 0; //Clearing EEIF bit
}

There are two big differences between this example and your code. One is the way that interrupts are saved and restored. The other is the bit that's being checked to see if the write operation is completed. This example is from a code library at this site (2 pages) https://electrosome.com/internal-eeprom-pic-microcontroller/1/
 

Thread Starter

Tajiknomi

Joined Mar 18, 2010
34
I just want to know why it accepts the 3 characters & writes in EEPROM at the same time But NOT 4 characters ?
 

Picbuster

Joined Dec 2, 2013
1,047
I am writing data to EEPROM at 9600BR. I am using HyperTerminal to transfer characters. When i transfer 3 character at the same time, Its works but the problem arise when i try to transfer greater then 3 bytes at the same time. Therefore i've done calculation to see where the problem lies.
(The screen-shot of the Write Module in given below)
1) My MCU Freq = 4Mhz, Each instruction takes 4-cycles so write module have 13 instructions (see the screen-shot), instruction will take 13usec (13/1M) & Instruction time + EEPROM Write time(10ms) -->Approx 10.013ms.
2) 4 bytes should take around 40msec (13u*4).
3) I am using BR 9600 bits/sec; converting to byte will be 1200 bytes/sec(9600/8) means --> 1 byte will take approx 833usec (1/1200) to be on the bus.

What we need is to look at your code ( from serial receiver) I think that that where your problem lives.



I may be missing something...
Any help would be appreciated.
I am writing data to EEPROM at 9600BR. I am using HyperTerminal to transfer characters. When i transfer 3 character at the same time, Its works but the problem arise when i try to transfer greater then 3 bytes at the same time. Therefore i've done calculation to see where the problem lies.
(The screen-shot of the Write Module in given below)
1) My MCU Freq = 4Mhz, Each instruction takes 4-cycles so write module have 13 instructions (see the screen-shot), instruction will take 13usec (13/1M) & Instruction time + EEPROM Write time(10ms) -->Approx 10.013ms.
2) 4 bytes should take around 40msec (13u*4).
3) I am using BR 9600 bits/sec; converting to byte will be 1200 bytes/sec(9600/8) means --> 1 byte will take approx 833usec (1/1200) to be on the bus.

I may be missing something...
Any help would be appreciated.
 

Papabravo

Joined Feb 24, 2006
21,159
I just want to know why it accepts the 3 characters & writes in EEPROM at the same time But NOT 4 characters ?
I'm guessing it is because your receiver is being overrun because you are not buffering the data. Also I'm guessing that the EEPROM Write is not overlapped with the reception of characters. Busy waiting is a bad bad thing!
 

Thread Starter

Tajiknomi

Joined Mar 18, 2010
34
@Papabravo: How can i buffer the data at the receiving end ? Or there is a way to handle this situation ?
I have tried to put a delay between sending characters, but that didn't work. I am using "Realterm Serial" for transmission serial ASCII Characters using UART protocol
 
Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
I'm guessing it is because your receiver is being overrun because you are not buffering the data. Also I'm guessing that the EEPROM Write is not overlapped with the reception of characters. Busy waiting is a bad bad thing!
+1 It is hard to say exactly why it quits when it does but as you know, writing the EEPROM takes a couple of mSec and the code waits on that. 9600baud is roughly 1ms/char. The best (only) way to get this working is with an interrupt driven FIFO buffer that is big enough to hold the characters received until you process them.

An easy way to make a FIFO in C is with an array of characters with two indexes, one that keeps track of chars put in and another to keep track of chars taken out. A separate counter keeps track of the number of characters in the FIFO. It is incremented when a character is received and decremented when a character is read. The UART is configured to interrupt the PIC when a character is received. The service routine reads the character then puts it in the FIFO. It then increments the counter and the index, wrapping it when it reaches the end of the array.

Reading is similar. If the counter is >0, read the character at the current index, increment the index with wrapping and decrement the counter. The UART interrupt must be temporarily disabled while reading the FIFO.

Good luck.
 

Papabravo

Joined Feb 24, 2006
21,159
John outlined the basic receive mechanism of using a FIFO buffer. That is an acronym for First-In, First-Out. What this means is that if the five characters in the string "hello" come into the UART Receive hardware, one after the other, in that order, then your firmware reads them from the hardware in an interrupt routine and "puts" them in the FIFO buffer in that order. Later another process will come along and check how many characters are in the FIFO and it finds there are five. So it "gets" or removes them from the FIFO one at a time in the same order as when they originally came in.

The second part of what I was suggesting was to overlap the EEPROM writes with other things. You do this by avoiding infinite loops like
Code:
  while (BUSY==1) ;
You do the write operations like this:
Code:
  if(BUSY==0)
  {
    eeprom_write(wdata) ;  //eeprom_write() sets BUSY=1
  }
  else
  {
    ...do other tasks
    eeprom_check() ;  // eeprom_check() sets BUSY=0
  }
In general try to do anything at all to avoid waiting on a single condition that may or may not ever happen.
 

MMcLaren

Joined Feb 14, 2010
861
Hi Tajiknomi,

Could you send the EEPROM data as Intel Hex records (an ASCII character format)? If so, you might consider setting up HyperTerminal with 'software' (Xon/Xoff) handshaking and setup your program to send an 'Xon' character to start receiving each INHEX record and to send an 'Xoff' character after receiving the last (checksum) byte in each record. Basically, your program asks Hyperterminal to wait while it takes the time necessary to write the data contained in the Hex record to EEPROM. Intel Hex records have a well defined format (citation or link needed) so it's easy to determine the end of each record.

Cheerful regards, Mike

<added>
Example InHex data (ASCII) excerpt from a project 'hex' file;
Code:
:104200000000000000000300400001000500000065
:1042100002000600900002001000000003001300DE
:104220009000040017009700050020009000060091
:104230002400790007002700900008004900900042
:104240000900600010000A00000000000B000000E0
:1042500000000C00000000000D00000000000E0037
:00000001FF
That's the InHex data for this corresponding section of code from an Assembly language program;
Code:
;
;  CI-V to BCD Band Data Decoder 3-byte "memories" in EEPROM
;
;  Memory 00 contains the target radio CI-V address in byte
;  number 0 (a value of 00 = "any radio")
;
;  Memory 01 through 15 contain CI-V "Frequency-To-BCD" data
;  MegaHertz, KiloHertz (x10), and a BCD band data byte
;
;  When a frequency record is received from the target radio
;  over the CI-V bus its 16-bit frequency (MHz and KHz x10)
;  is compared to the 16-bit frequency value in our memories
;
;
        org     h'2100'         ; 3-byte memories in eeprom

        de      0x00,0x00,0x00  ; target radio CI-V address
        de      0x03,0x40,0x01  ; 160m 00.000-03.399 (0001)
        de      0x05,0x00,0x02  ;  80m 03.400-04.999 (0010)
        de      0x06,0x90,0x02  ;  60m 05.000-06.899 (0010)
        de      0x10,0x00,0x03  ;  40m 06.900-09.999 (0011)
        de      0x13,0x90,0x04  ;  30m 10.000-13.899 (0100)
        de      0x17,0x97,0x05  ;  20m 13.900-17.969 (0101)
        de      0x20,0x90,0x06  ;  17m 17.970-20.899 (0110)
        de      0x24,0x79,0x07  ;  15m 29.900-24.789 (0111)
        de      0x27,0x90,0x08  ;  12m 24.790-27.899 (1000)
        de      0x49,0x90,0x09  ;  10m 27.900-49.899 (1001)
        de      0x60,0x10,0x0A  ;   6m 49.900-60.099 (1010)
        de      0x00,0x00,0x0B  ;
        de      0x00,0x00,0x0C  ;
        de      0x00,0x00,0x0D  ;
        de      0x00,0x00,0x0E  ;
 
Last edited:

Thread Starter

Tajiknomi

Joined Mar 18, 2010
34
@Papabravo @JohnInTX : I got an issue in the buffer solution.
I had enable interrupt after the char is fed in buffer. Then i enabled it and wait for 50ms. It works but the 'Proteus' simulator gives me warning msgs shown in the screenshot below. Overflow & Underflow errors.
NOTE: This error only appear when i transfer > 8 characters at the same time. If i transfer <= 8 characters, Error msg doesn't appear.

I have used the buffer[30].
 

Papabravo

Joined Feb 24, 2006
21,159
The explicit enabling of interrupts should be done only once in a program. Thereafter, the RETFIE instruction is used to return from an interrupt routine AND enable interrupts for the next device that requires service. You are apparently pushing the stack past it's limit with interrupts and not exiting from your interrupt service routines properly. In addition there is apparently a routine executing a RETFIE instruction that is not a proper function since the instruction is executed with an empty stack.

Each time the interrupt routine is executed you must transfer ONE AND ONLY ONE CHARACTER.
 

JohnInTX

Joined Jun 26, 2012
4,787
Post your code as text and using CODE tags - find them on the toolbar -no screen shots please. We can take a look at it. Make sure you are not setting GIE inside the interrupt routine.
 

Thread Starter

Tajiknomi

Joined Mar 18, 2010
34
@Papabravo : Pardon me but i didn't fully grasp your message. Kindly Explain it a little "In addition there is apparently a routine executing a RETFIE instruction that is not a proper function since the instruction is executed with an empty stack".

@JohnInTX :
Make sure you are not setting GIE inside the interrupt routine.
That's exactly what i am doing, I am enabling the GIE in the ISR. How can i be enabling Interrupts other-way around ?
What i am doing is, When i Enter UART ISR, there i wait for all characters to be received and put it in buffer. Like, When i receive first character, i put it in the buffer and Enable GIE & wait for another character (50ms). the process will go on like this until i get no characters. Once i am done will all characters, i put them in EEPROM.

Here is the code of the ISR. Line:30 is where i am receiving the characters
Code:
char dummy; short write_address=0x00; short read_address=0x00; char buffer[30];
short space_available=30; short push=0x00; short pop=0x00;
void interrupt(){

   if(PIR1.RCIF == 1){           // Byte recieved interrupt ?

          dummy = RCREG;         // Recieve byte
          if(dummy == 'o'){      // When 'o' is tranfer, Output all the stored Characters.
           // Read EEPROM & Transfer all Written bytes via UART
             for(read_address=0x00;read_address<write_address;read_address++){
              EEADR = read_address;   // Adress to Read From
              EECON1.EEPGD = 0;       // Select EEPROM DATA MEMORY
              EECON1.RD=1;            // Start Read Operation
              dummy = EEDATA;         // Assign the read byte in dummy
              Delay_ms(10);           // Delay between each transfer*/
              TXREG = dummy;          // Transmit byte
             }

           }
           else if(dummy == 'e'){     // When 'e' is transfer, it should erase EEPROM
              write_address = 0x00;
              space_available=30;
           }


           else{
           // ..............Write Byte in EEPROM....................
        
           if(space_available != 0){      // If Buffer isn't full then Proceed...

               buffer[push] = dummy;      // Recieved byte trasfer to buffer
               push++;  space_available--;
               if(push>29) push=0x00;
               INTCON.GIE=1;           // Interrupts Enable
               Delay_ms(50);             // Wait for all characters to be recieved
               INTCON.GIE=0;           // Interrupts Disable
               while(space_available != 30){
                 EEADR = write_address;   // Write Address
                 EEDATA = buffer[pop];    // Pick byte from Buffer
                 EECON1.EEPGD = 0;        // Select EEPROM DATA MEMORY
                 EECON1.WREN=1;           // Enable Program Operation
                 EECON2 = 0x55;          // Special Interuction 1 (Mentioned in Data sheet)
                 EECON2 = 0xAA;          // Special Interuction 2  (Mentioned in Data sheet)
                 EECON1.WR=1;            // Start Writing to EEPROM
                 while(EECON1.WR != 0);     // Wait for the Write to Complete
                 write_address++;     // Incriment EEPROM Address
                 pop++;space_available++;
                 if(pop>29) {pop=0x00;}
                 EECON1.WREN=0;          // Disable Writes
              }
           }
          PIR1.RCIF = 0;         // Clear Rcv Interrupt Flag
          }

   }
}
 
Last edited:

Papabravo

Joined Feb 24, 2006
21,159
If you enable GIE inside the interrupt routine you will hose things up in a major way by allowing the stack to be overflowed with multiple unserviced interrupts. GIE must remain disabled for the entire length of the interrupt routine.
The instruction RETFIE is supposed to do two things:
  1. Return from an interrupt, by placing the value on top of the stack into the program counter.
  2. Re-enable interrupts by setting GIE=1
If the stack is empty and there is no return address to go to the processor is very likely to malfunction. The only way this could happen is for a RETFIE instruction to be misplaced in the execution sequence. It is much harder to do with a compiler watching out for you. It is much easier to do if you code in assembly language.

OMG -- I just realized you are trying to do the EEPROM write inside the interrupt routine. Go back and reread my earlier posts.

NO --- NO ---- NO ---- you must not do this!!!!

The time you spend inside an interrupt routine is a critical resource. You must only receive the characters inside the interrupt routine. Get in and get back out as fast as possible. The writing of the EEPROM must take place outside the interrupt routine in the non-interrupt level code.
 
Last edited:

Thread Starter

Tajiknomi

Joined Mar 18, 2010
34
@Papabravo : I get it. I should only (receive+store in buffer) the characters in the ISR & Writing should be done in Main function.
I thought about it first, but i was avoiding the "Polling Approach" therefor i thought about writing EEPROM in ISR. I will be doing other tasks on the same MCU e.g. i will be monitoring External Interrupts + some other task to done. And EEPROM write should be done by user via SMS, so it can happen anytime. Either i have to poll it every-time or i have to use interrupt for it.
But you are right, i shouldn't wait in ISR
Now it works fine, Thanku very much for the help :)
 
Last edited:
Top