Baud rate and ADC sampling frequency

Go To Last Post
65 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Freaks,

I am using a M128 with ADC on channel 5. I am using the UART for an RS232 connection to my PC. I am trying to see a sampled sine wave on my PC. Sine wave frequency is 100Hz which I have confirmed with an O scope. Amplitude is about 700 mV. Xtal frequency is 8MHz. CLKDIV8 fuse is selected. UART baud rate is 1200 baud.

I am getting the instantaneous voltage values on the hyperterminal but I am getting multiple zeroes between the values. Could this be because of a wrong baud rate? Do I have to average the samples? Here is a part of my code:

I am using a timer to display the ADC values every second on my PC.


#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((8000000/ (USART_BAUDRATE * 16UL))) - 1) //value to load into UBRR

unsigned int a;
unsigned int b;
volatile unsigned char flag;

static int UART_putchar(char c, FILE *stream);

static FILE mystdout = FDEV_SETUP_STREAM(UART_putchar, NULL, _FDEV_SETUP_WRITE);

void USART0_init()
{


   UCSR0B |= (1 << RXEN0) | (1 << TXEN0); // Turn on the transmission and reception circuitry 
   UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); //Set to 8 bit no parity, 1 stop bit
   UBRR0L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register
   UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
   stdout = &mystdout; //Required for printf init

}


static int UART_putchar(char c, FILE *stream)
{
    if (c == '\n') UART_putchar('\r', stream);
  
    while ((UCSR0A & (1 << UDRE0)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it

    UDR0 = c;
    
    return 0;
}


char UART_getchar()
  {
   while((UCSR0A & (1 << RXC0)) == 0) {};
   return UDR0;
  }


void ADC_init()

{
   ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADATE); // Set ADC prescaler to 128   ADMUX = (1 << REFS0) | (1 << MUX2) | (1 << MUX0); // Set ADC reference to AVCC,select channel 5
   //using all 10 bit of ADC; so do not left justify the ADC value
   DIDR0 = (1 << ADC5D); //turn off the digital driver
   ADCSRA |= (1 << ADEN);  // Enable ADC
}


ISR(TIMER1_COMPA_vect)
{

PORTD ^= (1 <<PORTD7);
flag = 1;
}

/************MAIN**************/
int main (void)
    {

       
	   DDRC = 0x0F;
	   DDRD = 0xC0; 
	   DDRB = 0xFF;
	   PORTD = 0x00;
	   PORTB = 0x01; 
           USART0_init();
	   ADC_init(); 
	   timer_init();

          for(;;)
            {
			  	  
		 _delay_ms(100);

			  
                  ADCSRA |= (1 << ADSC); 
		  while((ADCSRA & _BV(ADIF)) != 0);
                  ADC_dummy = ADC;
						                       ADC_buffer_dummy =   ADC_dummy * 5;
		  a = ADC_buffer_dummy/1000;
                  b = ADC_buffer_dummy(percent)1000;

                 if(flag==1)
		      {

        		 printf("#d.#d\n",a,b); //percent sign replaced due to posting bug
		         flag = 0;
						 

                        }
               }
      }

So, with the /8 CLKDIV8 fuse, Xtal frequency of 8MHz ,13 cycles/sample and prescalar of 128 I should be getting a sample rate of about 600 sps. This should satisfy Nyquist for a 100Hz signal. I am using a baud rate of 1200 baud to display these samples on my PC. Does this mean I will get 1200 bps which is nearly twice my ADC sampling frequency?

What could be missing? Is it the averaging?

Appreciate your thoughts on this.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At 1200 Baud,you have only 1200 raw bits per second. This is 120 bytes per second. Even sending the data in binary form this is not enought to get a 100 Hz singal in real time. If you send the data as the ASCII output from floats it gets even worse: probably 6 or 7 bytes per sample.

So with binary data the minium baudrate is about 4800, with ascii data as in the programm its about 34 kBaud.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I am using a baud rate of 1200 baud to display these samples on my PC.

Kind of a mess.

Your code is taking 10 samples per second.

The message sent to the PC is, say, 10 characters long. At 1200bps and 10-bit character frames, that allows 120 characters per second. So you can get about 10 messages/second through your link. (It is an estimate because the message size is not deterministic.)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
CLKDIV8 fuse is selected

I don't have AVR Studio or a board where I am right now, but isn't this backwords? Selecting, (marking, ticking), the box activates the divide by 8, and gives you a 1 MHz clock.

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I don't have AVR Studio or a board where I am right now, but isn't this backwords? Selecting, (marking, ticking), the box activates the divide by 8, and gives you a 1 MHz clock.

The OP says he is using 1200 baud and 8MHz with div 8 on (so 1MHz final). The code says 9600 baud with 8MHz final. Both would require a UBRR value of 51.

@npat_avr: at no time are you clearing the ADIF flag, so after the first conversion (ADCSRA & _BV(ADIF)) != 0 will always be true. Either clear the flag or check for the ADSC bit to be low instead.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could make a sample buffer, and when full then send the data to the PC.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the feedback, guys.

First of all I had a typo in my first post. I am using an M168 not M128.

JC, Steve is correct, I will need an UBRR of 51.

Quote:

at no time are you clearing the ADIF flag, so after the first conversion (ADCSRA & _BV(ADIF)) != 0 will always be true. Either clear the flag or check for the ADSC bit to be low instead.

I want to run the ADC in a free running mode. Looking at the M168 datasheet page 248 timing diagram, I see that ADIF is always high after the 13th clock cycle.

@Kleinstein,
I will try the higher baud rate, it just seems too much horsepower to display the very slow 100 Hz signal.

I will also try to go to a lower frequency and see what I get.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I want to run the ADC in a free running mode

To what advantage? - you can only sample at 15.3KHz so just have the ADC interrupt each time a conversion is complete, initiate UART transmission (don't wait for completion) and set ADSC again. I guess it's true this does "cost" the time into/out of the ISR?

But your bandwidth limit here has already been identified as the 1200 baud comms. As a UART frame is 10 bits (8N1) this is 120 bytes per second. If you are making 10 bit reading you need to send a minimum of 2 bytes each time to encompass the 10 bits (with a 6 bit waste each time). So you can take a maximum of 60 readings per second if you really stuff the UART. That is what is going to determine how often you take ADC readings - nothing else - the ADC is more than capable of stuffing this pipe at 60Hz!

I'd consider moving to a clock source for the AVR that could support an accurate 115,200 baud so you can sens about 11KHz bytes. The ADC (assuming 2 bytes per reading) is still capable of feeding 5.5kHz as it's max rate is 15.3kHz as previously noted.

When doing a design like this you need to work through a similar "time budget" calculation to identify whether all parts of the system can run at sufficient speed and if not, do something about it (the ultimate is trading up to a faster CPU or ADC!). You choice of 1200baud here was a bad one given what you are trying to achieve. If you'd done a time budget you'd have known this from the outset.

In a similar vein the initial design phases should include a rough "space budget" too to determine whether you have sufficient of each type of used storage (flash, SRAM, EEPROM, external SRAM, external flash, etc etc). Add/change chips until you know what you propose is "do-able"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

So you can take a maximum of 60 readings per second if you really stuff the UART.

But the fragment showed a printf() string being produced and sent--perhaps 10 characters and 100+us. That brings it down even further, to like 12 reports per second.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I agree that my main limitation is the UART but I need to display the real time data on my PC.

@Cliff,
I will change my logic so it is ADC interrupt driven. I agree it does not make sense to use a free running mode.

Bear with me here, so if it is 12 reports/ second from the UART with 1200 bps, if I go to a 115200 bps should I get about 1152 reports/second?

If that is true, for a 100 cycles/second sine wave, should I get about 11.52 samples/cycle of the 100Hz sine wave?

If this is right, what crystal should I use? If I use 8MHz, I will get my UBRR value with 115200 bps as 3? Will that work?

Also right now I am connecting my signal generator straight to ADC input through a 10K input resistor. The GND of the signal generator goes to my breadboard ground. I am going to put a scope to see what the ADC is seeing but is there something I am missing hardware wise?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your signal will have to be centered between ground and Vref. If not, it will be "clipped". You might handle this in one of several ways:

1) If the signal generator has a DC offset control, then set it so that the zero-amplitude output is close to Vref/2

2) Build a resistive voltage divider to bias the ADC input. Then, capacitor couple the signal to the ADC input. If you do this, beware of that 10K series resistor. You could loose a LOT of amplitude, depending on the size of the resistors in your bias network.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
what crystal should I use?

