Formulae wth bytes and intergers

For Flowcode users to discuss projects, flowcharts, and any other issues related to Flowcode 2 and 3.

Moderators: Benj, Mods

Post Reply
echase
Posts: 429
Joined: Mon Jun 11, 2007 11:55 am
Has thanked: 49 times
Contact:

Formulae wth bytes and intergers

Post by echase »

I thought a byte and an integer could be in the same formula but it appears not. I thought the byte was converted into an integer automatically in the calculation.

I am analogue sampling two out of phase sine wave voltages which have 2.5V artifically added to keep them above zero volts: Integer 1 (10 bit, range 0-1023) and Byte 1 (8 bit, range 0-255) and multiplying them. To rebase them around zero volts like the original sine waves I subtract 511 and 127 respectively and then do some division to keep the intermediate answers from overflowing +/- 32,767.

So my formula is Integer2 = (((Integer1-511)/2 )* (Byte1-127))/20

The /20 is because I later add 20 samples together and don’t want the total to overflow +/- 32,767

But I find it only works if I change Byte1 into an integer before doing the calculation. Why?

Sampling both as integers could be a solution but in this case high speed is important and I am assuming that more analogue sampling to get 10 bits for both is slower than a bit more calculation.

User avatar
Steve
Matrix Staff
Posts: 3422
Joined: Tue Jan 03, 2006 3:59 pm
Has thanked: 114 times
Been thanked: 422 times
Contact:

Re: Formulae wth bytes and intergers

Post by Steve »

Does this work any better:

Code: Select all

Integer2 = Byte1-127
Integer2 = (((Integer1-511)/2 ) * Integer2)/20
I don't think this will add much time onto the processing.

echase
Posts: 429
Joined: Mon Jun 11, 2007 11:55 am
Has thanked: 49 times
Contact:

Re: Formulae wth bytes and intergers

Post by echase »

Thanks, I’ll try it.

Integer2 = (Byte1-127)/2
Integer2 = ((Integer1-511) * Integer2)/20

might be even better, if it works, as the dynamic range on Integer1 is large, so sometimes Integer1 is small even at the sine wave peak and a small number/2 looses granularity.

User avatar
Steve
Matrix Staff
Posts: 3422
Joined: Tue Jan 03, 2006 3:59 pm
Has thanked: 114 times
Been thanked: 422 times
Contact:

Re: Formulae wth bytes and intergers

Post by Steve »

When testing this code, it is often useful to pay special attention to the "corner cases". These are the values of Byte1 and Integer1 that are at the extremes of the ranges for these inputs, or values that cause parts of the calculations to be at or near zero.

You can hard-code the values into the program to see what the result is.

In your case, I would suggest the following values:

Code: Select all

Byte1:    0,  255,  126,  127,  128
Integer1: 0, 1023,  510,  511,  512

echase
Posts: 429
Joined: Mon Jun 11, 2007 11:55 am
Has thanked: 49 times
Contact:

Re: Formulae wth bytes and intergers

Post by echase »

That calculation seems to work in the hardware although I have not checked all the corner cases yet.

On reflection I see that the A/D always does a 10 bit conversion, so I am not saving conversion time by using 8 bits. Presumably the only saving I am making is in not having to read one of the ADRES registers to get the extra 2 bits, and manipulating a byte is presumably quicker than an integer. That is no a lot of saving and makes for more complex formula which may be using up any time saving made. Am I right?

User avatar
Steve
Matrix Staff
Posts: 3422
Joined: Tue Jan 03, 2006 3:59 pm
Has thanked: 114 times
Been thanked: 422 times
Contact:

Re: Formulae wth bytes and intergers

Post by Steve »

You are correct - the ADC conversion will be the bottleneck in your code.

To overcome this, you can make use of the ADC interrupt to tell you when the conversion is complete. Alternatively, you can set the ADC conversion going and then do some other processing (e.g. on a previously-stored ADC value or LCD display operation). Once this other processing is complete, poll the GO/DONE bit to see if you have the required info.

You would need to use C code to do this. Here's a very rough outline of what you'd need to do:

Code: Select all

1. Store old ADC value
2. Wait the required acquisition time
3. Start ADC conversion
4. Process and/or display old ADC, or do some other processing
5. Poll GO/DONE until conversion has finished
6. Repeat from (1)

echase
Posts: 429
Joined: Mon Jun 11, 2007 11:55 am
Has thanked: 49 times
Contact:

Re: Formulae wth bytes and intergers

Post by echase »

