BaseMachine UI Controllerに分離

Dependencies:   AverageAnalogIn PinDetect RotaryEncoder Sequence SequencerDisplay mbed-rtos mbed st7567LCD BaseMachineComon

Fork of BaseMachine_Sequencer by Ryo Od

main.cpp

Committer:
ryood
Date:
2016-10-28
Revision:
55:ab24d74eb0e6
Parent:
54:38187ac44253

File content as of revision 55:ab24d74eb0e6:

/*
 * main.cpp
 * SpiSequencerSender_test
 *
 * 2016.10.02 UI Controllerに分割
 * 2016.08.20 mbed Rev 121 / mbed-rtos Rev 117で動作確認
 *
 */

#include "mbed.h"
#include "rtos.h"
#include "st7565LCD.h"
#include "PinDetect.h"
#include "RotaryEncoder.h"
#include "AverageAnalogIn.h"

#define UART_TRACE  (0)
#include "BaseMachineCommon.h"
#include "Sequence.h"
#include "ST7565_SequencerDisplay.h"

#define TITLE_STR1  ("BaseMachine UI Controller")
#define TITLE_STR2  ("20161028")

#define SPI2_RATE   (1000000)
#define POT_RESOLUTION      (7)     // bit
#define AIN_AVERAGE         (16)     // AnalogInを移動平均する要素数
#define POLLING_POTS_WAIT   (20)    // POT読み取りThreadのWait値(ミリ秒)

const int bpmMax = 240;
const int bpmMin = 60;
const int octaveMax = 2;
const int octaveMin = -2;
const int waveShapeMax = 1;
const int UImodeMax = 2;

// Devices
//

//RESET Line
//DigitalOut ResetLine();

//SPI (PinName mosi, PinName miso, PinName sclk, PinName ssel=NC)
SPI SpiMaster(PA_7, PA_6, PA_5);
DigitalOut SpiMasterCs(PB_6);
InterruptIn StepChangeInterrupt(PC_7);
DigitalOut SlaveReset(PB_9);

//ST7565(PinName mosi, PinName sclk, PinName cs, PinName rst, PinName a0);
ST7565 gLCD(PB_15, PB_13, PB_12, PB_2, PB_1);

AverageAnalogIn AinPulseWidth(PC_2, AIN_AVERAGE);
AverageAnalogIn AinCutOff(PB_0, AIN_AVERAGE);
AverageAnalogIn AinResonance(PC_1, AIN_AVERAGE);
AverageAnalogIn AinLevel(PC_0, AIN_AVERAGE);
AverageAnalogIn AinDuration(PA_4, AIN_AVERAGE);
AverageAnalogIn AinDecay(PA_1, AIN_AVERAGE);
AverageAnalogIn AinSustain(PA_0, AIN_AVERAGE);
AverageAnalogIn AinAccentLevel(PC_3, AIN_AVERAGE);

RotaryEncoder RotEncStep(PA_11, PA_12, 0, SEQUENCE_N - 1, 0);
RotaryEncoder RotEncPitch(PB_5, PB_4, 0, Sequence::getMaxPitch() - 1, 0);
RotaryEncoder RotEncBpm(PC_12, PC_10, bpmMin, bpmMax, 120);

PinDetect PinWaveShape(PD_2, PullUp);
PinDetect PinUIMode(PC_11, PullUp);
PinDetect PinOctaveUp(PB_3, PullUp);
PinDetect PinOctaveDown(PA_10, PullUp);
PinDetect PinNoteOnOff(PC_5, PullUp);
PinDetect PinTie(PC_6, PullUp);
PinDetect PinAccent(PC_8, PullUp);
PinDetect PinRunStop(PC_9, PullUp);

// Grobal Variables
//
Sequence sequences[SEQUENCE_N];
ST7565_SequencerDisplay sequencerDisplay(&gLCD, sequences, SEQUENCE_N);

