View previous topic :: View next topic |
Author |
Message |
tepes
Joined: 25 Jan 2012 Posts: 18
|
Sampling a signal at a given frequency |
Posted: Thu Feb 16, 2012 9:25 am |
|
|
Hello everybody!
I am new in C and in PIC programming and also new on this forum, but i am despered for help! I need to sample a signal at a given frequency (100-200 kHz)! The input signal has a 15 kHz freguency so I need at least 10 samples/period!
Can anyone help me with that?
I am using a PIC 18F2550...
Thank you! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Thu Feb 16, 2012 9:45 am |
|
|
You can't do it.
The ADC on the whole of the PIC18 family, does not support sampling much above audio rates. To get to 200KHz, you are going to have to look at something like a DSPic.
On the 2550, the ADC, has a minimum Tad, of 0.7uSec. It takes 12 times this interval to take a reading, and another 6.4uSec to re-acquire between readings. So _minimum_ sample time (allowing nothing for actually reading the data etc.), is 8.4+6.4uSec. 67KHz _maximum_ sample rate (in practice more like 50K).
Best Wishes |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Feb 16, 2012 9:46 am |
|
|
you are being too vague
show the CIRCUIT this works with
and the CODE you have created so far to attempt what you
say you want
this is NOT a classwork forum as far as i know |
|
|
tepes
Joined: 25 Jan 2012 Posts: 18
|
|
Posted: Thu Feb 16, 2012 10:16 am |
|
|
Code: |
#define __USB_PIC_PERIF__ 1
#if !defined(__PCH__)
#error USB CDC Library requires PIC18
#endif
#if __USB_PIC_PERIF__
#include <18F2550.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN
#use delay(clock=48000000)
#endif //endif check to see which peripheral to use
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#include <usb_cdc.h>
#include <usb.h>
#include <usb.c>
#include <string.h>
signed int16 ecart_en_ticks ;
int i;
long adc_value;
int HALF_PERIOD;
int16 Ticks = 65400;
#int_timer1
void timer1_isr(){
set_timer1(Ticks);
read_adc(ADC_START_ONLY);
}
void main(){
setup_adc_ports(AN0);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_OFF);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
setup_low_volt_detect(FALSE);
setup_oscillator(FALSE);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
setup_adc(ADC_CLOCK_INTERNAL);
usb_cdc_init();
usb_init();
usb_task();
set_adc_channel(0);
delay_us(10);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
while(1)
{
adc_value=read_adc();
delay_ms(300);
printf(usb_cdc_putc,"%Lu \n\r",adc_value);
}
} |
Here is what I have at this point!
I was thinking to create a timer at high frequency and to make the reading at the frequency of the timer... like I said I am new in this think so it is not to much...
I also found this:
Code: | setup_adc(ADC_CLOCK_INTERNAL);
//enables the a/d module
//and sets the clock to internal adc clock
setup_adc_ports(ALL_ANALOG);
//sets all the adc pins to analog
set_adc_channel(0);
//the next read_adc call will read channel 0
delay_us(10);
//a small delay is required after setting the channel
//and before read
value=read_adc();
//starts the conversion and reads the result
//and store it in value
read_adc(ADC_START_ONLY);
//only starts the conversion
value=read_adc(ADC_READ_ONLY);
//reads the result of the last conversion and store it in value. Assuming the device hat a 10bit ADC module, value will range between 0-3FF. If #DEVICE ADC=8 had been used instead the result will yield 0-FF. If #DEVICE ADC=16 had been used instead the result will yield 0-FFC0 |
But I don't see were you can set the sampling frequency...
Many thanks to any suggestion. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Thu Feb 16, 2012 4:38 pm |
|
|
Read my post. _not possible_.
ADC clock internal is even worse. Typical 2uSec TAD, so maximum sampling rate just over 32KHz.
You can program a CCP, to issue a 'special event trigger' to trigger the ADC at a specific interval, but _you_ need to allow sufficient time for the hardware to actually perform the conversion and re-acquire the signal. The hardware on this PIC, _just does not support sampling this fast_.
Sorry, but you need to rethink your hardware.
Best Wishes |
|
|
tepes
Joined: 25 Jan 2012 Posts: 18
|
|
Posted: Fri Feb 17, 2012 1:40 am |
|
|
Ttelmah wrote: | Read my post. _not possible_.
ADC clock internal is even worse. Typical 2uSec TAD, so maximum sampling rate just over 32KHz.
You can program a CCP, to issue a 'special event trigger' to trigger the ADC at a specific interval, but _you_ need to allow sufficient time for the hardware to actually perform the conversion and re-acquire the signal. The hardware on this PIC, _just does not support sampling this fast_.
Sorry, but you need to rethink your hardware.
Best Wishes |
Thank you very much for you explanations! It would be rely helpful if you could explain how can I do the sampling at 30 kHz.
Thank you once again. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Fri Feb 17, 2012 5:07 am |
|
|
Select the right source clock. For your chip at 48MHz, you are going to be out of spec for the ADC (technically the maximum device frequency supported by the ADC, to meet it's specifications, is 40MHz....), and ADC_CLOCK_DIV_64 is needed to meet the Tad specification, so select this.
Code: |
//Obviously fuses here and setups to match your needs
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
//_always_ have 'ERRORS' when using hardware RS232, unless _you_
//are adding your own error handling code. _Essential_.....
int16 ad_vals[256]; //Array to store block of readings
int1 get_block=FALSE;
//Dummy function - takes no space, and does nothing, except to get rid of the
//compiler error about not using RS232_ERRORS.....
void dummy(void){
int8 scrap;
scrap=RS232_ERRORS;
}
#INT_AD
void adc_ready(void) { //retrieve an ADC reading - triggered by the CCP
static int8 array_address=0;
if (get_block) {
ad_vals[array_address++]=read_adc(ADC_READ_ONLY);
if (array_address==0) get_block=FALSE;
}
}
void main(void) {
int16 op_ctr16;
int8 line_ctr8;
setup_adc_ports(AN0);
set_adc_channel(0); //enable AN0 only, and select it.
setup_adc(ADC_CLOCK_DIV_64); //Fastest available ADC clock
//that meets the Tad spec.
setup_spi(FALSE);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_comparator(NC_NC_NC_NC);
//Now need to setup T1/CCP to trigger the ADC every 31.25uSec
setup_CCP2(CCP_COMPARE_RESET_TIMER);
CCP_2=399;
//This will trigger the ADC every 400 instructions
do {
//Now generate 256 ADC readings, and dump these to the serial
set_timer1(0);
clear_interrupt(INT_AD);
get_block=TRUE;
enable_interrupts(INT_AD);
enable_interrupts(GLOBAL);
do {
} while (get_block); //Now wait for the readings
disable_interrupts(INT_AD);
printf("Block of values from ADC\n\r");
for (op_ctr16=0;op_ctr16<256;op_ctr16+=8) {
for (line_ctr8=0;line_ctr8<8;line_ctr8++) {
printf("%04lx ",ad_vals[op_ctr16+line_ctr8]);
}
printf("\n\r");
}
do {
} while(!kbhit());
//Wait for a key to be pressed to get another block
line_ctr8=getc(); //Throw the character
} while (TRUE);
}
|
This takes a sequence of 256 ADC readings at 31.25uSec intervals, then displays them as 32 lines of 8 values on the UART.
Best Wishes
Last edited by Ttelmah on Fri Feb 17, 2012 8:50 am; edited 1 time in total |
|
|
tepes
Joined: 25 Jan 2012 Posts: 18
|
|
Posted: Fri Feb 17, 2012 5:25 am |
|
|
Thank you so very much Ttelmah!
I will test it and tell you how it works! |
|
|
tepes
Joined: 25 Jan 2012 Posts: 18
|
|
Posted: Tue Feb 21, 2012 5:09 am |
|
|
Once again, thank you Ttelmah!
I have tested the code and it works! I get a maximum sampling freguency of 15khz! In the future I will use a better PIC! Your example helped me understand the basics of sampling using PICs
Best regards! |
|
|
matheuslps
Joined: 29 Sep 2010 Posts: 73 Location: Brazil
|
|
Posted: Mon Apr 07, 2014 12:09 pm |
|
|
Ttelmah, may you explain this code a little bit?
You configured the CCP like this:
Code: | setup_CCP2(CCP_COMPARE_RESET_TIMER);
CCP_2=399; |
But, why 399? How this would interact with the 31.25uSec?
Just below that, you configured the reading like this:
Code: | //Now generate 256 ADC readings, and dump these to the serial
set_timer1(0);
clear_interrupt(INT_AD);
get_block=TRUE;
enable_interrupts(INT_AD);
enable_interrupts(GLOBAL);
do { }
while (get_block); //Now wait for the readings
|
So you enabled the IN_AD interrupt. But how the CCP2 will interact with it? I can not understand this part.
Thanks for the help. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19545
|
|
Posted: Mon Apr 07, 2014 2:36 pm |
|
|
You need to read the data sheet, and the part about 'special event trigger' on the CCP.
I'm programming the CCP to automatically start the ADC at a specified interval (400 counts of timer1). At this point the ADC starts, and then INT_AD, triggers when the ADC _finishes_ converting. The ADC automatically triggers every 400 instructions.
Best Wishes |
|
|
|