There are certain frequencies that are "Baud Rate Friendly". I believe 14.7456 MHz is one such frequency.

I think AVRCalc, (and Kevin's, I'm blanking on the name...), will show the errors for given xtals and baudrates.

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I see that ADIF is always high after the 13th clock cycle.

Which was my point. It is always high regardless of whether or not a new conversion has been completed.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It is always high regardless of whether or not a new conversion has been completed.

... which can well explain
Quote:

I am getting multiple zeroes between the values.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So you want to use 8mhz because you just have a 168 dip on a white board and no xtal. Seems like one could buy a bd with a 20mhz 168 with an 18.432mhz xtal, and you could run the serial at 115.2 or 230.4 kbits to the host computer. 115.2 is about 83usec per char, so you could send a 3 char report of ascii digits (maybe set the 0x80 bit on the 1st char so you know its the 1st char). You could send 4k reports per sec. Way faster than you could read. You could avg 4 readings and send the avg maybe.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, Jim. I will try point 1). I may have an offset on the signal.

@JC thanks for the AVRCalc idea.

I used Kevin Rosenberg's tool and found that the error for an 8MHz crystal @ 115200 baud is 8.5%.

Does this mean that if I have 11.52 samples/cycle, I will loose about 1 sample/cycle? Even with about 10 samples/cycle I should be able to get the 100 Hz sampled wave right?