struct OscillatorParam {
    uint8_t waveShape;
    uint8_t pulseWidth;
} oscillatorParam;

struct FilterParam {
    uint8_t cutoff;
    uint8_t resonance;
} filterParam;

struct EnvelopeParam {
    uint8_t level;
    uint8_t length;
    uint8_t duration;
    uint8_t decay;
    uint8_t sustain;
} envelopeParam;

uint8_t bpm = 120;
uint8_t accentLevel = 127;
   
int currentStep = 0;
int playingStep = 0;
bool isRunning = false;
bool isDirty = true;
int UImode = 0;

volatile bool isStepChanged = false;
volatile uint8_t pinFlag = 0x00;

enum PinBit {
    bWaveShape  = 0x01,
    bUIMode     = 0x02,
    bOctaveUp   = 0x04,
    bOctaveDown = 0x08,
    bNoteOnOff  = 0x10,
    bTie        = 0x20,
    bAccent     = 0x40,
    bRunStop    = 0x80
};

//------------------------------------------------------------------------
// InterruptIn ISR
//------------------------------------------------------------------------
void updateStep()
{
    isStepChanged = true;
}

//------------------------------------------------------------------------
// PinDetect ISR
//------------------------------------------------------------------------
void swWaveShapePressed()
{
    pinFlag |= bWaveShape;
}

void swUIModePressed()
{
    pinFlag |= bUIMode;
}

void swOctaveUpPressed()
{
    pinFlag |= bOctaveUp;
}

void swOctaveDownPressed()
{
    pinFlag |= bOctaveDown;
}

void swNoteOnOffPressed()
{
    pinFlag |= bNoteOnOff;
}

void swTiePressed()
{
    pinFlag |= bTie;
}

void swAccentPressed()
{
    pinFlag |= bAccent;
}

void swRunStopPressed()
{
    pinFlag |= bRunStop;
}

//------------------------------------------------------------------------
// Functions
//------------------------------------------------------------------------
uint8_t getNoteData(uint8_t step)
{
    uint8_t noteData = 0;
    
    noteData |= (step << 4);
    noteData |= (sequences[step].isAccent() ? 4 : 0);
    noteData |= (sequences[step].isTie()    ? 2 : 0);
    noteData |= (sequences[step].isNoteOn() ? 1 : 0);
    
    return noteData;
}

uint16_t SpiSendParams(uint8_t cmd, uint8_t data)
{
    SpiMasterCs = 0;
    SpiMaster.write(cmd);
    uint8_t rv = SpiMaster.write(data);
    SpiMasterCs = 1;
    //Thread::wait(1);

    return rv;
}

uint16_t SpiSendAllParams()
{
    SpiSendParams(CMD_BPM, bpm);
    SpiSendParams(CMD_ACCENT_LEVEL, accentLevel);
    SpiSendParams(CMD_WAVE_SHAPE , oscillatorParam.waveShape);
    SpiSendParams(CMD_PULSE_WIDTH, oscillatorParam.pulseWidth);
    SpiSendParams(CMD_CUTOFF, filterParam.cutoff);
    SpiSendParams(CMD_RESONANCE, filterParam.resonance);
    SpiSendParams(CMD_LEVEL, envelopeParam.level);
    SpiSendParams(CMD_DURATION, envelopeParam.duration);
    SpiSendParams(CMD_DECAY, envelopeParam.decay);
    SpiSendParams(CMD_SUSTAIN, envelopeParam.sustain);

    for (int i = 0; i < SEQUENCE_N; i++) {
        SpiSendParams(CMD_NOTE, getNoteData(i));
        SpiSendParams(CMD_PITCH, sequences[i].getPitch());
    }

    uint16_t retVal = SpiSendParams(CMD_RCV_PLAYING_STEP, 0xaa);   // Send dummy data
    
    return retVal;
}

