mirror of
https://github.com/ravinrabbid/DonCon2040.git
synced 2024-11-20 03:37:07 +01:00
Add support for external ADC
This commit is contained in:
parent
d82f751701
commit
96c34813dc
@ -27,6 +27,8 @@ target_link_libraries(
|
||||
tinyusb_board
|
||||
pico_stdlib
|
||||
hardware_adc
|
||||
hardware_i2c
|
||||
hardware_spi
|
||||
pico_multicore
|
||||
pio_ws2812
|
||||
pico_ssd1306
|
||||
|
@ -51,7 +51,7 @@ Few things which you probably want to change more regularly can be changed using
|
||||
|
||||
Those settings are persisted to flash memory if you choose 'Save' when exiting the Menu and will survive power cycles.
|
||||
|
||||
Defaults and everything else are compiled statically into the firmware. You can find everything in `include/GlobalConfiguration.h`. This covers default controller emulation mode, i2c pins, addresses and speed, default trigger thresholds, scale and debounce delay, button mapping, LED colors and brightness.
|
||||
Defaults and everything else are compiled statically into the firmware. You can find everything in `include/GlobalConfiguration.h`. This covers default controller emulation mode, i2c pins, external ADC configuration, addresses and speed, default trigger thresholds, scale and debounce delay, button mapping, LED colors and brightness.
|
||||
|
||||
### Debounce Delay / Hold Time
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "peripherals/StatusLed.h"
|
||||
|
||||
#include "hardware/i2c.h"
|
||||
#include "hardware/spi.h"
|
||||
|
||||
namespace Doncon::Config {
|
||||
|
||||
@ -45,8 +46,16 @@ const Peripherals::Drum::Config drum_config = {
|
||||
},
|
||||
230, // Trigger threshold scale level
|
||||
|
||||
50, // ADC sample count
|
||||
18, // Debounce delay in milliseconds
|
||||
true, // Use external ADC
|
||||
// SPI config for external ADC, unused if above is false
|
||||
{
|
||||
3, // MOSI Pin
|
||||
4, // MISO Pin
|
||||
2, // SCLK Pin
|
||||
1, // SCSn Pin
|
||||
spi0, // Block
|
||||
2000000, // Speed
|
||||
},
|
||||
};
|
||||
|
||||
const Peripherals::Buttons::Config button_config = {
|
||||
|
@ -3,7 +3,12 @@
|
||||
|
||||
#include "utils/InputState.h"
|
||||
|
||||
#include "hardware/spi.h"
|
||||
|
||||
#include <mcp3204/Mcp3204.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Doncon::Peripherals {
|
||||
@ -18,18 +23,30 @@ class Drum {
|
||||
uint16_t ka_right;
|
||||
};
|
||||
|
||||
struct {
|
||||
struct AdcInputs {
|
||||
uint8_t don_left;
|
||||
uint8_t ka_left;
|
||||
uint8_t don_right;
|
||||
uint8_t ka_right;
|
||||
} pins;
|
||||
};
|
||||
|
||||
AdcInputs adc_inputs;
|
||||
Thresholds trigger_thresholds;
|
||||
uint8_t trigger_threshold_scale_level;
|
||||
|
||||
uint8_t sample_count;
|
||||
uint16_t debounce_delay_ms;
|
||||
|
||||
bool use_external_adc;
|
||||
|
||||
struct Spi {
|
||||
uint8_t mosi_pin;
|
||||
uint8_t miso_pin;
|
||||
uint8_t sclk_pin;
|
||||
uint8_t scsn_pin;
|
||||
spi_inst_t *block;
|
||||
uint speed_hz;
|
||||
} external_adc_spi_config;
|
||||
};
|
||||
|
||||
private:
|
||||
@ -42,19 +59,40 @@ class Drum {
|
||||
|
||||
class Pad {
|
||||
private:
|
||||
uint8_t pin;
|
||||
uint8_t channel;
|
||||
uint32_t last_change;
|
||||
bool active;
|
||||
|
||||
public:
|
||||
Pad(uint8_t pin);
|
||||
Pad(uint8_t channel);
|
||||
|
||||
uint8_t getPin() const { return pin; };
|
||||
uint8_t getChannel() const { return channel; };
|
||||
bool getState() const { return active; };
|
||||
void setState(bool state, uint16_t debounce_delay);
|
||||
};
|
||||
|
||||
class AdcInterface {
|
||||
public:
|
||||
virtual uint16_t read(uint8_t channel) = 0;
|
||||
};
|
||||
|
||||
class InternalAdc : public AdcInterface {
|
||||
public:
|
||||
InternalAdc(const Config::AdcInputs &adc_inputs);
|
||||
virtual uint16_t read(uint8_t channel) final;
|
||||
};
|
||||
|
||||
class ExternalAdc : public AdcInterface {
|
||||
private:
|
||||
Mcp3204 m_mcp3204;
|
||||
|
||||
public:
|
||||
ExternalAdc(const Config::Spi &spi_config);
|
||||
virtual uint16_t read(uint8_t channel) final;
|
||||
};
|
||||
|
||||
Config m_config;
|
||||
std::unique_ptr<AdcInterface> m_adc;
|
||||
std::map<Id, Pad> m_pads;
|
||||
|
||||
private:
|
||||
|
@ -6,9 +6,10 @@
|
||||
class Mcp3204 {
|
||||
private:
|
||||
spi_inst *m_spi;
|
||||
uint8_t m_cs_pin;
|
||||
|
||||
public:
|
||||
Mcp3204(spi_inst *spi);
|
||||
Mcp3204(spi_inst *spi, uint8_t cs_pin);
|
||||
|
||||
uint16_t read(uint8_t channel);
|
||||
};
|
||||
|
@ -1,12 +1,8 @@
|
||||
#include "Mcp3204.h"
|
||||
|
||||
Mcp3204::Mcp3204(spi_inst *spi) : m_spi(spi) {
|
||||
// Put SPI in 1,1 mode. In this mode date will be clocked out on
|
||||
// a falling edge and latched from the ADC on a rising edge.
|
||||
// Also the CS will be held low in-between bytes as required
|
||||
// by the mcp3204.
|
||||
spi_set_format(m_spi, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
|
||||
}
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
Mcp3204::Mcp3204(spi_inst *spi, uint8_t cs_pin) : m_spi(spi), m_cs_pin(cs_pin) {}
|
||||
|
||||
uint16_t Mcp3204::read(uint8_t channel) {
|
||||
// Byte 1: '00000' to align the ADC's output,
|
||||
@ -18,7 +14,9 @@ uint16_t Mcp3204::read(uint8_t channel) {
|
||||
uint8_t data_out[3] = {0x06, static_cast<uint8_t>(channel << 6), 0x00};
|
||||
uint8_t data_in[3] = {};
|
||||
|
||||
gpio_put(m_cs_pin, false);
|
||||
spi_write_read_blocking(m_spi, data_out, data_in, 3);
|
||||
gpio_put(m_cs_pin, true);
|
||||
|
||||
// The 12 result bits are at the end of the ADC's output.
|
||||
return (static_cast<uint16_t>(data_in[1] & 0x0F) << 8) | data_in[2];
|
||||
|
@ -7,7 +7,49 @@
|
||||
|
||||
namespace Doncon::Peripherals {
|
||||
|
||||
Drum::Pad::Pad(uint8_t pin) : pin(pin), last_change(0), active(false) {}
|
||||
Drum::InternalAdc::InternalAdc(const Drum::Config::AdcInputs &adc_inputs) {
|
||||
adc_gpio_init(adc_inputs.don_left + 26);
|
||||
adc_gpio_init(adc_inputs.don_right + 26);
|
||||
adc_gpio_init(adc_inputs.ka_left + 26);
|
||||
adc_gpio_init(adc_inputs.ka_right + 26);
|
||||
|
||||
adc_init();
|
||||
}
|
||||
|
||||
uint16_t Drum::InternalAdc::read(uint8_t channel) {
|
||||
adc_select_input(channel);
|
||||
return adc_read();
|
||||
}
|
||||
|
||||
Drum::ExternalAdc::ExternalAdc(const Drum::Config::Spi &spi_config) : m_mcp3204(spi_config.block, spi_config.scsn_pin) {
|
||||
// Enable level shifter
|
||||
gpio_init(0);
|
||||
gpio_set_dir(0, GPIO_OUT);
|
||||
gpio_put(0, true);
|
||||
|
||||
gpio_set_function(spi_config.miso_pin, GPIO_FUNC_SPI);
|
||||
gpio_set_function(spi_config.mosi_pin, GPIO_FUNC_SPI);
|
||||
gpio_set_function(spi_config.sclk_pin, GPIO_FUNC_SPI);
|
||||
// gpio_set_function(spi_config.scsn_pin, GPIO_FUNC_SPI);
|
||||
|
||||
spi_init(spi_config.block, spi_config.speed_hz);
|
||||
|
||||
// Theoretically the ADC should work in SPI 1,1 mode.
|
||||
// In this mode date will be clocked out on
|
||||
// a falling edge and latched from the ADC on a rising edge.
|
||||
// Also the CS will be held low in-between bytes as required
|
||||
// by the mcp3204.
|
||||
// However this mode causes glitches during continuous reading,
|
||||
// so we need to use default mode and set CS manually.
|
||||
// spi_set_format(m_spi, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
|
||||
gpio_init(spi_config.scsn_pin);
|
||||
gpio_set_dir(spi_config.scsn_pin, GPIO_OUT);
|
||||
gpio_put(spi_config.scsn_pin, true);
|
||||
}
|
||||
|
||||
uint16_t Drum::ExternalAdc::read(uint8_t channel) { return m_mcp3204.read(channel); }
|
||||
|
||||
Drum::Pad::Pad(uint8_t channel) : channel(channel), last_change(0), active(false) {}
|
||||
|
||||
void Drum::Pad::setState(bool state, uint16_t debounce_delay) {
|
||||
if (active == state) {
|
||||
@ -23,16 +65,16 @@ void Drum::Pad::setState(bool state, uint16_t debounce_delay) {
|
||||
}
|
||||
|
||||
Drum::Drum(const Config &config) : m_config(config) {
|
||||
m_pads.emplace(Id::DON_LEFT, config.pins.don_left);
|
||||
m_pads.emplace(Id::KA_LEFT, config.pins.ka_left);
|
||||
m_pads.emplace(Id::DON_RIGHT, config.pins.don_right);
|
||||
m_pads.emplace(Id::KA_RIGHT, config.pins.ka_right);
|
||||
|
||||
adc_init();
|
||||
|
||||
for (const auto &pad : m_pads) {
|
||||
adc_gpio_init(pad.second.getPin());
|
||||
if (m_config.use_external_adc) {
|
||||
m_adc = std::make_unique<ExternalAdc>(config.external_adc_spi_config);
|
||||
} else {
|
||||
m_adc = std::make_unique<InternalAdc>(config.adc_inputs);
|
||||
}
|
||||
|
||||
m_pads.emplace(Id::DON_LEFT, config.adc_inputs.don_left);
|
||||
m_pads.emplace(Id::KA_LEFT, config.adc_inputs.ka_left);
|
||||
m_pads.emplace(Id::DON_RIGHT, config.adc_inputs.don_right);
|
||||
m_pads.emplace(Id::KA_RIGHT, config.adc_inputs.ka_right);
|
||||
}
|
||||
|
||||
std::map<Drum::Id, uint16_t> Drum::sampleInputs() {
|
||||
@ -40,8 +82,7 @@ std::map<Drum::Id, uint16_t> Drum::sampleInputs() {
|
||||
|
||||
for (uint8_t sample_number = 0; sample_number < m_config.sample_count; ++sample_number) {
|
||||
for (const auto &pad : m_pads) {
|
||||
adc_select_input(pad.second.getPin());
|
||||
values[pad.first] += adc_read();
|
||||
values[pad.first] += m_adc->read(pad.second.getChannel());
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user