wufeng2991 发表于 2015-4-27 21:30:15

[救急!]Arduino定时器是否与其他端口冲突

我用pulse sensor测量心率,并且用dht11测量温湿度。pulse sensor的用到端口A0和timer2定时器中断,dht11用端口7.现在的问题是,只要将这两个模块代码组合在一起,就只能读出心率,而温湿度都为0,只要分开测试都是好的,请问是什么原因??
#include "dht11.h"
#include "Wire.h"

dht11 DHT11;

#define DHT11PIN 8
int pulsePin = 0;               // Pulse Sensor purple wire connected to analog pin 0
int blinkPin = 13;                // pin to blink led at each beat
int fadePin = 5;                  // pin to do fancy classy fading blink at each beat
int fadeRate = 0;               // used to fade LED on with PWM on fadePin


// these variables are volatile because they are used during the interrupt service routine!
volatile int BPM;                   // used to hold the pulse rate
volatile int Signal;                // holds the incoming raw data
volatile int IBI = 600;             // holds the time between beats, the Inter-Beat Interval
volatile boolean Pulse = false;   // true when pulse wave is high, false when it's low
volatile boolean QS = false;      // becomes true when Arduoino finds a beat.

void setup()
{
Serial.begin(9600);
   pinMode(blinkPin,OUTPUT);         // pin that will blink to your heartbeat!
pinMode(fadePin,OUTPUT);          // pin that will fade to your heartbeat!
interruptSetup();               // sets up to read Pulse Sensor signal every 2mS

Serial.println("DHT11 TEST PROGRAM ");
}
void loop()
{
Serial.println("\n");

int chk = DHT11.read(DHT11PIN);

Serial.print("Read sensor: ");
switch (chk)
{
    case DHTLIB_OK:
                Serial.println("OK");
                break;
    case DHTLIB_ERROR_CHECKSUM:
                Serial.println("Checksum error");
                break;
    case DHTLIB_ERROR_TIMEOUT:
                Serial.println("Time out error");
                break;
    default:
                Serial.println("Unknown error");
                break;
}
Serial.print("Humidity (%): ");
Serial.println((float)DHT11.humidity, 2);
Serial.print("Temperature (oC): ");
Serial.println((float)DHT11.temperature, 2);
   delay(2000);
//sendDataToProcessing('S', Signal);   // send Processing the raw Pulse Sensor data
if (QS == true){                     // Quantified Self flag is true when arduino finds a heartbeat
      fadeRate = 255;                  // Set 'fadeRate' Variable to 255 to fade LED with pulse
      sendDataToProcessing('B',BPM);   // send heart rate with a 'B' prefix
      sendDataToProcessing('Q',IBI);   // send time between beats with a 'Q' prefix
      QS = false;                      // reset the Quantified Self flag for next time   
   }

ledFadeToBeat();

delay(20);                           //take a break
}
void ledFadeToBeat(){
    fadeRate -= 15;                         //set LED fade value
    fadeRate = constrain(fadeRate,0,255);   //keep LED fade value from going into negative numbers!
    analogWrite(fadePin,fadeRate);          //fade LED
}


void sendDataToProcessing(char symbol, int data ){
    Serial.print(symbol);                // symbol prefix tells Processing what type of data is coming
    Serial.println(data);                // the data to send culminating in a carriage return
}


下面是定时器中断的代码


volatile int rate;                  // used to hold last ten IBI values
volatile unsigned long sampleCounter = 0;          // used to determine pulse timing
volatile unsigned long lastBeatTime = 0;         // used to find the inter beat interval
volatile int P =512;                      // used to find peak in pulse wave
volatile int T = 512;                     // used to find trough in pulse wave
volatile int thresh = 512;                // used to find instant moment of heart beat
volatile int amp = 100;                   // used to hold amplitude of pulse waveform
volatile boolean firstBeat = true;      // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = true;       // used to seed rate array so we startup with reasonable BPM