void pollingRotEncs()
{
    int _bpm = RotEncBpm.getVal();
    if (_bpm != bpm) {
        bpm = _bpm;
        sequencerDisplay.setBpm(_bpm);
        SpiSendParams(CMD_BPM, _bpm);
        isDirty = true;
    }
    
    int _step = RotEncStep.getVal();
    if (_step != currentStep) {
        currentStep = _step;
        // syncronize sequence value & Rotary Encoder's value
        RotEncPitch.setVal(sequences[currentStep].getPitch());
        SpiSendParams(CMD_NOTE, getNoteData(_step));
        SpiSendParams(CMD_PITCH, sequences[_step].getPitch());
        isDirty = true;
    }
    
    int _pitch = RotEncPitch.getVal();
    if (_pitch != sequences[currentStep].getPitch()) {
        sequences[currentStep].setPitch(_pitch);
        SpiSendParams(CMD_NOTE, getNoteData(currentStep));
        SpiSendParams(CMD_PITCH, _pitch);
        isDirty = true;
    }
}

void pollingPins()
{
    if (pinFlag & bWaveShape) {
        #if (UART_TRACE)
        printf("PinWaveShape Pushed\r\n");
        #endif
        uint8_t _waveShape = oscillatorParam.waveShape;
        _waveShape++;
        if (_waveShape > waveShapeMax) {
            _waveShape = 0;
        }
        oscillatorParam.waveShape = _waveShape;
        sequencerDisplay.setWaveShape(_waveShape);
        pinFlag &= ~bWaveShape;
        SpiSendParams(CMD_WAVE_SHAPE, _waveShape);
        isDirty = true;
    }
    
    if (pinFlag & bUIMode) {
        #if (UART_TRACE)
        printf("PinUIMode Pushed\r\n");
        #endif
        UImode++;
        if (UImode > UImodeMax) {
            UImode = 0;
        }
        pinFlag &= ~bUIMode;
        isDirty = true;
    }
    
    if (pinFlag & bOctaveUp) {
        #if (UART_TRACE)
        printf("PinOctaveUp Pushed\r\n");
        #endif
        if (sequencerDisplay.getOctave() < octaveMax) {
            sequencerDisplay.setOctave(sequencerDisplay.getOctave() + 1);
            isDirty = true;
        }
        pinFlag &= ~bOctaveUp;
    }

    if (pinFlag & bOctaveDown) {
        #if (UART_TRACE)
        printf("PinOctaveDown Pushed\r\n");
        #endif
        if (sequencerDisplay.getOctave () > octaveMin) {        
            sequencerDisplay.setOctave(sequencerDisplay.getOctave() - 1);
            isDirty = true;
        }
        pinFlag &= ~bOctaveDown;
    }
    
    if (pinFlag & bNoteOnOff) {
        #if (UART_TRACE)
        printf("PinNoteOnOff Pushed\r\n");
        #endif
        sequences[currentStep].setNoteOn(!sequences[currentStep].isNoteOn());
        pinFlag &= ~bNoteOnOff;
        SpiSendParams(CMD_NOTE, getNoteData(currentStep));
        isDirty = true;
    }
    
    if (pinFlag & bTie) {
        #if (UART_TRACE)
        printf("PinTie Pushed\r\n");
        #endif
        sequences[currentStep].setTie(!sequences[currentStep].isTie());
        pinFlag &= ~bTie;
        SpiSendParams(CMD_NOTE, getNoteData(currentStep));
        isDirty = true;
    }
    
    if (pinFlag & bAccent) {
        #if (UART_TRACE)
        printf("PinAccent Pushed\r\n");
        #endif
        sequences[currentStep].setAccent(!sequences[currentStep].isAccent());
        pinFlag &= ~bAccent;
        SpiSendParams(CMD_NOTE, getNoteData(currentStep));
        isDirty = true;
    }
    
    if (pinFlag & bRunStop) {
        #if (UART_TRACE)
        printf("PinRunStop Pushed\r\n");
        #endif
        if (isRunning) {
            isRunning = false;
        } else {
            isRunning = true;
        }
        pinFlag &= ~bRunStop;
        SpiSendParams(CMD_RUN, isRunning);
        isDirty = true;
    }
}

