DEV Community

codemee
codemee

Posted on

Arduino MAX30100 程式庫讀取資料的小臭蟲

如果你有使用 Arduino MAX30100 程式庫撰寫 Arduino 程式, 可能會遇到一個小問題, 若是沒有快速的讀取資料, 就可能會導致後續不再能夠讀取新的資料。以下以程式庫內的 MAX30100_RawData為例, 原本的 loop() 如下:

void loop()
{
    uint16_t ir, red;

    sensor.update();

    while (sensor.getRawValues(&ir, &red)) {
        Serial.print(ir);
        Serial.print('\t');
        Serial.println(red);
    }
}
Enter fullscreen mode Exit fullscreen mode

因為是不間斷的讀取資料, 所以可以正常運作, 不斷顯示讀到的原始資料:

Initializing MAX30100..SUCCESS
2105    1025
2114    1031
2128    1040
2142    1062
2171    1067
...
Enter fullscreen mode Exit fullscreen mode

但是如果因為進行其他事務導致耽擱了時間, 我們以加入 delay(500) 為例:

void loop()
{
    uint16_t ir, red;

    sensor.update();

    while (sensor.getRawValues(&ir, &red)) {
        Serial.print(ir);
        Serial.print('\t');
        Serial.println(red);
    }
    Serial.println("=======");
    delay(500);
}
Enter fullscreen mode Exit fullscreen mode

你看到的輸出結果就可能會是這樣:

Initializing MAX30100..SUCCESS
35  36
=======
=======
=======
=======
=======
=======
...
Enter fullscreen mode Exit fullscreen mode

你會看到 loop 裡面的 while 後來都沒有發生作用, 只看到印出分隔每次 loop() 的 "======="。這表示已經沒有讀取到新的資料了, 最主要的原因是程式庫從 MAX30100 模組讀取資料的 MAX30100::readFifoData() 是這樣寫的:

void MAX30100::readFifoData()
{
    uint8_t buffer[MAX30100_FIFO_DEPTH * 4];
    uint8_t toRead;

    toRead = (readRegister(MAX30100_REG_FIFO_WRITE_POINTER) - readRegister(MAX30100_REG_FIFO_READ_POINTER)) & (MAX30100_FIFO_DEPTH - 1);

    if (toRead)
    {
        burstRead(MAX30100_REG_FIFO_DATA, buffer, 4 * toRead);

        for (uint8_t i = 0; i < toRead; ++i)
        {
            // Warning: the values are always left-aligned
            readoutsBuffer.push({.ir = (uint16_t)((buffer[i * 4] << 8) | buffer[i * 4 + 1]),
                                 .red = (uint16_t)((buffer[i * 4 + 2] << 8) | buffer[i * 4 + 3])});
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

其中 toRead 是用來計算模組上還有多少筆資料待讀取。MAX30100 使用一個總共 16 筆資料的環狀佇列, 並透過 FIFO_WR_PTR 暫存器紀錄下一筆資料要寫入的位置編號, 編號為 0~15;FIFO_RD_PTR 暫存器則是第一筆待讀取資料的位置編號。當兩個暫存器指向同一個位置時, 就表示佇列滿了, 這時若有新的資料就會被丟棄, 並且在 OVF_COUNTER (Overflow counter) 暫存器記錄被丟棄的資料筆數, 最多為 15。在上面的函式中, 透過這一列程式計算待讀取資料的筆數:

toRead = (readRegister(MAX30100_REG_FIFO_WRITE_POINTER) - readRegister(MAX30100_REG_FIFO_READ_POINTER)) & (MAX30100_FIFO_DEPTH - 1);
Enter fullscreen mode Exit fullscreen mode

也就是寫入位置與待讀取位置的差距, 但是並沒有考慮到佇列已滿, 也就是差距為 0 的狀況。如果讀取資料的速度不夠快, 佇列滿了, 這列程式就會得到待讀取資料為 0 筆的結論, 使得後面真正讀取資料的 if 內的程式根本不會執行。為了解決這個問題, 我們將程式小改一下, 只要 OVF_COUNTER 不為 0, 就表示佇列已滿, 將 toRead 設為 16 即可:

if(readRegister(MAX30100_REG_FIFO_OVERFLOW_COUNTER) == 0)
  toRead = (readRegister(MAX30100_REG_FIFO_WRITE_POINTER) - readRegister(MAX30100_REG_FIFO_READ_POINTER)) & (MAX30100_FIFO_DEPTH-1);
else
  toRead = 16;
Enter fullscreen mode Exit fullscreen mode

修改過後, 同樣的程式就可以得到正確的結果了:

Initializing MAX30100..SUCCESS
=======
8649    2722
8646    2725
8639    2731
8615    2718
8601    2716
8582    2702
8570    2703
8570    2698
8579    2701
8591    2709
8594    2707
8602    2712
8585    2716
8568    2714
8526    2711
8491    2700
=======
8437    2672
8422    2664
8426    2667
8431    2667
.....
Enter fullscreen mode Exit fullscreen mode

Top comments (0)