连续读取后重置 ADC 控制标志 (ATMega2560)/Arduino

Posted

技术标签:

【中文标题】连续读取后重置 ADC 控制标志 (ATMega2560)/Arduino【英文标题】:Resetting ADC control flags after continuous read (ATMega2560)/Arduino 【发布时间】:2021-12-08 15:48:51 【问题描述】:

我正在使用带有电阻触摸屏的 ATMega2560(使用带有 analogRead 命令的 ADC)——我还使用带有 analogRead(NTC 温度传感器)的其他 ADC 引脚。这一切都很好。

我最近添加了一个功能,该功能要求我以已知的时间间隔获取 ADC 读数,并决定最好使用自动采样 - 我在网上找到了执行此操作的代码,它适用于所需的功能。

我的问题是,在进行连续采样之后,我无法让 ADC analogRead 值返回与连续采样之前相同的值。

我检查了数据表,它显示了 ADCSRA、ADCSRB 和 ADMUX = 0 的初始值——我已经尝试过这个以及 很多其他方法,但没有成功。

我附上了一些与我的程序相同的代码——即analogRead 在连续采样之后返回一个不同的值。

byte samplesADC[100];
int samplesADCCount = 0;
bool ADCEnd = false;

void startADCAcquire()
  int dacChan = 14;
  Serial.println("Start ADC Aquire: ");
  
  cli();//diable interrupts
  
  //set up continuous sampling of analog pin 0
  //clear ADCSRA and ADCSRB registers
  ADCSRA = 0;
  ADCSRB = 0;
  
  ADMUX |= dacChan&0x07;
  if (dacChan >= 8) 
      ADCSRB |= _BV(MUX5);
  else
      ADCSRB &= ~_BV(MUX5);
  ADMUX |= (1 << REFS0); //set reference voltage
  ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only

  ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
  ADCSRA |= (1 << ADATE); //enabble auto trigger
  ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN); //enable ADC
  ADCSRA |= (1 << ADSC); //start ADC measurements
  sei();//enable interrupts


void stopADCAcquire()
  Serial.println("");
  Serial.println("Stop ADC Acquire");
  cli();//diable interrupts
  
  //set up continuous sampling of analog pin 0
  //clear ADCSRA and ADCSRB registers
  ADCSRA = 0;
 ADCSRB = 0;
  ADMUX = 0;
//  ADCSRB &= ~_BV(MUX5);
//  ADMUX &= ~(1 << ADLAR); 
//   ADCSRA &= ~(1 << ADSC);       // disable adc interrupt
  sei();//enable interrupts 


void setup()
  
  Serial.begin(9600);
  Serial.println("");
  Serial.println("START");
  Serial.print("Pin A2 (1): ");
  Serial.println(analogRead(A2));
  startADCAcquire();
  int i;
  while(ADCEnd == false)
    i++;
  
  while ((UCSR0A & _BV (TXC0)) == 0)
     
  Serial.print("Pin A2 (2): ");
  Serial.println(analogRead(A2));
  Serial.println("END");


ISR(ADC_vect) //when new ADC value ready
  samplesADC[samplesADCCount] = ADCH;
  Serial.print(samplesADC[samplesADCCount]);
  Serial.print(", ");
  samplesADCCount++;
  if(samplesADCCount == 100)
   
   stopADCAcquire();
    ADCEnd = true;
  


void loop()

输出:

START
Pin A2 (1): 0
Start ADC Aquire: 
127, 127, 127...... (x100) 
Stop ADC Acquire
Pin A2 (2): 510
END

【问题讨论】:

在interrupt 上阅读 Nick Gammon。 【参考方案1】:

谢谢@hcheung...

所以我不确定您是否将我指向了中断页面,参考了我的中断处理程序周围的代码问题 - 我想我已经通过使用 volatile 变量和从 ISR 处理程序中删除多余的代码/打印语句对这些进行了排序.所以感谢您的链接 - 这是一个有用的提醒。

但是,这并没有解决我的问题,并且输出保持不变!在中断页面上,我注意到一些代码将寄存器复制到缓冲区,然后再重新分配。我决定试试这个,并在程序加载时复制 ADCSRA、ADCSRB 和 ADMUX 寄存器,并在 AD 扫描后重新分配它们。 这也失败了——最后,我在调用开始 AD 扫描之前复制了寄存器(即在调用了模拟读取之后)并且它起作用了。我现在可以在连续阅读后使用analogRead 命令。

我附上了有效的更新代码 - 即模拟读取在处于连续模式后工作。

volatile byte samplesADC[100];
volatile int samplesADCCount = 0;
volatile bool ADCEnd = false;
uint8_t  ADCSRASave;
uint8_t  ADCSRBSave;
uint8_t  ADMUXSave;


void startADCAcquire()
  int dacChan = 14;

  ADCSRASave = ADCSRA;
  ADCSRBSave = ADCSRB;
  ADMUXSave = ADMUX;

  cli();//diable interrupts
  
  //set up continuous sampling of analog pin 0
  //clear ADCSRA and ADCSRB registers
  ADCSRA = 0;
  ADCSRB = 0;
  ADMUX |= dacChan&0x07;
  if (dacChan >= 8) 
     ADCSRB |= _BV(MUX5);
  else
    ADCSRB &= ~_BV(MUX5);
  ADMUX |= (1 << REFS0); //set reference voltage
  ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
  ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
  ADCSRA |= (1 << ADATE); //enabble auto trigger
  ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN); //enable ADC
  ADCSRA |= (1 << ADSC); //start ADC measurements
  sei();//enable interrupts


void stopADCAcquire()
  cli();//diable interrupts
  ADCSRA = ADCSRASave; //0;
  ADCSRB = ADCSRBSave; //0;
  ADMUX = ADMUXSave; //;
  sei();//enable interrupts 


void setup()
  Serial.begin(9600);
  Serial.println("");
  Serial.println("START");
  Serial.print("Pin A2 (1): ");
  Serial.println(analogRead(A14));
  Serial.println("Start ADC Aquire: ");
  while ((UCSR0A & _BV (TXC0)) == 0) 
  startADCAcquire();
   while(ADCEnd == false)  
  stopADCAcquire();
  for(int x = 0; x < 100; x++)
     Serial.print(samplesADC[x]);
    Serial.print(", ");
  
  Serial.println("");
  Serial.println("Stop ADC Acquire");
  Serial.print("Pin A2 (2): ");
  Serial.println(analogRead(A14));
  Serial.println("END");


ISR(ADC_vect) //when new ADC value ready
  samplesADC[samplesADCCount] = ADCH;
// Serial.print("p");
  samplesADCCount++;
  if(samplesADCCount == 100)
       ADCEnd = true;
  


void loop()

输出:

START
Pin A2 (1): 511
Start ADC Aquire: 
127, 127, 127, 127 <<< x 100
Stop ADC Acquire
Pin A2 (2): 511    <<< value now same as first
END

【讨论】:

以上是关于连续读取后重置 ADC 控制标志 (ATMega2560)/Arduino的主要内容,如果未能解决你的问题,请参考以下文章

ESP8266(micropython)读取adc

对STM32 ADC单次转换模式 连续转换模式 扫描模式的理解

STM8S系列基于IAR开发单通道ADC连续采样示例

当我在 STM32 上激活 ADC 中断时,程序停止工作

我能以多快的速度读取二维数组的 ADC?

007_STM32程序移植之_多通道ADC转换