Bob, I am using an 8MHz crystal for my board.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
the error for an 8MHz crystal @ 115200 baud is 8.5%.

One often sees a figure of Max of 2% error for baud rates for reliable communications, but I don't have a reference for that handy.

What 8.5% tells you is that you can't do that baud rate with that crystal when communicating with a PC, or other device which will have an accurate baud rate at its end. You need to pick a clock frequency which will give you much tighter accuracy in your baud rate.

There are other threads which discuss tweaking the baud rate to make it work. One can measure the incoming serial signal and calibrate the baud rate against that incoming signal, for example. One could also calibrate against a clock crystal. Much easier, in my mind, to just pick the correct crystal to begin with.

The XMegas have a much tighter tolerance on their internal clock, and may well be able to give one reliable serial comms without an external crystal.

Note that if you had two boards with AVRs with that crystal frequency they could communicate with each other just fine, as they both would be equally off of the true baud rate.

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lose.... not the winner. loose..... not tight

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Quote:

It is always high regardless of whether or not a new conversion has been completed.

... which can well explain
Quote:

I am getting multiple zeroes between the values.

Ok I tried to use interrupt driven ADC, and I am still getting zeroes. Here is my code:

volatile unsigned int ADC_dummy;
volatile unsigned int ADC_buffer_dummy;
volatile unsigned int a;
volatile unsigned int b;
volatile unsigned char flag;


static int UART_putchar(char c, FILE *stream);

static FILE mystdout = FDEV_SETUP_STREAM(UART_putchar, NULL, _FDEV_SETUP_WRITE);

void USART0_init()
{


   UCSR0B |= (1 << RXEN0) | (1 << TXEN0); // Turn on the transmission and reception circuitry 
   UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); //Set to 8 bit no parity, 1 stop bit
   UBRR0L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register
   UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
   stdout = &mystdout; //Required for printf init

}


static int UART_putchar(char c, FILE *stream)
{
    if (c == '\n') UART_putchar('\r', stream);
  
    while ((UCSR0A & (1 << UDRE0)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it

    UDR0 = c;
    
    return 0;
}


char UART_getchar()
  {
   while((UCSR0A & (1 << RXC0)) == 0) {};
   return UDR0;
  }

void ADC_init()

{
   ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADATE); 
   ADMUX = (1 << REFS0) | (1 << MUX2) | (1 << MUX0); // Set ADC reference to AVCC,select channel 5
   //using all 10 bit of ADC; so do not left justify the ADC value
   DIDR0 = (1 << ADC5D); //turn off the digital driver

    ADCSRA |= (1 << ADEN);  // Enable ADC
    ADCSRA |= (1 << ADIE);  // Enable ADC Interrupt
    sei();   // Enable Global Interrupts
    ADCSRA |= (1 << ADSC);  // Start A2D Conversions 
}

ISR(ADC_vect)
{

  						             flag = 1;
						             ADC_dummy = ADC;
						              ADC_buffer_dummy = ADC_dummy * 5;
	 a = ADC_buffer_dummy/1000;
         b = ADC_buffer_dummy#1000;//percent sign replaced
}

int main (void)
    {

       

	   DDRB = 0xFF;
	   PORTD = 0x00;
	   PORTB = 0x01;
           USART0_init();
	   ADC_init(); 
	   timer_init();

            for(;;)
             {	
                 if(flag==1)
	             {
			 printf("#d.#d\n",a,b);
			 flag = 0;
						 
                      }
            }

    }



I am setting a flag in my ADC interrupt routine, update my a and b values and then send them to the UART in the main for loop.

Are the zeroes due to the wrong crystal used?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

no one ever mentioned a sampling theorem like Nyquist-Shannon... Please sample at least 10 times your input signal so at 100 Hz bare min should be 1000... if you want it to look like a sine wave not aliased garbage.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I am setting a flag in my ADC interrupt routine, update my a and b values and then send them to the UART in the main for loop.

The one thing you are not doing in the ISR is triggering the next conversion each time. Try adding a:

ADCSRA |= (1<<ADSC);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bobgardner wrote:
lose.... not the winner. loose..... not tight

DefinItely, they are two sepArate words.

Four legs good, two legs bad, three legs stable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Cliff,
I tried adding that in my ADC ISR but I still get random zeroes.

I tried a few things:

1. I checked my sine wave and there is no offset.
2. When I power off the signal generator, and have it connected to my ADC input, I get all zeroes.
3. When I remove my signal generator connection to my ADC input and connect the ADC input to 5V, I get 5V.
4. When I leave my ADC input floating, I get random (noise) values but no zeroes.

Could this be a signal generator GND problem?

Or do I need an internal/external pull up on my ADC input pin?

I understand that this could still be because of the wrong crystal values and the undersampling, but would I get zeroes due to that? I would expect some garbage similar to the noise I see when I keep my ADC input floating.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

random zeroes

An extremely interesting oxymoron. Wonder what it means?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

:) What I mean by that is I get two non zero values then a zero value and at other times I get one non zero value and then a zero value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you get steady readings at the pc when avr a/d is converting dc from a pot across the 5v?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

