1

To make an oscilloscope in matlab environment, for data acquisition on arduino side, when inbuilt analogRead() is used, it's working fine, but with very low sampling rate, that aliasing is clearly observed. That too, non-uniform sampling. Thus listing out the drawbacks:

  1. Very slow data rate, even if, sent with 115200 baudrate,
  2. Aliasing
  3. Non-uniform sampling

Observed waveform, analogRead()

Thus, to overcome above, ADC in free running mode was used, with 16 as prescaling factor, thus 76.9KHz sampling rate (counting each ADC as 13 clock cycles). Thus, data is uniformly sampled now. Also it is quite fast, because of sampling, and is observed. The only problem now exists is, output has a specific noise. The data being sampled is 50Hz sinusoidal current waveform by current sensor ACS712. It overlays instantaneous current waveform over a bias of 2.5V. Thus, when signal is not present, a constant voltage of 2.5V is observed, thus, 512, because of mapping to 255 range.. 127. But in the case of free running mode, along with 127, 191, 63 only are observed. And it can also be seen that nearly 127 = 63*2, and 191 = 63*3. And when current waveform exists, it's highly distorted, but perceptible that it's not the previous signal of just 127, 191, 63. In the below plot, current is passed since 33.5sec. Observed waveform, free running mode

I am unable to understand this, structured noise. What may be the reason ? I have once read, last two MSB bits get noisy at this sampling rate, but then how would 127, jump to 191. Why is 63 getting added, subtracted at each instant ? I understand the reason for non-uniform sampling now is because of Serial.print(), whose interrupts are obstructing the ADC interrupts, but don't understand the reason behind the noise observed.

Following is the code uploaded in arduino. Serial data is read from matlab by fread() function.

long t1=0,ts1=0,ts2=0;
int aval=0, val=0;

void setup() {

 TIMSK0 = 0x00;           // disable timer (causes anoying interrupts)
 DIDR0 = 0x3F;            // digital inputs disabled
 ADMUX = 0x40;            // measuring on ADC0, right adjust, AREF reference
 ADCSRA = 0xAC;           // AD-converter on, interrupt enabled, prescaler = 16
 ADCSRB = 0x00;           // AD channels MUX on, free running mode
 bitWrite(ADCSRA, 6, 1);  // Start the conversion by setting bit 6 (=ADSC) in ADCSRA
 sei(); 
 Serial.begin(115200); 
 t1=millis(); 
 }

ISR(ADC_vect)
{
 aval = ADCL;    
 aval += ADCH << 8; 
} 

void loop() {
val = map(aval, 0, 1023, 0, 255);
Serial.write(val);
delay(10);
}
  • 1
    I wonder how are the functions like millis() and delay(10) working as you've disabled it's interrupt. – KIIV Apr 3 '17 at 11:59
  • 1
    Also, if you want 8-bit data, have the ADC left-adjust the result, don't enable it's interrupt, and Serial.write(ADCH);. – Edgar Bonet Apr 3 '17 at 12:26
  • 1
    You might want to define aval as volatile. – Gerben Apr 3 '17 at 15:23
  • @KIIV, even though TIMER0 is disabled, millis() was still working fine, but it's not the important problem now. I'll enable it, when I use millis() from now. Thank you, for making me aware of it. – Wupadrasta Santosh Apr 4 '17 at 14:44
  • @Edgar Bonet, I have used interrupt to handle ADCL, ADCH registers properly, instead of facing a situation of reading them while they are being written (for I thought, ADC writes into these registers the instant they are obtained at 13th clock cycle). I lean't now, that it was my misconception. It was given in datasheet, "When ADCL is read, the ADC Data Register is not updated until ADCH is read, when right adjusted". I'll try it now. Thank you. – Wupadrasta Santosh Apr 4 '17 at 14:58
1

It is very unclear what you are trying to achieve here. You are clocking the ADC at its maximum frequency, which has it take one reading every 13 µs, and yet you are delaying for 10 ms after sending one reading through the serial port. So basically your Matlab program is getting one reading out of every 769 you take.

If you want fast data acquisition with a consistent sampling frequency, you basically have two options:

  • clock the ADC as fast as you dare, buffer the data in RAM and, when the buffer is full, send it to the host computer
  • set a high baud rate and take the data as fast as the UART can send it.

The first option allows for faster sampling rates, but only as short bursts, as limited by the available RAM. Given what you have already written I assume you want continuous acquisition, and thus the second option. I also assume you are using an Arduino Uno or similar.

At 115.2 kb/s (actually 117.647 kb/s given the rounding), every bit sent takes 136 CPU cycles, or 8.5 µs. One byte takes 10 bits (start bit + 8 data bits + stop bit), i.e. 85 µs. This means there is no point in clocking your ADC that fast: you can set the prescaler to 64 and each reading will take 52 µs.

What I would do here is configure the ADC to be triggered by Timer 1, and have that timer deliver the trigger signal every 1360 CPU cycles exactly. Then,

ISR(ADC_vect)
{
    Serial.write(ADCH);
}

assuming of course you have set the left-adjust mode (ADLAR bit in ADMUX).

With the ADC and the UART working at the very same frequency, you keep the UART busy without ever filling the output buffer. If your ADC runs faster than the UART, the buffer will fill out and your timings will get nasty. On the other hand, if you are really confident in your timings, you could skip the Serial managing of that buffer and just do UDR0 = ADCH in the ADC ISR.

  • What Serial communication baudrate would 1360 CPU cycles related to, for ADC and Serial to be in sync ? – Wupadrasta Santosh Apr 4 '17 at 15:16
  • @WupadrastaSantosh: Assuming a 16 MHz clock (like in the Uno), if you Serial.begin(115200);, the UART will take exactly 1360 CPU cycles per byte. – Edgar Bonet Apr 4 '17 at 15:26
1

If you use an ISR to retrieve the ADC result, you will need to protect the read of the variable. An interrupt may occur when reading aval in the loop(), as this is a 16-bit value and the read requires several instructions. This may give corrupt values.

void loop() {
  noInterrupts();
    val = aval;
  interrupts();
  val = map(val, 0, 1023, 0, 255);
  Serial.write(val);
  delay(10);
}

Cheers!

  • You're right, but running an ISR every 16 CPU cycles is a bad idea to start with. – Edgar Bonet Apr 3 '17 at 13:25
  • Yes, especially if there is a delay(10) in loop(). – Mikael Patel Apr 3 '17 at 14:23
  • > but running an ISR every 16 CPU cycles is a bad idea to start with. It is a bad idea to put a slow transmission inside of an isr, not to mention issues from re-entrance of serial.print(). – dannyf Apr 4 '17 at 15:53
0

Not sure what "structured noise" you are referring to but some advices.

  1. Check the datasheet to see adc timing requirement. When not enough time is given, acc results are highly correlated serially.

  2. In the acc isr, make sure that atomicity is preserved.

  3. Isr has its overhead so make sure that it is consistent with your acc speed setting.

  4. If you are using only the msb. You can configure the acc to be left alighened, saving you lots of time and headache.

  • What I meant by structured noise is that the output which has to be at 127, is reaching to either 191 (1.5*127) and 63 (0.5*127). Isn't this very exact form of pulses... Like it's pdf has just three values, where as the actual one is 127. – Wupadrasta Santosh Apr 4 '17 at 15:09
  • For serially correlated results, check your reference, supply decoupling, input signal conditioning, and acc clock setting, to name a few. – dannyf Apr 4 '17 at 15:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.