1

I want to output some adc conversions on cutecom using atmega2560, set 16 bit clock with 64 prescale at 16 Mhz to trigger adc conversion (each takes 13 clock cycles) and that should be around 19000 conversions per second (16M/64=250k and 250k/13=19200), but I get these 512 samples each half a second outputted on cutecom.

#include <util/delay.h>
#include <stdio.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>

#include <stdbool.h>

#define BAUD 57600
uint16_t myubbr= (F_CPU/16/BAUD-1);

uint16_t ticks=1;
uint8_t counter=0;

void usart_init( uint16_t ubrr) {
    // Set baud rate
    UBRR0H = (uint8_t)(ubrr>>8);
    UBRR0L = (uint8_t)ubrr;

    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */ 
    UCSR0B = _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0);   /* Enable RX and TX */  
}

void setup_adc(void) {
  // enable adc, auto trigger, interrupt enable, prescale=128
  ADCSRA = (( 1<<ADEN ) | ( 1<<ADATE ) | ( 1<<ADIE ) | ( 1<<ADPS2 ) | ( 1<<ADPS1 ) | ( 1<<ADPS0 )); 
  // Timer/Counter 1 Compare Match B 
  ADCSRB = (( 1<<ADTS2 ) | ( 1<<ADTS0 ));
  // ref=AVcc + adc chan   
  ADMUX = (1 << REFS0) | (1 << ADLAR); //set Voltage reference to Avcc (5v), left adjust converted value, if this is commented we use AREF, last tree are for selecting A7 as input as shown in the table
}

void timer1_init ( uint16_t ticks )
{
  
  TCCR1A  = 0;
  TCCR1B  = 0;
  TCNT1   = 0;
  TIMSK1  = 0;
  
  TCCR1B  = ( 1 << WGM12 ) ;  // Configure for CTC mode 4 OCR1A=TOP
  
  OCR1B   = ticks;            // compare value
  OCR1A   = ticks;            // Set CTC TOP value, must be >= OCR1B
  
  // start timer, give it a clock
  TCCR1B |= ( 1 << CS10 ) | ( 1 << CS11 ); //no prescaler //(( 1 << CS10 ) | ( 1 << CS12 )) ;  // Fcpu/1024, 64us tick @ 16 MHz
  
}


void set_ADMUX(void){
    ADMUX = (1 << REFS0) | (1 << ADLAR);
    }

// ADC complete interrupt service routine
ISR(ADC_vect) {
  counter=counter+1;
  while(UCSR0A&(1<<UDRE0)==0){;}
  UDR0 = '\n';

  set_ADMUX();
  TIFR1     = ( 1<<OCF1B ); // clear Compare Match B Flag
}


//main function
int main(void){
  timer1_init (ticks);
  usart_init(myubbr);
  setup_adc();
  //clear interrupt registers
  cli();
  //enable global interrupts
  sei();
  //start first rilevation
  ADCSRA |= (1 << ADSC);
  while(1){
      }
}

I have simplified the code for testing by sending the character '\n' on usart0, I do that in the ISR of the ADC conversion interrupt, I was expecting 19000 lines per second but I get way lower amount than that.

I get 512 timestamps like that

.
.
.

[17:35:58:542] ␊
[17:35:58:542] ␊
[17:35:58:542] ␊
[17:35:58:542] ␊
[17:35:58:542] ␊
[17:35:58:542] ␊
[17:35:58:542] ␊
.
.
.

and then another 512 timestmps lke that


.
.
.