void dumpToLCD00()
{
    char buff[64];
    int col = 0;
    
    gLCD.clear();

    sprintf(buff, "Run:%d BPM:%03d", isRunning, bpm);
    gLCD.drawstring(0, col++, buff);

    sprintf(buff, "Stp:%02d Nto:%d Pch:%02d",
        currentStep, sequences[currentStep].isNoteOn(), sequences[currentStep].getPitch());
    gLCD.drawstring(0, col++, buff);
    
    sprintf(buff, "Oct:%-2d Tie:%d Acc:%d", 
        sequencerDisplay.getOctave(), sequences[currentStep].isTie(),sequences[currentStep].isAccent());
    gLCD.drawstring(0, col++, buff);

    sprintf(buff, "Wsp:%d Mod:%d pStp:%d", oscillatorParam.waveShape, UImode, playingStep);
    gLCD.drawstring(0, col++, buff);
    
    sprintf(buff, "PW :%4d   CO :%4d", oscillatorParam.pulseWidth, filterParam.cutoff);
    gLCD.drawstring(0, col++, buff);
    sprintf(buff, "RSO:%4d   ACL:%4d", filterParam.resonance, accentLevel);
    gLCD.drawstring(0, col++, buff);
    sprintf(buff, "LVL:%4d   DUR:%4d", envelopeParam.level, envelopeParam.duration);
    gLCD.drawstring(0, col++, buff);
    sprintf(buff, "DCY:%4d   SUS:%4d", envelopeParam.decay, envelopeParam.sustain);
    gLCD.drawstring(0, col++, buff);
    
    gLCD.display();
}

void dumpToLCD01()
{
    static int errCutoff = 0;
    static int errDuration = 0;
    static int errDecay = 0;
    static int errSustain = 0;
    static int errResonace = 0;
    static int errLevel = 0;
    static int errPulseWidth = 0;
    static int errAccentLevel = 0;

    uint8_t v;
    char buff[64];

    v = AinCutOff.read_u16() >> (16 - POT_RESOLUTION);
    if (v != filterParam.cutoff) {
        errCutoff++;
        filterParam.cutoff = v;
    }
    v = AinDuration.read_u16() >> (16 - POT_RESOLUTION);
    if (v != envelopeParam.duration) {
        errDuration++;
        envelopeParam.duration = v;
    }        
    v = AinDecay.read_u16() >> (16 - POT_RESOLUTION);
    if (v != envelopeParam.decay) {
        errDecay++;
        envelopeParam.decay = v;
    }
    v = AinSustain.read_u16() >> (16 - POT_RESOLUTION);
    if (v != envelopeParam.sustain) {
        errSustain++;
        envelopeParam.sustain = v;
    }
    
    v = AinResonance.read_u16() >> (16 - POT_RESOLUTION);
    if (v != filterParam.resonance) {
        errResonace++;
        filterParam.resonance = v;
    }
    v = AinLevel.read_u16() >> (16 - POT_RESOLUTION);
    if (v != envelopeParam.level) {
        errLevel++;
        envelopeParam.level = v;
    }
    v = AinPulseWidth.read_u16() >> (16 - POT_RESOLUTION);
    if (v != oscillatorParam.pulseWidth) {
        errPulseWidth++;
        oscillatorParam.pulseWidth = v;
    }
    v = AinAccentLevel.read_u16() >> (16 - POT_RESOLUTION);
    if (v != accentLevel) {
        errAccentLevel++;
        accentLevel = v;
    }
    
    gLCD.clear();
    
    sprintf(buff, "Cutoff     %3d %5d", filterParam.cutoff, errCutoff);
    gLCD.drawstring(0, 0, buff);
    sprintf(buff, "Duration   %3d %5d", envelopeParam.duration, errDuration);
    gLCD.drawstring(0, 1, buff);
    sprintf(buff, "Decay      %3d %5d", envelopeParam.decay, errDecay);
    gLCD.drawstring(0, 2, buff);
    sprintf(buff, "Sustain    %3d %5d", envelopeParam.sustain, errSustain);
    gLCD.drawstring(0, 3, buff);
    sprintf(buff, "Resonance  %3d %5d", filterParam.resonance, errResonace);
    gLCD.drawstring(0, 4, buff);
    sprintf(buff, "Level      %3d %5d", envelopeParam.level, errLevel);
    gLCD.drawstring(0, 5, buff);
    sprintf(buff, "PulseWidth %3d %5d", oscillatorParam.pulseWidth, errPulseWidth);
    gLCD.drawstring(0, 6, buff);
    sprintf(buff, "AccentLvl  %3d %5d", accentLevel, errAccentLevel);
    gLCD.drawstring(0, 7, buff);
    
    gLCD.display();
    Thread::wait(10);
}

