问题
I'm using an Arduino Mega to control a CS1237 ADC. I'm sending a signal to the clock pin and after each clock pulse, waiting 1ms and then reading the response, according to the datasheet I found (via https://github.com/SiBangkotan/CS1237-ADC-cpp-library). This seems to be working in some capacity, because when I do Serial.println()
for each bit received, and for the resulting dataword, I get a 24 bit dataword that matches the 24 separate bits I got. However, when I take out the extra debugging uses of Serial.println()
that print each bit as they are received, I get a different dataword as well. It's 20 bits of all 1's every single time, instead of 24 bits of various 1's and 0s. I cant figure out why this extra debugging output in the serial communication channel should change the dataword that comes into the serial monitor?
Here's my setup and pre-setup code:
// 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);
}
Here's the relevant code:
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);
}
The output of this in serial monitor is
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
...and so forth. However, when I add an extra Serial.println(new_bit);
just before the closing bracket of the for(int i = 0; i < 24; i++)
loop, I get output like this in the Arduino IDE's serial monitor (shown with timestamps turned on):
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
This doesn't happen if I'm Serial.println()
-ing anything other than the new_bit
on that line, eg if I do Serial.println(dataword);
or if I introduce a small delay instead of doing the serial print. In those cases, it still does the twenty 1's output. I can't figure out what is wrong with the serial communication, since it seems like reading from the ADC is going OK. If I introduce a delay of 5000us or more, then there is a change in the contents of dataword
, which seems to then become a function of the length of the delay. I.e. the content of dataword
is constant for each delay length (5000us, 6000us, 10000us, and 20000us are what I tried). If the delay is long enough, it goes back to being all 1's.
回答1:
From looking at the datasheet...
First when the chip boots... Al pins are input by default. You do not set your clock pin mode, so you have no clock. Also, the ADC could take up to 300 milliseconds to wake up. That is part of your boot sequence, the chip should be ready when you exit setup(). You may also include setting of any ADC registers in setup() as well. See datasheet startup sequence @ figures 5 and 6.
Also, if you want to try lower clock speeds, do not leave clck
high longer than 100us
From datasheet, 2.5: "When SCLK goes from low to high and stays high for more than 100μs, the CS1237 entersPowerDown mode, which consumes less than 0.1μA. When SCLK goes back low, the chip will resume normal operation."
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)) {}
}
The chart "figure 7" of the datasheet says:
Wait until /DRDY is low, wait for duration t4 (which is 0), so no wait is OK, then loop for each bit:
- set clock high
- wait at least for duration t6 (455 ns)
- read input bit.
- set clock low.
- clock must be held low for least duration t5 (455 ns) before next clock.
You could read the data bit while the clock is low, but note how on database fig. 8, the 27th bit becomes invalid as soon as the clock bit goes low. From experience, this hints that you are expected to read while clock is high. Some datasheets are very tricky to read, and some are even wrong. That's how I interpret this one, but I may be wrong, you may want to also try reading while clock is high.
Your input procedure then becomes:
// 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(")");
}
// ...
}
Once you have this running smoothly, you can try and make reading a bit faster by making your own 455ns delay function, using no ops
#define NOOP() __asm__("nop\n\t") // 1 operation cycle delay, for 8-bit ATMega,
// 1 op cycle == 1 clock cycle.
The actual delay will depend on your clock speed. Usually, these are implemented using macros.
Example, in a multi-line macro. Note the backslash at the end of line. These should be the very last character of the line, and there should not be any blank lines in the macro
// 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();
来源:https://stackoverflow.com/questions/64804220/serial-monitor-showing-unexpected-input-from-arduino-mega