[17:35:59:063] ␊
[17:35:59:063] ␊
[17:35:59:063] ␊
[17:35:59:063] ␊
[17:35:59:063] ␊
[17:35:59:063] ␊
[17:35:59:063] ␊
.
.
.
beginner
  • 11
  • 4
  • 2
    On 57600 baud serial line with 8-bit character you can't transfer more than 5760 characters per second in best case. How can you expect 19200 lines per second? – dimich Aug 06 '23 at 16:18
  • Also you should write to UDR0 only when UDRE in UCSR0A is set. You can setup interrupt for it or check the flag programmically. – dimich Aug 06 '23 at 16:29
  • You mean I shold use the UDRE0 ISR? – beginner Aug 06 '23 at 19:05
  • You should either write to UDR0 from UDRE0 ISR with interrupt enabled or wait until UDRE bit in UCSR0A becomes 1 with interrupt disabled. – dimich Aug 06 '23 at 19:43
  • 1
    To reduce dependency on UART speed, you could only send one `\n` per 10 ADC samples. – Gerhardh Aug 07 '23 at 08:38
  • Thank you I modified the code to check UDRE0, I will look for your suggestion @Gerhardh, I need a resolution of 10 kHz – beginner Aug 07 '23 at 12:49
  • You can have a resultion of 10kHz on the ADC sample rate but you can obviously not send 10kB data via UART if the baudrate is only sufficient for 5760 bytes. – Gerhardh Aug 07 '23 at 13:08
  • 2
    @beginner Don't use UART for direct sample rate measurements. Count number of measured samples and send counter value e.g. once per second. – dimich Aug 07 '23 at 13:50
  • You can also go the opposite way. You can increase the transmission speed. On my Arduino mega, I use the maximum it can - 2 Mbit/s (for debugging only) – Peter Plesník Aug 07 '23 at 16:01
  • When I get over 100 000 baud rate cutecom begins to surrender and no more send timestamps but some continous hex output with no apparent schema, I may be getting something wrong – beginner Aug 08 '23 at 20:50
  • I don't know cutecom. I use a teraterm. Try use a different terminal program (for example putty). Many graphics terminals are slow for high transfer speeds. – Peter Plesník Aug 09 '23 at 13:34
  • @beginner For high bitrates on serial line put your UART adapter close to MCU, wires must be as short as possible. Or use another type for communication, e.g. implement it with V-USB. – dimich Aug 09 '23 at 17:39
  • I did try teraterm, it shows the correct amount of lines, so cutecom is in fact too slow for the job @PeterPlesník – beginner Aug 11 '23 at 16:30

1 Answers1

4

You get about 512 timestamps per second, each with 15 bytes, which amounts to 7680 bytes per second. The serial port is set to 57600 bauds, ie: 57600 bits per second, which is not even enough for 7680 bytes, counting at least 1 start bit and 1 stop bit. The actual throughput should be a bit less at 384 timestamps per second. There is a chance the remote device fills a buffer with sample output until it is full and then wait for some threshold to occur as the buffer gets transmitted before it starts filling it again, producing bursts of identical samples every 0.4 or 0.5 seconds.

EDIT: since you confirmed that the timestamp is displayed by the receiving program, the problem is not directly the data size needed for the timestamps, yet you mention 10K samples per second: a baud rate of 57600 baud is barely sufficient for 5K bytes per second, so you still cannot transmit data at the rate of sampling synchronously through the serial port.

You should store the measurements in an array and transfer the array contents later, or possibly perform some statistical analysis locally and only transfer the resulting data.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • So we have 512 char per 0.5 seconds wich is 1024 bytes per second or 8192 bit per second wich is too much for 57600 baud rate (7200 bit per second)? – beginner Aug 06 '23 at 18:06
  • @beginner: It looks like the limiting factor is the output to USART0, but I might be wrong. – chqrlie Aug 07 '23 at 08:04
  • Where do you see 15 bytes per line? Do you include the timestamp in your counting? I would assume the timestamp is added on the PC side whenever a line is received. – Gerhardh Aug 07 '23 at 08:37
  • @Gerhardh: The timestamp is 14 bytes, the newline makes 15 (at least... there seems to be a space after the `]`). The OP specifies: *I have simplified the code for testing by outputting the character '\n' on usart0* which leads me be to believe the timestamp is produced by the device and output to the serial line, but the code is not available so it is just a conjecture. – chqrlie Aug 07 '23 at 08:52
  • @beginner no, chqrlie is talking about 512*15 char per 0.5s which is 15,360 char == 153,600 bits per second. (You also forgot that 1 byte takes 10 bits via UART as you need 1 start bit and 1 stop bit). Could you please clarify where the timestamp is created? Do you send it via UART or does the terminal program create it? – Gerhardh Aug 07 '23 at 09:04
  • 1
    The timestamp is added by the receiving program, I only send the '\n' – beginner Aug 07 '23 at 10:21