|
|
View previous topic :: View next topic |
Author |
Message |
Amin
Joined: 19 Feb 2004 Posts: 15
|
block of bytes transmission, array is the only way? |
Posted: Fri Mar 10, 2006 11:02 pm |
|
|
Hi
I trying to send 16 bytes over the serial port. I have an array of 16 cells and the ISR increment the index and grabs the byte and sends it for transmission until it reaches the end when it stops the cycle.
I am wondering if there is any faster way? Array handling seems like a slow way of doing it.
THANKS _________________ McMaster Solar Car Project
http://www.solarcar.mcmaster.ca |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sat Mar 11, 2006 5:15 am |
|
|
The slowest thing in transmitting anything over a serial line is the actual transmission of the data itself. If you don't want to wait for the transmission to finish the most common solution is to move the transmission away from your main process. Write the data to a large enough buffer and than have an interrupt driven routine transmit the data in the background.
An implementation example of this approach is EX_STISR.C in the examples directory, or search this forum for the keywords 'circular buffer'. |
|
|
Ttelmah Guest
|
|
Posted: Sat Mar 11, 2006 9:54 am |
|
|
Array handling is 'slow', but we are talking 'slow' in computer terms here. Typically perhaps 30 machine cycles to access a byte (will depend on the size of the element, and the processsor involved). A circular buffer will impose the same 'sloth'. Any other process will involve this same overhead.
I have posted warnings about the 'sloth' of array accesses in the past, but here dealing with operations, where people have multiple array accesses in one ISR, perhaps unneccessarily. You can reduce the 'work' slightly, by declaring the access variable as a int pointer, and just incrementing this for each access, up to the size of the array. Advantage here is that the address does not need to be recalculated on each interrupt.
Nothing else is going to be 'faster', for accessing a block of data. It is the internal overhead involved in using indirect addressing of the RAM, which causes the rest of the sloth, and this is inherent in the processor architecture.
Best Wishes |
|
|
Amin
Joined: 19 Feb 2004 Posts: 15
|
|
Posted: Sat Mar 11, 2006 2:13 pm |
|
|
thanks guys,
Ttelmah,
That really sounds like a better solution because of that lack of address calculation each time.
You think you could give me some lead there, how to use an INT or char as pointer?
When I say slow, it is not in comparison with my 2400bps serial communication. But the I mean in term of processor time.
ckielstra,
When you say let the ISR handle it in the background, the problem is this background processing is time from the mainloop, there is a single cpu anyways. _________________ McMaster Solar Car Project
http://www.solarcar.mcmaster.ca |
|
|
Ttelmah Guest
|
|
Posted: Sat Mar 11, 2006 3:12 pm |
|
|
You don't 'use an int or char as a pointer'. You just use a pointer to an int.
If (for instance), your data is sitting in memory declared as a structure, or an array of int 16's, then declare a pointer, and a size.
So if the structure is called 'fred', then just declare:
Code: |
int * ptr;
int ctr;
int size;
//Then in the initialsation code:
ptr=&fred;
size=sizeof(fred);
ctr=size;
Then to getthe byte in the ISR:
val=*ptr++;
if (--ctr==0) {
//here you have finished. To reset:
ctr=size;
fred-=size;
}
|
This way, the ISR address moves forwards by one byte, no matter what 'size' the actual data elements are (because ptr is an integer pointer).
The 'count', is done by simply decrementing a counter, equal to the number of bytes in the entire structure, and resetting when this reaches zero. This way the count is a single byte operation, using the fast '==0' test, which the compiler wll automatically do in the single dec/test instruction.
Best Wishes |
|
|
Ttelmah Guest
|
|
Posted: Sat Mar 11, 2006 3:14 pm |
|
|
Make the last line 'val-=size', not fred....
Best Wishes |
|
|
Ttelmah Guest
|
|
Posted: Sat Mar 11, 2006 3:16 pm |
|
|
Aaargh. My typing is going to pot.
ptr-=size;
God, I am gladI don't type that wrongly too often!...
Best Wishes |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Mar 12, 2006 1:52 pm |
|
|
Quote: |
I am trying to send 16 bytes over the serial port. I have an array of 16
cells and the ISR incerement the index and grabs the byte and sends it
for tranmission until it reaches the end when it stops the cycle.
I am wondering if there is any faster way? Array handling seems like a
slow way of doing it. |
I made a small test program to look at your problem. This program
uses the UART Tx interrupt to send 16 bytes from a buffer to the PC.
Code: |
#include <16F877.H>
#device *=16
#fuses HS, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#define MAX_INDEX 15
char buffer[MAX_INDEX +2] = {"0123456789ABCDEF"};
int8 index;
#int_tbe
void tbe_isr(void)
{
char c;
c = buffer[index];
putc(c);
index++;
if(index > MAX_INDEX)
disable_interrupts(INT_TBE);
}
//===============================
void main()
{
index = 0;
enable_interrupts(INT_TBE);
enable_interrupts(GLOBAL);
while(1);
} |
I looked at the .LST file, and it doesn't use many instructions to fetch
a byte from the array. It only takes 6 instructions, as shown in bold
below. So I'm not sure why you're concerned about this. The thing
that uses the most time is the interrupt dispatcher. It takes over 50
instructions just to get in and out of the isr. That's not including the
isr code.
Quote: |
.................... #int_tbe
.................... void tbe_isr(void)
.................... {
.................... char c;
....................
.................... c = buffer[index];
0035: MOVLW 29
0036: ADDWF 3A,W
0037: MOVWF 04
0038: BCF 03.7
0039: MOVF 00,W
003A: MOVWF 3B
....................
.................... putc(c);
003B: MOVF 3B,W
003C: BTFSS 0C.4
003D: GOTO 03C
003E: MOVWF 19
....................
.................... index++;
003F: INCF 3A,F
....................
.................... if(index > MAX_INDEX)
0040: MOVF 3A,W
0041: SUBLW 0F
0042: BTFSC 03.0
0043: GOTO 047
.................... disable_interrupts(INT_TBE);
0044: BSF 03.5
0045: BCF 0C.4
0046: BCF 03.5
....................
.................... }
....................
....................
//===============================
0047: BCF 0C.4
0048: BCF 0A.3
0049: BCF 0A.4
004A: GOTO 01F |
|
|
|
Amin
Joined: 19 Feb 2004 Posts: 15
|
|
Posted: Sun Mar 12, 2006 2:05 pm |
|
|
PCM Programmer,
thanks for the try. I never really looked at the dispatcher's default code. Mostly because I thought it would just right from the vector to the start my code.
I might have to just do something other than using CCS IRQ handlers.
Ttelman,
I haven't got a chance to try the pointer way, and I made a simple change (taken from your post) and that was to make my index to decrement from SIZE to zero as opposed to zero to size and that reduced the code size by 19 INSTR!!!! thanks
EDIT:
I goofed up, the 19 count decrease happened because I had index=0 as opposed to index==0!
and I will try the pointer business soon. _________________ McMaster Solar Car Project
http://www.solarcar.mcmaster.ca |
|
|
Amin
Joined: 19 Feb 2004 Posts: 15
|
|
Posted: Mon Mar 13, 2006 12:06 am |
|
|
Ttelmah wrote: | Aaargh. My typing is going to pot.
ptr-=size;
God, I am gladI don't type that wrongly too often!...
Best Wishes |
you mean CTR or PTR?
My understanding is that the counter should be decremented ? _________________ McMaster Solar Car Project
http://www.solarcar.mcmaster.ca |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Mon Mar 13, 2006 3:23 am |
|
|
I compared both implementations for a PIC18F458, PCWH v3.245.
1) Using index:
Code: | .................... c = buffer[index];
009C: CLRF 03
009E: MOVF 2A,W
00A0: ADDLW 19
00A2: MOVWF FE9
00A4: MOVLW 00
00A6: ADDWFC 03,W
00A8: MOVWF FEA
00AA: MOVFF FEF,2F
.................... index++;
00AE: INCF 2A,F |
2: Using pointer, short notation: Code: | .................... val = *ptr++;
00C0: MOVFF 2C,03
00C4: MOVF 2B,W
00C6: INCF 2B,F
00C8: BTFSC FD8.2
00CA: INCF 2C,F
00CC: MOVWF FE9
00CE: MOVFF 03,FEA
00D2: MOVFF FEF,30 | Slower because of the post increment the ptr must be saved.
3: Using pointer, long notation: Code: | .................... val = *ptr;
00D6: MOVFF 2B,FE9
00DA: MOVFF 2C,FEA
00DE: MOVFF FEF,30
.................... ptr++;
00E2: INCF 2B,F
00E4: BTFSC FD8.2
00E6: INCF 2C,F |
1) 9 instructions, 10 clock cycles, 20 bytes.
2) 8 instructions, 11 clock cycles, 22 bytes.
3) 6 instructions, 9 clock cycles, 18 bytes
Conclusion:
Using pointers is 1 clock cycle faster than index based addressing but only when you take care of the notation. To me the increased difficulty in code readability doesn't outweigh the tiny performance gain. |
|
|
Ttelmah Guest
|
|
Posted: Mon Mar 13, 2006 5:07 am |
|
|
It is important here to distinguish between simple arrays, and larger forms. A integer array, with a one byte index, is pretty good. The problems appear with larger arrays, involving me complex data sizes, where the array fetch, has to involve multiplying the index, by the element size, and for multi-dimensional arrays, by the row size as well. This is why I am quite 'happy' about the array fetch for normal buffer handling. As I said, my 'worry' about arrays, comes in places, where people are using multiple arrays inside an ISR, where I have seen twenty or thirty extra instruction times being involved, for this type of work..
My suggestion, was not to use the ptr form, as a replacement for normal buffer handling (the savings are gnerally too small to be worthwhile), but as a method of transmitting data, sitting in something like a complex structure, where the pointer form, allows this to be handled as if it was in a simple array (an alternative is a union to an integer array, but this then involves redeclaring the variables, if they are used elsewhere).
The times given by Ckielstra are 'interesting', since I looked at these times some while ago, and the differences where then rather more marked. Improvements have obviously happened to the optimiser.
Best Wishes |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Mon Mar 13, 2006 8:01 am |
|
|
I guess it would be better when Amin explains a bit more of the troubles he is experiencing. We are now trying to optimize array access while we have very little info; What does the array look like? Why is timing so critical? How is it possible that reversing the loop from counting up to down counting gives a 19 byte reduction? I never get more than about 4 instructions reduction. etc.
Transmitting at 2400bps means the interrupt routine will only be entered 300 times per second. Assume the processor is running at 4MHz, 1 million instructions/second, and that we can save 100 instructions (which is a lot). Than this will give a performance increase of 3%. Nice, but not huge. Very likely there are other more effective targets for increasing performance. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|