I want to get meteo sensors values, and turn avr based 16mhz arduino (i have mega, but that seems not matter alot vs uno) into multichannel oscilloscope.
Task details. Desired data rate is 8(uno) to 16(mega) analog channels divide by 4 to 16 reads per second. Extra adc "speed capacity" must be spent on summing up on arduino. Prescale is almost not needed. Each reading is 32bit, lsb is always zeroed. Plus there is 0xffffffff between packets (easiest binary proto, i really hate those ascii low-quality slow shit, but this is personal and offtopic). So now you got the setup and purpose, lest get down to my simple sketch
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
// packet starts after indicator and goes before next such indicator.
const long packetStartIndicator = 0xFFFFFFFFl;
// values are fixed point ints 0..65534 (with LSB always cleared)
// such descision is made because least significant bit almast always == noise;
// also its just easist pck splitting scheme
const long packetValueMask = 0xFFFFFFFEl;
const int numPins = 8;
long sensorValues[numPins+1];
// TODO: configure this from host via serial (inside setup)
int pinNumbers[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8};
//int pinNumbers[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15};
// 1 reading is 10 bit. we can make 2^6==64 readings and sum them up (to top up to full 16 bit, to make kind of digital expoure)
// upd. 1 reading is counted as 16 bit, for host convenience (no matter or real adc)
// sum up like 256 of 16-bit readings to get 24bit value. send as 32bit long variable.
const int adcOrder = 10;
const int readingsOrder = 8;
const int targetOrder = 24;
const int numReadings = 1 << readingsOrder; //64;
void setup() {
// TODO: reset ADCSRA, or it would not work. Also: get clue on free run mode
// sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
// for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles
//ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // 32 prescaler for 38.5 KHz
//ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz
//ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // 8 prescaler for 153.8 KHz
//sbi(ADCSRA,ADPS2); cbi(ADCSRA,ADPS1); sbi(ADCSRA,ADPS0); // set prescale to 32 for 38.5 KHz
//sbi(ADCSRA,ADPS2); cbi(ADCSRA,ADPS1); cbi(ADCSRA,ADPS0); // set prescale to 16 for 76.9 KHz
//cbi(ADCSRA,ADPS2); sbi(ADCSRA,ADPS1); sbi(ADCSRA,ADPS0); // set prescale to 8 for 153.8 KHz
sensorValues[numPins] = packetStartIndicator;
//analogReadResolution(10);
Serial.begin(115200, SERIAL_8N1);
//Serial.begin(250000, SERIAL_8N1);
//Serial.begin(500000, SERIAL_8N1);
}
void zero_values() {
int i;
for(i=0; i<numPins; i++) {
sensorValues[i] = 0;
}
}
void clear_lsb() {
int i;
int shiftOrder = targetOrder - (adcOrder + readingsOrder);
for(i=0; i<numPins; i++) {
sensorValues[i] <<= shiftOrder;
sensorValues[i] &= packetValueMask;
}
}
void serial_write() {
Serial.write( (uint8_t*)sensorValues, sizeof(long)*(numPins+1) );
}
void loop() {
int i, j;
zero_values();
for(i=0; i<numPins; i++) {
for (j=0;j<numReadings; j++) {
sensorValues[i] += analogRead(pinNumbers[i]);
}
}
clear_lsb();
serial_write();
}
I am weak with reading avr datasheets and lacking of knowledge on internals. My question is tied with my desire to switch it to freerun mode. I really dislike analogRead, seems it was written by real juniors, and is real slow. AFAIK free run mode is something when you have interrupt function running when conversion is done. I feel (dont know but really feel) that working with serial inside interrupt is a bad idea. How, in somple words, this can be reworked to event-based ISR with sending over serial when you would encounter inside your interrupt following: currentPin == lastPin && currentReading == numReading
means you have summed up desired bumber of readings, and this is last pin, so its time to post it over serial.