c++ - 串行监视器显示来自 Arduino Mega 的意外输入

标签 c++ arduino arduino-ide serial-communication adc

我正在使用 Arduino Mega 来控制 CS1237 ADC。根据 datasheet,我向时钟引脚发送信号,并在每个时钟脉冲后等待 1ms,然后读取响应。我发现(通过https://github.com/SiBangkotan/CS1237-ADC-cpp-library)。这似乎在某种程度上起作用,因为当我这样做时 Serial.println()对于接收到的每一位,以及生成的数据字,我得到一个 24 位数据字,它与我得到的 24 个单独的位相匹配。但是,当我取出 Serial.println() 的额外调试用途时当收到时打印每一位,我也得到一个不同的数据字。每次都是 20 位全 1,而不是 24 位各种 1 和 0。我不明白为什么串行通信 channel 中的额外调试输出应该更改进入串行监视器的数据字?

这是我的设置和预设置代码:

// Using pins 2 and 3 on the Arduino, since 0 and 1 are used to talk to the USB port.
int ndrdy = 2;
int clck = 3;

// the setup routine runs once when you press reset:
void setup() {
    // initialize serial communication at 9600 bits per second:
    Serial.begin(9600);
    while (!Serial) {
        ; // wait for serial port to connect. Needed for native USB port only
    }
    // make the drdy's pin an input and clock an output:
    pinMode(ndrdy, INPUT);
}

相关代码如下:

void loop() {
  // Hacky way of waiting for the signal that !DRDY is ready.
  while(digitalRead(ndrdy) == LOW) {
    // do nothing until pin pulls high.
  }
  while(digitalRead(ndrdy) == HIGH) {
    // keep doing nothing until pin goes low again.
  }
  // now data is ready, we can read

  long dataword = 0;

  for(int i = 0; i < 24; i++) {
    digitalWrite(clck, HIGH);
    delayMicroseconds(1);
    digitalWrite(clck, LOW);
    int new_bit = digitalRead(ndrdy);
    dataword <<= 1;       // shift everything one place to the left
    dataword |= new_bit;  // add the new bit to the newly empty place
  }

  // There's a total of 27 bits but we don't care about the last 3.
  // Write HIGH 3 times to flush it out.
  for (int i = 0; i < 3; i++) {
    digitalWrite(clck, HIGH);
    delayMicroseconds(1);
    digitalWrite(clck, LOW);
  }

  // Send out the data to the USB serial out:
  Serial.println(dataword, BIN);
}

串行监视器中的输出是

13:44:45.685 -> 11111111111111111111
13:44:45.685 -> 11111111111111111111
13:44:45.718 -> 11111111111111111111
13:44:45.751 -> 11111111111111111111
13:44:45.751 -> 11111111111111111111
13:44:45.785 -> 11111111111111111111
13:44:45.818 -> 111111111111111111111
13:44:45.852 -> 11111111111111111111
13:44:45.852 -> 11111111111111111111
13:44:45.885 -> 11111111111111111111
13:44:45.918 -> 111111111111111111111
13:44:45.918 -> 11111111111111111111
13:44:45.951 -> 11111111111111111111

...等等。但是,当我添加额外的Serial.println(new_bit);时就在 for(int i = 0; i < 24; i++) 的右括号之前循环后,我在 Arduino IDE 的串行监视器中得到如下输出(显示时间戳已打开):

14:41:19.992 -> 0
14:41:19.992 -> 1
14:41:19.992 -> 1
14:41:19.992 -> 1
14:41:19.992 -> 1
14:41:19.992 -> 1
14:41:19.992 -> 1
14:41:19.992 -> 1
14:41:19.992 -> 1
14:41:19.992 -> 0
14:41:19.992 -> 1
14:41:20.025 -> 1
14:41:20.025 -> 1
14:41:20.025 -> 1
14:41:20.025 -> 1
14:41:20.025 -> 0
14:41:20.025 -> 0
14:41:20.025 -> 1
14:41:20.025 -> 1
14:41:20.025 -> 1
14:41:20.025 -> 1
14:41:20.025 -> 1
14:41:20.058 -> 0
14:41:20.058 -> 1
14:41:20.058 -> 11111111011111001111101
14:41:20.091 -> 0
14:41:20.091 -> 1
14:41:20.091 -> 1
14:41:20.091 -> 1
14:41:20.091 -> 1
14:41:20.091 -> 1
14:41:20.091 -> 1
14:41:20.091 -> 1
14:41:20.091 -> 1
14:41:20.091 -> 0
14:41:20.125 -> 1
14:41:20.125 -> 1
14:41:20.125 -> 1
14:41:20.125 -> 1
14:41:20.125 -> 1
14:41:20.125 -> 0
14:41:20.125 -> 0
14:41:20.125 -> 1
14:41:20.125 -> 1
14:41:20.125 -> 1
14:41:20.125 -> 1
14:41:20.158 -> 1
14:41:20.158 -> 0
14:41:20.158 -> 1
14:41:20.158 -> 11111111011111001111101

如果我是Serial.println(),就不会发生这种情况- 除 new_bit 之外的任何内容在那一行,例如如果我这样做 Serial.println(dataword);或者如果我引入一个小的延迟而不是进行串行打印。在这些情况下,它仍然执行 20 个 1 的输出。我无法弄清楚串行通信出了什么问题,因为从 ADC 读取数据似乎一切正常。如果我引入5000us或更多的延迟,那么dataword的内容就会发生变化。 ,这似乎成为延迟长度的函数。 IE。 dataword的内容对于每个延迟长度都是恒定的(我尝试过5000us、6000us、10000us和20000us)。如果延迟足够长,则返回全 1。

最佳答案

查看数据表...

首先,当芯片启动时...Al引脚默认输入。您没有设置时钟引脚模式,因此没有时钟。此外,ADC 可能需要长达 300 毫秒的时间才能唤醒。这是启动顺序的一部分,当您退出 setup() 时芯片应该已准备好。您还可以在 setup() 中包含任何 ADC 寄存器的设置。请参阅数据表启动顺序@图 5 和图 6。

此外,如果您想尝试较低的时钟速度,请勿将 clck 保持在高电平超过 100us

来自数据表,2.5: “当SCLK从低电平变为高电平并保持高电平超过100μs时,CS1237进入PowerDown模式,功耗小于0.1μA。当SCLK回到低电平时,芯片将恢复正常工作。”

void setup() 
{
    // initialize serial communication at 9600 bits per second:
    Serial.begin(9600);
    while (!Serial) {}
    
    // make the drdy's pin an input and clock an output:
    // remove pullup on ndrdy
    digitalWrite(ndrdy, LOW);
    pinMode(ndrdy, INPUT);

    digitalWrite(clck, LOW);
    pinMode(clck, OUTPUT);

    // wait for ADC to end its own boot sequence.
    while (digitalRead(ndrdy)) {}
    while (!digitalRead(ndrdy)) {}
}

数据表的图表“图 7”显示:

等到/DRDY为低电平,等待持续时间t4(即0),所以不等待也可以,然后循环每个位:

  • 将时钟设置为高电平
  • 至少等待持续时间 t6(455 ns)
  • 读取输入位。
  • 将时钟设置为低电平。
  • 在下一个时钟之前,时钟必须保持低电平至少持续时间 t5 (455 ns)。

您可以在时钟较低时读取数据位,但请注意数据库图 1 中的操作方式。如图8所示,一旦时钟位变低,第27位就无效。根据经验,这暗示您应该在时钟较高时阅读。有些数据表非常难以阅读,有些甚至是错误的。这就是我对此的解释,但我可能是错的,您可能还想尝试在时钟高时阅读。

您的输入过程将变为:

// reads a 24 bit value from ADC, returns -1 if no data to read 
// note that this function does not wait, so your other processing 
// can still be responsive. 
long readADC() 
{
    // check if data is ready. 
    if (digitalRead(ndrdy))
        return -1;    

    long result = 0;

    // read 24 bits.
    for (int i = 0; i < 24; i++) 
    {
        // get ADC to output a bit.
        digitalWrite(clck, HIGH);
        delayMicroseconds(1);      

        // read it
        int new_bit = digitalRead(ndrdy);

        digitalWrite(clck, LOW);

        delayMicroseconds(1);      // this delay could be shorter, because of 
                                   // operations immediately taking some
                                   // time...  You may want to time it
                                   // using a scope, at least for the fun
                                   // of it.  On a slow 8-bit ATMega, it may not
                                   // be needed, there are move than 16 cycles
                                   // of processing below. plus 2 cycles for
                                   // jumping back to top of loop.
                                   // IS needed for sure at clock speeds above
                                   // 16 MHz.
        result <<= 1;
        result |= new_bit;
    }

    // emit 3 more clock cycles.
    for (int i = 0; i < 3; i++) 
    {
        digitalWrite(clck, HIGH);
        delayMicroseconds(1);      
        digitalWrite(clck, LOW);
        delayMicroseconds(1);
    }

    // note that the 27th clock cycle has set /DRDY high.
    // There is never any need to wait on /DRDY going high.

    return result;  // mask unwanted bits.
}


void loop()
{
    // ...

    long adcValue = readADC();

    if (adcValue >= 0)
    {
       // process ADC input
       Serial.print("ADC reading: ");
       Serial.print(adcValue);
       Serial.print(" (");
       Serial.print(adcValue, BIN);
       Serial.println(")");
    }

    // ...
}

一旦顺利运行,您可以尝试通过创建自己的 455ns 延迟函数(不使用任何操作)来加快读取速度

#define NOOP() __asm__("nop\n\t")  // 1 operation cycle delay, for 8-bit ATMega, 
                                   // 1 op cycle == 1 clock cycle.
                       

实际延迟取决于您的时钟速度。通常,这些是使用宏来实现的。

例如,在多行宏中。请注意行尾的反斜杠。这些应该是该行的最后一个字符,并且宏中不应有任何空行

  // 500 ns delay @ 16MHz clock, on an 8-bit ATMega.
  #define NOOP() __asm__("nop\n\t")
  #define DELAY_500ns()   NOOP(); NOOP(); NOOP(); NOOP(); \ 
                          NOOP(); NOOP(); NOOP(); NOOP(); 

关于c++ - 串行监视器显示来自 Arduino Mega 的意外输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64804220/

相关文章:

c++ - 类型转换指针和三元? : operator. 我是重新发明了轮子吗?

android - 通过蓝牙串口从arduino发送数据

c++ - Arduino 还是树莓派?

c++ - Arduino UDP 错误 WiFiUdp.cpp

使用 sprintf() 函数后 C 字符字符串不可读

c++ - 扭曲双向链表

c++ - Clang 中的嵌套 C 数组结构对齐

python - 如何提高 PySerial 读取速度

通过 LED 矩阵和按钮控制开和关

c++ - 工厂、unique_ptr 和 static_cast