View previous topic :: View next topic |
Author |
Message |
hops-cc
Joined: 06 Mar 2012 Posts: 6
|
18F2550 ADC Sampling Rate |
Posted: Tue Mar 06, 2012 7:50 am |
|
|
Hi everyone,
I'm new here. I made an ADC circuit with 18F2550. I need to take samples from an ECG signal. Maksimum frequency of ECG signal is about 45 Hz. My pic doesn't take sample fastly so i lost some signals when i see them on MATLAB software. When i apply sinus 10 Hz to ADC it shown on matlab like triangle signal. I want to write a loop to take samples from ECG signals fast enough. Maximum freq = 45 hz so from nyquist criteration i need to make sampling at least 45*2 = 90 hz. That means sampling period must be at least 1/95 sn = 10,5 ms.
So far so good. But now i dont know to configure the ADC time to adjust these values and is there anyone knows the maximum sampling rate can be archived by 18F2550 ?? Whats the maximum frequency ??
SIMPLY, I WANT TO LEARN HOW TO ADJUST THE TIME BETWEEN THE SAMPLES.
Here's my configuration codes. Thanks
Code: | #include <18F2550.h>
#device ADC=10
#fuses HSPLL,USBDIV,PLL5,PUT,CPUDIV1,VREGEN,NOWDT,NOPROTECT,NOLVP,NODEBUG,NOMCLR
#use delay(clock=48000000)
static unsigned int16 ADC1 = 0;
static unsigned int16 ADC2 = 0;
static unsigned int16 i=0;
void user_init(void)
{
set_tris_a(0x03); //RA0,RA1 input
set_tris_b(0xFF);
setup_adc_ports(AN0_TO_AN1);
setup_adc(ADC_CLOCK_INTERNAL);
}
unsigned int16 ADC_read(byte channel)
{
unsigned int16 value=0;
set_adc_channel(channel);
delay_us(20);
value = read_adc();
return value;
}
|
|
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9170 Location: Greensville,Ontario
|
|
Posted: Tue Mar 06, 2012 8:12 am |
|
|
first problem
simply put...you have to read the PIC datasheet. Everything you need to know is there.Nice thing about PICs..they're all very similar. Learn something once and you can apply to most PICs !!
1)
A 5 volt PIC ADC ONLY accepts input of zero to just less than +5 volts. NO negative voltages are allowed!! You'll need an ac/dc convertor for that.
Sinus (sine wave ?) values will only be read on the +ve phase. -ve hase will be zeros.
2) You do not need to use the set_tris_..... statements. Let the compiler do it for you, automatically.
3) Read the ADC section of the manual.There's a chart on the adc 'clock + speed' and almost always you NEVER want to use 'adc_clock_internal'.
4) CCS supplies lots of example programs in the 'examples' folder. You will find several that use the ADC and display data.
5) The PIC will easily sample at 90 Hz !!! |
|
![](templates/subSilver/images/spacer.gif) |
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
ECG to MATLAB |
Posted: Tue Mar 06, 2012 8:56 am |
|
|
You can use a PIC for negative voltages. You bias the analogue input to somewhere in it's common mode range.
From what you're saying, there should not be any problem with either sampling frequency or resolution.
However, from your code I can't see how you're getting the data from the PIC to MATLAB. There's something wrong/missing if you can't get a PIC to take enough samples to give a reasonable sine wave at 10Hz? What are you not telling us?
Nyquist is fine for the ultimate boundary, But I suspect you would be disappointed with what you got at the Nyquist limit. What you really need is at least an order of magnitude better than that. Again no problem for most PICs.
NB. Code is more readable in the code box.
Mike |
|
![](templates/subSilver/images/spacer.gif) |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue Mar 06, 2012 9:31 am |
|
|
Quote: |
from an ECG signal
|
these are TINY mv level signals at best
and hard to resolve
without SERIOUS analog processing -
as is - you have NO signal that you can resolve on even a 12Bit A/D pic |
|
![](templates/subSilver/images/spacer.gif) |
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
ECG signals |
Posted: Tue Mar 06, 2012 3:20 pm |
|
|
I remember now. Long time ago, I met early versions of ECGs. There were issues with, OPAs saturating for tens of seconds, and isolating the victim / test-subject from the lethal possibilities of mains connected displays.
On that note:-
Is you ECG signal already suitably processed?
Is it effectively ac or dc coupled?
Mike |
|
![](templates/subSilver/images/spacer.gif) |
hops-cc
Joined: 06 Mar 2012 Posts: 6
|
|
Posted: Fri Mar 09, 2012 8:52 am |
|
|
Hi guys,
I know ECG signal magnitude is about milivolts. But i use amplifier, gain controller and DC adder to adjust it between 0-5 V. I have no problem with hardware and i already took samples of eCG signals in matlab. But, the points is that, my codes don't work fast as it should be. I can't sampling fast enough. I see the problem when i apply 10 Hz sinusoidal (Dont say that,you cant apply negative voltages because it's DC added voltage. that's not the point). signals becomes triangle. That means sampling frequency isn't fast enough. I'm new on CCS programming. I tried to write a loop that takes 64 samples (64 because i will send this char on USB pipe,1024 recommended but i couldn't made it yet). Can you help me with the codes. I just want to make sampling at least above 90 hz (i prefer 200-400 hz). Take those samples to a array and send them on USB pipe. I this method, I'm sure i will not be failed. The below codes takes only one sample and after send it to matlab. This so slow i think. So i want to take samples as much as possible at one time instead of doing one by one. And after send them at one time. Then make an signal processing on matlab with that array.
Code: |
#include <18F2550.h>
#device ADC=10
#fuses HSPLL,USBDIV,PLL5,PUT,CPUDIV1,VREGEN,NOWDT,NOPROTECT,NOLVP,NODEBUG,NOMCLR
#use delay(clock=48000000)
#define USB_HID_DEVICE FALSE
#define USB_EP1_TX_ENABLE USB_ENABLE_BULK
#define USB_EP1_RX_ENABLE USB_ENABLE_BULK
#define USB_EP1_TX_SIZE 64
#define USB_EP1_RX_SIZE 64
#include <pic18_usb.h>
#include <USB_Konfigurasyon.h>
#include <usb.c>
#define UcNokta1 1
static unsigned int16 ADC_deger1 = 0;
void user_init(void)
{
set_tris_a(0x03);
set_tris_b(0x00);
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_INTERNAL);
}
unsigned int16 ADC_Oku(byte kanal)
{
unsigned int16 olcum=0;
set_adc_channel(kanal);
delay_us(20);
olcum = read_adc();
return olcum;
}
void main(void)
{
byte sayac=0;
byte gelen_paket[64];
byte gond_paket[64];
user_init();
usb_init();
usb_task();
usb_wait_for_enumeration();
if(usb_enumerated())
for (;;)
{
while(usb_enumerated())
{
[b]ADC_deger1=ADC_Oku(0); //Here's where i want to write a loop that takes 64 samples.
gond_paket[1] = (byte) ADC_deger1;
gond_paket[2] = (byte)(ADC_deger1>>8);
sayac = 0x02;[/b]
if(sayac!=0)
{
usb_put_packet(UcNokta1, gond_paket, sayac, USB_DTS_TOGGLE);
sayac = 0;
}
}
}
}
|
Last edited by hops-cc on Fri Mar 09, 2012 9:22 am; edited 1 time in total |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19360
|
|
Posted: Fri Mar 09, 2012 9:20 am |
|
|
Have a look at this thread:
<http://www.ccsinfo.com/forum/viewtopic.php?t=47494>
Send the samples to USB, instead of serial, and you should be starting in the right direction.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Mar 09, 2012 9:54 am |
|
|
here is a trick i use all the time when trying to take a lot of samples on different PIC channels.
every time you take a reading, and BEFORE formatting and sending the data - set the NEXT channel you will be reading - instead of just before you read it.
Also keep in mind that the nyquist frequency is only the outer limit for getting ANY data - not a sampling rate that preserves the WFM AT ALL well
when you are after waveform accuracy.
20X or even 100X that rate is where i would set my sights - and in the case of a 10 hz WFM - i'd be targetting 500 samples /sec to have a decent reconstruction of the signal. BTDT with biophysical waveforms that are much faster than 10hz. Then again i use an external parallel 16 bit ADC and exploit 230400 baud to get rid of the data in hex ........ |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19360
|
|
Posted: Fri Mar 09, 2012 10:14 am |
|
|
Actually you can do even better than this!....
As soon as the ADC starts actually converting, the voltage source is disconnected from the multiplexer. So you can do this:
Code: |
read_adc(ADC_START_ONLY);
set_adc_channel(next_channel);
while (!adc_done()) ;
val=read_adc(ADC_READ_ONLY);
|
and the 'new' channel will be connected to the ADC input as soon as the conversion finishes!. A good few cycles earlier than if you wait till after you have read!.
A 'little known' feature that can be useful if you are trying to really optimise the timing.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Mar 09, 2012 3:29 pm |
|
|
glad i posted that method of mine.
Now I want to be TTELMAH when i grow up , or at least his 65 YO adopted
student.
![Very Happy](images/smiles/icon_biggrin.gif) |
|
![](templates/subSilver/images/spacer.gif) |
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
Sampling Rate |
Posted: Fri Mar 09, 2012 5:14 pm |
|
|
How do you KNOW that your problem is with the PIC?
Performing 5,000 conversions per second should be well within range of a lowly '877 & 3.6864MHz crystal. That's 10bit conversion and two characters per @ 115,200baud. If you don't like sending data as binary, you should be able to get hex and a CR(or a comma) to go at 2.5k/s.
Is it possible, the problem lies at the PC end, not the PIC?
Mike |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9170 Location: Greensville,Ontario
|
|
Posted: Sat Mar 10, 2012 6:38 am |
|
|
I think Mike's right. as I KNOW I can get 1 megabaud serial communcations with an 18F4550 using the internal UART tied to a TTL<-->USB dongle. Even 'direct' to the USB I/F of the PIC, I get 1 megabaud with the CDC 'driver'.Data is formatted as CSV,stored into a file by realterm, to autoload into Excel.
A quick test...
You should try 'regular' rs232 serial to the PC ( bypassing ALL the PIC USB stuff). Just simply read 64 samples,store in buffer, transmit buffer data, pause a bit, do it again.On the PC side , use a terminal program to display data.
Getting USB on the PC may be your headache as we don't know what application you're using. |
|
![](templates/subSilver/images/spacer.gif) |
hops-cc
Joined: 06 Mar 2012 Posts: 6
|
|
Posted: Sat Mar 10, 2012 4:18 pm |
|
|
Hi everyone,
Thanks for all the advice. This project is my final project in my engineering faculty so I have to complete this task with USB and use MATLAB for signal processing. I will write a MATLAB GUI for see the signals and other purposes.
So, now I made a calculation, I need maximum 2 seconds of measurement each time. I chose my sampling frequency of 500 Hz, so I will acquire approximately 1000 samples at each measurement. I will write the measurement on a char and send them by isochronous USB transfer (1024 packet at one time). My second problem is to adjust the Sampling interval. I don't know how to. TAD values and Fosc values confused me.
Now i can only transfer 256 packets with BULK or INTERRUPT TRANSFER. But i want to switch to isochronous transfer. I couldn't find location that i will modify on the codes for isochronous transfer. Here's all my codes that show what i tried to do.
Code: |
#include <18F2550.h>
#device ADC=10
#fuses HSPLL,USBDIV,PLL5,PUT,CPUDIV1,VREGEN,NOWDT,NOPROTECT,NOLVP,NODEBUG,NOMCLR
#use delay(clock=20000000)
#define USB_HID_DEVICE FALSE
#define USB_EP1_TX_ENABLE USB_ENABLE_BULK //Uçnokta1'de Yığın transferi aktif
#define USB_EP1_RX_ENABLE USB_ENABLE_BULK
#define USB_EP1_TX_SIZE 1000 //Uçnokta1 için maksimum alınacak ve gonderilecek
#define USB_EP1_RX_SIZE 2 //veri boyutu (64 byte)
#include <pic18_usb.h>
#include <USB_Konfigurasyon.h> //USB konfigurasyon bilgileri bu dosyadadır.
#include <usb.c>
#define UcNokta1 1
#define Komut gelen_paket[0]
static unsigned int16 ADC_deger1 = 0;
void user_init(void)
{
set_tris_a(0x01); //RA0 giriş
set_tris_b(0x00);
output_b(0);
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_INTERNAL);
}
unsigned int16 ADC_Oku(byte kanal)
{
unsigned int16 olcum=0;
set_adc_channel(kanal);
delay_us(10);
olcum = read_adc();
return olcum;
}
void main(void)
{
unsigned int16 sayac=0;
unsigned int16 gond_paket[1000];
byte gelen_paket[2] ; //gelen paket
user_init();
usb_init();
usb_task();
usb_wait_for_enumeration(); //Cihaz, hazır olana kadar bekle
if(usb_enumerated())
output_high(Pin_A2); //USB bağlantısı kurulduysa LED'i yak.
while(usb_enumerated())
{
unsigned int16 i;
for (i=1;i<=500;++i) // Here should be a loop that takes 1000 samples during 2 seconds with 500Hz sampling frequency.
{
ADC_deger1=ADC_Oku(0);
gond_paket[(2*i)-1] = (byte) ADC_deger1;
gond_paket[(2*i)] = (byte)(ADC_deger1>>8);
delay_us( ); // I Don't know the delay ? what it should be for 500 Hz sampling rate ?
}
sayac = 1000;
if(sayac!=0)
{
usb_put_packet(UcNokta1, gond_paket, sayac, USB_DTS_TOGGLE);
sayac = 0;
Komut = 0;
}
}
}
}
|
|
|
![](templates/subSilver/images/spacer.gif) |
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Sat Mar 10, 2012 5:28 pm |
|
|
Create a 2ms interrupt. Use that to control the ADC timing. That way you'll not miss any data. The ADC could continue whilst sending data out.
A problem I see with 1000 samples is you're close to running out of RAM. Do you really need 10bit ADC? With 8bit ADC you will only need half the RAM. (Unless you do some sort of data packing.)
Mike |
|
![](templates/subSilver/images/spacer.gif) |
hops-cc
Joined: 06 Mar 2012 Posts: 6
|
|
Posted: Sat Mar 10, 2012 5:34 pm |
|
|
Mike Walne wrote: | Create a 2ms interrupt. Use that to control the ADC timing. That way you'll not miss any data. The ADC could continue whilst sending data out.
A problem I see with 1000 samples is you're close to running out of RAM. Do you really need 10bit ADC? With 8bit ADC you will only need half the RAM. (Unless you do some sort of data packing.)
Mike |
First, for now I only can copy paste from other codes. I'm new on CCS programming. But I will try that advice about interrupt. I don't know why interrupt is used and when used. I will search it. Second thing that is PIC can make ADC conversion to 8 bit instead of 10 bit ?
I guess it modified from # device ADC=10 code. Can you help me with that interrupt codes ? please. |
|
![](templates/subSilver/images/spacer.gif) |
|