What I mean by that is I get two non zero values then a zero value and at other times I get one non zero value and then a zero value.

Capture some output in your terminal and post it here. Maybe include the raw ADC reading as well as the processed 'a' and 'b'?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I discovered that I accidentally had turned on the AM on my signal generator. When I turned it off, I saw that the zeroes in my data were gone but I still don't get a sine wave. I also tried a 1N4001 diode (didn't have a pot) connected from ADC input to GND with a 10K pull up from ADC input to VCC and I got voltage values from 0.515V to about 0.645V so I have quite a bit of variation (about 25%)here.

I have attached the raw data output files. In the files the first value is the raw ADC value and then a comma and then the a and the b values as a.b.

The AC signal has the following:

Type: Sine wave
Amplitude: 539 mVpp
Frequency: 101.84 Hz
DC Offset: 76 mV

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just out of curiosity, I changed the input frequency only to 1Hz. Attached is a waveform I got. I think I will need a positive bias on my ADC pin to prevent the clipping for the negative voltages.How can I prevent the spikes at each zero crossing? Do I need a filter at the input?

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That actually looks awfully like the positive part of a sine wave if you discount the peaks as it transitions the 0 boundary. Consider the following small bit of image editing:

(did you edit that post while I was doing this I wonder? Anyway we agree - that just needs pulling up into the +ve domain at which point the 0 crossing peaks will likely cease to be a problem anyway)

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes I noticed the same thing. Two questions:

1. I am applying a 1Hz sine wave at the ADC input.I am getting 54 samples in one cycle (or one second). I am using 9600 baud with a 8MHz crystal and a prescalar of 128. How can I confirm that I am getting 1 Hz on my PC?

2. How can I remove the spikes at the zero crossings? Will I need a filter?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1) thread search/Google "zero crossing". Time between the zero crossing is the period and 1/period is the frequency.

2) wait and see if you still have the spikes when you've applied a DC offset. I'm no h/w engineer but I think I've read enough threads here to know you are going to need an op-amp for that.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
         o 5v
         |
         \
         / 5k
         \
    10k  |
o-/\/\/\-+----o  0-5v
+-10v    |
         \
         / 10k
         \
         /
         |
         V

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It looks like overflow, are some of the numberes signed and some unsigned ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, Bob.

I will try that cricuit, although as Jim mentioned I may be faced with too much voltage drop due to the 10K series resistor? I will also try an OPAMP based offset adder circuit.

@sparrow2:
All my ints are unsigned but you may be right, I may be getting an overflow because it does seem to coincide with the zero crossings.

How do I verify if the frequency of the signal I get on my PC is 1 Hz?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

npat_avr wrote:
How do I verify if the frequency of the signal I get on my PC is 1 Hz?

Just look at the byte locations that correspond to your zero crossing a/d values. If the a/d values that correspond to zero crossing occur every 0.5 second, then you have 1Hz (a sine wave has two zero crossings per period).

To know the time between zero crossings, you use the byte count, the byte rate, and the number of bytes sent per a/d value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd get it rigged up with a pot so a nice slow turn from 0v to 5v gives a nice smooth linear increase from 0x000 to 0x3ff. Then you can put any old shape signal into it with harmonics less than 4.5khz, and sample it at 9k samples per sec to a buffer, then print it out with a bunch of spaces and a * like a teletype with the ***s scrolling up the screen.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, I got it working with an 18.432 MHz crystal, 115200 baud and 100 Hz.Waveform is attached.

I am getting 6 samples/cycle in my captured waveform. I am trying to figure out how this is possible and how I can calculate the frequency to be 100 Hz.I am not sure if the captured signal on my PC is really 100 Hz. Any ideas? Appreciate your patience.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well the horizontal axis of that graph is your "timebase". It's not clear from the diagram what the units are. But lets say it's 1 second. I see about 9.5 cycles in that so I guess it's 9.5Hz if the timebase really was 1 second. The bottom line is that one cycle looks like:

  +
 + +
+   +
+   +
    +   +
    +   +
     + +
      +

So just measure the time between the zero crossings as noted previously - you know how often the AVR is sending samples.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

make a autocorrelation on the signal.

Edit and find the peak.

Can you have more that 1 freq. at the time ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Should be able to convert 10k samples per sec at 18.4mhz. and should be able to send 11.5k bytes per sec at 115.2kbps. The problem MIGHT be your pc cant receive serial at 115200 bps without missing chars? Did you write the program?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

npat_avr wrote:
I am getting 6 samples/cycle in my captured waveform.
Assuming that each "bar" in your plot is one sample, I agree that it is close to 6. My money is that it is either 6.06 or 5.76. You'll see why in a moment.
Quote:
I am trying to figure out how this is possible
How many bytes are sent for each sample?
Quote:
and how I can calculate the frequency to be 100 Hz.
If you know the number of bytes per sample and the number of bytes per unit of time, it's easy.

