问题描述
我正在使用Arduino Mega来控制CS1237 ADC。根据我发现的datasheet(通过https://github.com/SiBangkotan/CS1237-ADC-cpp-library),我正在向时钟引脚发送信号,并在每个时钟脉冲之后等待1ms,然后读取响应。这似乎在某种程度上起作用,因为当我对接收到的每个位以及所得的数据字执行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,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);
,或者我引入了一个小的延迟而不是做串行打印。在那些情况下,它仍然会进行二十一的输出。我无法弄清楚串行通信出了什么问题,因为从ADC读取似乎正常。如果我引入了5000us或更长时间的延迟,那么dataword
的内容就会发生变化,这似乎成为延迟时间长度的函数。即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)。
您可以在时钟为低电平时读取数据位,但请注意数据库上的数据。如图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();