|
|
View previous topic :: View next topic |
Author |
Message |
JimB
Joined: 25 Aug 2005 Posts: 65 Location: Huntington Beach, CA
|
Any reason why my A/D data is cyclic? |
Posted: Tue May 30, 2006 12:00 am |
|
|
I am getting data out of the A/D that is cyclic in nature. I am generating a 5.122 KHz square wave with the PWM output. I then feed back this same square wave to use as an interrupt to read the A/D. The same square wave is sent to the electronics and after being processed the output waveform looks similar to a decaying exponential. I trigger the external interrupt on a L to H ransistion. The interrupt routine merely delays the reading of the A/D until the second half of the square wave since the signal gets inverted and the data occurs during the second half of the square wave. This tells me that what most likely is happening is that the reading is being taken at varying times rather than a fixed time with respect to the L to H transiiton and that the time changes in a regular way. Typical data follows:
449
440
914
640
447
420
914
631
447
419
916
639
447
423
921
642
464
440
916
640
449
428
922
628
443
425
907
When the above data is plotted an obvious cyclic nature can be seen.
The code follows:
Code: |
#include <16F877A.h>
#device ICD=TRUE
#fuses HS,NOWDT,NOPROTECT,NOLVP
#device ADC=10
#use delay(clock=5000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
//#use rs232(debugger)
#define ld_reg_b (0x2000)
#define ld_a_update_ab (0x9000)
#define PUSH1 PIN_A5
#define PUSH2 PIN_A4
#define ampon PIN_C5 //Turn on opamps
#define comm_on PIN_B5 // Turn on RS232 driver
#define vref24 (0xd8)
#define maxsel PIN_B1 // Low activates the D/A
#define maxclk PIN_B4 // D/A clock
#define maxdat PIN_B2 // D/A data
//#include <lcd_os.c>
#int_ext
void int_ext_isr(void)
{
delay_us(60);
}
main()
{
long offset = 290; // 01A6 in hex
long refout = 540; // 034D in HEX
long const_off, const_ref;
int refh,refl,offh,offl,chk_offh,chk_offl,chk_refh,chk_refl;
long vref=0xd800;
long chk_off,chk_ref,value,sum;
float check=345;
int i;
(Lots of stuff to setup electronics and dual D/A in here)
output_low(comm_on); // disable rs232 transceiver
output_low(ampon); // turn off opamps
clear_interrupt(INT_ext);
while(1)
{
if(!input(PUSH1))
{
setup_ccp1(ccp_pwm);
setup_timer_2(T2_DIV_BY_4, 60, 2);
set_pwm1_duty(30);
setup_adc_ports(AN0_AN1_VSS_VREF); //Analog in A0, A1 and ext ref A3
setup_adc( ADC_CLOCK_INTERNAL );
set_adc_channel( 0 );
output_high(ampon);
i=0;
sum=0;
output_high(comm_on);
while (i<=31)
{
enable_interrupts(int_ext);
enable_interrupts(global);
EXT_INT_EDGE(L_to_H);
value = Read_ADC();
sum=sum+value;
printf("%lu\n\r",value);
delay_ms(5);
clear_interrupt(INT_EXT);
i++;
}
//output_high(comm_on);
sum=sum/32;
printf("%lu\n\r", sum);
delay_ms(100);
//output_low(comm_on);
setup_ccp1(CCP_OFF);
i=0;
sum=0;
}
else
{
setup_ccp1(CCP_OFF);
printf("No push\n\r\n\r");
//printf("value = %04LX\n\r", sum);
output_low(comm_on);
delay_ms(300);
sum=0;
}
}
}
|
Normally I only want to read a 32 data average, which is also cyclic, but here we are looking at the raw data from which the average is being taken.[/code] |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue May 30, 2006 12:29 am |
|
|
The INT_EXT interrupt is asynchronous to what ever is happening
in your while() loop. You're just injecting approximately a 100 us
delay at random locations in your while() loop. If you want to read
the analog voltage at a specific time after the rising edge of a waveform,
then do it inside the isr and store the data in an array. |
|
|
FirstSteps
Joined: 09 May 2006 Posts: 7
|
|
Posted: Tue May 30, 2006 9:03 am |
|
|
Are you bandwidth limiting in front of the A/D converter? You could be seeing aliasing if you don't filter for your frequencies of interest. |
|
|
Guest
|
Another question or 2 |
Posted: Wed May 31, 2006 1:56 am |
|
|
Did you mean to take all 32 readings while in the interrupt and without leaving it between each reading?
I gather that one cannot obtain the value read by the A/D in the interrupt routine, but you can write the data to an array? Would the array be declared as global?
Can one place an interrupt inside of another interrupt? That would allow repeated readings at the proper time without going back to the main program.
I also gather from some earlier questions, that possibly using a timer would be a better approach? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed May 31, 2006 2:41 pm |
|
|
Quote: | Did you mean to take all 32 readings while in the interrupt and without leaving it between each reading? |
No.
Quote: |
I gather that one cannot obtain the value read by the A/D in the interrupt routine, |
You mean the INT_EXT interrupt routine ? Yes, you can call the
read_adc() function while inside the isr.
Quote: |
but you can write the data to an array? Would the array be declared as global? |
Yes, you can write it to an array. Yes, the array would be global.
However, in your code it's apparent that you want to add each value
to a running sum, so do that instead of saving it to an array.
Quote: | Can one place an interrupt inside of another interrupt? That would allow repeated readings at the proper time without going back to the main program. |
No.
Quote: | I also gather from some earlier questions, that possibly using a timer would be a better approach? |
Don't know. Upon looking at your code, I'm not really sure what you're
doing, but apparently you want to sample a value once every 5 ms.
When you do sample it, you want to take the sample at a certain
point on the waveform, at a pre-defined time after the rising edge.
You don't really need an interrupt to do that. You could just
poll the INTF flag to see when the rising edge has been found.
Then delay for your pre-defined time and read the A/D.
Don't create an #int_ext isr. It isn't needed for this method.
Also don't enable INT_EXT interrupts. Don't enable GLOBAL
interrupts either, unless you have some other interrupt that
needs it, such as a timer.
Here is a sample program that shows how to do this.
This program isn't really complete, as it could get stuck in the
while() loop if the INTF interrupt never occurs. You really should
have a timeout that will break you out of the loop in that case.
Possibly this could be done by starting a hardware timer, and
adding a check in the while() statement for the timer interrupt
flag being set. Or, a variable that's decremented by a timer isr
could be checked to see if it has counted down to zero and thus
you have "timed out". Adding this safety feature is up to you.
The program below only demonstrates how to do your inner loop.
It doesn't include all of your other code.
Code: | #include <16F877A.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#bit INTF = 0x0B.1 // External interrupt flag
//===================================
void main()
{
int8 i;
int16 value, sum;
ext_int_edge(L_to_H);
sum = 0;
for(i = 0; i < 32; i++) // Take 32 samples
{
delay_ms(5);
clear_interrupt(INT_EXT);
while(!INTF); // Wait for rising edge
delay_us(100);
value = read_adc();
sum += value;
}
while(1);
} |
|
|
|
Guest
|
Sweet |
Posted: Fri Jun 02, 2006 2:09 am |
|
|
Thanks PCM,
That worked out just great. I now have very tight control over the readings and it looks like I can speed the whole process up.
Thanks very much.
Regards,
JimB |
|
|
|
|
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
|