Arduino/ESP8266使用中断采样
Arduino/ESP8266 Using interrupts to take samples
我正在尝试使用 NodeMCU 板测量电容器的放电时间。请参阅下面的完整草图。这段代码工作正常,但我想通过使用 ESP.getCycleCount()
函数和中断计时来改进它以获得更好的时间尺度。感兴趣的部分是这样的:
startTime = micros();
while (digitalRead(capPos) == HIGH) {
delayMicroseconds (1);
}
endTime = micros();
while
循环我想重写成某种形式的基于中断的函数,监听 capPos 引脚的下降沿,用这样的东西替换上面的函数:
startTime = micros();
attachInterrupt(digitalPinToInterrupt(capPos), dischargeInterrupt, FALLING);
}
void dischargeInterrupt() {
endTime = micros();
detachInterrupt(digitalPinToInterrupt(capPos));
之后继续原始代码。
我遇到的问题是如何获取所需的 100 个样本。在执行 startTime
后设置中断时,此例程将完成并执行 100 次迭代中的下一次。相反,它应该等待:按照原始草图,等待中断到来,然后等待例程的其余部分完成。作为中断的新手,我不知道从哪里开始。
所以必须要做的事情:
- loop()
呼叫 getEC()
getEC()
取 100 个样本:
给盖子充电,设置放电引脚,设置中断以测量放电时间。
中断来了,测量经过的时间。执行负上限循环并完成一轮采样。
此更改的主要目的是使计时更加准确:在引脚降至低电平时立即做出反应,并使用更高的时间分辨率。当前的微秒分辨率可以完成这项工作,但这是一个严重的限制。
这是我完整的工作草图:
// capacitor based TDS measurement
// pin D5 C+ - 330 ohm resistor----------|------------|
// | |
// cap EC probe or
// | resistor (for simulation)
// pin D6 C- ----------------------------| |
// |
// pin A0 EC -----------------------------------------|
#include <Average.h>
int capPos = D5; //C+
int capNeg = D6; //C-
int EC = D7; //EC
float CAP = 47; // capacity in nF
#define calibration 150 // a calibration factor to link time with EC.
void setup() {
Serial.begin(9600);
}
void loop () {
float EC = getEC(); // get the EC as mS/cm.
Serial.println (", EC: " + String(EC) + " mS/cm");
delay(100);
}
float getEC() {
int samples = 100; // number of EC samples to take and average.
unsigned long startTime; // the time stamp (in microseconds) the measurement starts.
unsigned long endTime; // the time stamp (in microseconds) the measurement is finished.
unsigned int dischargeTime; // the time it took for the capacitor to discharge.
Average<unsigned int> discharge(samples); // Take measurements on both the positive and negative cycles.
unsigned int chargeDelay = 500; // The time (in microseconds) given to the cap to fully charge/discharge - about 10x RC is a good value.
int startLevel; // analog level of the pin.
int endLevel;
pinMode(A0, INPUT);
for(int i=0; i<samples; i++) { // take <samples> measurements of the EC.
// Stage 1: fully charge capacitor for positive cycle.
// C+ high, C- low, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, HIGH);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, LOW);
delayMicroseconds(chargeDelay);
// Stage 2: positive side discharge; measure time it takes.
// C+ disconnected, C- low, EC low.
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, LOW);
// Measure time until capPos goes LOW. Can't use pulseIn() here as the pin will be high already.
startTime = micros();
while (digitalRead(capPos) == HIGH) {
delayMicroseconds (1);
}
endTime = micros();
// handle potential overflow of micros() just as we measure, this happens every 70 minutes.
if (endTime < startTime) dischargeTime = 4294967295 - startTime + endTime;
else dischargeTime = endTime - startTime;
discharge.push(dischargeTime);
// Stage 3: fully charge capacitor for negative cycle. C+ low, C- high, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, LOW);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, HIGH);
delayMicroseconds(chargeDelay);
// Stage 4: negative side charge; don't measure as we just want to balance it the directions.
// C+ disconnected, C- low, EC low.
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, HIGH);
delayMicroseconds(dischargeTime);
}
float dischargeAverage = discharge.mean();
Serial.print("Discharge time: ");
Serial.print(dischargeAverage);
// Calculate EC from the discharge time.
return dischargeAverage;
}
所以,我自己得到了这个答案。
对于时间分辨率:您可以使用计算处理器周期的 ESP.getCycleCount()
获得更准确的时间 - 在我的 80 MHz NodeMCU 板上,每个周期 12.5 ns 或每微秒 80 个周期。我应该在第一部分提到这一点。
中断:这是我误会了。现在我通过让主函数在循环中等待直到达到超时(设置为 1 毫秒,正常预期时间在 1-100 微秒范围内)或直到中断函数设置全局变量来解决它。所以现在我正在以 12.5 纳秒的分辨率进行测量!
这个草图中缺少的一件事是对处理时序所花费的程序时间的更正:从 EC 引脚上的值下降到开始计数所花费的时间,以及从接收到中断停止计数。如果这个开销是 100 个周期,那就是 1.25 微秒,这完全在我的测量时间内。
// 基于电容器的 TDS 测量
// pin D5 C+ - 330 ohm resistor----------|------------|
// | |
// cap EC probe or
// | resistor (for simulation)
// pin D6 C- ----------------------------| |
// |
// pin A0 EC -----------------------------------------|
#include <Average.h>
int capPos = D5; //C+
int capNeg = D6; //C-
int EC = D7; //EC
unsigned long startCycle;
unsigned long endCycle;
#define CYCLETIME 12.5 // the time it takes in nanoseconds to complete one CPU cycle (12.5 ns on a 80 MHz processor)
float CAP = 47; // capacity in nF
#define calibration 150 // a calibration factor to link time with EC.
void setup() {
Serial.begin(9600);
}
void loop () {
float EC = getEC(); // get the EC as mS/cm.
Serial.println (", EC: " + String(EC) + " mS/cm");
delay(500);
}
float getEC() {
int samples = 100; // number of EC samples to take and average.
unsigned long startTime; // the time stamp (in microseconds) the measurement starts.
unsigned long endTime; // the time stamp (in microseconds) the measurement is finished.
unsigned int dischargeTime; // the time it took for the capacitor to discharge.
Average<unsigned int> discharge(samples); // The sampling results.
unsigned int chargeDelay = 500; // The time (in microseconds) given to the cap to fully charge/discharge - about 10x RC is a good value.
unsigned int timeout = 1; // discharge timeout in milliseconds - if not triggered within this time, the EC probe is probably not there.
int startLevel; // analog level of the pin.
int endLevel;
pinMode(A0, INPUT);
for(int i=0; i<samples; i++) { // take <samples> measurements of the EC.
// Stage 1: fully charge capacitor for positive cycle.
// C+ high, C- low, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, HIGH);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, LOW);
delayMicroseconds(chargeDelay);
// Stage 2: positive side discharge; measure time it takes.
// C+ disconnected, C- low, EC low.
startCycle = ESP.getCycleCount();
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, LOW);
// Use cycle counts and an interrupt to get a much more precise time measurement, especially for high-EC situations.
endCycle = 0;
startTime = millis();
attachInterrupt(digitalPinToInterrupt(capPos), capDischarged, FALLING);
while (endCycle == 0) {
if (millis() > (startTime + timeout)) break;
}
detachInterrupt(digitalPinToInterrupt(capPos));
if (endCycle == 0) dischargeTime = 0;
else {
// Handle potential overflow of micros() just as we measure, this happens about every 54 seconds
// on a 80-MHz board.
if (endCycle < startCycle) dischargeTime = (4294967295 - startCycle + endCycle) * CYCLETIME;
else dischargeTime = (endCycle - startCycle) * CYCLETIME;
discharge.push(dischargeTime);
}
// Stage 3: fully charge capacitor for negative cycle. C+ low, C- high, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, LOW);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, HIGH);
delayMicroseconds(chargeDelay);
// Stage 4: negative side charge; don't measure as we just want to balance it the directions.
// C+ disconnected, C- high, EC high.
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, HIGH);
delayMicroseconds(dischargeTime/1000);
}
float dischargeAverage = discharge.mean();
Serial.print("Discharge time: ");
Serial.print(dischargeAverage);
// Calculate EC from the discharge time.
return dischargeAverage;
}
// Upon interrupt: register the cycle count of when the cap has discharged.
void capDischarged() {
endCycle = ESP.getCycleCount();
detachInterrupt(digitalPinToInterrupt(capPos));
}
我正在尝试使用 NodeMCU 板测量电容器的放电时间。请参阅下面的完整草图。这段代码工作正常,但我想通过使用 ESP.getCycleCount()
函数和中断计时来改进它以获得更好的时间尺度。感兴趣的部分是这样的:
startTime = micros();
while (digitalRead(capPos) == HIGH) {
delayMicroseconds (1);
}
endTime = micros();
while
循环我想重写成某种形式的基于中断的函数,监听 capPos 引脚的下降沿,用这样的东西替换上面的函数:
startTime = micros();
attachInterrupt(digitalPinToInterrupt(capPos), dischargeInterrupt, FALLING);
}
void dischargeInterrupt() {
endTime = micros();
detachInterrupt(digitalPinToInterrupt(capPos));
之后继续原始代码。
我遇到的问题是如何获取所需的 100 个样本。在执行 startTime
后设置中断时,此例程将完成并执行 100 次迭代中的下一次。相反,它应该等待:按照原始草图,等待中断到来,然后等待例程的其余部分完成。作为中断的新手,我不知道从哪里开始。
所以必须要做的事情:
- loop()
呼叫 getEC()
getEC()
取 100 个样本:给盖子充电,设置放电引脚,设置中断以测量放电时间。
中断来了,测量经过的时间。执行负上限循环并完成一轮采样。
此更改的主要目的是使计时更加准确:在引脚降至低电平时立即做出反应,并使用更高的时间分辨率。当前的微秒分辨率可以完成这项工作,但这是一个严重的限制。
这是我完整的工作草图:
// capacitor based TDS measurement
// pin D5 C+ - 330 ohm resistor----------|------------|
// | |
// cap EC probe or
// | resistor (for simulation)
// pin D6 C- ----------------------------| |
// |
// pin A0 EC -----------------------------------------|
#include <Average.h>
int capPos = D5; //C+
int capNeg = D6; //C-
int EC = D7; //EC
float CAP = 47; // capacity in nF
#define calibration 150 // a calibration factor to link time with EC.
void setup() {
Serial.begin(9600);
}
void loop () {
float EC = getEC(); // get the EC as mS/cm.
Serial.println (", EC: " + String(EC) + " mS/cm");
delay(100);
}
float getEC() {
int samples = 100; // number of EC samples to take and average.
unsigned long startTime; // the time stamp (in microseconds) the measurement starts.
unsigned long endTime; // the time stamp (in microseconds) the measurement is finished.
unsigned int dischargeTime; // the time it took for the capacitor to discharge.
Average<unsigned int> discharge(samples); // Take measurements on both the positive and negative cycles.
unsigned int chargeDelay = 500; // The time (in microseconds) given to the cap to fully charge/discharge - about 10x RC is a good value.
int startLevel; // analog level of the pin.
int endLevel;
pinMode(A0, INPUT);
for(int i=0; i<samples; i++) { // take <samples> measurements of the EC.
// Stage 1: fully charge capacitor for positive cycle.
// C+ high, C- low, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, HIGH);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, LOW);
delayMicroseconds(chargeDelay);
// Stage 2: positive side discharge; measure time it takes.
// C+ disconnected, C- low, EC low.
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, LOW);
// Measure time until capPos goes LOW. Can't use pulseIn() here as the pin will be high already.
startTime = micros();
while (digitalRead(capPos) == HIGH) {
delayMicroseconds (1);
}
endTime = micros();
// handle potential overflow of micros() just as we measure, this happens every 70 minutes.
if (endTime < startTime) dischargeTime = 4294967295 - startTime + endTime;
else dischargeTime = endTime - startTime;
discharge.push(dischargeTime);
// Stage 3: fully charge capacitor for negative cycle. C+ low, C- high, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, LOW);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, HIGH);
delayMicroseconds(chargeDelay);
// Stage 4: negative side charge; don't measure as we just want to balance it the directions.
// C+ disconnected, C- low, EC low.
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, HIGH);
delayMicroseconds(dischargeTime);
}
float dischargeAverage = discharge.mean();
Serial.print("Discharge time: ");
Serial.print(dischargeAverage);
// Calculate EC from the discharge time.
return dischargeAverage;
}
所以,我自己得到了这个答案。
对于时间分辨率:您可以使用计算处理器周期的 ESP.getCycleCount()
获得更准确的时间 - 在我的 80 MHz NodeMCU 板上,每个周期 12.5 ns 或每微秒 80 个周期。我应该在第一部分提到这一点。
中断:这是我误会了。现在我通过让主函数在循环中等待直到达到超时(设置为 1 毫秒,正常预期时间在 1-100 微秒范围内)或直到中断函数设置全局变量来解决它。所以现在我正在以 12.5 纳秒的分辨率进行测量!
这个草图中缺少的一件事是对处理时序所花费的程序时间的更正:从 EC 引脚上的值下降到开始计数所花费的时间,以及从接收到中断停止计数。如果这个开销是 100 个周期,那就是 1.25 微秒,这完全在我的测量时间内。
// 基于电容器的 TDS 测量
// pin D5 C+ - 330 ohm resistor----------|------------|
// | |
// cap EC probe or
// | resistor (for simulation)
// pin D6 C- ----------------------------| |
// |
// pin A0 EC -----------------------------------------|
#include <Average.h>
int capPos = D5; //C+
int capNeg = D6; //C-
int EC = D7; //EC
unsigned long startCycle;
unsigned long endCycle;
#define CYCLETIME 12.5 // the time it takes in nanoseconds to complete one CPU cycle (12.5 ns on a 80 MHz processor)
float CAP = 47; // capacity in nF
#define calibration 150 // a calibration factor to link time with EC.
void setup() {
Serial.begin(9600);
}
void loop () {
float EC = getEC(); // get the EC as mS/cm.
Serial.println (", EC: " + String(EC) + " mS/cm");
delay(500);
}
float getEC() {
int samples = 100; // number of EC samples to take and average.
unsigned long startTime; // the time stamp (in microseconds) the measurement starts.
unsigned long endTime; // the time stamp (in microseconds) the measurement is finished.
unsigned int dischargeTime; // the time it took for the capacitor to discharge.
Average<unsigned int> discharge(samples); // The sampling results.
unsigned int chargeDelay = 500; // The time (in microseconds) given to the cap to fully charge/discharge - about 10x RC is a good value.
unsigned int timeout = 1; // discharge timeout in milliseconds - if not triggered within this time, the EC probe is probably not there.
int startLevel; // analog level of the pin.
int endLevel;
pinMode(A0, INPUT);
for(int i=0; i<samples; i++) { // take <samples> measurements of the EC.
// Stage 1: fully charge capacitor for positive cycle.
// C+ high, C- low, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, HIGH);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, LOW);
delayMicroseconds(chargeDelay);
// Stage 2: positive side discharge; measure time it takes.
// C+ disconnected, C- low, EC low.
startCycle = ESP.getCycleCount();
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, LOW);
// Use cycle counts and an interrupt to get a much more precise time measurement, especially for high-EC situations.
endCycle = 0;
startTime = millis();
attachInterrupt(digitalPinToInterrupt(capPos), capDischarged, FALLING);
while (endCycle == 0) {
if (millis() > (startTime + timeout)) break;
}
detachInterrupt(digitalPinToInterrupt(capPos));
if (endCycle == 0) dischargeTime = 0;
else {
// Handle potential overflow of micros() just as we measure, this happens about every 54 seconds
// on a 80-MHz board.
if (endCycle < startCycle) dischargeTime = (4294967295 - startCycle + endCycle) * CYCLETIME;
else dischargeTime = (endCycle - startCycle) * CYCLETIME;
discharge.push(dischargeTime);
}
// Stage 3: fully charge capacitor for negative cycle. C+ low, C- high, EC disconnected.
pinMode (EC, INPUT);
pinMode (capPos,OUTPUT);
digitalWrite (capPos, LOW);
pinMode (capNeg, OUTPUT);
digitalWrite (capNeg, HIGH);
delayMicroseconds(chargeDelay);
// Stage 4: negative side charge; don't measure as we just want to balance it the directions.
// C+ disconnected, C- high, EC high.
pinMode (capPos,INPUT); //set C+ to input to keep voltage from grounding a discharging thru this output pin
pinMode (EC, OUTPUT);
digitalWrite (EC, HIGH);
delayMicroseconds(dischargeTime/1000);
}
float dischargeAverage = discharge.mean();
Serial.print("Discharge time: ");
Serial.print(dischargeAverage);
// Calculate EC from the discharge time.
return dischargeAverage;
}
// Upon interrupt: register the cycle count of when the cap has discharged.
void capDischarged() {
endCycle = ESP.getCycleCount();
detachInterrupt(digitalPinToInterrupt(capPos));
}