Continously sampling via ADC, how to start
Continously sampling via ADC, how to start
Hi,
I'm new to USB programming and would like some advice on how I should proceed.
I'm trying to write a program that will continuously (and preferably uniformly) sample a voltage via the integrated ADC on an AVR, and send the values over USB. I'll either use an ADC resolution of 8 or 10 bits and I'd like to sample as fast as possible, but at least 1 kHz. Is this even possible with the 16.5 MHz RC oscillator code (I could also use a 20 MHz crystal)?
I'm still a bit confused by "end points" and "device classes", will endpoint 0 be suitable or will I need another one? I'd like to be able to control the device as well, so the communication should be two way.
I noticed that the driver needs to be polled often via usbPoll(). How long does this function take to process and how frequently should it be called? Can I just set it up on a timer and then just ignore it?
Thanks,
Roger
I'm new to USB programming and would like some advice on how I should proceed.
I'm trying to write a program that will continuously (and preferably uniformly) sample a voltage via the integrated ADC on an AVR, and send the values over USB. I'll either use an ADC resolution of 8 or 10 bits and I'd like to sample as fast as possible, but at least 1 kHz. Is this even possible with the 16.5 MHz RC oscillator code (I could also use a 20 MHz crystal)?
I'm still a bit confused by "end points" and "device classes", will endpoint 0 be suitable or will I need another one? I'd like to be able to control the device as well, so the communication should be two way.
I noticed that the driver needs to be polled often via usbPoll(). How long does this function take to process and how frequently should it be called? Can I just set it up on a timer and then just ignore it?
Thanks,
Roger
I've made some progress and have something that works, but I still need to work out the best way to send the ADC output back to the host. So far I've only tried the default control endpoint, and I can get about 300 bytes/s sending back the ADC contents when requested by the host. FYI the code is something like this:
where the ADC is in free running mode (with no interrupts).
I've also played with sending 254 bytes at a time with usbFunctionRead() using dummy data, and I get much better performance (>7 kbytes/s, more than I need).
I still have my original problem though, how can I send the ADC output back to the host with the highest sample rate?
Polling for it byte by byte is too slow, and buffering and sending large chunks at once uses too much memory and there are timing issues (since I'd like the sampling to be uniform).
Would using interrupt and bulk-in endpoints help?
Thanks,
Roger
Code: Select all
switch(rq->bRequest)
{
case CMD_ADC_READ:
usbMsgPtr = (uchar *)&ADC;
return 2;
...
where the ADC is in free running mode (with no interrupts).
I've also played with sending 254 bytes at a time with usbFunctionRead() using dummy data, and I get much better performance (>7 kbytes/s, more than I need).
I still have my original problem though, how can I send the ADC output back to the host with the highest sample rate?
Polling for it byte by byte is too slow, and buffering and sending large chunks at once uses too much memory and there are timing issues (since I'd like the sampling to be uniform).
Would using interrupt and bulk-in endpoints help?
Thanks,
Roger
Some answers:
(1) You can't call usbPoll() from a timer interrupt. You must call it from the main code. usbPoll() usually returns within microseconds, but since your usbFunctionSetup(), usbFUnctionRead() and usbFunctionWrite() routines are called from there, the maximum duration depends on your routines.
(2) This can all be done on endpoint 0 with control transfers. The host polls with control-in and receives as many bytes as there are available on each read. Bulk endpoints are forbidden for low speed, interrupt endpoints would be possible, although polling might be too slow.
(3) You need some kind of buffering, preferably a ring buffer. The ADC may be in free-running mode and trigger an interrupt (which must be declared so that it re-enables interrupts immediately), or you poll it from the main loop as well.
(4) Each control-in (handled through usbFunctionSetup()) returns as many bytes from the ring buffer as there are available.
(1) You can't call usbPoll() from a timer interrupt. You must call it from the main code. usbPoll() usually returns within microseconds, but since your usbFunctionSetup(), usbFUnctionRead() and usbFunctionWrite() routines are called from there, the maximum duration depends on your routines.
(2) This can all be done on endpoint 0 with control transfers. The host polls with control-in and receives as many bytes as there are available on each read. Bulk endpoints are forbidden for low speed, interrupt endpoints would be possible, although polling might be too slow.
(3) You need some kind of buffering, preferably a ring buffer. The ADC may be in free-running mode and trigger an interrupt (which must be declared so that it re-enables interrupts immediately), or you poll it from the main loop as well.
(4) Each control-in (handled through usbFunctionSetup()) returns as many bytes from the ring buffer as there are available.
Sure, happy to share.
Here is the most relevant code:
I can sample up to about 6 kHz in 8 bit mode and a bit less in 10 bit mode with a 200 byte buffer. If you use a smaller one then you might run into trouble since it fills up too fast.
In 10 bit ADC mode the data is packed a bit weird to save space, instead of just using two bytes (and wasting 6 bits). The code could also be sped up by eliminating all the modulo calls, but it works good enough for me.
If you want the entire code, let me know. I also have a Python interface that uses pyUSB.
Good luck,
Roger
Here is the most relevant code:
Code: Select all
volatile uint8_t adcbuff[250]; // must be multiple of 5 for 10 bit mode
volatile uint8_t end = 0;
volatile uint8_t start = 0, bytes_ready;
volatile uint8_t adc_10bit_mode = 0;
#define ADCBufferSize() ((end - start + DAC_BUFF_SIZE) % DAC_BUFF_SIZE)
#define ADCBufferFull() ((end + 1) % DAC_BUFF_SIZE == start)
#define ADCBufferClear() end = 0; start = 0
ISR(ADC_vect, ISR_NOBLOCK)
{
uint8_t high_byte, shift_amount;
if(ADCBufferFull())
{
// throw away oldest samples if buffer is full
if(adc_10bit_mode)
start = (start + 5) % DAC_BUFF_SIZE;
else
start = (start + 1) % DAC_BUFF_SIZE;
}
if(adc_10bit_mode)
{
// pack four 10 bit samples into 5 bytes by using every 5th byte to store the 2 LSB's of the previous 4 samples
// skip every 5th byte
if((end + 1) % 5 == 0)
end = (end + 1) % DAC_BUFF_SIZE;
high_byte = end + 5 - ((end + 1) % 5); // find index of byte to store 2 LSB's
shift_amount = 6 - (end % 5)*2; // find bit position in byte to store the 2 bits
adcbuff[high_byte] = (adcbuff[high_byte] & ~(0b11000000 >> shift_amount)) | (ADCL >> shift_amount);
}
adcbuff[end] = ADCH;
end = (end + 1) % DAC_BUFF_SIZE;
TIFR |= (1 << OCF0A); // clear interrupt flag
}
Code: Select all
usbMsgLen_t usbFunctionSetup(uchar setupData[8])
{
usbRequest_t *rq = (void *)setupData;
static uchar buf;
switch(rq->bRequest)
{
// <more non essential commands here>
case CMD_ADC_BUFFER_READ:
bytes_ready = MIN(ADCBufferSize(), rq->wValue.bytes[0]);
if(adc_10bit_mode)
// need to send 5 bytes at a time in 10 bit mode
bytes_ready = (bytes_ready/5)*5;
return USB_NO_MSG;
case CMD_ADC_BUFFER_CLEAR:
ADCBufferClear();
return 0;
case CMD_ADC_SAMPLERATE_SET:
ADCSampleRateSet(rq->wValue.bytes[0]);
return 0;
}
Code: Select all
uchar usbFunctionRead(uchar *data, uchar len)
{
if(len > bytes_ready)
len = bytes_ready;
for(uint8_t i = 0; i < len; ++i)
data[i] = adcbuff[(i + start) % DAC_BUFF_SIZE]; // copy data to ADC buffer
start = (start + len) % DAC_BUFF_SIZE;
bytes_ready -= len;
return len;
}
I can sample up to about 6 kHz in 8 bit mode and a bit less in 10 bit mode with a 200 byte buffer. If you use a smaller one then you might run into trouble since it fills up too fast.
In 10 bit ADC mode the data is packed a bit weird to save space, instead of just using two bytes (and wasting 6 bits). The code could also be sped up by eliminating all the modulo calls, but it works good enough for me.
If you want the entire code, let me know. I also have a Python interface that uses pyUSB.
Good luck,
Roger
Sorry I haven't had a chance to post this until now or to clean up my code, but here it is: http://www.megaupload.com/?d=EXUBE3WX
usbtest.c is the main program.
I can almost guarantee there are lots of bugs, so good luck
usbtest.c is the main program.
I can almost guarantee there are lots of bugs, so good luck
-
- Rank 1
- Posts: 29
- Joined: Mon Oct 13, 2008 7:11 pm
Hi.
Nice done.
How have you messured the sample rate?
I need to fetch information from ADCs as fast as possible, but i may not need it to be circular.
What microcontroller are you using? Crystal oscillator? ADC prescaler settings? How many channels are you sending? etc.
How fast can AVR-USB transfer a buffer in your case?
You say that the sample rate are of about "6 khz", but sample rate are messured in k-samples per second. What do you really mean with 6 khz? I think that it is too much slow. With an ATmega32 at 16Mhz it is possible to get 76923 kSPS and 8 bits.
Thanks.
Nice done.
How have you messured the sample rate?
I need to fetch information from ADCs as fast as possible, but i may not need it to be circular.
What microcontroller are you using? Crystal oscillator? ADC prescaler settings? How many channels are you sending? etc.
How fast can AVR-USB transfer a buffer in your case?
You say that the sample rate are of about "6 khz", but sample rate are messured in k-samples per second. What do you really mean with 6 khz? I think that it is too much slow. With an ATmega32 at 16Mhz it is possible to get 76923 kSPS and 8 bits.
Thanks.
I think that it's not the problem with ADC speed (or it's only partially a problem), but with speed of software-usb:
http://forums.obdev.at/viewtopic.php?t=19&start=15
up to 7kB/s? it means sending up to less than 7k samples (8bit) per second.
PS:
is there any Windows-based host for sending and receiving data from avr-usb device except gPowerSwitch?
http://forums.obdev.at/viewtopic.php?t=19&start=15
up to 7kB/s? it means sending up to less than 7k samples (8bit) per second.
PS:
is there any Windows-based host for sending and receiving data from avr-usb device except gPowerSwitch?
Hi.
The sample rate is variable and is set by the host (from 1.1 kHz - >10 kHz), and from my testing it is quite accurate.
I built a small circuit using an ATTINY85 with a diode and BJT to reduce the 5V USB supply down to ~3.6 V. I used the 16.5 MHz RC module, which is good enough for me. My software only uses one channel, but it should be easy to expand that to several (I didn't have any free pins in my circuit).
My testing shows that I can reliably and continuously transfer up to about 7 kbps using AVR-USB and a large enough buffer. So in 8 bit mode that's up to 7 ksps or (4/5)*7 ksps in 10 bit mode.
Here are some test plots I made with various sample rates and varying input sine frequencies.
As you may see, there's very little distortion and the timing is quite accurate.
This is what happens when you try to sample too fast and the buffer can't keep up:
The sample rate is variable and is set by the host (from 1.1 kHz - >10 kHz), and from my testing it is quite accurate.
I built a small circuit using an ATTINY85 with a diode and BJT to reduce the 5V USB supply down to ~3.6 V. I used the 16.5 MHz RC module, which is good enough for me. My software only uses one channel, but it should be easy to expand that to several (I didn't have any free pins in my circuit).
My testing shows that I can reliably and continuously transfer up to about 7 kbps using AVR-USB and a large enough buffer. So in 8 bit mode that's up to 7 ksps or (4/5)*7 ksps in 10 bit mode.
Here are some test plots I made with various sample rates and varying input sine frequencies.
As you may see, there's very little distortion and the timing is quite accurate.
This is what happens when you try to sample too fast and the buffer can't keep up:
-
- Rank 1
- Posts: 29
- Joined: Mon Oct 13, 2008 7:11 pm
epsilon_da wrote:How fast can AVR-USB transfer a buffer?
I did some tests transferring 5000 times a buffer of 1800 bytes from the uC to PC doing most transfers in chuncks of 250 bytes.
The result was a transfer rate of about 55 kb/s.
The firmware does nothing more than blink a led each second and transfer the buffer.
I suspect that doing a realtime adquisition board up to 25 ksps in 10 bit mode, is doable.
That's a bit of a suspicious result I think.
According to http://forums.obdev.at/viewtopic.php?t=19&postdays=0&postorder=asc&start=0, the theoretical limit is 24kbps and no one got above 7kbps.
Can you post more details and double check your results?
According to http://forums.obdev.at/viewtopic.php?t=19&postdays=0&postorder=asc&start=0, the theoretical limit is 24kbps and no one got above 7kbps.
Can you post more details and double check your results?
-
- Rank 1
- Posts: 29
- Joined: Mon Oct 13, 2008 7:11 pm