You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ATmega2560 ADC采样速率与串口输出实际不符问题排查

ATmega2560 ADC采样与USART输出速率不匹配问题

问题背景

使用ATmega2560,配置16MHz时钟、64分频触发ADC单次转换(单次转换需13个时钟周期),理论采样速率约19200次/秒。但实际每半秒仅输出512个样本,输出呈现每512个样本为一组、间隔约半秒的规律。

为简化测试,在ADC中断服务程序中仅发送'\n'到USART0,预期每秒输出约19000行,但实际速率远低于预期。

测试代码

#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){
      }
}

输出现象

连续输出512个换行符,时间戳密集:

.
.
.

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

随后间隔约半秒,再次输出下一组512个换行符:

.
.
.

[17:35:59:063] ␊
[17:35:59:063] ␊
[17:35:59:063] ␊
.
.
.

问题分析与解决方案

核心原因:USART带宽不足+中断阻塞

  1. 波特率限制传输能力
    57600波特率下,每个字符(含起始/停止位)需约173.6微秒传输,每秒最多传输约5760个字符。预期的19200次/秒输出,远超USART的最大负载,这是根本矛盾。

  2. ADC中断被USART发送阻塞
    在ADC中断里原地等待USART发送完成(while(UCSR0A&(1<<UDRE0)==0)),导致后续ADC触发被挂起,直到当前字符发送完毕。USART只有1级发送缓冲区,高采样速率下,系统会频繁陷入等待,采样节奏彻底打乱。

  3. 定时器配置错误,采样速率远超预期
    Timer1配置为CTC模式,ticks=1且分频64,定时器溢出周期为8微秒,触发ADC频率达125kHz。但ADC转换需104微秒,实际采样速率约9615次/秒,进一步加重USART负担。

修复方案

方案一:降低采样速率匹配USART能力

调整Timer1的ticks值,将采样速率降到5000次/秒以内:

// 采样速率5000次/秒时,ticks计算:(16MHz/64)/5000 -1 = 50-1=49
uint16_t ticks=49;

方案二:用USART发送中断+缓冲区,避免阻塞ADC

启用USART发送完成中断,用环形缓冲区缓存要发送的字符,ADC中断只负责写缓冲区,不等待发送:

#define BUFFER_SIZE 256
uint8_t tx_buffer[BUFFER_SIZE];
volatile uint8_t tx_head = 0;
volatile uint8_t tx_tail = 0;

void usart_send_char(uint8_t c) {
    uint8_t next_head = (tx_head + 1) % BUFFER_SIZE;
    while(next_head == tx_tail); // 等待缓冲区空位
    tx_buffer[tx_head] = c;
    tx_head = next_head;
    UCSR0B |= _BV(UDRIE0); // 开启发送中断
}

ISR(USART_UDRE_vect) {
    if(tx_head != tx_tail) {
        UDR0 = tx_buffer[tx_tail];
        tx_tail = (tx_tail + 1) % BUFFER_SIZE;
    } else {
        UCSR0B &= ~_BV(UDRIE0); // 缓冲区空,关闭中断
    }
}

ISR(ADC_vect) {
    counter++;
    usart_send_char('\n'); // 写入缓冲区后立即返回
    set_ADMUX();
    TIFR1 = (1<<OCF1B);
}

// 修改USART初始化,移除不需要的RXCIE0
void usart_init( uint16_t ubrr) {
    UBRR0H = (uint8_t)(ubrr>>8);
    UBRR0L = (uint8_t)ubrr;
    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); 
    UCSR0B = _BV(RXEN0) | _BV(TXEN0);  
}

方案三:提升USART波特率

如果需要更高采样率,可将波特率提至115200(需硬件支持),此时每秒可传输约11520个字符,能承载更高的输出量。


内容的提问来源于stack exchange,提问作者beginner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 06:16:01