In your step 4 you are using the processor to do other processing in parallel. Does the A/D not also need this processor to do its conversion so won’t they slow down a lot ? Or does the A/D have its own mini processor dedicated to conversion?

As stated in my other current post A/D Speed Up I am wondering if the Flowcode A/D routine is as fast as it could be and so instead of what you say above I am wondering if I could replace the Flowcode sampling routine with my own faster C Code block based on Flowcode’s as below? I’d keep your Return Value as Integer routine which is presumably just reading the ADRESL and ADRESH registers. Would need 2 blocks to cover the 2 analogue channels I am using. Would that work? But by comparing it against other code from internet I am hoping to locate what is causing it to run slowly and then modify that part. E.g. yours is much longer than some others I have found, like their's not having 12 sections like (although I am not clear if this code gets executed just once upon boot or at every sample):

#if (%a == 0)
#define MX_ADC_TRIS_REG trisa
#define MX_ADC_TRIS_MSK 0x01
ansel = 0x01;
#endif

Flowcode's routine:

ADCCapture="//set up ADC conversion
char old_tris, cnt;
#define MX_ADC_SAMP_TIME 40
adcon1 = 0x20;

//find appropriate bit
#if (%a == 0)
#define MX_ADC_TRIS_REG trisa
#define MX_ADC_TRIS_MSK 0x01
ansel = 0x01;
#endif
.
.
.
#if (%a == 11)
#define MX_ADC_TRIS_REG trisb
#define MX_ADC_TRIS_MSK 0x20
anselh = 0x08;
#endif

//sanity check
#ifndef MX_ADC_TRIS_REG
#pragma error ADC conversion code error - please contact technical support
#endif

//store old tris value, and set the i/o pin as an input
old_tris = MX_ADC_TRIS_REG;
MX_ADC_TRIS_REG = MX_ADC_TRIS_REG | MX_ADC_TRIS_MSK;

//turn ADC on
adcon0 = 0x01 | (%a << 2);

//wait the acquisition time
cnt = 0;
while (cnt < MX_ADC_SAMP_TIME) cnt++;

//begin conversion and wait until it has finished
adcon0 = adcon0 | 0x02;
while (adcon0 & 0x02);

//restore old tris value, and reset adc registers
MX_ADC_TRIS_REG = old_tris;
ansel = 0;
anselh = 0;
adcon0 = 0x00;
#undef MX_ADC_TRIS_REG
#undef MX_ADC_TRIS_MSK
#undef MX_ADC_SAMP_TIME

Of course this won’t simulate but I am no longer using the simulate function for this particular programme as never was very suitable for the simulate mode.

User avatar
Steve
Matrix Staff
Posts: 3422
Joined: Tue Jan 03, 2006 3:59 pm
Has thanked: 114 times
Been thanked: 422 times
Contact:

Re: Formulae wth bytes and intergers

Post by Steve »

There are two "bottlenecks" in the code. One is the delay for the acquisition time and the other is the loop which waits for the ADC conversion to complete.

You can try reducing the acquisition time. If you are using only one ADC channel then this can probably be quite low.

The loop to wait for the completed conversion is probably more significant. While the ADC is performing the conversion, it does not need to consume any processor time - it's basically a hardware logic routine separate to the main part of the processor. Having this wait in a blocking loop is the most inefficient part of the routine, which is why I suggested that you do additional processing of a previous value in parallel.

