https://developer.mbed.org/users/ryood/code/BaseMachine_Sequencer/ Revision:40
main.cpp
/* * main.cpp * SpiSequencerSender_test * * 2016.10.10 UIを分離 * 2016.08.20 mbed Rev 121 / mbed-rtos Rev 117で動作確認 * */ #include "mbed.h" #include "rtos.h" #define UART_TRACE (0) #include "SpiSequenceSender.h" #include "EnvelopeGenerator.h" #include "SpiAmpController.h" #include "SpiFilterController.h" #define TITLE_STR1 ("BaseMachine Sequencer") #define TITLE_STR2 ("20161010") #define SEQUENCE_N (16) #define SPI1_RATE (10000000) #define SPI2_RATE (3000000) // Actual frequency about 2.6MHz enum SequencerCmd { CMD_RCV_PLAYING_STEP = 0x01, CMD_RUN = 0x11, CMD_BPM = 0x12, CMD_ACCENT_LEVEL = 0x13, CMD_WAVE_SHAPE = 0x21, CMD_PULSE_WIDTH = 0x22, CMD_CUTOFF = 0x31, CMD_RESONANCE = 0x32, CMD_LEVEL = 0x41, CMD_DURATION = 0x42, CMD_DECAY = 0x43, CMD_SUSTAIN = 0x44, CMD_NOTE = 0x51, CMD_PITCH = 0x52, }; const int samplingPeriod = 1; // ms const int bpm = 120; const int envelopeLength = (60 * 1000 / (bpm * 4)) / samplingPeriod; const int waveShape = SpiSequenceSender::WAVESHAPE_SQUARE; const int baseNoteNumber = 36; // Initial Sequence const int noteOn[SEQUENCE_N] = { 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0 }; const int pitch[SEQUENCE_N] = {36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36 }; const int tie[SEQUENCE_N] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; const int accent[SEQUENCE_N] = { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 }; // Devices // //SPI1 (PinName mosi, PinName miso, PinName sclk, PinName ssel=NC) SPI SpiMaster(PA_7, PA_6, PA_5); //SPI2 (PinName mosi, PinName miso, PinName sclk, PinName ssel) SPISlave SpiSlave(PB_15, PB_14, PB_13, PB_12); DigitalOut StepChangePin(PB_1); BusOut Leds(PA_0, PA_1, PA_4, PB_0, PC_1, PC_0, PA_10, PB_3); // Grobal Variables // Sequence sequences[SEQUENCE_N]; SpiSequenceSender sequenceSender(&SpiMaster, D9, sequences, SEQUENCE_N, samplingPeriod, bpm); Envelope envelope(4095, envelopeLength, envelopeLength*3/4, envelopeLength/2, 2047); EnvelopeGenerator envelopeGenerator; SpiAmpController ampController(&SpiMaster, D8, D7); SpiFilterController filterController(&SpiMaster, D10); int playingStep = 0; int editingStep = 0; bool isRunning = false; //------------------------------------------------------------------------ // Callback functions //------------------------------------------------------------------------ void updateTicks(int ticks) { if (ticks == 0) { envelopeGenerator.init(envelope); playingStep = sequenceSender.getStep(); // Masterにinterruptをかける。 StepChangePin.write(1); StepChangePin.write(0); } if (sequences[sequenceSender.getStep()].isNoteOn()) { uint16_t level = envelopeGenerator.getModLevel(); if (!sequences[sequenceSender.getStep()].isAccent()) { level = level * 1 / 2; } ampController.outDca(level); } else { ampController.outDca(0); } envelopeGenerator.update(); filterController.outDcf(); } //------------------------------------------------------------------------ // Functions //------------------------------------------------------------------------ void spiSlaveRecieve() { if (SpiSlave.receive()) { SpiSlave.reply(playingStep); uint16_t recievedVal = SpiSlave.read(); uint8_t cmd = recievedVal >> 8; uint8_t data = recievedVal & 0xff; if (cmd != CMD_RCV_PLAYING_STEP) { Leds.write(cmd); } switch(cmd) { case CMD_RCV_PLAYING_STEP: // Do nothing break; case CMD_RUN: if (data == 0) { ampController.outDca(0); sequenceSender.stop(); isRunning = false; } else { sequenceSender.run(playingStep); isRunning = true; } break; case CMD_BPM: sequenceSender.setBpm(data); break; case CMD_ACCENT_LEVEL: // ToDo: Impl. accentLevel break; case CMD_WAVE_SHAPE: sequenceSender.setWaveShape(data); break; case CMD_PULSE_WIDTH: sequenceSender.setPulseWidth(data << 1); break; case CMD_CUTOFF: filterController.setCutoff(data << 1); break; case CMD_RESONANCE: filterController.setResonance(data << 1); break; case CMD_LEVEL: envelope.setLevel(data << 5); break; case CMD_DURATION: envelope.setDuration((float)data / 128.0f * envelopeLength); break; case CMD_DECAY: envelope.setDecay((float)data / 128.0f * envelopeLength); break; case CMD_SUSTAIN: envelope.setSustain(data << 5); break; case CMD_NOTE: editingStep = data >> 4; sequences[editingStep].setNoteOn(data & 0x01); sequences[editingStep].setTie((data & 0x02) >> 1); sequences[editingStep].setAccent((data & 0x04) >> 2); break; case CMD_PITCH: sequences[editingStep].setPitch(data); break; default: Leds.write(cmd); error("invalid SPI cmd"); } } } //------------------------------------------------------------------------ // Main routine //------------------------------------------------------------------------ int main() { #if (UART_TRACE) printf("*** BaseMachine Sequencer ***\r\n"); #endif // Check LED for (int i = 0; i < 8; i++) { Leds[i].write(1); Thread::wait(100); } Thread::wait(500); Leds.write(0x00); //-------------------------------------------------------------------- // Setup Devices // SpiMaster.format(8, 0); SpiMaster.frequency(SPI1_RATE); SpiSlave.format(16, 0); SpiSlave.frequency(SPI2_RATE); // Mute output ampController.outDca(0); //-------------------------------------------------------------------- // Initialize objects // Sequence::setBaseNoteNumber(baseNoteNumber); for (int i = 0; i < SEQUENCE_N; i++) { Sequence& seq = sequenceSender.getSequences()[i]; seq.setPitch(pitch[i]); seq.setNoteOn(noteOn[i]); seq.setTie(tie[i]); seq.setAccent(accent[i]); } envelopeGenerator.init(envelope); sequenceSender.attachUpdate(&updateTicks); sequenceSender.setWaveShape(waveShape); //-------------------------------------------------------------------- // Main loop // for (;;) { spiSlaveRecieve(); } }
UIとSequencer本体をMPUを分離したおかげでファームウェアは随分スッキリした。(実際の動作はクラスにカプセル化しているが)
BaseMachine UI Controller (SPI Master)
https://developer.mbed.org/users/ryood/code/BaseMachine_UI_Controller/ Revision:48
メモ:
MasterとSlaveで電源の入れる順番とパラメーターの変更の仕方でうまく動作しない場合がある。というかほとんどの場合初期状態で動作しない(^q^;
設定していないコマンドを受け取ったときにerror()を呼び出して停止するようにしているので、これでひっかかりがちなのかも。
ちなみにerror()という関数はmbedに用意されているもので、引数の文字列をUARTで出力して、ボード上の緑色のLEDが点滅して停止する。
UI Controllerの起動時に一旦全パラメーターをSequencerに送って、データを同期するようにした方がいいか。
MasterとSlaveで共有する設定値は定義ファイルとして分離するべきか。
0 件のコメント:
コメントを投稿