void interruptSetup(){   
// Initializes Timer2 to throw an interrupt every 2mS.
TCCR2A = 0x02;   // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x06;   // DON'T FORCE COMPARE, 256 PRESCALER
OCR2A = 0X7C;      // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02;   // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A 中断屏蔽寄存器
sei();             // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED      
}


// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.
// Timer 2 makes sure that we take a reading every 2 miliseconds
ISR(TIMER2_COMPA_vect){                         // triggered when Timer2 counts to 124
    cli();                                    // disable interrupts while we do this
    Signal = analogRead(pulsePin);            // read the Pulse Sensor
    sampleCounter += 2;                         // keep track of the time in mS with this variable
    int N = sampleCounter - lastBeatTime;       // monitor the time since the last beat to avoid noise

//find the peak and trough of the pulse wave
    if(Signal < thresh && N > (IBI/5)*3){       // avoid dichrotic noise by waiting 3/5 of last IBI
      if (Signal < T){                        // T is the trough
            T = Signal;                         // keep track of lowest point in pulse wave
         }
       }
      
    if(Signal > thresh && Signal > P){          // thresh condition helps avoid noise
      P = Signal;                           // P is the peak
       }                                        // keep track of highest point in pulse wave
   
//NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){                                 // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){      
    Pulse = true;                               // set the Pulse flag when we think there is a pulse
    digitalWrite(blinkPin,HIGH);                // turn on pin 13 LED
    IBI = sampleCounter - lastBeatTime;         // measure time between beats in mS
    lastBeatTime = sampleCounter;               // keep track of time for next pulse
         
         if(firstBeat){                         // if it's the first time we found a beat, if firstBeat == TRUE
             firstBeat = false;               // clear firstBeat flag
             return;                            // IBI value is unreliable so discard it
            }   
         if(secondBeat){                        // if this is the second beat, if secondBeat == TRUE
            secondBeat = false;               // clear secondBeat flag
               for(int i=0; i<=9; i++){         // seed the running total to get a realisitic BPM at startup
                  rate = IBI;                     
                  }
            }
         
    // keep a running total of the last 10 IBI values
    word runningTotal = 0;                   // clear the runningTotal variable   

    for(int i=0; i<=8; i++){                // shift data in the rate array
          rate = rate;            // and drop the oldest IBI value
          runningTotal += rate;          // add up the 9 oldest IBI values
      }
      
    rate = IBI;                        // add the latest IBI to the rate array
    runningTotal += rate;                // add the latest IBI to runningTotal
    runningTotal /= 10;                     // average the last 10 IBI values
    BPM = 60000/runningTotal;               // how many beats can fit into a minute? that's BPM!
    QS = true;                              // set Quantified Self flag
    // QS FLAG IS NOT CLEARED INSIDE THIS ISR
    }                     
}

if (Signal < thresh && Pulse == true){   // when the values are going down, the beat is over
      digitalWrite(blinkPin,LOW);            // turn off pin 13 LED
      Pulse = false;                         // reset the Pulse flag so we can do it again
      amp = P - T;                           // get amplitude of the pulse wave
      thresh = amp/2 + T;                  // set thresh at 50% of the amplitude
      P = thresh;                            // reset these for next time
      T = thresh;
   }

if (N > 2500){                           // if 2.5 seconds go by without a beat
      thresh = 512;                        // set thresh default
      P = 512;                               // set P default
      T = 512;                               // set T default
      lastBeatTime = sampleCounter;          // bring the lastBeatTime up to date      
      firstBeat = true;                      // set these to avoid noise
      secondBeat = true;                     // when we get the heartbeat back
   }

sei();                                     // enable interrupts when youre done!
}// end isr



zoologist 发表于 2015-4-28 07:50:34

看一下 vcc 是多少?我最近遇到的几个问题都是 arduino pro micro 出来的 vcc 比较低 4.6v导致设备无法工作

tsaiwn 发表于 2015-4-28 10:54:48