Your period is 6 bars. You claim there is one bar for each sample received via serial. Your serial rate is 11,520 [bytes/second]. Divide that by the number of bytes per sample and by 6 samples per period. If you get something close to 100[cycle/sec], then the frequency is 100 Hz.

Are there between 19 and 20 bytes sent for each sample? If so, you're there. 19 bytes sent per sample would yield 5.76 samples per cycle. 20 bytes sent per sample would yield 6.06.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The problem MIGHT be your pc cant receive serial at 115200 bps without missing chars? Did you write the program?

I think you may be right. I wrote the AVR code but I am using hyperterminal to see the output.

Quote:

How many bytes are sent for each sample?

I am sending 2 bytes/sample.

printf("#d,#d",a,b);

So am I losing 17-18 bytes/sample because of a serial comm. issue?
I will check my hyperterminal settings to see if there is something missing/wrong.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looks like you are printing the 10 bit integer as 'decimal', so it can be 1 to 4 ascii chars.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Disclaimer: The HUGE assumption here is that your device is continuously sampling and sending with no delay between bytes or messages.

Can you be sure that there is no delay between messages?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

npat_avr wrote:
I am using hyperterminal to see the output.
Does hyperterminal show the total byte count received? If so, that would be a sanity check. Have your device run for x seconds and see how many total bytes are rx'ed by hyperterminal.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I always test my ad converter setups with a pot. Read a sample, print it out with a %4d\r so it doesnt walk back and forth on the line. With the pot down, should get 0. With the pot up should get 1023. What would I expect at halfway? Does your rig work like this? (I've asked this questiuon twice before already....)

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I fixed it. Actually I was sending "RAW ADC = 499,2.5" which of course was too many bytes. I changed it to just 2.5 and it works like a charm. Now I get about 19 samples/cycle.

My input signal is about 500 mVpp but I am getting only about 140 mVpp and also the signal does not look symmetrical. Could it be due to the resistive divider? Also I checked and the offset measures almost zero (a few mV) on my scope. Like suggested I will try the opamp circuit.

@Bob,
I will also try the potentiometer expt.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is generating the 500mv ptop sine wave? Usually the idea is to get the input signal to use as much of the whole a/d range as possible. That circuit I posted to convert +-10 to 0-5 would work on 2.5v too... that would cvt +-5v to 0-2.5. Then you put the 2.5V on the a/d vref pin, and the range of the a/d converter is 0-2.5v. That should give a much prettier picture, because there will be twice as many vertical steps.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok so now I am using 7.3278MHz (I am limited to using this crystal) and trying to capture 1kHz at 115200 bps.

#define USART_BAUDRATE 115200
#define BAUD_PRESCALE (((7327800/ (USART_BAUDRATE * 16UL))) - 1) //value to load into UBRR

unsigned int a;
unsigned int b;
volatile unsigned char flag;

static int UART_putchar(char c, FILE *stream);

static FILE mystdout = FDEV_SETUP_STREAM(UART_putchar, NULL, _FDEV_SETUP_WRITE);

void USART0_init()
{


   UCSR0B |= (1 << RXEN0) | (1 << TXEN0); // Turn on the transmission and reception circuitry
   UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); //Set to 8 bit no parity, 1 stop bit
   UBRR0L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register
   UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
   stdout = &mystdout; //Required for printf init

}