The code that Flowcode produces by default is not the most efficient. It is also a bit complicated to look at because it needs to be flexible (so we use preprocessor directives - e.g. #if and #define - to allow it to work with different pins/settings). One of Flowcode's aims is to make things simple to do. So performing an ADC conversion in Flowcode is very simple (at the expense of maximum efficiency). As you require more efficient code, then using C code in your flowcharts becomes necessary.

echase
Posts: 429
Joined: Mon Jun 11, 2007 11:55 am
Has thanked: 49 times
Contact:

Re: Formulae wth bytes and intergers

Post by echase »

So can I use C Code based on little more than:

char old_tris, cnt;
#define MX_ADC_SAMP_TIME 40
adcon1 = 0x20;

//turn ADC on
adcon0 = 0x01 | (%a << 2);

//wait the acquisition time
cnt = 0;
while (cnt < MX_ADC_SAMP_TIME) cnt++;

//begin conversion and wait until it has finished
adcon0 = adcon0 | 0x02;
while (adcon0 & 0x02);

And maybe even have a shorter ADC_SAMP_TIME on the first of the 2 samples and leave the ADC on permanently to save the on and off commands, as ADC time is around 20% of the entire code time so little point turning it off.

And by doing some other code during the conversion I can shave off 15us. Could be more but that is the only code that is suitable for execution at that point.

User avatar
Steve
Matrix Staff
Posts: 3422
Joined: Tue Jan 03, 2006 3:59 pm
Has thanked: 114 times
Been thanked: 422 times
Contact:

Re: Formulae wth bytes and intergers

Post by Steve »

You will also need to ensure that the TRIS setting for your ADC bit is set as an input, and that the ANSEL register is set correctly.

echase
Posts: 429
Joined: Mon Jun 11, 2007 11:55 am
Has thanked: 49 times
Contact:

Re: Formulae wth bytes and intergers

Post by echase »

How long do you think the Read as Integer macro takes? In Flowcode’s C Code it is only 4 lines but the ASM is 32 lines long which I make a very long 40us as my f/4 is 1.25us. Yet there is an example in the 690 datasheet for ASM code of only 6 ASM lines for this.:-

MOVF ADRESH,W ;Read upper 2 bits
MOVWF RESULTHI ;
BSF STATUS,RP0 ;Bank 1
MOVF ADRESL,W ;Read lower 8 bits
BCF STATUS,RP0 ;Bank 0
MOVWF RESULTLO

Can they do it much quicker if the ADFM is set to 1 (right justified data)? Or have they cheated by not actually adding the 2 results together?

User avatar
Benj
Matrix Staff
Posts: 15312
Joined: Mon Oct 16, 2006 10:48 am
Location: Matrix TS Ltd
Has thanked: 4803 times
Been thanked: 4314 times
Contact:

Re: Formulae wth bytes and intergers

Post by Benj »

Hello,

The read As Integer function itself is not very long, however it calls the sample function so this is probably what is taking the time.

You can read the ADC registers by using the following C code where you have an INT variable named ADCread in your program.

Code: Select all

	FCV_ADCREAD = (adresh << 2);
	FCV_ADCREAD = FCV_ADCREAD | (adresl >> 6);
The names of the registers may change slightly and the shifts may also change depending on your device and the bit depth of the ADC. This sample is for a 10-bit variety.

echase
Posts: 429
Joined: Mon Jun 11, 2007 11:55 am
Has thanked: 49 times
Contact:

Re: Formulae wth bytes and intergers

Post by echase »

Thanks. I remeasured the Read as Integer macro with my scope as 9us with 'scope, puzzlingly not the 40us above. Especially as most of the 9us will be A1 output time.

But I think I’ll use your

FCV_ADCREAD = (adresh << 2);
FCV_ADCREAD = FCV_ADCREAD | (adresl >> 6);

instead as probably more repeatable and shorter. I need consistent timing.

echase
Posts: 429
Joined: Mon Jun 11, 2007 11:55 am
Has thanked: 49 times
Contact:

Re: Formulae wth bytes and intergers

Post by echase »

Have not tested it in hardware yet but this code compiles without errors:

char cnt;
#define MX_ADC_SAMP_TIME 70
adcon1 = 0x20;
trisb= something // to match PORT B pin
ansel=0
anselh = something //to match AN channel 10 or 11 as currently used
adcon0 = 0x01 | (%a << 2); //turn ADC on

//wait the acquisition time
cnt = 0;
while (cnt < MX_ADC_SAMP_TIME) cnt++;

//begin conversion and wait until it has finished
adcon0 = adcon0 | 0x02;
while (adcon0 & 0x02);

Which lines can I remove and only do once at boot so I don’t have to waste time repeating upon every sample? My guess would be:

char cnt;
#define MX_ADC_SAMP_TIME 70
ansel=0
adcon1 = 0x20;
trisb= 0b00110000 // to cover AN channels on B4 and5
adcon0 = 0x01 | (%a << 2); //turn ADC on permanently

I never use the 2 pins as outputs so presumably only need the trisb command once to permanently set them as inputs. But I guess it’s conceivable that actions done by Flowcode on other PORTB output pins may lead to these 2 pins also being regularly reset as outputs, e.g. pin B7 is regularly used as an output.

echase
Posts: 429
Joined: Mon Jun 11, 2007 11:55 am
Has thanked: 49 times
Contact:

Re: Formulae wth bytes and intergers

Post by echase »

I have the hardware working and can shave about 3us off Flowcode's time with C Code similar to above. Another 3us can be removed by setting the lines mentioned above only once, bringing it close to the theoretical minimum of 37.6us. All in all not a lot of improvement and as Flowcode is more proven I am inclined to stick to it.

But I first saved a much larger 40us by reducing MX_ADC_SAMP_TIME to 12, see other post. That is enough saving for the moment.

Post Reply