Calculating approx delay

Discussion in 'Programmer's Corner' started by Tajiknomi, Jan 11, 2016.

  1. Tajiknomi

    Thread Starter Active Member

    Mar 18, 2010
    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: Jan 11, 2016
  2. sailorjoe

    Active Member

    Jun 4, 2013
    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.
  3. Tajiknomi

    Thread Starter Active Member

    Mar 18, 2010
    MCU = PIC16F877A
  4. sailorjoe

    Active Member

    Jun 4, 2013
    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)
  5. Tajiknomi

    Thread Starter Active Member

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

    Active Member

    Dec 2, 2013
  7. Papabravo


    Feb 24, 2006
    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!
  8. Tajiknomi

    Thread Starter Active Member

    Mar 18, 2010
    @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: Jan 11, 2016
  9. JohnInTX


    Jun 26, 2012
    +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.
    Tajiknomi likes this.
  10. Papabravo


    Feb 24, 2006
    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 (Text):
    2.   while (BUSY==1) ;
    You do the write operations like this:
    Code (Text):
    2.   if(BUSY==0)
    3.   {
    4.     eeprom_write(wdata) ;  //eeprom_write() sets BUSY=1
    5.   }
    6.   else
    7.   {
    8. other tasks
    9.     eeprom_check() ;  // eeprom_check() sets BUSY=0
    10.   }
    In general try to do anything at all to avoid waiting on a single condition that may or may not ever happen.
    Tajiknomi likes this.
  11. dannyf

    Well-Known Member

    Sep 13, 2015
    "I am writing data to EEPROM at 9600BRBR"

    Just how do you write to internaleeprom at 9600BR?
  12. MMcLaren

    Distinguished Member

    Feb 14, 2010
    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

    Example InHex data (ASCII) excerpt from a project 'hex' file;
    Code (Text):
    1. :104200000000000000000300400001000500000065
    2. :1042100002000600900002001000000003001300DE
    3. :104220009000040017009700050020009000060091
    4. :104230002400790007002700900008004900900042
    5. :104240000900600010000A00000000000B000000E0
    6. :1042500000000C00000000000D00000000000E0037
    7. :00000001FF
    That's the InHex data for this corresponding section of code from an Assembly language program;
    Code (Text):
    2. ;
    3. ;  CI-V to BCD Band Data Decoder 3-byte "memories" in EEPROM
    4. ;
    5. ;  Memory 00 contains the target radio CI-V address in byte
    6. ;  number 0 (a value of 00 = "any radio")
    7. ;
    8. ;  Memory 01 through 15 contain CI-V "Frequency-To-BCD" data
    9. ;  MegaHertz, KiloHertz (x10), and a BCD band data byte
    10. ;
    11. ;  When a frequency record is received from the target radio
    12. ;  over the CI-V bus its 16-bit frequency (MHz and KHz x10)
    13. ;  is compared to the 16-bit frequency value in our memories
    14. ;
    15. ;
    16.         org     h'2100'         ; 3-byte memories in eeprom
    18.         de      0x00,0x00,0x00  ; target radio CI-V address
    19.         de      0x03,0x40,0x01  ; 160m 00.000-03.399 (0001)
    20.         de      0x05,0x00,0x02  ;  80m 03.400-04.999 (0010)
    21.         de      0x06,0x90,0x02  ;  60m 05.000-06.899 (0010)
    22.         de      0x10,0x00,0x03  ;  40m 06.900-09.999 (0011)
    23.         de      0x13,0x90,0x04  ;  30m 10.000-13.899 (0100)
    24.         de      0x17,0x97,0x05  ;  20m 13.900-17.969 (0101)
    25.         de      0x20,0x90,0x06  ;  17m 17.970-20.899 (0110)
    26.         de      0x24,0x79,0x07  ;  15m 29.900-24.789 (0111)
    27.         de      0x27,0x90,0x08  ;  12m 24.790-27.899 (1000)
    28.         de      0x49,0x90,0x09  ;  10m 27.900-49.899 (1001)
    29.         de      0x60,0x10,0x0A  ;   6m 49.900-60.099 (1010)
    30.         de      0x00,0x00,0x0B  ;
    31.         de      0x00,0x00,0x0C  ;
    32.         de      0x00,0x00,0x0D  ;
    33.         de      0x00,0x00,0x0E  ;
    Last edited: Jan 11, 2016
  13. Papabravo


    Feb 24, 2006
    You receive that characters via the UART @ 9600 Baud and write them to the EEPROM.
  14. Tajiknomi

    Thread Starter Active Member

    Mar 18, 2010
    @Papabravo @JohnInTX : Thank you very much for the kind suggestions. i will create a buffer for it.

    @MMcLaren I will consider your suggestion, though i didn't fully understand it yet. Thanks

    Will get back here if i got any issues.
  15. Tajiknomi

    Thread Starter Active Member

    Mar 18, 2010
    @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].
  16. Papabravo


    Feb 24, 2006
    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.
  17. JohnInTX


    Jun 26, 2012
    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.
  18. Tajiknomi

    Thread Starter Active Member

    Mar 18, 2010
    @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 :
    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 (Text):
    2. char dummy; short write_address=0x00; short read_address=0x00; char buffer[30];
    3. short space_available=30; short push=0x00; short pop=0x00;
    4. void interrupt(){
    6.    if(PIR1.RCIF == 1){           // Byte recieved interrupt ?
    8.           dummy = RCREG;         // Recieve byte
    9.           if(dummy == 'o'){      // When 'o' is tranfer, Output all the stored Characters.
    10.            // Read EEPROM & Transfer all Written bytes via UART
    11.              for(read_address=0x00;read_address<write_address;read_address++){
    12.               EEADR = read_address;   // Adress to Read From
    13.               EECON1.EEPGD = 0;       // Select EEPROM DATA MEMORY
    14.               EECON1.RD=1;            // Start Read Operation
    15.               dummy = EEDATA;         // Assign the read byte in dummy
    16.               Delay_ms(10);           // Delay between each transfer*/
    17.               TXREG = dummy;          // Transmit byte
    18.              }
    20.            }
    21.            else if(dummy == 'e'){     // When 'e' is transfer, it should erase EEPROM
    22.               write_address = 0x00;
    23.               space_available=30;
    24.            }
    27.            else{
    28.            // ..............Write Byte in EEPROM....................
    30.            if(space_available != 0){      // If Buffer isn't full then Proceed...
    32.                buffer[push] = dummy;      // Recieved byte trasfer to buffer
    33.                push++;  space_available--;
    34.                if(push>29) push=0x00;
    35.                INTCON.GIE=1;           // Interrupts Enable
    36.                Delay_ms(50);             // Wait for all characters to be recieved
    37.                INTCON.GIE=0;           // Interrupts Disable
    38.                while(space_available != 30){
    39.                  EEADR = write_address;   // Write Address
    40.                  EEDATA = buffer[pop];    // Pick byte from Buffer
    41.                  EECON1.EEPGD = 0;        // Select EEPROM DATA MEMORY
    42.                  EECON1.WREN=1;           // Enable Program Operation
    43.                  EECON2 = 0x55;          // Special Interuction 1 (Mentioned in Data sheet)
    44.                  EECON2 = 0xAA;          // Special Interuction 2  (Mentioned in Data sheet)
    45.                  EECON1.WR=1;            // Start Writing to EEPROM
    46.                  while(EECON1.WR != 0);     // Wait for the Write to Complete
    47.                  write_address++;     // Incriment EEPROM Address
    48.                  pop++;space_available++;
    49.                  if(pop>29) {pop=0x00;}
    50.                  EECON1.WREN=0;          // Disable Writes
    51.               }
    52.            }
    53.           PIR1.RCIF = 0;         // Clear Rcv Interrupt Flag
    54.           }
    56.    }
    57. }
    Last edited: Jan 14, 2016
  19. Papabravo


    Feb 24, 2006
    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: Jan 14, 2016
    JohnInTX and Tajiknomi like this.
  20. Tajiknomi

    Thread Starter Active Member

    Mar 18, 2010
    @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: Jan 14, 2016