static int UART_putchar(char c, FILE *stream)
{
    if (c == '\n') UART_putchar('\r', stream);
 
    while ((UCSR0A & (1 << UDRE0)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it

    UDR0 = c;
   
    return 0;
}


char UART_getchar()
  {
   while((UCSR0A & (1 << RXC0)) == 0) {};
   return UDR0;
  }


void ADC_init()

{
   ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADATE); // Set ADC prescaler to 128   ADMUX = (1 << REFS0) | (1 << MUX2) | (1 << MUX0); // Set ADC reference to AVCC,select channel 5
   //using all 10 bit of ADC; so do not left justify the ADC value
   DIDR0 = (1 << ADC5D); //turn off the digital driver
   ADCSRA |= (1 << ADEN);  // Enable ADC
}


ISR(TIMER1_COMPA_vect)
{

PORTD ^= (1 <<PORTD7);
flag = 1;
}

/************MAIN**************/
int main (void)
    {

       
      DDRC = 0x0F;
      DDRD = 0xC0;
      DDRB = 0xFF;
      PORTD = 0x00;
      PORTB = 0x01;
           USART0_init();
      ADC_init();
      timer_init();

          for(;;)
            {
               
       _delay_ms(100);

          
                  ADCSRA |= (1 << ADSC);
        while((ADCSRA & _BV(ADIF)) != 0);
                  ADC_dummy = ADC;
                                         ADC_buffer_dummy =   ADC_dummy * 5;
        a = ADC_buffer_dummy/1000;
                  b = ADC_buffer_dummy(percent)1000;

                 if(flag==1)
            {

               printf("#d.#d\n",a,b); //percent sign replaced due to posting bug
               flag = 0;
                  

                        }
               }
      } 

I think I am sending 7 bytes/sample i.e.two integers,a and b (4 bytes), one dot (1 byte), one newline (1 byte) and one CR (1 byte).

Is my printf adding overhead? How can I get rid of my printf statement? Can I use a char instead of an integer to capture my ADC data?

Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you want to get the data from the AVR to the PC quicker then why not send it in (human non-readable) binary rather than doing the binary to ASCII conversion on the AVR. Literally just send ADCH and ADCL readings each time to the PC - so 2 bytes per reading then do the float conversion and binary to ASCII at the PC end.

(though I believe you are using these to plot a graph so ASCII need never be involved).

If always sending 2 fixed bytes you needn't bother with '\n' delimiters. Just read 2 bytes every time though I guess you could waste a byte and send 0x00 or '\n' or ',' or something?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok I tried sending the raw ADC value and I cannot seem to capture a 1kHz sine wave. I am sending a printf("#d",ADC); statement. I can comfortably capture a 100Hz sine wave. Following are the various combinations I have tried:

Prescalar  Crystal  Baud rate  I/p freq.  Works?
64           7.3278M   230400     100 Hz     Yes
64           7.3278M   230400     1kHz       No
64           9.216M    115200     100Hz      Yes
64           9.216M    115200     1kHz       No

I am using a prescalar of 64 so that I am still within the 200kHz limitation of the ADC. (9.216M/64/13 = 11.076Ksps). Could this be the MAX232? The crystal does not seem to make a difference. I seem to be hitting a wall at 1 kHz (and I want to try and go up to 10kHz)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anybody?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which AVR is this? One approach would be to forget trying to use the (slow) UART while actually sampling but sample to a RAM buffer then, later, at leisure, transmit these samples to the PC. To draw your graph it's not as if you really need more than a few hundred samples - if that.

If continuing with your current plan you may want to profile printf() in the simulator but it alone could be eating hundreds or even thousands of cycles on each invocation. This is why I'd just send the raw binary and forget printf() all together. Just literally:

 UART_sendchar(ADCL);
 UART_sendchar(ADCH);

then, as I suggested previously, do all your processing at the Gigahungry PC end.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the idea, Cliff.

All I need is to transmit 9 bits/sample. (my values are < 512)

I tried itoa to convert the entire 16 bit ADC value to a string and sent it to the hyperterminal, and at 1kHz, 9.216MHz and 115200, I got an aliased sine wave.Again works fine at 100Hz.

I also tried to send ADCL and ADCH separately but all I got was smiley faces and other ASCII characters on my hyperterminal. I am using the standard sendchar function which is defined as follows:

void UART_sendchar(unsigned char c)
{
while ((UCSR0A & (1 << UDRE0)) == 0) {}; 
UDR = c;
}

I also tried converting the unsigned char c to an unsigned int c and still got the same ASCII characters on my hyperterminal.

How can I convert the ASCII characters I get on my hyperterminal into decimals? Is there an easier way or do I have to write a script (say in Bray's terminal program)?

Another question: Does the 200kHz sampling limitation hold before or after the 13 cycles/ conversion has been taken into account?

Are there any other AVR's that do not have this 200 ksps limitation?

Appreciate your patience on this.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
int addat[1000];
//------------------------
void acquire(void){
int n;

  for(n=0; n<1000; n++)}
    addat[n]=readadchan(5);
  }
}

//----------------------
void dump(void){
char i,j;
int *p;

  p=addat;
  for(j=0; j<50; j++){
    for(i=0; i<20; i++){
      printf("pct 3x ",*p++);
    }
    crlf();
  }
  crlf(); 
}

//------------------
while(1){
  acquire();
  dump();
}

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
int addat[1000]; 

2,000 bytes - not all AVRs could cope with a sample buffer so big.

Quote:

I also tried to send ADCL and ADCH separately but all I got was smiley faces and other ASCII characters on my hyperterminal.