//------------------------------------------------------------------------
// Threads
//------------------------------------------------------------------------

void pollingPots(void const *args)
{
    for (;;)
    {
        uint8_t pulseWidth = AinPulseWidth.read_u16() >> (16 - POT_RESOLUTION);
        if (pulseWidth != oscillatorParam.pulseWidth) {
            oscillatorParam.pulseWidth = pulseWidth;
            SpiSendParams(CMD_PULSE_WIDTH, pulseWidth);
            isDirty = true;
        }
       
        uint8_t cutoff = AinCutOff.read_u16() >> (16 - POT_RESOLUTION);
        if (cutoff != filterParam.cutoff) {
            filterParam.cutoff = cutoff;
            SpiSendParams(CMD_CUTOFF, cutoff);
            isDirty = true;
        }
    
        uint8_t resonance = AinResonance.read_u16() >> (16 - POT_RESOLUTION);
        if (resonance != filterParam.resonance) {
            filterParam.resonance = resonance;
            SpiSendParams(CMD_RESONANCE, resonance);
            isDirty = true;
        }
        
        uint8_t level = AinLevel.read_u16() >> (16 - POT_RESOLUTION);
        if (level != envelopeParam.level) {
            envelopeParam.level = level;
            SpiSendParams(CMD_LEVEL, level);
            isDirty = true;
        }
    
        uint8_t duration = AinDuration.read_u16() >> (16 - POT_RESOLUTION);
        if (duration != envelopeParam.duration) {
            envelopeParam.duration = duration;
            SpiSendParams(CMD_DURATION, duration);
            isDirty = true;
        }
    
        uint8_t decay = AinDecay.read_u16() >> (16 - POT_RESOLUTION);
        if (decay != envelopeParam.decay) {
            envelopeParam.decay = decay;
            SpiSendParams(CMD_DECAY, decay);
            isDirty = true;
        }
        
        uint8_t sustain = AinSustain.read_u16() >> (16 - POT_RESOLUTION);
        if (sustain != envelopeParam.sustain) {
            envelopeParam.sustain = sustain;
            SpiSendParams(CMD_SUSTAIN, sustain);
            isDirty = true;
        }
        
        uint8_t _accentLevel = AinAccentLevel.read_u16() >> (16 - POT_RESOLUTION);
        if (_accentLevel != accentLevel) {
            accentLevel = _accentLevel;
            SpiSendParams(CMD_ACCENT_LEVEL, accentLevel);
            isDirty = true;
        }
        
        Thread::wait(POLLING_POTS_WAIT);
    }
}