因为 DHT 在读取数据时不可以被 interrupt 太久,
你的 ISR( ) 做太多事情,
尤其那个Signal = analogRead(pulsePin);            
就要用掉大约 110 us, (别怀疑, 这句 analogRead( ) 就是要大约 0.11 ms 啦!
加上其它句子铁定会让 DHT 读取错误
比较简单可以先在读取 DHT11 的时候把中断禁止:

cli( ); // 禁止中断
int chk = DHT11.read(DHT11PIN);
sei( );// 允许中断

但是, 这样是否影响到你的 pulse sensor 取样就要测试才知道 !
你测试看看再分享给大家

suoma 发表于 2015-4-28 12:41:25

建议独立供电,DHT113.3V就可以

tsaiwn 发表于 2015-4-28 18:48:04

你看看 DHT11 是如何读取数据的
然后你就会相信为何被 ISR( ) 打断太久就无法读到正确的数据 !//
//    FILE: dht11.cpp
// VERSION: 0.4.1
// PURPOSE: DHT11 Temperature & Humidity Sensor library for Arduino
// LICENSE: GPL v3 (http://www.gnu.org/licenses/gpl.html)
//
// DATASHEET: http://www.micro4you.com/files/sensor/DHT11.pdf
//
// HISTORY:
// George Hadjikyriacou - Original version (??)
// Mod by SimKard - Version 0.2 (24/11/2010)
// Mod by Rob Tillaart - Version 0.3 (28/03/2011)
// + added comments
// + removed all non DHT11 specific code
// + added references
// Mod by Rob Tillaart - Version 0.4 (17/03/2012)
// + added 1.0 support
// Mod by Rob Tillaart - Version 0.4.1 (19/05/2012)
// + added error codes
//

#include "dht11.h"

// Return values:
// DHTLIB_OK
// DHTLIB_ERROR_CHECKSUM
// DHTLIB_ERROR_TIMEOUT
int dht11::read(int pin)
{
        // BUFFER TO RECEIVE
        uint8_t bits;
        uint8_t cnt = 7;
        uint8_t idx = 0;

        // EMPTY BUFFER
        for (int i=0; i< 5; i++) bits = 0;

        // REQUEST SAMPLE
        pinMode(pin, OUTPUT);
        digitalWrite(pin, LOW);
        delay(18);
        digitalWrite(pin, HIGH);
        delayMicroseconds(40);
        pinMode(pin, INPUT);

        // ACKNOWLEDGE or TIMEOUT
        unsigned int loopCnt = 10000;
        while(digitalRead(pin) == LOW)
                if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

        loopCnt = 10000;
        while(digitalRead(pin) == HIGH)
                if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

        // READ OUTPUT - 40 BITS => 5 BYTES or TIMEOUT
        for (int i=0; i < 40; i++)
        {
                loopCnt = 10000;
                while(digitalRead(pin) == LOW)
                        if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

                unsigned long t = micros();

                loopCnt = 10000;
                while(digitalRead(pin) == HIGH)
                        if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

                if ((micros() - t) > 40) bits |= (1 << cnt);
                if (cnt == 0)   // next byte ?
                {
                        cnt = 7;    // restart at MSB
                        idx++;      // next byte!
                }
                else cnt--;
        }

        // WRITE TO RIGHT VARS
      // as bits and bits are allways zero they are omitted in formulas.
        humidity    = bits;
        temperature = bits;

        uint8_t sum = bits + bits;

        if (bits != sum) return DHTLIB_ERROR_CHECKSUM;
        return DHTLIB_OK;
}
//
// END OF FILE

tsaiwn 发表于 2015-4-28 18:57:57

tsaiwn 发表于 2015-4-28 18:48 static/image/common/back.gif
你看看 DHT11 是如何读取数据的
然后你就会相信为何被 ISR( ) 打断太久就无法读到正确的数据 !

如果库的看不太懂
请看看这我加入注释的独立版本:
只要在收取 40 bits 期间被 ISR( ) 打断就错乱了 !//Test the DHT11 -- Modified by [email protected]
int dhPin = 8;// 温溼度讯號接脚连入 Arduino 的 Pin 8
byte dat;   // 存放湿度2byte, 温度 2 byte, checksum 1 byte
byte readData( ) {// 每次读取 8 bits( one byte)
byte data;
for(int i=0; i<8; i++) {
if(digitalRead(dhPin) == LOW) {   // 一开始要 LOW 才表示要传过来
    while(digitalRead(dhPin) == LOW); //等待 50us;
    // 现在已经变成 HIGH 了
    delayMicroseconds(30); //判断高电平持续时间,以判定资料是‘0’还是‘1’;
    if(digitalRead(dhPin) == HIGH)// 持续了 30 us 以上就是 1
      data |= (1<<(7-i)); //高位在前,低位元在后;
    //如果这时已经是 LOW, 表示这 bit 是 0, 不必塞入 data
    //..而且以下的 while 也会立即结束(因为 LOW), 准备接收下一个 bit
    while(digitalRead(dhPin) == HIGH); // 等待下一bit的接收;
    //这时一定已经变成 LOW 了
}// if
}// for(
return data;   // 收完 8 bit = one byte = one char
} // readData(
void start_test() {// 每次要与 DHT11 沟通
digitalWrite(dhPin,LOW); //拉低到 LOW,发表示要开始沟通的信號;
delay(30); //延时要大於 18ms,以便 DHT11 能检测到开始信號;我们用30ms
digitalWrite(dhPin,HIGH);   // 拉高HIGH, 让 DHT11 拉低到 LOW 告诉我们要传送
delayMicroseconds(40);// 给40us等待 DHT11 响应;
pinMode(dhPin,INPUT);// 改为输入 mode 准备 digitalRead( )
while(digitalRead(dhPin) == HIGH);   // 必须等到 LOW
delayMicroseconds(80); //DHT11 发出响应,会拉低 80us;所以至少等80us
while(digitalRead(dhPin) == LOW);// 继续等到变 HIGH
delayMicroseconds(80); //DHT11 会拉高到HIGH 80us 后开始发送资料;
/// 以下连续读入 5 bytes (40 bits), 最后的 byte 是 checksum 校验值
for(int i=0;i < 5;i++) dat = readData( ); //接收温湿度资料,校验位元;
pinMode(dhPin,OUTPUT);// 改为 Output mode, 准备拉高HIGH
digitalWrite(dhPin,HIGH); //发送完一次资料后释放bus,等待下一次开始信號;
}
void setup() {Serial.begin(9600);pinMode(dhPin, OUTPUT); }
void loop() {
start_test( );// 读取湿度温度和检核位到 dat[ ]; 其中dat是checkSum
// 根据datasheet规定, dat 要 == (dat+dat+dat+dat) %256
// 否则表示沟通有错误 !!
Serial.print("Current humdity = ");
Serial.print(dat, DEC); //显示湿度的整数部分;
Serial.print('.');
Serial.print(dat,DEC); //显示湿度的小数位;(其实是 0)
Serial.println(" %");// 注意有空格要用 " %"不可用 ' %'
Serial.print("Current temperature = ");
Serial.print(dat, DEC); //显示温度的整数部分;
Serial.print('.');
Serial.print(dat,DEC); //显示温度的小数位;(其实是 0)
Serial.println(" C");
delay(1985);
}
/// END

wufeng2991 发表于 2015-5-4 15:01:38

tsaiwn 发表于 2015-4-28 10:54 static/image/common/back.gif
因为 DHT 在读取数据时不可以被 interrupt 太久,
你的 ISR( ) 做太多事情,
尤其那个Signal = analogRea ...

我也怀疑就是因为中断打乱了dht11的时序,我的解决方法是没有用中断,直接用delay(2)进行取样,测试能够同时读出数据,但是稳定性不太好。

剑如歌 发表于 2020-3-27 17:47:04

wufeng2991 发表于 2015-5-4 15:01
我也怀疑就是因为中断打乱了dht11的时序,我的解决方法是没有用中断,直接用delay(2)进行取样,测试能 ...

你好楼主,我也遇到了同样的问题,我不太明白怎么用delay解决,您能详细说说么
页: [1]
查看完整版本: [救急!]Arduino定时器是否与其他端口冲突