├── .clang-format ├── .gitignore ├── .travis.yml ├── LICENSE-HW ├── LICENSE-SW ├── README.md ├── Synapse.cpp ├── Synapse.h ├── examples ├── ClockDivide │ └── ClockDivide.ino ├── EuclidianTriggerSequencer │ └── EuclidianTriggerSequencer.ino ├── NoteSelect │ └── NoteSelect.ino ├── SimpleLFO │ └── SimpleLFO.ino ├── SimpleOctaveSwitch │ └── SimpleOctaveSwitch.ino ├── SynaBLE │ ├── CurieMIDI.cpp │ ├── CurieMIDI.h │ └── SynaBLE.ino └── Test │ └── Test.ino ├── hardware ├── synapse.brd ├── synapse.cop ├── synapse.pdf └── synapse.sch ├── keywords.txt └── library.properties /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -2 4 | AlignAfterOpenBracket: false 5 | AlignEscapedNewlinesLeft: true 6 | AlignOperands: true 7 | AlignTrailingComments: true 8 | AllowAllParametersOfDeclarationOnNextLine: true 9 | AllowShortBlocksOnASingleLine: false 10 | AllowShortIfStatementsOnASingleLine: false 11 | AllowShortLoopsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: false 13 | AlwaysBreakTemplateDeclarations: true 14 | AlwaysBreakBeforeMultilineStrings: false 15 | BreakBeforeBinaryOperators: true 16 | BreakBeforeTernaryOperators: true 17 | BreakConstructorInitializersBeforeComma: true 18 | BinPackArguments: false 19 | BinPackParameters: false 20 | BreakBeforeBraces: Allman 21 | ColumnLimit: 100 22 | CommentPragmas: '^!' 23 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 24 | ConstructorInitializerIndentWidth: 2 25 | ContinuationIndentWidth: 2 26 | Cpp11BracedListStyle: true 27 | DerivePointerAlignment: false 28 | DisableFormat: false 29 | ExperimentalAutoDetectBinPacking: false 30 | IndentCaseLabels: true 31 | IndentFunctionDeclarationAfterType: false 32 | IndentWrappedFunctionNames: false 33 | IndentWidth: 2 34 | KeepEmptyLinesAtTheStartOfBlocks: true 35 | MaxEmptyLinesToKeep: 2 36 | NamespaceIndentation: None 37 | PenaltyBreakBeforeFirstCallParameter: 19 38 | PenaltyBreakComment: 300 39 | PenaltyBreakString: 1000 40 | PenaltyBreakFirstLessLess: 120 41 | PenaltyExcessCharacter: 100 42 | PenaltyReturnTypeOnItsOwnLine: 600 43 | PointerAlignment: Left 44 | SpaceBeforeAssignmentOperators: true 45 | SpaceBeforeParens: ControlStatements 46 | SpaceInEmptyParentheses: false 47 | SpacesBeforeTrailingComments: 1 48 | SpacesInAngles: false 49 | SpacesInContainerLiterals: true 50 | SpacesInCStyleCastParentheses: false 51 | SpacesInParentheses: false 52 | Standard: Cpp11 53 | TabWidth: 2 54 | UseTab: Never 55 | ... 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eagle 2 | *.b## 3 | *.s## 4 | 5 | # Mac specific 6 | *.DS_Store 7 | 8 | # Windows specific 9 | *.bak 10 | 11 | # Temporary files 12 | *~ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | before_install: 3 | - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" 4 | - sleep 3 5 | - export DISPLAY=:1.0 6 | - wget http://downloads.arduino.cc/arduino-1.6.12-linux64.tar.xz 7 | - tar xf arduino-1.6.12-linux64.tar.xz 8 | - sudo mv arduino-1.6.12 /usr/local/share/arduino 9 | - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino 10 | install: 11 | - ln -s $PWD /usr/local/share/arduino/libraries/synapse 12 | - git clone https://github.com/mmarchetti/DirectIO.git /usr/local/share/arduino/libraries/DirectIO 13 | script: 14 | - arduino --verify --board arduino:avr:uno $PWD/examples/Test/Test.ino 15 | notifications: 16 | email: 17 | on_success: change 18 | on_failure: change 19 | -------------------------------------------------------------------------------- /LICENSE-HW: -------------------------------------------------------------------------------- 1 | This is a human-readable summary of the Legal Code (the full licence). 2 | The full license can be optained here: 3 | http://creativecommons.org/licenses/by-sa/3.0/legalcode 4 | 5 | All data in the "hardware" folder is published under the following License 6 | 7 | Attribution-ShareAlike 3.0 CC BY-SA 3.0 8 | 9 | This means: 10 | 11 | You are free: 12 | * to copy, distribute, display, and perform the work. 13 | * to make derivative works. 14 | 15 | Under the following conditions: 16 | Attribution — You must give the original author credit. 17 | Share Alike — If you alter, transform, or build upon this work, you may distribute the resulting work only under a licence identical to this one. 18 | 19 | With the understanding that: 20 | Waiver — Any of the above conditions can be waived if you get permission from the copyright holder. 21 | Public Domain — Where the work or any of its elements is in the public domain under applicable law, that status is in no way affected by the license. 22 | Other Rights — In no way are any of the following rights affected by the license: 23 | * Your fair dealing or fair use rights, or other applicable copyright exceptions and limitations; 24 | * The author's moral rights; 25 | * Rights other persons may have either in the work itself or in how the work is used, such as publicity or privacy rights. 26 | Notice — For any reuse or distribution, you must make clear to others the licence terms of this work. 27 | -------------------------------------------------------------------------------- /LICENSE-SW: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Vincenzo Pacella 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | View this project on [CADLAB.io](https://cadlab.io/project/1080). 2 | 3 | [![Build Status](https://travis-ci.org/shaduzlabs/synapse.svg?branch=master)](https://travis-ci.org/shaduzlabs/synapse) 4 | 5 | # Synapse 6 | ![A rendering of the Arduino shield](https://cloud.githubusercontent.com/assets/804931/13776151/e4bdfa4c-eaa8-11e5-8b88-48274cfd1437.png) 7 | Synapse is an Arduino shield (and the corresponding library) which provides CV and Gate I/O for Arduino boards. 8 | 9 | ## Features 10 | - 2 x control voltage inputs, 0 to 5V 11 | - 2 x control voltage outputs, individually configurable as 0 to 10V or -5 to 5V via software, with 12bit of resolution 12 | - 2 x gate inputs (with interrupt) 13 | - 2 x gate outputs 14 | - 1 x eurorack power connector (10 pin) 15 | - An easy to use software library 16 | 17 | ## Compatible Boards 18 | The shield should work fine with **any Arduino with onboard 5V regulator** which can tolerate up to 12V on the **VIN** pin (e.g. it will **NOT** work with an Arduino Yùn or Tian and will most likely damage the board). 19 | ### Verified Boards 20 | - Arduino Uno 21 | - Arduino Due 22 | - Arduino 101 (requires a small modification to the shield) 23 | 24 | ## Hardware 25 | [![Order from OSH Park](https://oshpark.com/assets/badge-5b7ec47045b78aef6eb9d83b3bac6b1920de805e9a0c227658eac6e19a045b9c.png)](https://oshpark.com/shared_projects/3d4FCRE4) 26 | 27 | ### Passive components 28 | - 2 x ferrite beads (or 10 ohm resistors) 29 | - 8 x 1K 1% resistor 30 | - 4 x 10K 1% resistor 31 | - 6 x 100K 1% resistor 32 | - 2 x 1M 1% resistor 33 | - 2 x 47p capacitor 34 | - 4 x 100n capacitor 35 | - 1 x 10u 25V electrolytic capacitor 36 | 37 | ### Diodes, transistors ICs 38 | - 4 x BAT85 diode 39 | - 4 x 2N3904 transistor 40 | - 1 x REF02CP voltage reference 41 | - 1 x MCP4922E SPI DAC 42 | - 1 x TL072 dual opamp 43 | - 1 x 4053N 44 | 45 | ### Mechanical 46 | - 2 x 8 pin dip socket 47 | - 1 x 16 pin dip socket 48 | - 1 x 14 pin dip socket 49 | - 1 x 5x2 male strip connector (angled if you want to stack another shield on top) (2.54mm pitch) 50 | - 1 x 3x2 female strip connector (2.54mm pitch) 51 | - 1 x 10x1 male strip connector (2.54mm pitch) 52 | - 2 x 8x1 male strip connector (2.54mm pitch) 53 | - 1 x 6x1 male strip connector (2.54mm pitch) 54 | - 8 x PJ301BM vertical mount 'Erthenvar' jack 55 | 56 | ## Software library usage 57 | The library can be installed using Arduino Library Manager (Sketch -> Include Library -> Library Manager), just search for "synapse". 58 | 59 | Once the library is installed (please remember to install the dependencies, as well - see the last paragraph below), you can call SynapseShield.begin() in the setup() function to initialize the library and the shield 60 | ```cpp 61 | #include 62 | using namespace sl; 63 | 64 | void setup() 65 | { 66 | SynapseShield.begin(); 67 | // ... 68 | } 69 | ``` 70 | when calling the begin() function you can optionally specify the SPI divider (by default it's set to 8) 71 | 72 | Optionally, you can configure a callback function for each gate input channel which will be called when a specific condition is met. 73 | ```cpp 74 | #include 75 | using namespace sl; 76 | 77 | void onGateAChanged(); 78 | void onGateBRisingEdge(); 79 | 80 | void setup() 81 | { 82 | SynapseShield.begin(); 83 | 84 | SynapseShield.gateInputInterrupt( 85 | Synapse::GateChannel::A, 86 | onGateAChanged, 87 | Synapse::GateInterrupt::ValueChange 88 | ); 89 | 90 | SynapseShield.gateInputInterrupt( 91 | Synapse::GateChannel::B, 92 | onGateBRisingEdge, 93 | Synapse::GateInterrupt::RisingEdge 94 | ); 95 | // ... 96 | } 97 | void loop() 98 | { 99 | // ... 100 | } 101 | 102 | void onGateAChanged() 103 | { 104 | // Do something when the logic value of gate input A changes 105 | // WARNING: keep this function as unexpensive as possible 106 | } 107 | 108 | void onGateBRisingEdge() 109 | { 110 | // Do something when the logic value of gate input B goes from LOW to HIGH 111 | // WARNING: keep this function as unexpensive as possible 112 | } 113 | ``` 114 | 115 | The range of each CV output channel can be individually configured (even dinamically) 116 | ```cpp 117 | #include 118 | using namespace sl; 119 | 120 | void setup() 121 | { 122 | SynapseShield.begin(); 123 | 124 | SynapseShield.setCVRange( 125 | Synapse::CVChannel::A, 126 | Synapse::Range::ZeroToTenVolts 127 | ); 128 | 129 | SynapseShield.setCVRange( 130 | Synapse::CVChannel::B, 131 | Synapse::Range::MinusFiveToFiveVolts 132 | ); 133 | // ... 134 | } 135 | 136 | void loop() 137 | { 138 | Synapse::Range rangeA = SynapseShield.getCVRange( Synapse::CVChannel::A ); 139 | 140 | if(rangeA == Synapse::Range::MinusFiveToFiveVolts) 141 | { 142 | SynapseShield.setCVRange( 143 | Synapse::CVChannel::A, 144 | Synapse::Range::ZeroToTenVolts 145 | ); 146 | } 147 | else 148 | { 149 | SynapseShield.setCVRange( 150 | Synapse::CVChannel::A, 151 | Synapse::Range::MinusFiveToFiveVolts 152 | ); 153 | } 154 | delay(1000); 155 | } 156 | ``` 157 | 158 | CV and Gate I/O operations are also very easy: 159 | ```cpp 160 | #include 161 | using namespace sl; 162 | 163 | bool gateInA{false}; 164 | unsigned cvInA{0}; 165 | 166 | void setup() 167 | { 168 | SynapseShield.begin(); 169 | // ... 170 | } 171 | 172 | void loop() 173 | { 174 | gateInA = SynapseShield.readGate(Synapse::GateChannel::A); 175 | cvInA = SynapseShield.readCV(Synapse::CVChannel::A); 176 | 177 | if(gateInA) 178 | { 179 | SynapseShield.writeCV(Synapse::CVChannel::A, 4095); 180 | SynapseShield.writeCV(Synapse::CVChannel::B, 0); 181 | } 182 | else 183 | { 184 | SynapseShield.writeCV(Synapse::CVChannel::A, 0); 185 | SynapseShield.writeCV(Synapse::CVChannel::B, 4095); 186 | } 187 | 188 | if(cvInA > 2047) 189 | { 190 | SynapseShield.writeGate(Synapse::GateChannel::A, false); 191 | SynapseShield.writeGate(Synapse::GateChannel::B, true); 192 | } 193 | else 194 | { 195 | SynapseShield.writeGate(Synapse::GateChannel::A, true); 196 | SynapseShield.writeGate(Synapse::GateChannel::B, false); 197 | } 198 | } 199 | ``` 200 | 201 | ## Dependencies 202 | - [DirectIO](https://github.com/mmarchetti/DirectIO), for fast digital pin access 203 | -------------------------------------------------------------------------------- /Synapse.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ########## Copyright (C) 2016 Vincenzo Pacella 3 | ## ## Distributed under MIT license, see file LICENSE-SW 4 | ## ## or 5 | ## ## 6 | ########## ############################################################# shaduzlabs.com #####*/ 7 | 8 | #include "Synapse.h" 9 | 10 | //-------------------------------------------------------------------------------------------------- 11 | 12 | namespace 13 | { 14 | static constexpr uint8_t k_writeChannelA = 0b01110000; 15 | static constexpr uint8_t k_writeChannelB = 0b11110000; 16 | 17 | static constexpr uint8_t k_pinCVInA = A0; 18 | static constexpr uint8_t k_pinCVInB = A1; 19 | } 20 | 21 | //-------------------------------------------------------------------------------------------------- 22 | 23 | namespace sl 24 | { 25 | 26 | //-------------------------------------------------------------------------------------------------- 27 | 28 | Synapse SynapseShield; 29 | 30 | //-------------------------------------------------------------------------------------------------- 31 | 32 | void Synapse::begin(unsigned spiDivider_) 33 | { 34 | pinMode(k_pinCVInA, INPUT); 35 | pinMode(k_pinCVInB, INPUT); 36 | pinMode(k_pinGateInA, INPUT); 37 | pinMode(k_pinGateInB, INPUT); 38 | 39 | pinMode(k_pinChipSelectDAC, OUTPUT); 40 | pinMode(k_pinCVOutConfA, OUTPUT); 41 | pinMode(k_pinCVOutConfB, OUTPUT); 42 | pinMode(k_pinGateOutA, OUTPUT); 43 | pinMode(k_pinGateOutB, OUTPUT); 44 | 45 | #if defined __AVR__ 46 | m_outputChipSelectDAC = HIGH; 47 | m_outputCVOutConfA = LOW; 48 | m_outputCVOutConfB = LOW; 49 | #else 50 | digitalWrite(k_pinChipSelectDAC, HIGH); 51 | digitalWrite(k_pinCVOutConfA, LOW); 52 | digitalWrite(k_pinCVOutConfB, LOW); 53 | #endif 54 | 55 | SPI.begin(); 56 | 57 | SPI.setBitOrder(MSBFIRST); 58 | SPI.setDataMode(SPI_MODE0); 59 | setSPIDivider(spiDivider_); 60 | 61 | updateCVRanges(); 62 | } 63 | 64 | //-------------------------------------------------------------------------------------------------- 65 | 66 | unsigned Synapse::readCV(CVChannel channel_) 67 | { 68 | switch (channel_) 69 | { 70 | case CVChannel::A: 71 | { 72 | return analogRead(k_pinCVInA); 73 | } 74 | case CVChannel::B: 75 | { 76 | return analogRead(k_pinCVInB); 77 | } 78 | default: 79 | { 80 | return 0U; 81 | } 82 | } 83 | } 84 | 85 | //-------------------------------------------------------------------------------------------------- 86 | 87 | void Synapse::writeCV(CVChannel channel_, uint16_t value_) 88 | { 89 | uint8_t msg1 = (uint8_t)((value_ >> 8) & 0x0F); 90 | uint8_t msg2 = (uint8_t)(value_ & 0xFF); 91 | 92 | switch (channel_) 93 | { 94 | case CVChannel::A: 95 | { 96 | msg1 |= k_writeChannelA; 97 | break; 98 | } 99 | case CVChannel::B: 100 | { 101 | msg1 |= k_writeChannelB; 102 | break; 103 | } 104 | default: 105 | { 106 | return; 107 | } 108 | } 109 | 110 | #if defined __AVR__ 111 | m_outputChipSelectDAC = LOW; 112 | #else 113 | digitalWrite(k_pinChipSelectDAC, LOW); 114 | #endif 115 | 116 | SPI.transfer(msg1); 117 | SPI.transfer(msg2); 118 | 119 | #if defined __AVR__ 120 | m_outputChipSelectDAC = HIGH; 121 | #else 122 | digitalWrite(k_pinChipSelectDAC, HIGH); 123 | #endif 124 | } 125 | 126 | //-------------------------------------------------------------------------------------------------- 127 | 128 | Synapse::Range Synapse::getCVRange(CVChannel channel_) 129 | { 130 | switch (channel_) 131 | { 132 | case CVChannel::A: 133 | { 134 | return m_channelRange[0]; 135 | } 136 | case CVChannel::B: 137 | { 138 | return m_channelRange[1]; 139 | } 140 | default: 141 | { 142 | return Range::Unknown; 143 | } 144 | } 145 | } 146 | 147 | //-------------------------------------------------------------------------------------------------- 148 | 149 | void Synapse::setCVRange(CVChannel channel_, Range range_) 150 | { 151 | switch (channel_) 152 | { 153 | case CVChannel::A: 154 | { 155 | m_channelRange[0] = range_; 156 | break; 157 | } 158 | case CVChannel::B: 159 | { 160 | m_channelRange[1] = range_; 161 | break; 162 | } 163 | default: 164 | { 165 | return; 166 | } 167 | } 168 | 169 | updateCVRanges(); 170 | } 171 | 172 | //-------------------------------------------------------------------------------------------------- 173 | 174 | bool Synapse::readGate(GateChannel channel_) 175 | { 176 | switch (channel_) 177 | { 178 | case GateChannel::A: 179 | { 180 | #if defined __AVR__ 181 | return m_inputGateA; 182 | #else 183 | return digitalRead(k_pinGateInA); 184 | #endif 185 | } 186 | case GateChannel::B: 187 | { 188 | #if defined __AVR__ 189 | return m_inputGateB; 190 | #else 191 | return digitalRead(k_pinGateInB); 192 | #endif 193 | } 194 | default: 195 | { 196 | return false; 197 | } 198 | } 199 | } 200 | 201 | //-------------------------------------------------------------------------------------------------- 202 | 203 | void Synapse::writeGate(GateChannel channel_, bool state_) 204 | { 205 | switch (channel_) 206 | { 207 | case GateChannel::A: 208 | { 209 | #if defined __AVR__ 210 | m_outputGateA = state_ ? LOW : HIGH; 211 | #else 212 | digitalWrite(k_pinGateOutA, (state_ ? LOW : HIGH)); 213 | #endif 214 | 215 | break; 216 | } 217 | case GateChannel::B: 218 | { 219 | #if defined __AVR__ 220 | m_outputGateB = state_ ? LOW : HIGH; 221 | #else 222 | digitalWrite(k_pinGateOutB, (state_ ? LOW : HIGH)); 223 | #endif 224 | break; 225 | } 226 | default: 227 | { 228 | return; 229 | } 230 | } 231 | } 232 | 233 | //-------------------------------------------------------------------------------------------------- 234 | 235 | void Synapse::gateInputInterrupt(GateChannel channel_, void (*callback_)(void), GateInterrupt mode_) 236 | { 237 | switch (channel_) 238 | { 239 | case GateChannel::A: 240 | { 241 | attachInterrupt(digitalPinToInterrupt(k_pinGateInA), callback_, static_cast(mode_)); 242 | break; 243 | } 244 | case GateChannel::B: 245 | { 246 | attachInterrupt(digitalPinToInterrupt(k_pinGateInB), callback_, static_cast(mode_)); 247 | break; 248 | } 249 | default: 250 | { 251 | return; 252 | } 253 | } 254 | } 255 | 256 | //-------------------------------------------------------------------------------------------------- 257 | 258 | void Synapse::setSPIDivider(unsigned spiDivider_) 259 | { 260 | #ifdef __AVR__ 261 | switch (spiDivider_) 262 | { 263 | case SPI_CLOCK_DIV2: 264 | case SPI_CLOCK_DIV4: 265 | case SPI_CLOCK_DIV8: 266 | case SPI_CLOCK_DIV16: 267 | case SPI_CLOCK_DIV32: 268 | case SPI_CLOCK_DIV64: 269 | case SPI_CLOCK_DIV128: 270 | { 271 | m_spiDivider = spiDivider_; 272 | break; 273 | } 274 | default: 275 | { 276 | m_spiDivider = SPI_CLOCK_DIV2; 277 | } 278 | } 279 | #else 280 | m_spiDivider = SPI_CLOCK_DIV2; 281 | #endif 282 | SPI.setClockDivider(m_spiDivider); 283 | } 284 | 285 | //-------------------------------------------------------------------------------------------------- 286 | 287 | void Synapse::updateCVRanges() 288 | { 289 | for (unsigned channel = 0; channel < k_numCVOutputs; channel++) 290 | { 291 | switch (m_channelRange[channel]) 292 | { 293 | case Range::ZeroToTenVolts: 294 | { 295 | if (channel == 0) 296 | { 297 | #if defined __AVR__ 298 | m_outputCVOutConfA = LOW; 299 | #else 300 | digitalWrite(k_pinCVOutConfA, LOW); 301 | #endif 302 | } 303 | else 304 | { 305 | #if defined __AVR__ 306 | m_outputCVOutConfB = LOW; 307 | #else 308 | digitalWrite(k_pinCVOutConfB, LOW); 309 | #endif 310 | } 311 | break; 312 | } 313 | case Range::MinusFiveToFiveVolts: 314 | { 315 | if (channel == 0) 316 | { 317 | #if defined __AVR__ 318 | m_outputCVOutConfA = HIGH; 319 | #else 320 | digitalWrite(k_pinCVOutConfA, HIGH); 321 | #endif 322 | } 323 | else 324 | { 325 | #if defined __AVR__ 326 | m_outputCVOutConfB = HIGH; 327 | #else 328 | digitalWrite(k_pinCVOutConfB, HIGH); 329 | #endif 330 | } 331 | break; 332 | } 333 | case Range::Unknown: 334 | default: 335 | break; 336 | } 337 | } 338 | } 339 | 340 | //-------------------------------------------------------------------------------------------------- 341 | 342 | } // namespace sl 343 | -------------------------------------------------------------------------------- /Synapse.h: -------------------------------------------------------------------------------- 1 | /* 2 | ########## Copyright (C) 2016 Vincenzo Pacella 3 | ## ## Distributed under MIT license, see file LICENSE-SW 4 | ## ## or 5 | ## ## 6 | ########## ############################################################# shaduzlabs.com #####*/ 7 | 8 | #pragma once 9 | 10 | #include 11 | #if defined __AVR__ 12 | #include // --> https://github.com/mmarchetti/DirectIO.git 13 | #endif 14 | 15 | //-------------------------------------------------------------------------------------------------- 16 | 17 | namespace sl 18 | { 19 | 20 | //-------------------------------------------------------------------------------------------------- 21 | 22 | /** 23 | \class SynapseBoard 24 | \brief This class is used to configure and handle the Synapse shield for Arduino 25 | */ 26 | class Synapse 27 | { 28 | public: 29 | 30 | enum class Range : uint8_t 31 | { 32 | ZeroToTenVolts, //!< CV output is 0 to 10V 33 | MinusFiveToFiveVolts, //!< CV output is -5 to 5V 34 | Unknown, //!< CV output range is unknown 35 | }; 36 | 37 | enum class CVChannel : uint8_t 38 | { 39 | A, //!< CV channel A 40 | B, //!< CV channel B 41 | Unknown, //!< Unknown/unspecified 42 | }; 43 | 44 | enum class GateChannel : uint8_t 45 | { 46 | A, //!< Gate channel A 47 | B, //!< Gate channel A 48 | Unknown, //!< Unknown/unspecified 49 | }; 50 | 51 | enum class GateInterrupt : uint32_t 52 | { 53 | ValueLow = HIGH, //!< Interrupt when the value of the digital input is LOW 54 | #ifdef _SAM3XA_ 55 | ValueHigh = LOW, //!< Interrupt when the value of the digital input is HIGH 56 | #endif 57 | ValueChange = CHANGE, //!< Interrupt when the value of the digital input changes 58 | RisingEdge = FALLING, //!< Interrupt when the value of the digital input goes from LOW to HIGH 59 | FallingEdge = RISING, //!< Interrupt when the value of the digital input goes from HIGH to LOW 60 | }; 61 | 62 | //! Initialize the board 63 | /*! 64 | \param spiDivider_ The SPI divider (default = 8) 65 | */ 66 | void begin(unsigned spiDivider_ = SPI_CLOCK_DIV2); 67 | 68 | //! Read from a CV input channel 69 | /*! 70 | \param channel_ The channel to read from 71 | \return The value measured on the selected channel 72 | */ 73 | unsigned readCV(CVChannel channel_); 74 | 75 | //! Write to a CV output channel 76 | /*! 77 | \param channel_ The channel to write to 78 | */ 79 | void writeCV(CVChannel channel_, uint16_t value_); 80 | 81 | //! Get the CV range of the selected channel 82 | /*! 83 | \param channel_ The selected channel 84 | \return The range currently configured on the selected output channel 85 | */ 86 | Range getCVRange(CVChannel channel_); 87 | 88 | //! Set the CV range of the selected channel 89 | /*! 90 | \param channel_ The selected channel 91 | \param range_ The desired output range 92 | */ 93 | void setCVRange(CVChannel channel_, Range range_); 94 | 95 | //! Read from a gate input channel 96 | /*! 97 | \param channel_ The selected channel 98 | \return True if the selected gate input channel is HIGH, false otherwise 99 | */ 100 | bool readGate(GateChannel channel_); 101 | 102 | //! Write to a gate output channel 103 | /*! 104 | \param channel_ The selected channel 105 | \param state_ The logical state (true = HIGH, false = LOW) 106 | */ 107 | void writeGate(GateChannel channel_, bool state_); 108 | 109 | //! Attach an interrupt to a gate input channel 110 | /*! 111 | \param channel_ The selected channel 112 | \param callback_ The callback (void function) 113 | \param mode_ The interrupt condition 114 | */ 115 | void gateInputInterrupt(GateChannel channel_, 116 | void (*callback_)(void), 117 | GateInterrupt mode_ = GateInterrupt::ValueChange); 118 | 119 | private: 120 | static constexpr unsigned k_numCVOutputs = 2; 121 | 122 | static constexpr unsigned k_pinChipSelectDAC = 10; 123 | 124 | static constexpr uint8_t k_pinGateInA = 3; 125 | static constexpr uint8_t k_pinGateInB = 2; 126 | static constexpr uint8_t k_pinGateOutA = 5; 127 | static constexpr uint8_t k_pinGateOutB = 4; 128 | 129 | static constexpr uint8_t k_pinCVOutConfA = 6; 130 | static constexpr uint8_t k_pinCVOutConfB = 7; 131 | 132 | void setSPIDivider(unsigned spiDivider_); 133 | void updateCVRanges(); 134 | 135 | #if defined __AVR__ 136 | Input m_inputGateA; 137 | Input m_inputGateB; 138 | 139 | Output m_outputChipSelectDAC; 140 | Output m_outputGateA; 141 | Output m_outputGateB; 142 | Output m_outputCVOutConfA; 143 | Output m_outputCVOutConfB; 144 | #endif 145 | 146 | Range m_channelRange[k_numCVOutputs]{Range::ZeroToTenVolts, Range::ZeroToTenVolts}; 147 | unsigned m_spiDivider; 148 | }; 149 | 150 | //-------------------------------------------------------------------------------------------------- 151 | 152 | extern Synapse SynapseShield; 153 | 154 | //-------------------------------------------------------------------------------------------------- 155 | 156 | } // namespace sl 157 | -------------------------------------------------------------------------------- /examples/ClockDivide/ClockDivide.ino: -------------------------------------------------------------------------------- 1 | // ============================================================ 2 | // 3 | // Program: Synapse ClockDivide 4 | // 5 | // Based on https://github.com/darwingrosse/ArdCore-Code/tree/master/20%20Objects/AC05_ClockDivide 6 | // 7 | // Description: Given incoming clock pulses, output two 8 | // triggers with varying delays 9 | // 10 | // Analog In 2: Division of clock out 1 (1-32) 11 | // Analog In 3: Division of clock out 2 (1-32) 12 | // Analog In 4: Additional division offset (1-16) 13 | // GateOut A: Trigger output 1 14 | // GateOut B: Trigger output 2 15 | // GateIn A: External trigger input 16 | // GateIn B: HIGH to reset clock 1 & 2 17 | // 18 | // ============================================================ 19 | 20 | #include 21 | using namespace sl; 22 | 23 | const int trigTime = 10; // triggers are 10 ms. 24 | 25 | // variables for interrupt handling of the clock input and reset 26 | volatile int clkState = LOW; 27 | volatile int rstState = LOW; 28 | int clockTick[2] = {1, 1}; 29 | 30 | // variables used to control the current gate output states 31 | int digState[2] = {LOW, LOW}; // start with both set low 32 | unsigned long prevMilli[2] = {0, 0}; // the last time of a loop 33 | Synapse::GateChannel channel[2] = {Synapse::GateChannel::A, Synapse::GateChannel::B}; 34 | 35 | void onGateARisingEdge(); 36 | void onGateBRisingEdge(); 37 | 38 | void setup() { 39 | SynapseShield.begin(); 40 | Serial.begin(9600); 41 | 42 | // attach clock in interrupt 43 | SynapseShield.gateInputInterrupt( 44 | Synapse::GateChannel::A, 45 | onGateARisingEdge, 46 | Synapse::GateInterrupt::RisingEdge 47 | ); 48 | 49 | // attach reset interrupt 50 | SynapseShield.gateInputInterrupt( 51 | Synapse::GateChannel::B, 52 | onGateBRisingEdge, 53 | Synapse::GateInterrupt::RisingEdge 54 | ); 55 | } 56 | 57 | void loop() 58 | { 59 | int i; 60 | 61 | // deal with possible reset 62 | if (rstState == HIGH) { 63 | rstState = LOW; 64 | for (i=0; i<2; i++) { 65 | clockTick[i] = (analogRead(i+2) >> 5) + 1; 66 | } 67 | } 68 | 69 | // deal with incoming clock ticks 70 | if (clkState == HIGH) { 71 | clkState = LOW; // reset for the next clock 72 | 73 | for (int i=0; i<2; i++) { 74 | clockTick[i] --; 75 | if (clockTick[i] < 1) { 76 | digState[i] = HIGH; 77 | prevMilli[i] = millis(); 78 | SynapseShield.writeGate(channel[i], true); 79 | clockTick[i] = (analogRead(i+2) >> 5) + 1 + (analogRead(4) >> 5); 80 | } 81 | } 82 | } 83 | 84 | // deal with trigger turnoff 85 | for (int i=0; i<2; i++) { 86 | if ((digState[i] == HIGH) && ((millis() - prevMilli[i]) > trigTime)) { 87 | digState[i] = LOW; 88 | SynapseShield.writeGate(channel[i], false); 89 | } 90 | } 91 | 92 | } 93 | 94 | void onGateARisingEdge() 95 | { 96 | clkState = HIGH; 97 | } 98 | 99 | void onGateBRisingEdge() 100 | { 101 | rstState = HIGH; 102 | } 103 | -------------------------------------------------------------------------------- /examples/EuclidianTriggerSequencer/EuclidianTriggerSequencer.ino: -------------------------------------------------------------------------------- 1 | // ============================================================ 2 | // 3 | // Program: Synapse Dual Euclidian Trigger Sequencer 4 | // 5 | // Based on https://github.com/darwingrosse/ArdCore-Code/blob/master/20%20Objects/AC30_DualEuclidean/AC30_DualEuclidean.ino 6 | // 7 | // Description: An implementation of a dual trigger sequencer 8 | // using Euclidian Rhythm concepts 9 | // 10 | // Thanks to Robin Price (http://crx091081gb.net/) 11 | // for the use of his algorithm for Euclidean 12 | // Rhythm Generation. 13 | // 14 | // I/O Usage: 15 | // A2: Steps for rhythm A 16 | // A3: Steps for rhythm B 17 | // A4: Pulses for rhythm A 18 | // A5: Pulses for rhythm B 19 | // CVIn A: rotate rhythm A 20 | // CVIn B: rotate rhythm B 21 | // GateOut A: Rhythm A output 22 | // GateOut B: Rhythm B output 23 | // GateIn: External clock input 24 | // 25 | // ============================================================ 26 | // 27 | // License: 28 | // 29 | // This software is licensed under the Creative Commons 30 | // "Attribution-NonCommercial license. This license allows you 31 | // to tweak and build upon the code for non-commercial purposes, 32 | // without the requirement to license derivative works on the 33 | // same terms. If you wish to use this (or derived) work for 34 | // commercial work, please contact 20 Objects LLC at our website 35 | // (www.20objects.com). 36 | // 37 | // For more information on the Creative Commons CC BY-NC license, 38 | // visit http://creativecommons.org/licenses/ 39 | // 40 | // ============================================================ 41 | #include 42 | using namespace sl; 43 | 44 | #define CHANNELS 2 45 | 46 | // constants related to the trigger out 47 | const int trigTime = 25; // 25 ms trigger timing 48 | 49 | // variables for interrupt handling of the clock input 50 | volatile int clkState = LOW; 51 | volatile int rstState = LOW; 52 | 53 | // variables used to control the gate output states 54 | int digState[2] = {LOW, LOW}; // start with both set low 55 | unsigned long digMilli[2] = {0, 0}; // a place to store millis() 56 | Synapse::GateChannel channel[2] = {Synapse::GateChannel::A, Synapse::GateChannel::B}; 57 | 58 | // the euclidian rhythm settings 59 | int inSteps[2]; 60 | int inPulses[2]; 61 | int inRotate[2]; 62 | 63 | int euArray[2][32]; 64 | 65 | unsigned long currPulse = 0; 66 | int doCalc = 0; 67 | 68 | void onGateARisingEdge(); 69 | void onGateBRisingEdge(); 70 | 71 | // ==================== start of setup() ====================== 72 | void setup() 73 | { 74 | SynapseShield.begin(); 75 | 76 | // get the analog reading to set up the system 77 | inSteps[0] = (analogRead(2) >> 5) + 1; 78 | inSteps[1] = (analogRead(3) >> 5) + 1; 79 | inPulses[0] = (analogRead(4) >> 5) + 1; 80 | inPulses[1] = (analogRead(5) >> 5) + 1; 81 | 82 | inRotate[0] = map(SynapseShield.readCV(Synapse::CVChannel::A), 0, 4095, 0, inSteps[0]); 83 | inRotate[1] = map(SynapseShield.readCV(Synapse::CVChannel::B), 0, 4095, 0, inSteps[1]); 84 | 85 | euCalc(0); 86 | euCalc(1); 87 | 88 | // Note: Interrupt 0 is for Gate A (clkIn) 89 | SynapseShield.gateInputInterrupt( 90 | Synapse::GateChannel::A, 91 | onGateARisingEdge, 92 | Synapse::GateInterrupt::RisingEdge 93 | ); 94 | 95 | // Note: Interrupt 0 is for Gate A (clkIn) 96 | SynapseShield.gateInputInterrupt( 97 | Synapse::GateChannel::B, 98 | onGateBRisingEdge, 99 | Synapse::GateInterrupt::RisingEdge 100 | ); 101 | 102 | } 103 | 104 | void loop() 105 | { 106 | int doClock = 0; 107 | 108 | // check to see if the clock as been set 109 | if (clkState == HIGH) { 110 | clkState = LOW; 111 | currPulse++; 112 | doClock = 1; 113 | } 114 | 115 | if (rstState == HIGH) { 116 | rstState = LOW; 117 | currPulse = 0; 118 | } 119 | 120 | if (doClock) { 121 | int outPulse[2] = {0, 0}; 122 | 123 | for (int i=0; i < CHANNELS; i++) { 124 | int myPulse = (currPulse + inRotate[i]) % inSteps[i]; 125 | if (euArray[i][myPulse] > 0) { 126 | digState[i] = HIGH; 127 | digMilli[i] = millis(); 128 | SynapseShield.writeGate(channel[i], true); 129 | } 130 | } 131 | } 132 | 133 | // do we have to turn off any of the Gate outs? 134 | for (int i=0; i trigTime)) { 136 | digState[i] = LOW; 137 | SynapseShield.writeGate(channel[i], false); 138 | } 139 | } 140 | 141 | // reread the inputs in case we need to change 142 | doCalc = 0; 143 | int tmp = (analogRead(2) >> 5) + 1; 144 | if (tmp != inSteps[0]) { 145 | inSteps[0] = tmp; 146 | doCalc = 1; 147 | } 148 | 149 | tmp = (analogRead(4) >> 5) + 1; 150 | if (tmp != inPulses[0]) { 151 | inPulses[0] = tmp; 152 | doCalc = 1; 153 | } 154 | 155 | if (doCalc) { 156 | euCalc(0); 157 | } 158 | 159 | doCalc = 0; 160 | tmp = (analogRead(3) >> 5) + 1; 161 | if (tmp != inSteps[1]) { 162 | inSteps[1] = tmp; 163 | doCalc = 1; 164 | } 165 | 166 | tmp = (analogRead(5) >> 5) + 1; 167 | if (tmp != inPulses[1]) { 168 | inPulses[1] = tmp; 169 | doCalc = 1; 170 | } 171 | 172 | if (doCalc) { 173 | euCalc(1); 174 | } 175 | 176 | inRotate[0] = map(SynapseShield.readCV(Synapse::CVChannel::A), 0, 4095, 0, inSteps[0]); 177 | inRotate[1] = map(SynapseShield.readCV(Synapse::CVChannel::B), 0, 4095, 0, inSteps[1]); 178 | } 179 | 180 | // onGateARisingEdge() - quickly handle interrupts from the clock input 181 | // ------------------------------------------------------ 182 | void onGateARisingEdge() 183 | { 184 | clkState = HIGH; 185 | } 186 | 187 | // onGateBRisingEdge() - quickly handle interrupts from the rst input 188 | // ------------------------------------------------------ 189 | void onGateBRisingEdge() 190 | { 191 | rstState = HIGH; 192 | } 193 | 194 | // euCalc(int) - create a Euclidean Rhythm array. 195 | // 196 | // NOTE: Thanks to Robin Price for his excellent implementation, and for 197 | // making the source code available on the Interwebs. 198 | // For more info, check out: http://crx091081gb.net/ 199 | // ---------------------------------------------------------------------- 200 | void euCalc(int ar) { 201 | int loc = 0; 202 | 203 | // clear the array to start 204 | for (int i=0; i<32; i++) { 205 | euArray[ar][i] = 0; 206 | } 207 | 208 | if ((inPulses[ar] >= inSteps[ar]) || (inSteps[ar] == 1)) { 209 | if (inPulses[ar] >= inSteps[ar]) { 210 | for (int i = 0; i < inSteps[ar]; i++) { 211 | euArray[ar][loc] = 1; 212 | loc++; 213 | } 214 | } 215 | } else { 216 | int offs = inSteps[ar] - inPulses[ar]; 217 | if (offs >= inPulses[ar]) { 218 | int ppc = offs / inPulses[ar]; 219 | int rmd = offs % inPulses[ar]; 220 | 221 | for (int i = 0; i < inPulses[ar]; i++) { 222 | euArray[ar][loc] = 1; 223 | loc++; 224 | for (int j = 0; j < ppc; j++) { 225 | euArray[ar][loc] = 0; 226 | loc++; 227 | } 228 | if (i < rmd) { 229 | euArray[ar][loc] = 0; 230 | loc++; 231 | } 232 | } 233 | } else { 234 | int ppu = (inPulses[ar] - offs) / offs; 235 | int rmd = (inPulses[ar] - offs) % offs; 236 | 237 | for (int i = 0; i < offs; i++) { 238 | euArray[ar][loc] = 1; 239 | loc++; 240 | euArray[ar][loc] = 0; 241 | loc++; 242 | for (int j = 0; j < ppu; j++) { 243 | euArray[ar][loc] = 1; 244 | loc++; 245 | } 246 | if (i < rmd) { 247 | euArray[ar][loc] = 1; 248 | loc++; 249 | } 250 | } 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /examples/NoteSelect/NoteSelect.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Simple note select with a potentiometer 3 | * connect a 10 kilohm potentiometer to A2 to select the note 4 | */ 5 | 6 | #include 7 | 8 | using namespace sl; 9 | 10 | // 0 - 4095 = 0 - 5V 11 | // 0 - 819 = 0 - 1V 12 | // 68.25 = 1V / 12 13 | 14 | #define OCTAVE (4095 / 5) 15 | #define NOTE (OCTAVE / 12) 16 | 17 | void setup() { 18 | SynapseShield.begin(); 19 | } 20 | 21 | int pot = 0; 22 | int cvInput = 0; 23 | int cvOutput = 0; 24 | int noteFromPot; 25 | int noteAsDacValue; 26 | 27 | void loop() { 28 | // Read the the potentiometer 29 | pot = analogRead(A2); 30 | // Map the value onto an octave 31 | noteFromPot = map(pot, 0, 4095, 0, 12); 32 | noteFromPot = constrain(noteFromPot, 0, 12); 33 | // Calculate output value for DAC from the note in the second octave 34 | noteAsDacValue = OCTAVE * 2 + (NOTE * noteFromPot); 35 | // Write the value to CV Output 1 36 | SynapseShield.writeCV(Synapse::CVChannel::A, noteAsDacValue); 37 | 38 | delayMicroseconds(100); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /examples/SimpleLFO/SimpleLFO.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Based on https://www.arduino.cc/en/Tutorial/DueSimpleWaveformGenerator 3 | */ 4 | 5 | /* 6 | Simple Waveform generator with Synapse Shield 7 | * connect a 10 kilohm potentiometer to A2 to control the signal frequency 8 | */ 9 | 10 | #include 11 | 12 | #define maxWaveform 4 13 | #define maxSamplesNum 120 14 | 15 | static int waveformsTable[maxWaveform][maxSamplesNum] = { 16 | // Sin wave 17 | { 18 | 0x7ff, 0x86a, 0x8d5, 0x93f, 0x9a9, 0xa11, 0xa78, 0xadd, 0xb40, 0xba1, 19 | 0xbff, 0xc5a, 0xcb2, 0xd08, 0xd59, 0xda7, 0xdf1, 0xe36, 0xe77, 0xeb4, 20 | 0xeec, 0xf1f, 0xf4d, 0xf77, 0xf9a, 0xfb9, 0xfd2, 0xfe5, 0xff3, 0xffc, 21 | 0xfff, 0xffc, 0xff3, 0xfe5, 0xfd2, 0xfb9, 0xf9a, 0xf77, 0xf4d, 0xf1f, 22 | 0xeec, 0xeb4, 0xe77, 0xe36, 0xdf1, 0xda7, 0xd59, 0xd08, 0xcb2, 0xc5a, 23 | 0xbff, 0xba1, 0xb40, 0xadd, 0xa78, 0xa11, 0x9a9, 0x93f, 0x8d5, 0x86a, 24 | 0x7ff, 0x794, 0x729, 0x6bf, 0x655, 0x5ed, 0x586, 0x521, 0x4be, 0x45d, 25 | 0x3ff, 0x3a4, 0x34c, 0x2f6, 0x2a5, 0x257, 0x20d, 0x1c8, 0x187, 0x14a, 26 | 0x112, 0x0df, 0x0b1, 0x087, 0x064, 0x045, 0x02c, 0x019, 0x00b, 0x002, 27 | 0x000, 0x002, 0x00b, 0x019, 0x02c, 0x045, 0x064, 0x087, 0x0b1, 0x0df, 28 | 0x112, 0x14a, 0x187, 0x1c8, 0x20d, 0x257, 0x2a5, 0x2f6, 0x34c, 0x3a4, 29 | 0x3ff, 0x45d, 0x4be, 0x521, 0x586, 0x5ed, 0x655, 0x6bf, 0x729, 0x794 30 | }, 31 | 32 | // Triangular wave 33 | { 34 | 0x000, 0x044, 0x088, 0x0cc, 0x110, 0x154, 0x198, 0x1dc, 0x220, 0x264, 35 | 0x2a8, 0x2ec, 0x330, 0x374, 0x3b8, 0x3fc, 0x440, 0x484, 0x4c8, 0x50c, 36 | 0x550, 0x594, 0x5d8, 0x61c, 0x660, 0x6a4, 0x6e8, 0x72c, 0x770, 0x7b4, 37 | 0x7f8, 0x83c, 0x880, 0x8c4, 0x908, 0x94c, 0x990, 0x9d4, 0xa18, 0xa5c, 38 | 0xaa0, 0xae4, 0xb28, 0xb6c, 0xbb0, 0xbf4, 0xc38, 0xc7c, 0xcc0, 0xd04, 39 | 0xd48, 0xd8c, 0xdd0, 0xe14, 0xe58, 0xe9c, 0xee0, 0xf24, 0xf68, 0xfac, 40 | 0xffe, 0xfac, 0xf68, 0xf24, 0xee0, 0xe9c, 0xe58, 0xe14, 0xdd0, 0xd8c, 41 | 0xd48, 0xd04, 0xcc0, 0xc7c, 0xc38, 0xbf4, 0xbb0, 0xb6c, 0xb28, 0xae4, 42 | 0xaa0, 0xa5c, 0xa18, 0x9d4, 0x990, 0x94c, 0x908, 0x8c4, 0x880, 0x83c, 43 | 0x7f8, 0x7b4, 0x770, 0x72c, 0x6e8, 0x6a4, 0x660, 0x61c, 0x5d8, 0x594, 44 | 0x550, 0x50c, 0x4c8, 0x484, 0x440, 0x3fc, 0x3b8, 0x374, 0x330, 0x2ec, 45 | 0x2a8, 0x264, 0x220, 0x1dc, 0x198, 0x154, 0x110, 0x0cc, 0x088, 0x044 46 | }, 47 | 48 | // Sawtooth wave 49 | { 50 | 0x022, 0x044, 0x066, 0x088, 0x0aa, 0x0cc, 0x0ee, 0x110, 0x132, 0x154, 51 | 0x176, 0x198, 0x1ba, 0x1dc, 0x1fe, 0x220, 0x242, 0x264, 0x286, 0x2a8, 52 | 0x2ca, 0x2ec, 0x30e, 0x330, 0x352, 0x374, 0x396, 0x3b8, 0x3da, 0x3fc, 53 | 0x41e, 0x440, 0x462, 0x484, 0x4a6, 0x4c8, 0x4ea, 0x50c, 0x52e, 0x550, 54 | 0x572, 0x594, 0x5b6, 0x5d8, 0x5fa, 0x61c, 0x63e, 0x660, 0x682, 0x6a4, 55 | 0x6c6, 0x6e8, 0x70a, 0x72c, 0x74e, 0x770, 0x792, 0x7b4, 0x7d6, 0x7f8, 56 | 0x81a, 0x83c, 0x85e, 0x880, 0x8a2, 0x8c4, 0x8e6, 0x908, 0x92a, 0x94c, 57 | 0x96e, 0x990, 0x9b2, 0x9d4, 0x9f6, 0xa18, 0xa3a, 0xa5c, 0xa7e, 0xaa0, 58 | 0xac2, 0xae4, 0xb06, 0xb28, 0xb4a, 0xb6c, 0xb8e, 0xbb0, 0xbd2, 0xbf4, 59 | 0xc16, 0xc38, 0xc5a, 0xc7c, 0xc9e, 0xcc0, 0xce2, 0xd04, 0xd26, 0xd48, 60 | 0xd6a, 0xd8c, 0xdae, 0xdd0, 0xdf2, 0xe14, 0xe36, 0xe58, 0xe7a, 0xe9c, 61 | 0xebe, 0xee0, 0xf02, 0xf24, 0xf46, 0xf68, 0xf8a, 0xfac, 0xfce, 0xffe 62 | } 63 | , 64 | 65 | // Square wave 66 | { 67 | 0x800, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 68 | 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 69 | 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 70 | 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 71 | 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 72 | 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0x800, 73 | 0x800, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 74 | 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 75 | 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 76 | 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 77 | 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 78 | 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x800 79 | } 80 | 81 | }; 82 | 83 | #define oneHzSample 1000000/maxSamplesNum // sample for the 1Hz signal expressed in microseconds 84 | 85 | // 0 - Sine, 86 | // 1 - Tri, 87 | // 2 - Saw, 88 | // 3 - Square 89 | volatile int wave0 = 0, wave1 = 2; 90 | 91 | int i = 0; 92 | int sample; 93 | 94 | using namespace sl; 95 | 96 | void setup() { 97 | SynapseShield.begin(); 98 | Serial.begin(57600); 99 | } 100 | 101 | int pot = 0; 102 | 103 | long lastUpdate = -1; 104 | long now; 105 | 106 | void loop() { 107 | 108 | // Read the the potentiometer and map the value between the maximum and the minimum 109 | // sample available 110 | 111 | pot = analogRead(A2); 112 | now = millis(); 113 | 114 | if(lastUpdate == -1 || (now - lastUpdate) > 1000){ 115 | lastUpdate = now; 116 | Serial.println(pot); 117 | } 118 | 119 | sample = map(pot, 0, 4095, 0, oneHzSample); 120 | sample = constrain(sample, 0, oneHzSample); 121 | 122 | SynapseShield.writeCV(Synapse::CVChannel::A, waveformsTable[wave0][i]); 123 | SynapseShield.writeCV(Synapse::CVChannel::B, waveformsTable[wave1][i]); 124 | 125 | i++; 126 | 127 | if(i == maxSamplesNum){ // Reset the counter to repeat the wave 128 | i = 0; 129 | } 130 | delayMicroseconds(sample); // Hold the sample value for the sample time 131 | } 132 | -------------------------------------------------------------------------------- /examples/SimpleOctaveSwitch/SimpleOctaveSwitch.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Playing CV Input 1 an octave higher on CV Output 1 3 | */ 4 | 5 | #include 6 | 7 | using namespace sl; 8 | 9 | // 0 - 4095 = 0 - 5V 10 | // 0 - 819 = 0 - 1V 11 | // 68.25 = 1V / 12 12 | 13 | #define OCTAVE (4095 / 5) 14 | #define NOTE (OCTAVE / 12) 15 | #define FIFTH NOTE * 5 16 | 17 | void setup() { 18 | SynapseShield.begin(); 19 | } 20 | 21 | int cvInput = 0; 22 | int cvOutputAnOctaveHigher = 0; 23 | int cvOutputFiveNotesHigher = 0; 24 | 25 | void loop() { 26 | // Example 2 octave switcher 27 | // Read CV Input 1 28 | cvInput = SynapseShield.readCV(Synapse::CVChannel::A); 29 | // Add an octave 30 | cvOutputAnOctaveHigher = constrain(cvInput + OCTAVE, 0, 4095); 31 | // Play CV Input 1 an octave higher on CV Output 1 32 | SynapseShield.writeCV(Synapse::CVChannel::A, cvOutputAnOctaveHigher); 33 | // Add five notes 34 | cvOutputFiveNotesHigher = constrain(cvInput + FIFTH, 0, 4095); 35 | // Play CV Input 1 five noteshigher on CV Output 2 36 | SynapseShield.writeCV(Synapse::CVChannel::B, cvOutputFiveNotesHigher); 37 | 38 | delayMicroseconds(100); 39 | } 40 | -------------------------------------------------------------------------------- /examples/SynaBLE/CurieMIDI.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ########## Copyright (C) 2016 Vincenzo Pacella 3 | ## ## Distributed under MIT license, see file LICENSE-SW 4 | ## ## or 5 | ## ## 6 | ########## ############################################################# shaduzlabs.com #####*/ 7 | 8 | #include "CurieMIDI.h" 9 | 10 | //-------------------------------------------------------------------------------------------------- 11 | 12 | namespace 13 | { 14 | const char* k_serviceMIDI{"03B80E5A-EDE8-4B33-A751-6CE34EC4C700"}; 15 | const char* k_characteristicMIDI{"7772E5DB-3868-4112-A1A9-F2669D106BF3"}; 16 | const unsigned k_characteristicEventsMask{BLEWrite | BLEWriteWithoutResponse | BLENotify | BLERead}; 17 | 18 | sl::CurieMIDI* g_this{nullptr}; // I know, I know. This is horrible. ಠ_ಠ 19 | } 20 | 21 | //-------------------------------------------------------------------------------------------------- 22 | 23 | namespace sl 24 | { 25 | 26 | //-------------------------------------------------------------------------------------------------- 27 | 28 | CurieMIDI::CurieMIDI() 29 | : m_service(k_serviceMIDI) 30 | , m_characteristic(k_characteristicMIDI, k_characteristicEventsMask, k_maxDataLength) 31 | { 32 | g_this = this; 33 | } 34 | 35 | //-------------------------------------------------------------------------------------------------- 36 | 37 | void CurieMIDI::begin(const char* name_) 38 | { 39 | m_outputBuffer[0] = 0x80; // timestamp HI (0) 40 | m_outputBuffer[1] = 0x80; // timestamp LO (0) 41 | 42 | m_peripheral.setLocalName(name_); 43 | m_peripheral.setDeviceName(name_); 44 | 45 | m_peripheral.setAdvertisedServiceUuid(m_service.uuid()); 46 | 47 | m_peripheral.addAttribute(m_service); 48 | m_peripheral.addAttribute(m_characteristic); 49 | 50 | m_peripheral.setEventHandler(BLEConnected, &CurieMIDI::onConnect); 51 | m_peripheral.setEventHandler(BLEDisconnected, &CurieMIDI::onDisconnect); 52 | 53 | m_characteristic.setEventHandler(BLEWritten, &CurieMIDI::onMidiReceived); 54 | 55 | m_peripheral.begin(); 56 | } 57 | 58 | //-------------------------------------------------------------------------------------------------- 59 | 60 | void CurieMIDI::setCallbackConnectionStatus(tConnectionStatusCallback cb_) 61 | { 62 | m_callbackConnectionStatus = cb_; 63 | } 64 | 65 | //-------------------------------------------------------------------------------------------------- 66 | 67 | void CurieMIDI::setCallbackNote(tNoteCallback cb_) 68 | { 69 | m_callbackNote = cb_; 70 | } 71 | 72 | //-------------------------------------------------------------------------------------------------- 73 | 74 | void CurieMIDI::setCallbackControlChange(tControlChangeCallback cb_) 75 | { 76 | m_callbackControlChange = cb_; 77 | } 78 | 79 | //-------------------------------------------------------------------------------------------------- 80 | 81 | bool CurieMIDI::sendNoteOn(uint8_t note_, uint8_t velocity_, uint8_t channel_) 82 | { 83 | m_outputBuffer[2] = 0x90 | (channel_ & 0x0F); 84 | m_outputBuffer[3] = note_ & 0x7F; 85 | m_outputBuffer[4] = velocity_ & 0x7F; 86 | return send(5); 87 | } 88 | 89 | //-------------------------------------------------------------------------------------------------- 90 | 91 | bool CurieMIDI::sendNoteOff(uint8_t note_, uint8_t velocity_, uint8_t channel_) 92 | { 93 | m_outputBuffer[2] = 0x80 | (channel_ & 0x0F); 94 | m_outputBuffer[3] = note_ & 0x7F; 95 | m_outputBuffer[4] = velocity_ & 0x7F; 96 | return send(5); 97 | } 98 | 99 | //-------------------------------------------------------------------------------------------------- 100 | 101 | bool CurieMIDI::sendControlChange(uint8_t cc_, uint8_t value_, uint8_t channel_) 102 | { 103 | m_outputBuffer[2] = 0xB0 | (channel_ & 0x0F); 104 | m_outputBuffer[3] = cc_ & 0x7F; 105 | m_outputBuffer[4] = value_ & 0x7F; 106 | return send(5); 107 | } 108 | 109 | //-------------------------------------------------------------------------------------------------- 110 | 111 | void CurieMIDI::onConnect(BLECentral& central_) 112 | { 113 | if (g_this == nullptr) 114 | { 115 | return; 116 | } 117 | g_this->notifyConnectionStatus(true); 118 | } 119 | 120 | //-------------------------------------------------------------------------------------------------- 121 | 122 | void CurieMIDI::onDisconnect(BLECentral& central_) 123 | { 124 | if (g_this == nullptr) 125 | { 126 | return; 127 | } 128 | g_this->notifyConnectionStatus(false); 129 | } 130 | 131 | //-------------------------------------------------------------------------------------------------- 132 | 133 | void CurieMIDI::onMidiReceived(BLECentral& central_, BLECharacteristic& characteristic_) 134 | { 135 | if (g_this == nullptr) 136 | { 137 | return; 138 | } 139 | 140 | unsigned packetSize = characteristic_.valueLength(); 141 | if (packetSize < 3) 142 | { 143 | return; 144 | } 145 | 146 | unsigned i = 0; 147 | unsigned timestampBase = (characteristic_.value()[i++] & 0x3F) << 7; 148 | unsigned timestamp = timestampBase; 149 | uint8_t timestampByte = 0; 150 | uint8_t statusByte = 0; 151 | 152 | while (i < packetSize) 153 | { 154 | if ((characteristic_.value()[i] & 0x80) > 0) 155 | { 156 | timestampByte = characteristic_.value()[i++] & 0x7F; 157 | } 158 | if ((characteristic_.value()[i] & 0x80) > 0) 159 | { 160 | statusByte = characteristic_.value()[i++]; 161 | } 162 | timestamp = timestampBase | timestampByte; 163 | uint8_t type = statusByte & 0xF0; 164 | uint8_t channel = statusByte & 0x0F; 165 | 166 | if (type == 0x80 || type == 0x90 || type == 0xB0) 167 | { 168 | if ((i + 1) >= packetSize) 169 | { 170 | return; 171 | } 172 | uint8_t data1 = characteristic_.value()[i++]; 173 | uint8_t data2 = characteristic_.value()[i++]; 174 | switch (type) 175 | { 176 | case 0x80: // Note off 177 | { 178 | g_this->notifyNoteOff(data1, data2, channel); 179 | break; 180 | } 181 | case 0x90: // Note on 182 | { 183 | if (data2 > 0) 184 | { 185 | g_this->notifyNoteOn(data1, data2, channel); 186 | } 187 | break; 188 | } 189 | case 0xB0: // Control change 190 | { 191 | g_this->notifyControlChange(data1, data2, channel); 192 | break; 193 | } 194 | } 195 | } 196 | } 197 | } 198 | 199 | //-------------------------------------------------------------------------------------------------- 200 | 201 | void CurieMIDI::notifyConnectionStatus(bool connected_) 202 | { 203 | if (m_callbackConnectionStatus) 204 | { 205 | m_callbackConnectionStatus(connected_); 206 | } 207 | } 208 | 209 | //-------------------------------------------------------------------------------------------------- 210 | 211 | void CurieMIDI::notifyNoteOff(uint8_t note_, uint8_t velocity_, uint8_t channel_) 212 | { 213 | if (m_callbackNote) 214 | { 215 | m_callbackNote(false, note_, velocity_, channel_); 216 | } 217 | } 218 | 219 | //-------------------------------------------------------------------------------------------------- 220 | 221 | void CurieMIDI::notifyNoteOn(uint8_t note_, uint8_t velocity_, uint8_t channel_) 222 | { 223 | if (m_callbackNote) 224 | { 225 | m_callbackNote(true, note_, velocity_, channel_); 226 | } 227 | } 228 | 229 | //-------------------------------------------------------------------------------------------------- 230 | 231 | void CurieMIDI::notifyControlChange(uint8_t cc_, uint8_t value_, uint8_t channel_) 232 | { 233 | if (m_callbackControlChange) 234 | { 235 | m_callbackControlChange(cc_, value_, channel_); 236 | } 237 | } 238 | 239 | //-------------------------------------------------------------------------------------------------- 240 | 241 | bool CurieMIDI::send(unsigned length_) 242 | { 243 | if (length_ > k_maxDataLength) 244 | { 245 | return false; 246 | } 247 | 248 | return m_characteristic.setValue(&m_outputBuffer[0], length_); 249 | } 250 | 251 | //-------------------------------------------------------------------------------------------------- 252 | 253 | } // namespace sl 254 | -------------------------------------------------------------------------------- /examples/SynaBLE/CurieMIDI.h: -------------------------------------------------------------------------------- 1 | /* 2 | ########## Copyright (C) 2016 Vincenzo Pacella 3 | ## ## Distributed under MIT license, see file LICENSE-SW 4 | ## ## or 5 | ## ## 6 | ########## ############################################################# shaduzlabs.com #####*/ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | //-------------------------------------------------------------------------------------------------- 14 | 15 | namespace sl 16 | { 17 | 18 | //-------------------------------------------------------------------------------------------------- 19 | 20 | class CurieMIDI 21 | { 22 | public: 23 | using tConnectionStatusCallback = std::function; 24 | using tNoteCallback = std::function; 25 | using tControlChangeCallback = std::function; 26 | 27 | CurieMIDI(); 28 | virtual ~CurieMIDI() = default; 29 | 30 | void begin(const char*); 31 | 32 | void setCallbackConnectionStatus(tConnectionStatusCallback); 33 | void setCallbackNote(tNoteCallback); 34 | void setCallbackControlChange(tControlChangeCallback); 35 | 36 | bool sendNoteOn(uint8_t note_, uint8_t velocity_ = 127, uint8_t channel_ = 0); 37 | bool sendNoteOff(uint8_t note_, uint8_t velocity_ = 0, uint8_t channel_ = 0); 38 | bool sendControlChange(uint8_t cc_, uint8_t value_, uint8_t channel_ = 0); 39 | 40 | private: 41 | 42 | static void onConnect(BLECentral&); 43 | static void onDisconnect(BLECentral&); 44 | static void onMidiReceived(BLECentral&, BLECharacteristic&); 45 | 46 | void notifyConnectionStatus(bool /*connected_*/); 47 | void notifyNoteOff(uint8_t /*note_*/, uint8_t /*velocity_*/, uint8_t /*channel_*/); 48 | void notifyNoteOn(uint8_t /*note_*/, uint8_t /*velocity_*/, uint8_t /*channel_*/); 49 | void notifyControlChange(uint8_t /*cc_*/, uint8_t /*value_*/, uint8_t /*channel_*/); 50 | 51 | static constexpr unsigned k_maxDataLength{128}; 52 | 53 | bool send(unsigned); 54 | 55 | uint8_t m_outputBuffer[k_maxDataLength]; 56 | 57 | BLEPeripheral m_peripheral; 58 | BLEService m_service; 59 | BLECharacteristic m_characteristic; 60 | 61 | tConnectionStatusCallback m_callbackConnectionStatus; 62 | tNoteCallback m_callbackNote; 63 | tControlChangeCallback m_callbackControlChange; 64 | }; 65 | 66 | //-------------------------------------------------------------------------------------------------- 67 | 68 | } // namespace sl 69 | -------------------------------------------------------------------------------- /examples/SynaBLE/SynaBLE.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ########## Copyright (C) 2016 Vincenzo Pacella 3 | ## ## Distributed under MIT license, see file LICENSE-SW 4 | ## ## or 5 | ## ## 6 | ########## ############################################################# shaduzlabs.com #####*/ 7 | 8 | /* 9 | 10 | This example is designed for the Arduino 101 board, equippend with the Intel Curie module and will 11 | not work with any other Arduino boards. 12 | 13 | The Synapse board rev. 1.0 needs a modification in order to work properly when used with an 14 | Arduino 101: you'll need to cut the 5V pin of the shield and then connect the Arduino 5V pin to the 15 | Synapse 5V pin through diode with low (~200mV) forward voltage drop (e.g. a Schottky like BAT42 or 16 | BAT85). 17 | 18 | The BLE MIDI I/O device will be visible with the name "SynaBLE". 19 | The MIDI in port will translate note events on channel 1 and 2 to a pitch CV (on output A and B 20 | respectively). 21 | The MIDI out port will send control change events (CV input A is cc#60 and CV input B is cc#61) when 22 | the input voltage on the respective input channels changes. Please note that Arduino 101 uses a 3.3V 23 | module, this means that anything above 3.3V on the CV inputs will just saturate to the maximum value 24 | 25 | */ 26 | 27 | #include 28 | 29 | #include "CurieMIDI.h" 30 | 31 | using namespace sl; 32 | 33 | //-------------------------------------------------------------------------------------------------- 34 | 35 | CurieMIDI g_device; 36 | 37 | int g_noteOnCountA{0}; 38 | int g_noteOnCountB{0}; 39 | 40 | uint8_t g_lastValueA{0xFF}; 41 | uint8_t g_lastValueB{0xFF}; 42 | 43 | //-------------------------------------------------------------------------------------------------- 44 | 45 | void connectionStatus(bool /*connected_*/); 46 | void noteEvent(bool /*noteOn_*/, uint8_t /*note_*/, uint8_t /*velocity_*/, uint8_t /*channel_*/); 47 | void ccEvent(uint8_t /*cc_*/, uint8_t /*value_*/, uint8_t /*channel_*/); 48 | uint16_t midiNoteToCV(uint8_t /*note_*/); 49 | 50 | //-------------------------------------------------------------------------------------------------- 51 | 52 | void setup() 53 | { 54 | SynapseShield.begin(); 55 | SynapseShield.setCVRange(Synapse::CVChannel::A, Synapse::Range::MinusFiveToFiveVolts); 56 | SynapseShield.setCVRange(Synapse::CVChannel::B, Synapse::Range::MinusFiveToFiveVolts); 57 | 58 | g_device.begin("SynaBLE"); 59 | g_device.setCallbackNote(noteEvent); 60 | g_device.setCallbackControlChange(ccEvent); 61 | g_device.setCallbackConnectionStatus(connectionStatus); 62 | } 63 | 64 | //-------------------------------------------------------------------------------------------------- 65 | 66 | void loop() 67 | { 68 | uint16_t voltageA = SynapseShield.readCV(Synapse::CVChannel::A); 69 | uint16_t voltageB = SynapseShield.readCV(Synapse::CVChannel::B); 70 | uint8_t midiValueA = (voltageA >> 3) & 0x7F; 71 | uint8_t midiValueB = (voltageB >> 3) & 0x7F; 72 | 73 | if (midiValueA != g_lastValueA) 74 | { 75 | g_lastValueA = midiValueA; 76 | g_device.sendControlChange(60, midiValueA); 77 | } 78 | 79 | if (midiValueB != g_lastValueB) 80 | { 81 | g_lastValueB = midiValueB; 82 | g_device.sendControlChange(61, midiValueB); 83 | } 84 | } 85 | 86 | //-------------------------------------------------------------------------------------------------- 87 | 88 | void connectionStatus(bool connected_) 89 | { 90 | } 91 | 92 | //-------------------------------------------------------------------------------------------------- 93 | 94 | void noteEvent(bool noteOn_, uint8_t note_, uint8_t velocity_, uint8_t channel_) 95 | { 96 | if (noteOn_ && velocity_ > 0) 97 | { 98 | if (channel_ == 0) 99 | { 100 | g_noteOnCountA++; 101 | SynapseShield.writeGate(Synapse::GateChannel::A, true); 102 | SynapseShield.writeCV(Synapse::CVChannel::A, midiNoteToCV(note_)); 103 | } 104 | else if (channel_ == 1) 105 | { 106 | g_noteOnCountB++; 107 | SynapseShield.writeGate(Synapse::GateChannel::B, true); 108 | SynapseShield.writeCV(Synapse::CVChannel::B, midiNoteToCV(note_)); 109 | } 110 | } 111 | else 112 | { 113 | if (channel_ == 0) 114 | { 115 | g_noteOnCountA--; 116 | if (g_noteOnCountA <= 0) 117 | { 118 | g_noteOnCountA = 0; 119 | SynapseShield.writeGate(Synapse::GateChannel::A, false); 120 | } 121 | } 122 | else if (channel_ == 1) 123 | { 124 | g_noteOnCountB--; 125 | if (g_noteOnCountB <= 0) 126 | { 127 | g_noteOnCountB = 0; 128 | SynapseShield.writeGate(Synapse::GateChannel::B, false); 129 | } 130 | } 131 | } 132 | } 133 | 134 | //-------------------------------------------------------------------------------------------------- 135 | 136 | void ccEvent(uint8_t cc_, uint8_t value_, uint8_t channel_) 137 | { 138 | } 139 | 140 | //-------------------------------------------------------------------------------------------------- 141 | 142 | uint16_t midiNoteToCV(uint8_t note_) 143 | { 144 | // Quick & dirty conversion, for more accuracy you should consider a LUT and some calibration... 145 | constexpr double noteIncrement = (4096 / 10.0f) / 12.0f; 146 | return static_cast(noteIncrement * note_); 147 | } 148 | -------------------------------------------------------------------------------- /examples/Test/Test.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ########## Copyright (C) 2016 Vincenzo Pacella 3 | ## ## Distributed under MIT license, see file LICENSE-SW 4 | ## ## or 5 | ## ## 6 | ########## ############################################################# shaduzlabs.com #####*/ 7 | 8 | #include 9 | 10 | using namespace sl; 11 | 12 | bool gate{false}; 13 | unsigned val{0}; 14 | 15 | void setup() 16 | { 17 | SynapseShield.begin(); 18 | } 19 | 20 | void loop() 21 | { 22 | SynapseShield.writeCV(Synapse::CVChannel::A, val); 23 | SynapseShield.writeCV(Synapse::CVChannel::B, val++); 24 | if (val > 4095) 25 | { 26 | val = 0; 27 | gate = !gate; 28 | SynapseShield.writeGate(Synapse::GateChannel::A, gate); 29 | SynapseShield.writeGate(Synapse::GateChannel::B, gate); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /hardware/synapse.cop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaduzlabs/synapse/439046c2564aa29225ee4122e73c3bd9b036003b/hardware/synapse.cop -------------------------------------------------------------------------------- /hardware/synapse.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaduzlabs/synapse/439046c2564aa29225ee4122e73c3bd9b036003b/hardware/synapse.pdf -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Synapse 3 | ####################################### 4 | # Class 5 | ####################################### 6 | 7 | Synapse KEYWORD1 8 | SynapseShield KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions 12 | ####################################### 13 | 14 | begin KEYWORD2 15 | readCV KEYWORD2 16 | writeCV KEYWORD2 17 | getCVRange KEYWORD2 18 | setCVRange KEYWORD2 19 | readGate KEYWORD2 20 | writeGate KEYWORD2 21 | gateInputInterrupt KEYWORD2 22 | 23 | ####################################### 24 | # Constants 25 | ####################################### 26 | 27 | ZeroToTenVolts LITERAL1 28 | MinusFiveToFiveVolts LITERAL1 29 | CVChannel LITERAL1 30 | GateChannel LITERAL1 31 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Synapse 2 | version=1.0.2 3 | author=Vincenzo Pacella 4 | maintainer=Vincenzo Pacella 5 | sentence=A library designed for the Synapse CV/Gate I/O shield 6 | paragraph=2 gate in, 2 gate out, 2 CV in (0-5V) and 2 CV out (-5 to 5V or 0 to 10V). This library must be used together with the Synapse shield 7 | category=Communication 8 | url=https://github.com/shaduzlabs/synapse 9 | architectures=* 10 | --------------------------------------------------------------------------------