Skip to content

Instantly share code, notes, and snippets.

@UriShX
Created February 5, 2020 21:05
Show Gist options
  • Save UriShX/a0cf2a0e9770fb016faa0da292c08822 to your computer and use it in GitHub Desktop.
Save UriShX/a0cf2a0e9770fb016faa0da292c08822 to your computer and use it in GitHub Desktop.
Multiplex 64 analog values to a single Arduino pin, using MCP23x08 GPIO expander & CD4067 MUX
/*
* fastAnalogRead8bit V0.1
* some code copied from wiring_analog.c
* edited to fit only Uno and Mega (168 & 328p & 1280 & 2560 AVRs)
*/
//
#ifndef FAST_ANALOG_READ_8BIT
#define FAST_ANALOG_READ_8BIT
#include "Arduino.h"
//#include "wiring_private.h"
//#include "pins_arduino.h"
#include <stdio.h>
//#include "wiring_analog.c"
//
//uint8_t analog_reference = DEFAULT;
//
//void analogReference(uint8_t mode)
//{
// // can't actually set the register here because the default setting
// // will connect AVCC and the AREF pin, which would cause a short if
// // there's something connected to AREF.
// analog_reference = mode;
//}
#define _DEFAULT 0
#define _SLOW 1
#define _FAST 2
#define _FASTER 3
#define _FASTEST 4
class fastAnalogRead8bit
{
// int pos; // current servo position
// byte updateInterval; // interval between updates in ms
// unsigned long lastUpdate; // last update of position
private:
byte eightBitPin; // analog pin used for 8 bit conversion
public:
fastAnalogRead8bit(byte pin)
{
eightBitPin = pin;
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if (eightBitPin >= 54) eightBitPin -= 54; // allow for channel or pin numbers
#else
if (eightBitPin >= 14) eightBitPin -= 14; // allow for channel or pin numbers
#endif
#if defined(ADCSRB) && defined(MUX5)
// the MUX5 bit of ADCSRB selects whether we're reading from channels (for Mega)
// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((eightBitPin >> 3) & 0x01) << MUX5);
#endif
}
void begin()
{
ADCSRA = 0; // clear ADCSRA register
ADCSRB = 0; // clear ADCSRB register
// set the analog reference (high two bits of ADMUX) and select the
// channel (low 4 bits). this also sets ADLAR (left-adjust result)
// to 0 (the default).
// #if defined(ADMUX)
// ADMUX = (analog_reference << 6) | (eightBitPin & 0x07);
// #endif
// ADMUX |= (eightBitPin & 0x07); // set A0 analog input pin
// ADMUX |= (1 << REFS0); // set reference voltage
// ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register
// 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 << ADPS1) | (1 << ADPS0); // 128 prescaler for 9.6 kHz (normal arduino)
ADCSRA |= (0 << ADATE); // disable auto trigger
ADCSRA |= (0 << ADIE); // disable interrupts when measurement complete
ADCSRA |= (1 << ADEN); // enable ADC
ADCSRA |= (1 << ADSC); // start ADC measurements - single measurement, for initialization
}
void begin(byte speedDesignator)
{
ADCSRA = 0; // clear ADCSRA register
ADCSRB = 0; // clear ADCSRB register
// ADMUX |= (eightBitPin & 0x07); // set A0 analog input pin
// ADMUX |= (1 << REFS0); // set reference voltage
// ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register
//
// sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
// for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles
switch(speedDesignator)
{
case _DEFAULT:
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 128 prescaler for 9.6 kHz (normal arduino)
break;
case _SLOW:
ADCSRA |= (1 << ADPS2) | (1 << ADPS1); // 64 prescaler for 19.2 KHz
break;
case _FAST:
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // 32 prescaler for 38.5 KHz
break;
case _FASTER:
ADCSRA |= (1 << ADPS2); // 16 prescaler for 76.9 KHz
break;
case _FASTEST:
ADCSRA |= (1 << ADPS1) | (1 << ADPS0); // 8 prescaler for 153.8 KHz
break;
}
ADCSRA |= (0 << ADATE); // disable auto trigger
ADCSRA |= (0 << ADIE); // disable interrupts when measurement complete
ADCSRA |= (1 << ADEN); // enable ADC
ADCSRA |= (1 << ADSC); // start ADC measurements - single measurement, for initialization
}
//
// void Attach(int pin)
// {
// servo.attach(pin);
// }
//
// void Detach()
// {
// servo.detach();
// }
int analogRead8bit()
{
uint8_t sample;
#if defined(ADMUX)
ADMUX = (1 << ADLAR | (eightBitPin & 0x07));//analog_reference << 6) |
// ADMUX |= (1 << ADLAR); // left align ADC value to 8 bits from ADCH register
#endif
// without a delay, we seem to read from the wrong channel
//delay(1);
#if defined(ADCSRA) && defined(ADCL)
ADCSRA |= (1 << ADSC); // start the conversion
//delayMicroseconds(105);
while (_SFR_BYTE(ADCSRA) & _BV(ADSC));//bitRead(ADCSRA, ADSC));// wait until conversion is done
sample = ADCH; // read 8 bit value from ADC
// // start the conversion
// sbi(ADCSRA, ADSC);
//
// // ADSC is cleared when the conversion finishes
// while (bit_is_set(ADCSRA, ADSC));
//
// // for 8 bit
// sample = ADCH;
#else
// we dont have an ADC, return 0
sample = 0;
#endif
// combine the two bytes
return sample;
}
//
// void analogRead8bit()
// {
// byte
// ADCSRA |= (1 << ADSC); // start the conversion
// //delayMicroseconds(105);
// while (_SFR_BYTE(ADCSRA) & _BV(ADSC));//bitRead(ADCSRA, ADSC));// wait until conversion is done
// sample = ADCH; // read 8 bit value from ADC
// }
// void Update(unsigned long currentMillis)
// {
// if((currentMillis - lastUpdate) > updateInterval) // time to update
// {
// lastUpdate = millis();
// pos += increment;
// // servo.write(pos);
// if ((pos >= 180) || (pos <= 0)) // end of sweep
// {
// // reverse direction
// increment = -increment;
// }
// }
// }
};
#endif
/*** Test skaetch for MIDI_MUX_SHIELD
* Use bit mask in MCP23x08 to enable a single channel on a single CD4067, then do analogRead
*
* based on library examples for https://github.com/sumotoy/gpio_expander
* and http://yaab-arduino.blogspot.co.il/2015/02/fast-sampling-from-analog-input.html
* timer function based on https://learn.adafruit.com/multi-tasking-the-arduino-part-2/timers
*
* fastAnalogRead8bit doesn't work ATM, attachced for reference.
*
* Written by Uri Shani, 2018. Part of AB4MC, see: https://hackaday.io/project/109296-arduino-blocks-for-midi-controllers
*/
#include <SPI.h>
#include <mcp23s08.h> // import library
#include <stdio.h>
#include "fastAnalogRead8bit.h"
#define MCP_CSPIN 5
#define MCP_ADRS 0x24
#define ANALOG_INPUT A0
//volatile boolean keyPressed;
volatile byte sample = 0;
byte oldSample = 0;
#define _VALUES 64
byte value[_VALUES];
unsigned long timing[_VALUES];
mcp23s08 mcp(MCP_CSPIN,MCP_ADRS);
//fastAnalogRead8bit read8bit(ANALOG_INPUT);
void setup()
{
Serial.begin(115200);
Serial.println("start");
// read8bit.begin();
analogRead(ANALOG_INPUT);
mcp.begin();
mcp.gpioRegisterWriteByte(mcp.IOCON,0b00001000);//set HAEN on GPIO
mcp.gpioPinMode(OUTPUT);
mcp.gpioPort(0b11110000);
}
void loop ()
{
unsigned long startTime = millis();
for (byte i = 0; i < 4; i++) {
byte muxChip = (0b00001111 ^ (1 << i));// set enable low for one pin
for (byte j = 0; j < 16; j++) {
byte muxPort = (muxChip << 4) | j;// move pin enable to upper bits, set lower bits to counter j
mcp.gpioPort(muxPort);// set gpio port on mcp, to set
//Serial.print(muxPort, BIN);
delayMicroseconds(105);
sample = 0;
oldSample = 0;
// get rid of noise?
for (byte k = 0; k < 3; k++) {
sample = analogRead(ANALOG_INPUT);
// sample = read8bit.analogRead8bit();
// ADCSRA |= (1 << ADSC); // start the conversion
// //delayMicroseconds(105);
// while (_SFR_BYTE(ADCSRA) & _BV(ADSC));//bitRead(ADCSRA, ADSC));// wait until conversion is done
// sample = ADCH; // read 8 bit value from ADC
if (k == 0) {
oldSample = sample;
} else if (k != 0 && sample != oldSample) {//((sample < (oldSample -10)) || (sample > (oldSample +10)))) {
oldSample = 0;
}
}
//Serial.print(" , ");
//Serial.println(oldSample);
value[j * (i + 1)] = oldSample / 2;// store sampled value in 7 bits for MIDI
timing[j * (i + 1)] = millis();
//mcp.gpioPort(0b11110000);
}
//oldSample = 0;
}
//delay(5);
for (byte counter = 1; counter <= _VALUES; counter++) {
Serial.print(counter);
Serial.print(": ");
Serial.print(value[counter]);
Serial.print("; Time to get measurement: ");
timing[counter] -= startTime;
Serial.println(timing[counter]);
}
delay(500);
} // end of loop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment