Calculating approx delay

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

Mar 18, 2010
29
0
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 Member

Jun 4, 2013
361
63
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.

Mar 18, 2010
29
0
MCU = PIC16F877A
EEPROM = INTERNAL

4. sailorjoe Member

Jun 4, 2013
361
63
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
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/

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

Dec 2, 2013
391
53

7. Papabravo Expert

Feb 24, 2006
10,340
1,850
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!

Mar 18, 2010
29
0
@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 Moderator

Jun 26, 2012
2,394
1,051
+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 Expert

Feb 24, 2006
10,340
1,850
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):
1.
2.   while (BUSY==1) ;
3.
You do the write operations like this:
Code (Text):
1.
2.   if(BUSY==0)
3.   {
4.     eeprom_write(wdata) ;  //eeprom_write() sets BUSY=1
5.   }
6.   else
7.   {
9.     eeprom_check() ;  // eeprom_check() sets BUSY=0
10.   }
11.
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
2,196
417
"I am writing data to EEPROM at 9600BRBR"

Just how do you write to internaleeprom at 9600BR?

12. MMcLaren Well-Known Member

Feb 14, 2010
759
116
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
8.
That's the InHex data for this corresponding section of code from an Assembly language program;
Code (Text):
1.
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
17.
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  ;
34.

Last edited: Jan 11, 2016
13. Papabravo Expert

Feb 24, 2006
10,340
1,850
You receive that characters via the UART @ 9600 Baud and write them to the EEPROM.

Mar 18, 2010
29
0
@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.

Mar 18, 2010
29
0
@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 Expert

Feb 24, 2006
10,340
1,850
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 Moderator

Jun 26, 2012
2,394
1,051
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.

Mar 18, 2010
29
0
@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):
1.
3. short space_available=30; short push=0x00; short pop=0x00;
4. void interrupt(){
5.
6.    if(PIR1.RCIF == 1){           // Byte recieved interrupt ?
7.
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
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.              }
19.
20.            }
21.            else if(dummy == 'e'){     // When 'e' is transfer, it should erase EEPROM
23.               space_available=30;
24.            }
25.
26.
27.            else{
28.            // ..............Write Byte in EEPROM....................
29.
30.            if(space_available != 0){      // If Buffer isn't full then Proceed...
31.
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){
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
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.           }
55.
56.    }
57. }
58.

Last edited: Jan 14, 2016
19. Papabravo Expert

Feb 24, 2006
10,340
1,850
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.