|
|
View previous topic :: View next topic |
Author |
Message |
tepes
Joined: 25 Jan 2012 Posts: 18
|
PIC 24HJ ADC sampling at 150khz+ |
Posted: Wed Apr 25, 2012 9:10 am |
|
|
Hello!
I need to sample a signal at a frequency above 150khz! I Use a pic 24hj but with the code I have until now I can not go above 40khz! Can anyone please help me?
Code: |
#if !defined(__PCD__)
#error This example will only compile for the PCD 24 bit compiler
#endif
#include <24HJ128GP504.h>
#device adc=10
#fuses HS,NOWDT,noprotect
//#fuses FRC,FRC_PLL,NOWDT,NOPROTECT,
#use delay(clock=7350000)
#pin_select U1TX = PIN_C9
#pin_select U1RX = PIN_C8
#use rs232(baud=9600, xmit=PIN_C9, rcv=PIN_C8,parity=N,bits=8)
#include <stdlib.h>
#include <stdio.h>
int ad_vals[256]; //Array to store block of readings
int1 get_block=FALSE;
int8 array_address;
int16 Number_of_Ticks = 65000;
#int_timer1
void timer1_isr(){
int k;
set_timer1(Number_of_Ticks);
if (get_block)
{
for(k=0;k<15;k++){
ad_vals[array_address]=read_adc(ADC_START_AND_READ);
array_address=array_address+1;
if (array_address==90)
{
array_address=0;
get_block=FALSE;
}
}
}
void main(){
char c ;
setup_spi(FALSE);
setup_wdt(WDT_OFF);
setup_low_volt_detect(FALSE);
SET_TRIS_B(0xFF);
// B7,B6,B5,B4 are outputs
// B15,B14,B13,B12,B11,B10,B9,B8, B3,B2,B1,B0 are inputs
setup_timer1(TMR_INTERNAL|TMR_DIV_BY_1);
// setup_timer2(TMR_INTERNAL|TMR_DIV_BY_1);
//set ADC
setup_adc_ports( ALL_ANALOG ); //setup_adc_ports(AN0);
delay_us(20);
set_adc_channel(0); //the next read_adc call will read channel 0
setup_adc(ADC_CLOCK_INTERNAL); //enables the a/d module and sets the clock to internal adc clock
// initial values
HALF_PERIOD=1;
//read_adc(ADC_START_AND_READ);
array_address=0;
get_block=TRUE;
enable_interrupts(int_timer1);
enable_interrupts(INTR_GLOBAL);
do {
for(i=0;i<90;i++)
{
printf("%lu ",ad_vals[i]);
// delay_ms(1000);
}
printf("ADC\n\r");
printf("\n\r");
get_block=TRUE;
} while (TRUE);
} |
Thank you! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Wed Apr 25, 2012 9:35 am |
|
|
The obvious problem is using 'START_AND_READ'. This means you will sit waiting at this point for the entire conversion.
Use 'READ_ONLY, and use Timer3, for your tick, and set this to automatically trigger the ADC, and interrupt on the ADC completing, instead of the timer. This way the conversion has been completed before you arrive in the interrupt.
Clock the chip faster, and switch to using the master oscillator, rather than the internal RC, and you can just about double the speed of the ADC - minimum Tad is 117.6nSec, while the internal RC, gives 250nSec typically.
Obviously for the fastest short burst, you can switch to using DMA.
However you should be able to get to about 200KHz best case without doing this, but probably only by raising the master clock to the processor.
Best Wishes |
|
|
tepes
Joined: 25 Jan 2012 Posts: 18
|
|
Posted: Thu Apr 26, 2012 4:03 am |
|
|
I tried to follow your instructions but I had no success! At this point I do not get any value printed...
Can you see what am I doing wrong?
Timer3 calls for INT_ADC1 every 150us (3k at this point) and INT_ADC1 should take a number of readings (50 for now) and then the values of the samples should be printed! But nothing happens!
Code: | #if !defined(__PCD__)
#error This example will only compile for the PCD 24 bit compiler
#endif
#include <24HJ128GP504.h>
#device adc=10 // resolution
#fuses HS,NOWDT,noprotect
//#fuses FRC,FRC_PLL,NOWDT,NOPROTECT,
#use delay(clock=7350000)
#pin_select U1TX = PIN_C9
#pin_select U1RX = PIN_C8
#use rs232(baud=9600, xmit=PIN_C9, rcv=PIN_C8,parity=N,bits=8)
#include <stdlib.h>
#include <stdio.h>
int ad_vals[256]; //Array to store block of readings
int1 get_block=FALSE;
int8 array_address;
int i,HALF_PERIOD;
int16 Number_of_Ticks = 65000;
int LOGIC = 0 ;
/**************************************************************************************************/
//Timers
/**************************************************************************************************/
#int_timer3
void timer3_isr(){
set_timer3(Number_of_Ticks);
clear_interrupt(INT_ADC1);
get_block=TRUE;
enable_interrupts(INT_ADC1);
enable_interrupts(INTR_GLOBAL);
if(HALF_PERIOD == 0) // test for interup
{
output_low(PIN_B4);
HALF_PERIOD = 1;
}
else
{
output_high(PIN_B4);
HALF_PERIOD = 0 ;
}
}
#INT_ADC1
void adc_ready(void) {
if (get_block)
{
//for(k=0;k<10;k++){
ad_vals[array_address]=read_adc(ADC_READ_ONLY);//ADC_START_AND_READ ADC_READ_ONLY
array_address=array_address+1;
if (array_address==50)
{
array_address=0;
get_block=FALSE;
}
// }
}
}
/*************************************************************************************************/
// Main
/*************************************************************************************************/
void main(){
//Configuration of the microcontroler
setup_spi(FALSE); //no SPI
setup_wdt(WDT_OFF); //no watchdog
setup_low_volt_detect(FALSE);
SET_TRIS_B(0xFF);
// B7,B6,B5,B4 are outputs
// B15,B14,B13,B12,B11,B10,B9,B8, B3,B2,B1,B0 are inputs
setup_timer1(TMR_INTERNAL|TMR_DIV_BY_1); // Internal clock
setup_timer3(TMR_INTERNAL|TMR_DIV_BY_1); // Internal clock
//set ADC
setup_adc_ports(sAN0); // sAN0 ALL_ANALOG
delay_us(20);
set_adc_channel(0); //the next read_adc call will read channel 0
setup_adc(ADC_CLOCK_INTERNAL); //enables the a/d module and sets the clock to internal adc clock
read_adc(ADC_START_ONLY); // is this correct?
// initial values
HALF_PERIOD=1;
array_address=0;
get_block=TRUE;
enable_interrupts(int_timer3);
set_timer3(Number_of_Ticks);
enable_interrupts(INTR_GLOBAL);
do {
/*
set_timer3(Number_of_Ticks);
clear_interrupt(INT_ADC1);
get_block=TRUE;
enable_interrupts(INT_ADC1);
enable_interrupts(INTR_GLOBAL);
*/
do {
} while (get_block); //Now wait for the readings
disable_interrupts(INT_ADC1);
for(i=0;i<50;i++)
{
printf("%lu ",ad_vals[i]);
delay_ms(400);
}
printf("ADC\n\r");
printf("\n\r");
get_block=TRUE;
}while (TRUE);
} | |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Fri Apr 27, 2012 3:04 am |
|
|
OK.
You seem to be ignoring, or not understanding most of what I said. So:
First, you need to clock faster.
Think about it. At 7.37MHz, you execute just over 3.5Mips. Sampling every 1/200000th second, would give you just 17 machine instructions between each sample. Simply writing the data to the array, takes about 10 instructions, add the loop etc., and you have 'no hope'. Remember, you can't have two oscillator fuse selections...
Then I said _not_ to use the timer interrupt. The point is the timer 'event', can be programmed to start the ADC. So what you do, is set this up, and then when the ADC interrupts, it has taken the reading _already_, and the number can just be read.
Now, Have not tested this, but just typed something to give the idea:
Code: |
#include <24HJ128GP504.h>
#FUSES FRC_PLL,NOWDT,NOBSS, NOIESO, NOCKSFSM, NOPROTECT, NOJTAG
#use delay(clock=66.8MHz)
int16 adc_buffer[256];
int8 adc_index=0;
int1 complete=TRUE;
#WORD AD1CON1=getenv("SFR:AD1CON1")
#int_ADC1
void ADC1_isr(void){
if (complete) return;
adc_buffer[adc_index]=read_adc(ADC_READ_ONLY);
if (adc_index<255)
adc_index++;
else {
complete=TRUE;
}
}
void main(void){
setup_oscillator(OSC_INTERNAL,66800000);
setup_spi( FALSE );
setup_spi2( FALSE );
setup_adc_ports(sAN0);
setup_adc(ADC_CLOCK_DIV_4); //This gives ADC clocked at 8.29MHz = 120.63nSec
//Almost the max allowed....
setup_timer2(TMR_DISABLED |TMR_DIV_BY_1 ,0);
setup_timer4(TMR_DISABLED |TMR_DIV_BY_1 ,0);
setup_timer5(TMR_DISABLED |TMR_DIV_BY_1 ,0);
setup_timer1(TMR_DISABLED|TMR_DIV_BY_1);
setup_timer3(TMR_INTERNAL |TMR_DIV_BY_1 ,156);
//Now use Timer3, since this can trigger the ADC
//To trigger every 5uSec, require total division by 334 - remember timer is
//fed off Fosc/2, and count is period+1, so 156 counts.
setup_adc_ports(sAN0);
//Now none of the internal functions appear to allow you to select to trigger
//the ADC on the timer, so go manual
AD1CON1=(AD1CON1 & 0xFFF8) | 2;
enable_interrupts(INT_ADC1);
enable_interrupts(INTR_GLOBAL);
do {
//Use whatever you want to start the conversion - following must happen
//when you want the conversion to run:
//index set to '0' and complete set to false.
adc_index=0;
complete=FALSE;
//Now wait for conversion
while (!complete) ;
//Here 256 samples have been read. Do what you want with them.
} while (TRUE);
}
|
No idea if it is even close, but it might be a start.
Best Wishes |
|
|
tepes
Joined: 25 Jan 2012 Posts: 18
|
|
Posted: Fri Apr 27, 2012 5:01 am |
|
|
Yes,you are right I did not understood your advices! I have problems in understanding the PIC programming, and the documentation from CCS is maybe helpful for those who have the basic notions of PIC programminng but difficult to understand by the beginers!
Thank you very much for your help and for your code example! I will try my best to make it work!
If you have same usefull reading that can help me undestand and improve my pic programming skills I would greathly apreciate it!
Once again thank you Ttelmah! |
|
|
tepes
Joined: 25 Jan 2012 Posts: 18
|
|
Posted: Fri Apr 27, 2012 9:10 am |
|
|
It still does not work! There is something I don't do it right! I tried to do something based on your example but I don't get to any result!
Code: |
#if !defined(__PCD__)
#error This example will only compile for the PCD 24 bit compiler
#endif
#include <24HJ128GP504.h>
#FUSES FRC_PLL,NOWDT,NOBSS, NOIESO, NOCKSFSM, NOPROTECT, NOJTAG
//#fuses HS,NOWDT,noprotect
#use delay(clock=66.8MHz)
#pin_select U1TX = PIN_C9
#pin_select U1RX = PIN_C8
#use rs232(baud=9600, xmit=PIN_C9, rcv=PIN_C8,parity=N,bits=8)
#include <stdlib.h>
#include <stdio.h>
int16 adc_buffer[256];
int8 adc_index=0;
int1 complete=TRUE;
int half_period;
#WORD AD1CON1=getenv("SFR:AD1CON1")
#int_ADC1
void ADC1_isr(void){
if (complete) return;
adc_buffer[adc_index]=read_adc(ADC_READ_ONLY);
if (adc_index<255)
adc_index++;
else {
complete=TRUE;
}
}
/*
#int_timer3
void timer3_isr(){
// set_timer3(Number_of_Ticks);
// clear_interrupt(INT_ADC1);
// adc_index=0;
// complete=FALSE;
enable_interrupts(INT_ADC1);
// enable_interrupts(INTR_GLOBAL);
}
*/
void main(void){
int i;
setup_oscillator(OSC_INTERNAL,66800000);
setup_spi( FALSE );
setup_spi2( FALSE );
setup_adc_ports(sAN0);
setup_adc(ADC_CLOCK_DIV_4); //This gives ADC clocked at 8.29MHz = 120.63nSec
//Almost the max allowed....
setup_timer2(TMR_DISABLED |TMR_DIV_BY_1 ,0);
setup_timer4(TMR_DISABLED |TMR_DIV_BY_1 ,0);
setup_timer5(TMR_DISABLED |TMR_DIV_BY_1 ,0);
setup_timer1(TMR_DISABLED|TMR_DIV_BY_1);
setup_timer3(TMR_INTERNAL |TMR_DIV_BY_1 ,156);
//Now use Timer3, since this can trigger the ADC
//To trigger every 5uSec, require total division by 334 - remember timer is
//fed off Fosc/2, and count is period+1, so 156 counts.
setup_adc_ports(sAN0);
//Now none of the internal functions appear to allow you to select to trigger
//the ADC on the timer, so go manual
AD1CON1=(AD1CON1 & 0xFFF8) | 2;
HALF_PERIOD=1;
enable_interrupts(INT_ADC1);
enable_interrupts(INTR_GLOBAL);
do {
adc_index=0;
complete=FALSE;
enable_interrupts(INT_ADC1);
enable_interrupts(INTR_GLOBAL);
/*
clear_interrupt(INT_ADC1);
enable_interrupts(INT_ADC1);
set_timer3(0);
enable_interrupts(int_timer3);
enable_interrupts(INTR_GLOBAL);
*/
//Now wait for conversion
do{}
while (!complete) ;
//Here 256 samples have been read.
disable_interrupts(INT_ADC1);
for(i=0;i<255;i++)
{
printf("%lu ",adc_buffer[i]);
// delay_ms(400);
}
printf("ADC\n\r");
printf("\n\r");
complete=FALSE;
adc_index=0;
enable_interrupts(INT_ADC1);
enable_interrupts(INTR_GLOBAL);
} while (TRUE);
}
|
|
|
|
tepes
Joined: 25 Jan 2012 Posts: 18
|
|
Posted: Wed May 02, 2012 2:32 am |
|
|
I had tried different variations based on the code but since I use these fuses setting I get nothing printed!
Code: |
#FUSES FRC_PLL,NOWDT,NOBSS, NOIESO, NOCKSFSM, NOPROTECT, NOJTAG
//#fuses HS,NOWDT,noprotect
#use delay(clock=66.8MHz)
|
Here is a code version that it seems fine for me (but it is not fine)!
Code: |
#if !defined(__PCD__)
#error This example will only compile for the PCD 24 bit compiler
#endif
#include <24HJ128GP504.h>
#FUSES FRC_PLL,NOWDT,NOBSS, NOIESO, NOCKSFSM, NOPROTECT, NOJTAG
//#fuses HS,NOWDT,noprotect
#use delay(clock=66.8MHz)
#pin_select U1TX = PIN_C9
#pin_select U1RX = PIN_C8
#use rs232(baud=9600, xmit=PIN_C9, rcv=PIN_C8,parity=N,bits=8)
#include <stdlib.h>
#include <stdio.h>
int16 adc_buffer[256];
int8 adc_index=0;
int1 complete=TRUE;
int half_period;
#WORD AD1CON1=getenv("SFR:AD1CON1")
#int_ADC1
void ADC1_isr(void){
if (complete) return;
adc_buffer[adc_index]=read_adc(ADC_READ_ONLY);
if (adc_index<255)
adc_index++;
else {
complete=TRUE;
}
}
#int_timer3
void timer3_isr(){
int k;
enable_interrupts(INT_ADC1);
if(HALF_PERIOD == 0) // test for interup frequency
{
output_low(PIN_B4);
HALF_PERIOD = 1;
}
else
{
output_high(PIN_B4);
HALF_PERIOD = 0 ;
}
}
void main(void){
int i;
setup_oscillator(OSC_INTERNAL,66800000);
setup_spi( FALSE );
setup_spi2( FALSE );
setup_adc_ports(sAN0);
setup_adc(ADC_CLOCK_DIV_4); //This gives ADC clocked at 8.29MHz = 120.63nSec
//Almost the max allowed....
setup_timer2(TMR_DISABLED |TMR_DIV_BY_1 ,0);
setup_timer4(TMR_DISABLED |TMR_DIV_BY_1 ,0);
setup_timer5(TMR_DISABLED |TMR_DIV_BY_1 ,0);
setup_timer1(TMR_DISABLED|TMR_DIV_BY_1);
setup_timer3(TMR_INTERNAL |TMR_DIV_BY_1 ,156);
//Now use Timer3, since this can trigger the ADC
//To trigger every 5uSec, require total division by 334 - remember timer is
//fed off Fosc/2, and count is period+1, so 156 counts.
setup_adc_ports(sAN0);
//Now none of the internal functions appear to allow you to select to trigger
//the ADC on the timer, so go manual
AD1CON1=(AD1CON1 & 0xFFF8) | 2;
HALF_PERIOD=1;
adc_index=0;
complete=FALSE;
// enable_interrupts(INT_ADC1);
enable_interrupts(int_timer3);
enable_interrupts(INTR_GLOBAL);
do {
adc_index=0;
complete=FALSE;
set_timer3(0);
//Now wait for conversion
do{}
while (!complete) ;
//Here 256 samples have been read.
disable_interrupts(INT_ADC1);
for(i=0;i<255;i++)
{
printf("%lu ",adc_buffer[i]);
delay_ms(500);
}
printf("ADC\n\r");
printf("\n\r");
} while (TRUE);
}
|
Any suggestion?
Thank you! |
|
|
bkamen
Joined: 07 Jan 2004 Posts: 1615 Location: Central Illinois, USA
|
|
Posted: Sat May 05, 2012 11:56 pm |
|
|
tepes wrote: | Yes,you are right I did not understood your advices! I have problems in understanding the PIC programming, and the documentation from CCS is maybe helpful for those who have the basic notions of PIC programminng but difficult to understand by the beginers!
|
I'm sorry -- but if you want to learn to program, buy education/instructional books on the language you want to learn.
If you want to learn to program for PIC's, you need to read the datasheet for the device in question... and then read it again... and then read it again... and then use the device...
And read the datasheet AGAIN.
The CCS compiler comes with a manual mostly for reference, not instruction. It also comes with lots of examples... So does microchip's website. (and their almost free compiler).
Ultimately, *you* have you do your homework. No one else will do it for you.
Cheers,
-Ben _________________ Dazed and confused? I don't think so. Just "plain lost" will do. :D |
|
|
tepes
Joined: 25 Jan 2012 Posts: 18
|
|
Posted: Mon May 07, 2012 1:09 am |
|
|
I am determined to learn PIC programming and I know this will be a long journey!
Regarding my homework I try to do it but sometimes even if I do my best with it, there is something missing and for someone without experience it can take a lot of time to fix the problem! Like now for instantace, I made more than 10 different versions, first based on my own knowlledge, and then based on ttelmahs advices to get to the point where I wanted to get!
Thank you! |
|
|
|
|
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
|