Created
February 5, 2020 21:05
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*** 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