C++ 串行监视器显示来自Arduino Mega的意外输入
我使用Arduino Mega控制CS1237 ADC。我向时钟引脚发送一个信号,在每个时钟脉冲后,等待1ms,然后根据我找到的(via)读取响应。这似乎在某种程度上起作用,因为当我对接收到的每个位执行C++ 串行监视器显示来自Arduino Mega的意外输入,c++,arduino,arduino-ide,serial-communication,adc,C++,Arduino,Arduino Ide,Serial Communication,Adc,我使用Arduino Mega控制CS1237 ADC。我向时钟引脚发送一个信号,在每个时钟脉冲后,等待1ms,然后根据我找到的(via)读取响应。这似乎在某种程度上起作用,因为当我对接收到的每个位执行Serial.println()操作时,对于得到的数据字,我会得到一个24位的数据字,它与我得到的24个独立位相匹配。然而,当我使用Serial.println()进行额外的调试时,我也会得到一个不同的数据字。每次都是所有1的20位,而不是各种1和0的24位。我不明白为什么串行通信通道中的这个额外
Serial.println()
操作时,对于得到的数据字,我会得到一个24位的数据字,它与我得到的24个独立位相匹配。然而,当我使用Serial.println()
进行额外的调试时,我也会得到一个不同的数据字。每次都是所有1的20位,而不是各种1和0的24位。我不明白为什么串行通信通道中的这个额外调试输出会改变进入串行监视器的数据字
以下是我的设置和预设置代码:
// 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);
}
…等等。但是,当我添加一个额外的Serial.println(新的\u位)时
在(int i=0;i<24;i++)的循环的结束括号之前,我在Arduino IDE的串行监视器中得到如下输出(显示时时间戳已打开):
如果我Serial.println()代码>或者如果我引入了一个小延迟而不是进行串行打印。在这些情况下,它仍然执行21的输出。我不知道串行通信有什么问题,因为从ADC读取数据似乎正常。如果引入5000us或更大的延迟,则数据字
的内容会发生变化,这似乎会成为延迟长度的函数。也就是说,dataword
的内容对于每个延迟长度都是恒定的(我尝试了5000us、6000us、10000us和20000us)。如果延迟足够长,它将返回到所有1。查看数据表
首先当芯片启动时。。。默认情况下,所有引脚都是输入的。您没有设置时钟pin模式,因此您没有时钟。此外,ADC可能需要300毫秒才能唤醒。这是启动顺序的一部分,当您退出setup()时,芯片应该准备就绪。您还可以在setup()中包括任何ADC寄存器的设置。参见图5和图6中的数据表启动顺序
此外,如果您想尝试更低的时钟速度,请不要让clck
高电平超过100us
根据数据表2.5:
‘当SCLK从低到高并保持高达100μs以上时,CS1237进入功耗小于0.1μA的断电模式。当SCLK返回低时,芯片将恢复正常工作。’
数据表的图表“图7”显示:
等待直到/DRDY为低,等待持续时间t4(为0),因此无等待是可以的,然后循环每个位:
- 把时钟调高
- 至少等待持续时间t6(455 ns)
- 读取输入位
- 把钟调低
- 在下一个时钟之前,时钟必须保持低电平至少持续t5(455 ns)
您可以在时钟较低时读取数据位,但请注意,在数据库图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(")");
}
// ...
}
实际延迟将取决于您的时钟速度。通常,这些都是使用宏实现的
例如,在多行宏中。请注意行末尾的反斜杠。这些应该是行的最后一个字符,并且宏中不应该有任何空行
// 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();
当您不将Serial.println(new_位)放在for()循环的结束括号之前,而是将一个小的延迟放在后面时会发生什么情况?对于您的位,使用clck
信号的延迟微秒(1)
和digitalRead
作为计时。最后3位缺少clck低相位的计时。这正常吗?如预期的那样?@ocrdu更改Serial.println(新的\u位)代码>to无效;就好像什么都没有一样。更改Serial.println(新的\u位)代码>到串行.println(新的\u位)代码>与删除该行相同@datafiddler只要我在读取第一个信号AFAICT的数据之前不发送另一个信号,那么发送clck
信号的速度就不重要了。这1us延迟将我置于数据表中的最大时钟速度内。使其工作的Serial.println()引入了延迟。你很可能需要一个高和低的延迟,而你现在只有一个。你试过了吗?@ocrdu是的,我试过引入延迟。它没有任何效果。我在上一篇评论中犯了一些奇怪的复制/粘贴错误,没有及时注意到需要编辑。但是是的,我确实尝试过更改Serial.println(新的\u位)代码>至延迟微秒(1)代码>就好像我只是删除了Serial.println(新的\u位)代码>并没有被任何东西取代。啊,我阅读数据表的方式不同。这是有道理的,我试过这样做,但即使完全按照您编写的方式运行代码,它仍然会将all 1的输出提供给串行监视器。我的理解正确吗,不是Arduino等待,而是noop让ADC本身等待?我最近确实对setup()做了一些更改。no ops是Arduino上的no operation cycles,如果你有示波器,它们只需执行一个操作周期,您应该将其连接到时钟和数据线,并检查其活动情况。在输入\ U上拉模式下使用管脚时,进行数字写入(xx,高)会激活上拉。该引脚不在INPUT_PULLUP模式下,但我喜欢在引导期间尽可能明确地完成所有设置
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)) {}
}
// 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(")");
}
// ...
}
#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();