Did you expect otherwise? It's BINARY not ASCII. I'm suggesting you collect the binary (because it crosses the comms channel quicker) then later post process it into ASCII (if that's what you want) or feed it into your graphing app as raw binary. In Hyperterminal use the Capture facility to have the "smiley faces" written to a file which is now a .bin file with 2 bytes per sample. Then fopen(,"rb") this file in the program that wants to process the data.

Or go with what Bob showed to sample quickly then send slowly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Bob and Cliff.

I tried to send raw data to my hyperterminal and converted it from ASCII to decimal. I am using 7.3728MHz, 115200 baud and capturing a 10KHz sine wave.

Attached is the file I am getting. It seems like it is working. I am sending ADCL first, then ADCH. It looks like the positive half of the cycle is getting inverted to a negative. the puchar() function accepts unsigned chars.

Is this a problem? I tried to change the function so it accepts signed chars but I got no output on my hyperterminal. I can of course multiply by -1 to get the right wave, but I wanted to understand the reason for this.

Also the raw data converted decimal samples are delimited by a 1 and occasionally a 10 between them. The 10 seems to be very close to a zero crossing.

What does the 1 represent? Could this be my converter?

 1 241 1 242 1 240 1 10 239 1 236 1 237 1 231 1 228 1 223 1 220 1 218 1 216 1 215 1 214 1 215 1 216 1 218 1 222 1 224 1 228 1 230 1 235 1 238 1 239 1 241 1 241 1 241 1 240 1 237 1 236 1 232 1 231 1 225 1 223 1 219 1 217 1 215 1 217 1 214 1 216 1 217 1 221 1 223 1 10 227 1 230 1 234 1 236 1 239 1 241 1 241 1 241 1 240 1 238 1 237 1 233 1 231 1 226 1 224 1 220 1 218 1 216 1 215 1 214 1 215 1 216 1 220 1 221 1 226 1 228 1 232

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

What does the 1 represent?

Post the AVR code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is the code:

#include 
#include 
#include 
#include 

#define USART_BAUDRATE 115200
#define BAUD_PRESCALE (((7372800/ (USART_BAUDRATE * 16UL))) - 1) //value to load into UBRR

//volatile unsigned int a;
//volatile unsigned int b;
volatile unsigned char flag;

//static int UART_putchar(char c, FILE *stream);

//static FILE mystdout = FDEV_SETUP_STREAM(UART_putchar, NULL, _FDEV_SETUP_WRITE);

void USART0_init()
{


   UCSR0B |= (1 << RXEN0) | (1 << TXEN0); // Turn on the transmission and reception circuitry 
   UCSR0C |= (1 << UCSZ01) | (1 << UCSZ00); //Set to 8 bit no parity, 1 stop bit
   UBRR0L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register
   UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
//   stdout = &mystdout; //Required for printf init

}


/*static int UART_putchar(char c, FILE *stream)
{
    if (c == '\n') UART_putchar('\r', stream);
  
    while ((UCSR0A & (1 << UDRE0)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it

    UDR0 = c;
    
    return 0;
}*/


  void USART_putchar(unsigned char c)
   {

     while ((UCSR0A & (1 << UDRE0)) == 0) {}; 
    
     UDR0 = c;

}

/*void USART_putstring(char *s)
{
    while (*s)
        USART_putchar(*s++);
}*/


void ADC_init() 

  {
     ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADATE); 
     ADMUX = (1 << REFS0) | (1 << MUX2) | (1 << MUX0); // Set ADC reference to AVCC,select channel 5
   //using all 10 bit of ADC; so do not left justify the ADC value
     DIDR0 = (1 << ADC5D); //turn off the digital driver
    ADCSRA |= (1 << ADEN);  // Enable ADC
    ADCSRA |= (1 << ADIE);  // Enable ADC Interrupt
    sei();   // Enable Global Interrupts
    ADCSRA |= (1 << ADSC);  // Start A2D Conversions 
  }

void timer_init()
  {

   TCCR1B |= (1 << WGM12);
   TIMSK1  |= (1 << OCIE1A);

   sei();

   OCR1A = 2000; 

   TCCR1B |= (1 << CS12) | (0 << CS11) | (0 << CS10);

  }

ISR(TIMER1_COMPA_vect)
 {

   PORTD ^= (1 <<PORTD7);
						
 }

ISR(ADC_vect)
  {
 						         flag = 1;

  }
int main (void)
    {   
	   DDRC = 0x0F;
	   DDRD = 0xC0; 
           USART0_init();
	   ADC_init(); 
	   timer_init();
    
            for(;;)
              {
			  	  
			  
                 ADCSRA |= (1 << ADSC); 
                 if(flag==1)
		    {
                         //itoa(ADC, string, 2); 
                          //USART_putstring(string);
                          USART_putchar(ADCL);
                          USART_putchar(ADCH);
						                               //printf("#d\n",ADC);

		          flag = 0;
                     }
               }
     }

Could the 1 be ADCH? The ADCL is always < 256 which makes sense for an 8 bit value. I also noticed that all my values are half of what they should be. Do I need to send the 9 bit value as one value instead of two? I guess the other option is to change my reference so that it is less than (1/2)VCC so that I need only ADCL?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm sorry but THAT program did not produce that data:

 1 241 1 242 1 240 1 10 239 1 236 1 237 1 231 1 228 1 223 1 220 1 218 1 216 1 215 1 214 1 215 1 216 1 218 1 222 1 224 1 228 1 230 1 235 1 238 1 239 1 241 1 241 1 241 1 240 1 237 1 236 1 232 1 231 1 225 1 223 1 219 1 217 1 215 1 217 1 214 1 ...

Can we either see the right program or the right data. I have half a suspicion that this data has been "post processed" in some way to represent it in ASCII. Don't do that - as an attachment post the raw .bin file that was written to the disk from HyperTerminal.