//------------------------------------------------------------------------
// Main routine
//------------------------------------------------------------------------
int main()
{
    #if (UART_TRACE)
    printf("*** BaseMachine UI Controller ***\r\n");
    #endif
    
    //--------------------------------------------------------------------
    // Setup Devices
    //
    gLCD.set_spi_frequency(SPI2_RATE);
    gLCD.begin(0x12);
    gLCD.clear();
    gLCD.drawstring(0, 0, TITLE_STR1);
    gLCD.drawstring(0, 1, TITLE_STR2);
    gLCD.display();
    Thread::wait(1000);
    
    RotEncStep.setInterval(100);
    RotEncPitch.setInterval(100);
    RotEncBpm.setInterval(100);
    
    PinWaveShape.attach_asserted(&swWaveShapePressed);
    PinWaveShape.setAssertValue(0);
    PinWaveShape.setSampleFrequency();  
    
    PinUIMode.attach_asserted(&swUIModePressed);
    PinUIMode.setAssertValue(0);
    PinUIMode.setSampleFrequency();
    
    PinOctaveUp.attach_asserted(&swOctaveUpPressed);
    PinOctaveUp.setAssertValue(0);
    PinOctaveUp.setSampleFrequency();  

    PinOctaveDown.attach_asserted(&swOctaveDownPressed);
    PinOctaveDown.setAssertValue(0);
    PinOctaveDown.setSampleFrequency();
    
    PinNoteOnOff.attach_asserted(&swNoteOnOffPressed);
    PinNoteOnOff.setAssertValue(0);
    PinNoteOnOff.setSampleFrequency();  
    
    PinTie.attach_asserted(&swTiePressed);
    PinTie.setAssertValue(0);
    PinTie.setSampleFrequency();  
    
    PinAccent.attach_asserted(&swAccentPressed);
    PinAccent.setAssertValue(0);
    PinAccent.setSampleFrequency();    
    
    PinRunStop.attach_asserted(&swRunStopPressed);
    PinRunStop.setAssertValue(0);
    PinRunStop.setSampleFrequency();
    
    //--------------------------------------------------------------------
    // Initialize SPI Master
    // 
    SpiMasterCs = 1;
    SpiMaster.format(SPI_UI_TO_SEQUENCER_BITS, SPI_UI_TO_SEQUENCER_MODE);
    SpiMaster.frequency(SPI_UI_TO_SEQUENCER_RATE);
    
    StepChangeInterrupt.fall(&updateStep);
    
    //--------------------------------------------------------------------
    // Initialize objects
    //
    Sequence::setBaseNoteNumber(baseNoteNumber);
    
    for (int i = 0; i < SEQUENCE_N; i++) {
        Sequence& seq = sequences[i];
        seq.setPitch(pitch[i]);
        seq.setNoteOn(noteOn[i]);
        seq.setTie(tie[i]);
        seq.setAccent(accent[i]);
    }
    RotEncPitch.setVal(sequences[0].getPitch());
    
    //--------------------------------------------------------------------
    // Initialize threads
    //
    Thread thPollingPots(pollingPots);

    //--------------------------------------------------------------------
    // Synchronize with Slave
    //
    SlaveReset = 0;
    SlaveReset = 1;
    Thread::wait(1000);
    SpiSendAllParams();
    
    //--------------------------------------------------------------------
    // Main loop
    //
    for (;;) {
        // Polling Devices
        /*
        if (UImode != 2) {
            pollingPots();
        }
        */
        pollingRotEncs();
        pollingPins();
        
        // Recieve playing step from SPI slave
        if (isStepChanged) {
            playingStep = SpiSendParams(CMD_RCV_PLAYING_STEP, 0x55);   // Send dummy data
            isStepChanged = false;
            isDirty = true;
        }

        // Display
        /*
        if (!isRunning) {
        */
            if (isDirty) {
                switch (UImode) {
                case 0:
                    sequencerDisplay.update(SequencerDisplay::stop, currentStep, playingStep);
                    break;
                case 1:
                    dumpToLCD00();
                    break;
                }
                isDirty = false;
            }
            if (UImode == 2) {
                dumpToLCD01();
            }
        /*
        } else {
        */
            if (isDirty) {
                sequencerDisplay.update(SequencerDisplay::run, currentStep, playingStep);
                isDirty = false;
            }
        /*
        }
        */
    }
}