Even if there hadn't been some kind of "hidden" binary to ASCII conversion then assuming ADCL then ADCH order the '1's would not be in the L position.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry about that,Cliff. Yes you are right, I did use a ASCII to decimal conversion website to post process the data.Attached is the raw data captured from the hyperterminal. This raw data is with the program I posted below. Also attached is a screen snapshot of the tool I am using to convert the raw data into ASCII characters.

Here is the link to the converter:

[url]
http://cs.carleton.edu/faculty/a...
[/url]

Here is an excerpt of the raw data. I am trying to attach it but the attach is not working.


ññòññïîêèäâÞÝÚØ×Ö×ØÚÜÞâäèêíðñòòñðîìéçãáÝ
ÚÚÙ×××ÙÚÞßãæéíîññòñðïíëçæáÞÜÙØ×××ØÚÜßáåé
ëðïññññðïìêæâàÜÚØÙÖ××ØÛÞàãçêìïðññññîíêæä
âßÛÚ××××ÙØÜàâæèìíðñòññðîêéçãßÝÚÙ××××ÙÜÝá
ãçéíîðñòñðîíèçãáÞÜÙÙ×××ÙÚßßãæéëîïññññïíì
èèâàÝÚØ××××ÚÚßàäæêìïðñòñòïìêååàßÛÚ×Ø×ÖØÛ
ÜßâçèëíðòñòñïîëéåãßÝÚØ×Ö×ØÙÜÝâäèêíððòòñð
îìéäãáÝÚÚÙÖ××ÙÛÝàãæéíïññòñðïìèçåáÝÜÙØØÖ×
ÙÚÜßáåéëíðòòòñïîëéåáßÜÚØ×ÖÖØÙÜÝáåçëìïðñò
ññîíêåäáÝÚÙ××××ÙØÝáãèéìîðññòððíêèäâÞÝØØ×
ÖÖØÚÞßãæéëîïñóòòðíìèæâàÝÛÚÙ×ÖØÚÛßáåçëìïð
òòòñïìëçåáÞÛÚØ××ÙÙÜÝàãæèìîðñòòñïîëêåäßÝÚ
Ø×××ÙÚÝÞâäèêîïñòòññîêéèãáÝÛÙØ××ÙÙÛÞàäæêì
ïññòòñðìëççáßÝÚØ××ØØÛÜàâæèìîðòòòòðîëëåäß
ÜÚØ×××ØÙÜÞáäçëíññòòòñîíêèäàÞÛÙØ×Ø×ÙÚÞàãè
éíîñòòòñðíìèäâßÝÙÚ×××ØÚÛßãåêëîïñòòóðïìéæ
äàÝÜÙØ××ØÙÛàáæçëíïðòòòñïìêçåâÞÛÚØ××ÙÙÜÝà

edit: looks like it worked. File is attached.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, here's my view of that data - I just loaded the ".txt" file into a binary editor. So the sample data you really have there is:

0x1DF, 0x1E0, 0x1E2, 0x1E6, 0x1E8, 0x1EC, 0x1EE, 0x1F2, etc.

To read your data the psuedo code would be:

FILE * fin;
uint8_t lobyte, hibyte;
uint16_t dataword;

fin = fopen("raw_data.txt", "rb");
while (!feof(fin)) {
  lobyte = fgetc(fin);
  hibyte = fgetc(fin);
  dataword = (hibyte<<8) + lobyte;
  // use 'dataword'...
}

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks again, Cliff.

Ok I ran the raw data with the C code and attached is the waveform I got (for one cycle).

Here is some thinking out loud calculations:

I have 28 samples/cycle. I am using 115200 bps. Since I am sending 9 bits/sample that is 1.125 bytes/sample. So (115200/1.125/28) gives me 3.657kHz. The input is 10kHz. I am using 7372800 Hz as my clock and a divide by 64 as the prescalar. So assuming 13 cycles/conversion I get my ADC sampling rate as 8.861ksps which is too low. (for Nyquist)

So if I use /16 as my prescalar, I get 7372800/16 = 460800 ksps and with 13 cycles/conversion, I will get 35.4ksps.

How stringent is the requirement that the sampling rate should be between 50-200 ksps for the ADC? Is that before or after the 13 cycles/conversion factor?

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

How stringent is the requirement that the sampling rate should be between 50-200 ksps for the ADC?

No one really seems to be able to give an accurate answer to that question. It's clear you trade bits of accuracy against conversion speed but exactly what accuracy you can expect for 460kHz is unknown.

But there's something suspicious in your calculations I think. Across the UART channel you are NOT sending either 9 or 10 bits per sample even when using binary. You are sending two bytes, 16 bits to convey 8/9 bits of info with 7 bits wasted. Hence the reason every second byte is only 0x01 or 0x00.

You have two options - drop the accuracy and use ADLAR and take 8 bit samples and send one UART byte per reading. Or you can consider packing the bits. Let's assume 10 bits per reading then (breaking the bits into groups of 8) you could pack and send as:

11111111 11222222 22223333 33333344 44444444

So you pack four 10 bit readings into 5 bytes that are transmitted. This will require some extra packet construction/deconstruction code though to get this benefit.