├── doc ├── .gitkeep ├── kikpad_board1.jpg ├── kikpad_stlink.jpg ├── kikpad_flashbootloader.jpg └── kikpad_stlink_protection.jpg ├── kikpad.mod_LaunchPadMk3.bin ├── kikpad.mod_MPC_stm32f103rb.bin ├── kikpad.mod_demo_stm32f103rb.bin ├── kikpad.mod_force_stm32f103rb.bin ├── Rotary.h ├── Rotary.cpp ├── usb_midi.h ├── hardware_config.h ├── ringbuffer.h ├── usb_midi.cpp ├── README.md ├── kikpad.h ├── mod_kikpad_demo.h ├── usb_midi_device.h ├── mod_kikpad_MPC.h ├── mod_kikpad_MPCForce.h ├── mod_kikpad_MPCClipsTest.h ├── mod_eeprom.h ├── mod_kikpad_LaunchPadMk3.h ├── usb_midi_device.c ├── KIKPAD.xmm └── kikpad.ino /doc/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/kikpad_board1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheKikGen/kikpad/HEAD/doc/kikpad_board1.jpg -------------------------------------------------------------------------------- /doc/kikpad_stlink.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheKikGen/kikpad/HEAD/doc/kikpad_stlink.jpg -------------------------------------------------------------------------------- /kikpad.mod_LaunchPadMk3.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheKikGen/kikpad/HEAD/kikpad.mod_LaunchPadMk3.bin -------------------------------------------------------------------------------- /doc/kikpad_flashbootloader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheKikGen/kikpad/HEAD/doc/kikpad_flashbootloader.jpg -------------------------------------------------------------------------------- /kikpad.mod_MPC_stm32f103rb.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheKikGen/kikpad/HEAD/kikpad.mod_MPC_stm32f103rb.bin -------------------------------------------------------------------------------- /doc/kikpad_stlink_protection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheKikGen/kikpad/HEAD/doc/kikpad_stlink_protection.jpg -------------------------------------------------------------------------------- /kikpad.mod_demo_stm32f103rb.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheKikGen/kikpad/HEAD/kikpad.mod_demo_stm32f103rb.bin -------------------------------------------------------------------------------- /kikpad.mod_force_stm32f103rb.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheKikGen/kikpad/HEAD/kikpad.mod_force_stm32f103rb.bin -------------------------------------------------------------------------------- /Rotary.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Rotary encoder library for Arduino. 3 | */ 4 | 5 | #ifndef Rotary_h 6 | #define Rotary_h 7 | 8 | #include "Arduino.h" 9 | 10 | // Enable this to emit codes twice per step. 11 | // #define HALF_STEP 12 | 13 | class Rotary 14 | { 15 | uint8_t _state; 16 | uint8_t _pin1; 17 | uint8_t _pin2; 18 | 19 | #ifdef _R_HALF_STEP 20 | static const uint8_t _ttable[6][4]; 21 | #else 22 | static const uint8_t _ttable[7][4]; 23 | #endif 24 | 25 | public: 26 | enum encoderStatusValues { 27 | noDir = 0x00, // No complete step yet. 28 | dirCw = 0x10, // Clockwise step. 29 | dirCCw = 0x20 // Counter-clockwise step. 30 | }; 31 | 32 | enum encodeStateTableValues { 33 | start, 34 | #ifdef _R_HALF_STEP 35 | // Use the half-step state table (emits a code at 00 and 11) 36 | ccwBegin, cwBegin, startM, cwBeginM, ccwBeginM 37 | #else 38 | // Use the full-step state table (emits a code at 00 only) 39 | cwFinal, cwBegin, cwNext, ccwBegin, ccwFinal, ccwNext 40 | #endif 41 | }; 42 | 43 | Rotary(); 44 | void begin(uint8_t, uint8_t); 45 | uint8_t read(); 46 | 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /Rotary.cpp: -------------------------------------------------------------------------------- 1 | /* Rotary encoder handler for arduino. 2 | * 3 | * Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3. 4 | * Contact: bb@cactii.net 5 | * 6 | */ 7 | 8 | #include "Arduino.h" 9 | #include "Rotary.h" 10 | 11 | /* 12 | * The below state table has, for each state (row), the new state 13 | * to set based on the next encoder output. From left to right in, 14 | * the table, the encoder outputs are 00, 01, 10, 11, and the value 15 | * in that position is the new state to set. 16 | */ 17 | #ifdef _R_HALF_STEP 18 | // Use the half-step state table (emits a code at 00 and 11) 19 | const uint8_t Rotary::_ttable[6][4] = { 20 | {startM , cwBegin , ccwBegin, start }, // R_START (00) 21 | {startM | dirCCw, start , ccwbegin, start }, // R_CCW_BEGIN 22 | {startM | dirCw, cwBegin , start , start }, // R_CW_BEGIN 23 | {startM , ccwBeginM, cwBeginM, start }, // R_START_M (11) 24 | {startM , startM , cwBeginM, start | dirCw }, // R_CW_BEGIN_M 25 | {startM , ccwBeginM, startM , start | dirCCw },// R_CCW_BEGIN_M 26 | }; 27 | #else 28 | // Use the full-step state table (emits a code at 00 only) 29 | const uint8_t Rotary::_ttable[7][4] = { 30 | {start , cwBegin , ccwBegin, start }, // R_START 31 | {cwNext , start , cwFinal , start | dirCw }, // R_CW_FINAL 32 | {cwNext , cwBegin , start , start }, // cwBegin 33 | {cwNext , cwBegin , cwFinal , start }, // cwNext 34 | {ccwNext, start , ccwBegin, start }, // ccwBegin 35 | {ccwNext, ccwFinal, start , start | dirCCw }, // R_CCW_FINAL 36 | {ccwNext, ccwFinal, ccwBegin, start }, // R_CCW_NEXT 37 | }; 38 | #endif 39 | 40 | Rotary::Rotary() { 41 | } 42 | 43 | void Rotary::begin(uint8_t pin1, uint8_t pin2 ) { 44 | // Assign variables. 45 | _pin1 = pin1; 46 | _pin2 = pin2; 47 | 48 | // Set pins to input. 49 | pinMode(_pin1, INPUT); 50 | pinMode(_pin2, INPUT); 51 | 52 | // Initialise state. 53 | _state = start; 54 | } 55 | 56 | uint8_t Rotary::read() { 57 | // Grab state of input pins. 58 | uint8_t pinstate = (digitalRead(_pin2) << 1) | digitalRead(_pin1); 59 | // Determine new state from the pins and state table. 60 | _state = _ttable[_state & 0xf][pinstate]; 61 | // Return emit bits, ie the generated event. 62 | return _state & 0x30; 63 | } 64 | -------------------------------------------------------------------------------- /usb_midi.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | USBMIDIKLIK 4X4 - USB Midi advanced firmware for STM32F1 platform. 8 | Copyright (C) 2019 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the USBMIDIKLIK-4x4 distribution 12 | https://github.com/TheKikGen/USBMidiKliK4x4 13 | Copyright (c) 2019 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | USB MIDI LIBRARY adapted by TheKikGenLab from USB LeafLabs LLC. USB API : 16 | Perry Hung, Magnus Lundin,Donald Delmar Davis, Suspect Devices. 17 | GPL Licence. 18 | ----------------------------------------------------------------------------- 19 | Disclaimer. 20 | 21 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 22 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 23 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 24 | 25 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 26 | commercial closed code solution without the licensor permission. 27 | 28 | You are free to copy and redistribute the material in any medium or format, 29 | adapt, transform, and build upon the material. 30 | 31 | You must give appropriate credit, a link to the github site 32 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 33 | and indicate if changes were made. You may do so in any reasonable manner, 34 | but not in any way that suggests the licensor endorses you or your use. 35 | 36 | You may not apply legal terms or technological measures that legally restrict 37 | others from doing anything the license permits. 38 | 39 | You do not have to comply with the license for elements of the material 40 | in the public domain or where your use is permitted by an applicable exception 41 | or limitation. 42 | 43 | No warranties are given. The license may not give you all of the permissions 44 | necessary for your intended use. This program is distributed in the hope that 45 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 46 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 47 | */ 48 | 49 | #pragma once 50 | 51 | /** 52 | * @brief Wirish USB MIDI port (MidiUSB). 53 | */ 54 | 55 | #ifndef _WIRISH_USB_MIDI_H_ 56 | #define _WIRISH_USB_MIDI_H_ 57 | 58 | #define USB_MIDI 59 | #define USB_HARDWARE 60 | 61 | #include 62 | #include 63 | 64 | 65 | class USBMidi { 66 | private: 67 | 68 | public: 69 | // Len of packets. Direct access allowed. 70 | static const uint8_t CINToLenTable[16]; 71 | // Constructor 72 | USBMidi(); 73 | 74 | void begin(); 75 | void end(); 76 | uint32_t available(void); 77 | bool isTransmitting(void); 78 | uint32_t readPackets(const void *buf, uint32_t len); 79 | uint32_t readPacket(); 80 | uint32_t peekPacket(); 81 | void markPacketRead(); 82 | void writePacket(const uint32*); 83 | void writePackets(const void*, uint32); 84 | uint8_t isConnected(); 85 | uint8_t pending(); 86 | }; 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /hardware_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | USBMIDIKLIK 4X4 - USB Midi advanced firmware for STM32F1 platform. 8 | Copyright (C) 2019 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the USBMIDIKLIK-4x4 distribution 12 | https://github.com/TheKikGen/USBMidiKliK4x4 13 | Copyright (c) 2019 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | Disclaimer. 16 | 17 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 18 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 19 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 20 | 21 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 22 | commercial closed code solution without the licensor permission. 23 | 24 | You are free to copy and redistribute the material in any medium or format, 25 | adapt, transform, and build upon the material. 26 | 27 | You must give appropriate credit, a link to the github site 28 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 29 | and indicate if changes were made. You may do so in any reasonable manner, 30 | but not in any way that suggests the licensor endorses you or your use. 31 | 32 | You may not apply legal terms or technological measures that legally restrict 33 | others from doing anything the license permits. 34 | 35 | You do not have to comply with the license for elements of the material 36 | in the public domain or where your use is permitted by an applicable exception 37 | or limitation. 38 | 39 | No warranties are given. The license may not give you all of the permissions 40 | necessary for your intended use. This program is distributed in the hope that 41 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | 44 | */ 45 | 46 | #ifndef _HARDWARE_CONFIG_H_ 47 | #define _HARDWARE_CONFIG_H_ 48 | #pragma once 49 | 50 | // Macros to expand preprocessor variables 51 | #define __VALUE_TO_STRING(x) #x 52 | #define __VALUE(x) __VALUE_TO_STRING(x) 53 | #define __VAR_NAME_VALUE(var) #var " = " __VALUE(var) 54 | 55 | // BCD Version 56 | #define VERSION_MAJOR 2 57 | #define VERSION_MINOR 5 58 | #define BCD_VERSION ( VERSION_MAJOR << 8 ) + (VERSION_MINOR << 4) 59 | 60 | // About STM32F103xx microcontrollers : 61 | // Low-density devices have a flash memory between 16 and 32 Kbytes. 62 | // Medium-density devices have a flash memory between 32 and 128 Kbytes. 63 | // High-density devices have a a flash memory between 256 and 512 Kbytes. 64 | // Low and medium-density devices use a 1K bytes page size. 65 | // High-density density devices use a 2K bytes page size. 66 | 67 | // Flash memory base address (cf STM32F103xx datasheet page 34) 68 | #define EE_FLASH_MEMORY_BASE 0x08000000 69 | 70 | #ifdef MCU_STM32F103RB 71 | 72 | // Set EEPROM parameters for the STMF103RB (medium density) 73 | #define EE_PAGE_SIZE 0x400 74 | #define EE_FLASH_SIZEK 128 75 | #define EE_NBPAGE 1 76 | #define EE_CAPACITY EE_NBPAGE*EE_PAGE_SIZE 77 | 78 | // This drives the DISC pin for USB connect 79 | #define HAS_DISC_ON_PIN PA8 80 | 81 | #define SERIAL_INTERFACE_MAX 1 82 | #define SERIALS_PLIST &Serial1 83 | #define HARDWARE_TYPE "MIDIPLUS SMARTPAD" 84 | #define LED_CONNECT PC9 85 | 86 | #else 87 | #error "PLEASE CHOOSE STM32F103RB variant to compile ." 88 | #endif 89 | 90 | #pragma message(__VAR_NAME_VALUE(HARDWARE_TYPE)) 91 | 92 | // Reserve the last pages for the EEPROM emulation 93 | #define EE_BASE EE_FLASH_MEMORY_BASE + EE_FLASH_SIZEK * 1024 - EE_CAPACITY 94 | #define EE_PAGE_BASE ( EE_BASE - EE_FLASH_MEMORY_BASE ) / EE_PAGE_SIZE 95 | 96 | #define USBCABLE_INTERFACE_MAX USB_MIDI_IO_PORT_NUM 97 | 98 | // USBDM (USB -) PIN 99 | #define PIN_USBDM PA11 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /ringbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | * RING BUFFER TEMPLATE CLASS. 8 | * 9 | * Original work from Francois Best for the Arduino MIDI Library 10 | * license MIT - Copyright (c) 2016 Francois Best 11 | * 12 | * Modified by TheKikGen Labs for the UsbMidiKliK4x4 project. 13 | * 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy 15 | * of this software and associated documentation files (the "Software"), to deal 16 | * in the Software without restriction, including without limitation the rights 17 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the Software is 19 | * furnished to do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be included in 22 | * all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | * THE SOFTWARE. 31 | * 32 | * 33 | */ 34 | 35 | #pragma once 36 | 37 | 38 | template 39 | class RingBuffer 40 | { 41 | public: 42 | RingBuffer(); 43 | ~RingBuffer(); 44 | 45 | public: 46 | int available() const; 47 | 48 | public: 49 | void write(DataType inData) volatile ; 50 | void write(const DataType* inData, int inSize) volatile; 51 | void flush() volatile; 52 | 53 | public: 54 | DataType read() volatile; 55 | void readBytes(DataType* outData, int inSize) volatile; 56 | 57 | private: 58 | volatile DataType mData[Size]; 59 | volatile DataType* mWriteHead; 60 | volatile DataType* mReadHead; 61 | }; 62 | 63 | template 64 | RingBuffer::RingBuffer() 65 | : mWriteHead(mData) 66 | , mReadHead(mData) 67 | { 68 | memset((void*)mData, DataType(0), Size * sizeof(DataType)) ; 69 | } 70 | 71 | template 72 | RingBuffer::~RingBuffer() 73 | { 74 | } 75 | 76 | // ----------------------------------------------------------------------------- 77 | 78 | template 79 | int RingBuffer::available() const 80 | { 81 | if (mReadHead == mWriteHead) 82 | { 83 | return 0; 84 | } 85 | else if (mWriteHead > mReadHead) 86 | { 87 | return int(mWriteHead - mReadHead); 88 | } 89 | else 90 | { 91 | return int(mWriteHead - mData) + Size - int(mReadHead - mData); 92 | } 93 | } 94 | 95 | // ----------------------------------------------------------------------------- 96 | 97 | template 98 | void RingBuffer::write(DataType inData) volatile 99 | { 100 | *mWriteHead++ = inData; 101 | if (mWriteHead >= mData + Size) 102 | { 103 | mWriteHead = mData; 104 | } 105 | } 106 | 107 | template 108 | void RingBuffer::write(const DataType* inData, int inSize) volatile 109 | { 110 | for (int i = 0; i < inSize; ++i) 111 | { 112 | write(inData[i]); 113 | } 114 | } 115 | 116 | template 117 | void RingBuffer::flush() volatile 118 | { 119 | memset(mData, DataType(0), Size * sizeof(DataType)); 120 | mReadHead = mData; 121 | mWriteHead = mData; 122 | } 123 | 124 | // ----------------------------------------------------------------------------- 125 | 126 | template 127 | DataType RingBuffer::read() volatile 128 | { 129 | const DataType data = *mReadHead++; 130 | if (mReadHead >= mData + Size) 131 | { 132 | mReadHead = mData; 133 | } 134 | return data; 135 | } 136 | 137 | template 138 | void RingBuffer::readBytes(DataType* outData, int inSize) volatile 139 | { 140 | for (int i = 0; i < inSize; ++i) 141 | { 142 | outData[i] = read(); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /usb_midi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | USBMIDIKLIK 4X4 - USB Midi advanced firmware for STM32F1 platform. 8 | Copyright (C) 2019 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the USBMIDIKLIK-4x4 distribution 12 | https://github.com/TheKikGen/USBMidiKliK4x4 13 | Copyright (c) 2019 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | USB MIDI LIBRARY adapted by TheKikGenLab from USB LeafLabs LLC. USB API : 16 | Perry Hung, Magnus Lundin,Donald Delmar Davis, Suspect Devices. 17 | GPL Licence. 18 | ----------------------------------------------------------------------------- 19 | Disclaimer. 20 | 21 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 22 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 23 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 24 | 25 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 26 | commercial closed code solution without the licensor permission. 27 | 28 | You are free to copy and redistribute the material in any medium or format, 29 | adapt, transform, and build upon the material. 30 | 31 | You must give appropriate credit, a link to the github site 32 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 33 | and indicate if changes were made. You may do so in any reasonable manner, 34 | but not in any way that suggests the licensor endorses you or your use. 35 | 36 | You may not apply legal terms or technological measures that legally restrict 37 | others from doing anything the license permits. 38 | 39 | You do not have to comply with the license for elements of the material 40 | in the public domain or where your use is permitted by an applicable exception 41 | or limitation. 42 | 43 | No warranties are given. The license may not give you all of the permissions 44 | necessary for your intended use. This program is distributed in the hope that 45 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 46 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 47 | */ 48 | #include "hardware_config.h" 49 | #include "usb_midi.h" 50 | #include 51 | #include 52 | #include 53 | #include "usb_midi_device.h" 54 | #include 55 | 56 | #include 57 | 58 | // -------------------------------------------------------------------------------------- 59 | // USB MIDI Class 60 | // -------------------------------------------------------------------------------------- 61 | // This class was adapted and CLEANED from the USBMidi library 62 | // It can work for any device, but was optimized for the MIDI 4X4 board from Miditech 63 | // based on a STM32F103RC. 64 | 65 | // MIDI USB packet lenght 66 | const uint8_t USBMidi::CINToLenTable[] = 67 | { 68 | 0, // 0X00 Miscellaneous function codes. Reserved for future extensions. 69 | 0, // 0X01 Cable events.Reserved for future expansion. 70 | 2, // 0x02 Two-byte System Common messages like MTC, SongSelect, etc. 71 | 3, // 0x03 Three-byte System Common messages like SPP, etc. 72 | 3, // 0x04 SysEx starts or continues 73 | 1, // 0x05 Single-byte System Common Message or SysEx ends with following single byte. 74 | 2, // 0x06 SysEx ends with following two bytes. 75 | 3, // 0x07 SysEx ends withfollowing three bytes. 76 | 3, // 0x08 Note-off 77 | 3, // 0x09 Note-on 78 | 3, // 0x0A Poly-KeyPress 79 | 3, // 0x0B Control Change 80 | 2, // 0x0C Program Change 81 | 2, // 0x0D Channel Pressure 82 | 3, // 0x0E PitchBend Change 83 | 1 // 0x0F Single Byte 84 | }; 85 | // Constructor 86 | USBMidi::USBMidi(void) { 87 | 88 | } 89 | 90 | // BEGIN - Call that function in SETUP 91 | void USBMidi::begin() { 92 | 93 | #ifdef HAS_DISC_ON_PIN 94 | // Reset the USB interface on the MIDITECH 4x4 board. 95 | // The MIDI 4X4 has a DISC command, but the level logic is inverted 96 | // Then configure USB and Endpoints callbacks 97 | usb_midi_enable(PIN_MAP[HAS_DISC_ON_PIN].gpio_device, PIN_MAP[HAS_DISC_ON_PIN].gpio_bit,1); 98 | #else 99 | usb_midi_enable(NULL, 0,0); 100 | #endif 101 | 102 | } 103 | 104 | void USBMidi::end(void) { 105 | 106 | #ifdef HAS_DISC_ON_PIN 107 | usb_midi_disable(PIN_MAP[HAS_DISC_ON_PIN].gpio_device, PIN_MAP[HAS_DISC_ON_PIN].gpio_bit,0); 108 | #else 109 | usb_midi_disable(NULL,0,0); 110 | #endif 111 | 112 | } 113 | 114 | void USBMidi::writePacket(const uint32_t *pk) { 115 | this->writePackets(pk, 1); 116 | } 117 | 118 | void USBMidi::writePackets(const void *buf, uint32_t len) { 119 | if (!this->isConnected() || !buf) { 120 | return; 121 | } 122 | 123 | uint32_t txed = 0; 124 | uint32_t old_txed = 0; 125 | uint32_t start = millis(); 126 | 127 | uint32_t sent = 0; 128 | 129 | while (txed < len && (millis() - start < USB_MIDI_TIMEOUT)) { 130 | sent = usb_midi_tx((const uint32*)buf + txed, len - txed); 131 | txed += sent; 132 | if (old_txed != txed) { 133 | start = millis(); 134 | } 135 | old_txed = txed; 136 | } 137 | 138 | 139 | if (sent == MIDI_STREAM_EPSIZE) { 140 | while (usb_midi_is_transmitting() != 0) { 141 | } 142 | /* flush out to avoid having the pc wait for more data */ 143 | usb_midi_tx(NULL, 0); 144 | } 145 | } 146 | 147 | uint32_t USBMidi::available(void) { 148 | return usb_midi_data_available(); 149 | } 150 | 151 | bool USBMidi::isTransmitting(void) { 152 | return usb_midi_is_transmitting(); 153 | } 154 | 155 | uint32_t USBMidi::readPackets(const void *buf, uint32_t len) { 156 | if (!buf) { 157 | return 0; 158 | } 159 | 160 | uint32_t rxed = 0; 161 | while (rxed < len) { 162 | rxed += usb_midi_rx((uint32*)buf + rxed, len - rxed); 163 | } 164 | 165 | return rxed; 166 | } 167 | 168 | /* Blocks forever until 1 byte is received */ 169 | uint32_t USBMidi::peekPacket() { 170 | uint32_t p=0; 171 | usb_midi_peek(&p,1); 172 | return p; 173 | } 174 | 175 | void USBMidi::markPacketRead() { 176 | usb_midi_mark_read(1) ; 177 | } 178 | 179 | /* Blocks forever until 1 byte is received */ 180 | uint32_t USBMidi::readPacket() { 181 | uint32_t p=0; 182 | usb_midi_rx(&p,1); 183 | return p; 184 | } 185 | 186 | uint8_t USBMidi::pending(void) { 187 | return usb_midi_get_pending(); 188 | } 189 | 190 | uint8_t USBMidi::isConnected(void) { 191 | return usb_is_connected(USBLIB) && usb_is_configured(USBLIB); 192 | } 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | __ __| | | /_) | ___| | | 2 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 3 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 4 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 5 | 6 | # KIKPAD 7 | KiKPad : the Midiplus SmartPad reinvented ! 8 | 9 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=thekikgen@gmail.com&lc=FR&item_name=Donation+to+TheKikGen+projects&no_note=0&cn=¤cy_code=EUR&bn=PP-DonationsBF:btn_donateCC_LG.gif:NonHosted) 10 | 11 | SmartPad from Midiplus 12 | 13 | We did it again ! 14 | 15 | The KikGen Labs proposes again a firmware entirely rewritten for a commercial product. This time it is the **Smartpad** from Midiplus, which technical characteristics being very close to the Launchpad from Novation. That firmware allows now 64 pads, 64 RGB colors (instead 3 !), 8 encoders, all buttons operationals, and obviously usb midin in/out. It was written with the stm32duino platform, for the STM32F1 uC family. 16 | 17 | The perspectives offered by this new open firmware are vast: fully custom0ized Ableton Live controller, chord generator, autonomous sequencer, master keyboard , etc....For example, I have developped an Akai Surface controller emulation for the MPC Live, allowing my Kikpad to be used as it was the native hardware. 18 | 19 | [![tkglctrl video](https://img.youtube.com/vi/l4OzAfEUoIQ/0.jpg)](https://www.youtube.com/watch?v=l4OzAfEUoIQ) 20 | 21 | [![tkglctrl video](https://img.youtube.com/vi/PQ-h3_DM6EI/0.jpg)](https://www.youtube.com/watch?v=PQ-h3_DM6EI) 22 | 23 | [![tkglctrl video](https://img.youtube.com/vi/fVG7otydEA0/0.jpg)](https://www.youtube.com/watch?v=fVG7otydEA0) 24 | 25 | For a quick start, you can check the "demo" module, transforming the Kikpad in a small midi keyboard and controller. 26 | 27 | I want to thank here DerFetzer for his similar project (developed in Rust) and the inspiration for the launch of this project realized under "lockdown " ! have a look here : https://github.com/DerFetzer/open-cleverpad 28 | 29 | Solenoid made also a great job by collecting a lot of information on the Midiplus Smartpad : https://github.com/s0len0id/smartpad-tester 30 | 31 |
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License. 32 | 33 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=thekikgen@gmail.com&lc=FR&item_name=Donation+to+TheKikGen+projects&no_note=0&cn=¤cy_code=EUR&bn=PP-DonationsBF:btn_donateCC_LG.gif:NonHosted) 34 | 35 | # How to flash 36 | 37 | STLINK stick 38 | 39 | SmartPad from Midiplus 40 | 41 | (pictures : credit to "de douby"). 42 | 43 | First, you must upload the bootloader at address 0x08000000, with STM-LINK UTILITY. You need an ST-LINK V2 stick to do that. 44 | It is necessary to open the Smartpad (remove panel from the back), and connect the stick to the CN2 . 45 | 46 | Pinout is : 47 | 48 | * 1.GND 49 | * 2.SWDIO 50 | * 3.SWDCLK 51 | * 4.RESET 52 | 53 | You must also connect the USB cable for powering the Smartpad 54 | 55 | Download the bootloader firmware here : 56 | https://github.com/TheKikGen/stm32-tkg-hid-bootloader/releases 57 | 58 | Once the Kikpad is connected to the STLINK : 59 | 60 | 1/ Open the ST-LINK utility, and flash the bootloader "bootloader_only_binaries/tkg_hid_midiplus_smartpad.bin" file at 0x08000000. 61 | The original firmware from Midiplus is protected. So you will have to remove the protection check to erase and reflash the Kikpad firmware. 62 | 63 | SmartPad from Midiplus 64 | 65 | 66 | 2/ Pickup one of the pre-compiled binary 67 | 68 | [kikpad.mod_MPC_stm32f103rb.bin](https://github.com/TheKikGen/kikpad/blob/master/kikpad.mod_MPC_stm32f103rb.bin) 69 | [kikpad.mod_demo_stm32f103rb.bin](https://github.com/TheKikGen/kikpad/blob/master/kikpad.mod_demo_stm32f103rb.bin) 70 | [kikpad.mod_force_stm32f103rb.bin](https://github.com/TheKikGen/kikpad/blob/master/kikpad.mod_force_stm32f103rb.bin) 71 | 72 | 3/ Upload the mod_x bin file with STLINK at 0x8001000 or use tkg_flash utility to do that for you in HID mode 73 | 74 | 4/ Disconnect Kikpad from USB and remove STLINK connector. 75 | Plug again in your computer. You should see a "KIKPAD" midi device. 76 | 77 | 78 | # Hacking around 79 | 80 | About Arduino environment : I use the [ Roger's core](https://github.com/rogerclarkmelbourne/Arduino_STM32) for stm32. 81 | 82 | Follow the instructions there to install this core after the Arduino platform setup. 83 | Install the midixparser library in your Arduino libraries directory : https://github.com/TheKikGen/midiXparser/archive/refs/heads/master.zip 84 | Try to compile some examples from the "File/examples" menu to check if the ARM compiler works. 85 | 86 | The uC within the Smartpad is a STM32F103RBT6. I have adjusted the Roger's core configuration files for this uC + some tuning regarding upload methods. 87 | You will find [here]( https://drive.google.com/file/d/1jRcGOslFXzFAclSNBjrFhx0LfZYUpfoG/view?usp=sharing) my full stm32duino package you can copy in your (home)/Arduino/hardware directory. You need to restart the Aduino IDE after that. 88 | 89 | In the Arduino IDE, you need then to choose STM32F1 boards (stm32duino in sketchbook), then STM32F103RB variant. 90 | 91 | At first use the simple module mod_kikpad_demo.h that is a basic midi keyboard sending notes to the usb midi port. 92 | Uncomment from the line #176 93 | 94 | ```` 95 | // Kikpad functionnal module.Uncomment only one. 96 | 97 | #include "mod_kikpad_demo.h" 98 | //#include "mod_kikpad_MPC.h" 99 | //#include "mod_kikpad_MPCClipsTest.h" 100 | //#include "mod_kikpad_MPCClipLauncher.h" 101 | //#include "mod_kikpad_MPCForce.h" 102 | ```` 103 | 104 | The upload method (bootloader) changes the start address of your binary, during the linking process. So you need to choose "Tkg HID bootloader 3.1" in the tools/upload method menu. 105 | 106 | Compile with ctrl+R. If everything is ok, get the binary in your working directory with ctrl+alt+S, the file shoud be named "kikpad.ino.generic_stm32f103rb.bin". 107 | At this point, use the tkgflash utility on the command line to upload the binary into your kikpad the first time, or if update, switch the Kikpad to update mode (see below). 108 | 109 | For all modules, you can : 110 | - RESET = HOLD BT_CONTROL4 & MASTER8 THEN PRESS SET 111 | - UPDATE (bootloader mode) = HOLD MODE2 & MASTER7 THEN PRESS SET 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /kikpad.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | KIKPAD - Alternative firmware for the Midiplus Smartpad. 8 | Copyright (C) 2020 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the KIKPAD distribution 12 | https://github.com/TheKikGen/kikpad 13 | Copyright (c) 2020 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | Disclaimer. 16 | 17 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 18 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 19 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 20 | 21 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 22 | commercial closed code solution without the licensor permission. 23 | 24 | You are free to copy and redistribute the material in any medium or format, 25 | adapt, transform, and build upon the material. 26 | 27 | You must give appropriate credit, a link to the github site 28 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 29 | and indicate if changes were made. You may do so in any reasonable manner, 30 | but not in any way that suggests the licensor endorses you or your use. 31 | 32 | You may not apply legal terms or technological measures that legally restrict 33 | others from doing anything the license permits. 34 | 35 | You do not have to comply with the license for elements of the material 36 | in the public domain or where your use is permitted by an applicable exception 37 | or limitation. 38 | 39 | No warranties are given. The license may not give you all of the permissions 40 | necessary for your intended use. This program is distributed in the hope that 41 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | */ 44 | 45 | 46 | #ifndef _KIKPAD_H_ 47 | #define _KIKPAD_H_ 48 | 49 | // Timer refresh time for colors, scan lines, and encoders 50 | #define TIMER_RGB_PERIOD 300 51 | #define TIMER_USER_EVENTS_PERIOD 50 52 | #define DELAY_DMC 1 53 | #define LED_BANK_SIZE 32 54 | #define LED_BANK_MAX 8 55 | #define PAD_COLOR_DEPTH 3 56 | #define PAD_SIZE 64 57 | #define RB_UEVENT_SIZE 32*sizeof(UserEvent_t) 58 | 59 | // Sysex internal buffer size 60 | #define GLOBAL_DATA_BUFF_SIZE 64 61 | 62 | // LED ON recovery time in msec when no dedicated LED for CONNECT USB 63 | #define LED_CONNECT_USB_RECOVER_TIME_MILLIS 500 64 | 65 | // Boot modes magic words 66 | #define BOOT_BTL_MAGIC 0x424C 67 | #define BOOT_BTL_REGISTER DR10 68 | #define BOOT_CONFIG_MAGIC 0x0000 69 | 70 | // Some leds ON/Off patterns 71 | #define LED_BK_PATTERN1 0B11111111111111111111111111111111 72 | #define LED_BK_PATTERN2 0B01010101010101010101010101010101 73 | #define LED_BK_PATTERN3 0B10101010101010101010101010101010 74 | #define LED_BK_PATTERN4 0B11110000111100001111000011110000 75 | #define LED_BK_PATTERN5 0B00001111000011110000111100001111 76 | #define LED_BK_PATTERN6 0B11111111000000001111111100000000 77 | #define LED_BK_PATTERN7 0B10000001010000100010010000011000 78 | 79 | // GPIO fast macros 80 | #define FAST_DIGITAL_WRITE(pin,value) gpio_write_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, value) 81 | #define FAST_DIGITAL_READ(pin) gpio_read_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit) 82 | #define FAST_PINMODE(pin,mode) gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, (gpio_pin_mode) mode) 83 | 84 | // Led on / off values 85 | enum { 86 | OFF, 87 | ON 88 | } Switch; 89 | 90 | // Button hold threshold around 2s. 91 | #define BT_HOLD_THRESHOLD 500*2 92 | 93 | // Pins of DMC13 and LS138 driving leds 94 | enum { 95 | DMC_DAI = PB15, 96 | DMC_DCK = PB14, 97 | DMC_LAT = PB13, 98 | DMC_EN = PB12, 99 | LS_EN = PA2, 100 | LS_A0 = PA3, 101 | LS_A1 = PA4, 102 | LS_A2 = PA5, 103 | } LedDriverPins; 104 | 105 | // LS138 lines for 8 encoders 106 | enum { 107 | EC_LS_A0 = PC0, 108 | EC_LS_A1 = PC1, 109 | EC_LS_A2 = PC2, 110 | EC_KA = PA1, 111 | EC_KB = PA0 112 | }; 113 | 114 | // LS138 address to drive leds for pads H,L, and buttons bars 115 | enum { 116 | LED_BANK_HPAD_B, 117 | LED_BANK_HPAD_R, 118 | LED_BANK_HPAD_G, 119 | LED_BANK_LPAD_B, 120 | LED_BANK_LPAD_R, 121 | LED_BANK_LPAD_G, 122 | LED_BANK_BT1, 123 | LED_BANK_BT2, 124 | } LedBankIds; 125 | 126 | // EGA Colors rgbRGB enum 127 | typedef enum { 128 | BLACK = 0B000000, 129 | BLUE = 0B000001, 130 | GREEN = 0B000010, 131 | CYAN = 0B000011, 132 | RED = 0B000100, 133 | MAGENTA = 0B000101, 134 | YELLOW = 0B110110, 135 | WHITE = 0B000111, 136 | ORANGE = 52, 137 | LT_RED = 32, 138 | LT_GREEN = 50, 139 | GREY = 48, 140 | } ledColor_t; 141 | 142 | enum { 143 | COLOR_LINE, 144 | COLOR_COL, 145 | COLOR_CROSS 146 | } colorLineModEnum; 147 | 148 | // Macros to get a button or a pad sequential index in the 8x11 matrix 149 | // 0,0 is the upper left corner 150 | #define SCAN_IDX(r,c) ( 8 * r + c ) 151 | 152 | // Buttons masks in the appropirate led bank 153 | enum { 154 | BTMSK_VOLUME = 0B00000000100000000000000000000000, 155 | BTMSK_SENDA = 0B00000000010000000000000000000000, 156 | BTMSK_SENDB = 0B00000000001000000000000000000000, 157 | BTMSK_PAN = 0B00000000000100000000000000000000, 158 | BTMSK_CONTROL1 = 0B00000000000010000000000000000000, 159 | BTMSK_CONTROL2 = 0B00000000000001000000000000000000, 160 | BTMSK_CONTROL3 = 0B00000000000000100000000000000000, 161 | BTMSK_CONTROL4 = 0B00000000000000010000000000000000, 162 | 163 | BTMSK_UP = 0B10000000000000000000000000000000, 164 | BTMSK_DOWN = 0B01000000000000000000000000000000, 165 | BTMSK_LEFT = 0B00100000000000000000000000000000, 166 | BTMSK_RIGHT = 0B00010000000000000000000000000000, 167 | BTMSK_CLIP = 0B00001000000000000000000000000000, 168 | BTMSK_MODE1 = 0B00000100000000000000000000000000, 169 | BTMSK_MODE2 = 0B00000010000000000000000000000000, 170 | BTMSK_SET = 0B00000001000000000000000000000000, 171 | 172 | BTMSK_MS1 = 0B10000000000000000000000000000000, 173 | BTMSK_MS2 = 0B01000000000000000000000000000000, 174 | BTMSK_MS3 = 0B00100000000000000000000000000000, 175 | BTMSK_MS4 = 0B00010000000000000000000000000000, 176 | BTMSK_MS5 = 0B00001000000000000000000000000000, 177 | BTMSK_MS6 = 0B00000100000000000000000000000000, 178 | BTMSK_MS7 = 0B00000010000000000000000000000000, 179 | BTMSK_MS8 = 0B00000001000000000000000000000000, 180 | } ButtonLedBankMaskEnum; 181 | 182 | // Buttons bar names 183 | enum { 184 | BT_BAR_MASTER, 185 | BT_BAR_MODES, 186 | BT_BAR_CONTROLS, 187 | } ButtonsBarNames; 188 | 189 | // Events names for buttons (pads are managed differently) 190 | enum { 191 | BT_MS1, BT_MS2, BT_MS3, BT_MS4, BT_MS5, BT_MS6, BT_MS7, BT_MS8, 192 | BT_UP, BT_DOWN, BT_LEFT, BT_RIGHT,BT_CLIP, BT_MODE1, BT_MODE2, BT_SET, 193 | BT_VOLUME,BT_SENDA,BT_SENDB,BT_PAN, BT_CONTROL1,BT_CONTROL2,BT_CONTROL3,BT_CONTROL4, 194 | BT_NB_MAX // Max buttons number 195 | } ButtonEventNames; 196 | 197 | // User Event type 198 | typedef enum { 199 | EV_NONE, 200 | EV_ERROR, 201 | EV_BTN_PRESSED, 202 | EV_BTN_RELEASED, 203 | EV_BTN_HOLDED, 204 | EV_PAD_PRESSED, 205 | EV_PAD_RELEASED, 206 | EV_EC_CW, 207 | EV_EC_CCW, 208 | } UserEventType_t; 209 | 210 | 211 | // Midi Control change 212 | enum { 213 | CC_VOL = 7, 214 | CC_PAN = 10, 215 | CC_EFFECT1 = 12, 216 | CC_EFFECT2 = 13, 217 | CC_CUTOFF = 71, 218 | CC_RESO = 74, 219 | 220 | } MidiControlChangeEnum; 221 | 222 | // High level user event management 223 | typedef struct{ 224 | uint8_t ev; 225 | uint8_t d1; 226 | uint8_t d2; 227 | } __packed UserEvent_t; 228 | 229 | // Use this structure to send and receive packet to/from USB /serial/BUS 230 | typedef union { 231 | uint32_t i; 232 | uint8_t packet[4]; 233 | } __packed midiPacket_t; 234 | 235 | 236 | // Functions prototypes 237 | void SerialPrintf(const char *format, ...) ; 238 | void RGBMaskUpdate(uint8_t padIdx); 239 | void RGBMaskUpdateAll(void); 240 | void PadSetColor(uint8_t padIdx,uint8_t color); 241 | void PadColorsSave(void); 242 | void PadColorsRestore(uint8_t padIdx); 243 | void PadColorsBackground(uint8_t color); 244 | void PadColorsRow(uint8_t mode, uint8_t padIdx,uint8_t color); 245 | void PadSetLed(uint8_t padIdx,uint8_t state); 246 | void ButtonSetLed(uint8_t bt,uint8_t state); 247 | uint8_t ButtonGetLed(uint8_t bt); 248 | void ButtonsBarSetLedMsk(uint8_t btBar,uint32_t bitMsk); 249 | uint32_t ButtonsBarGetLedMsk(uint8_t btBar); 250 | boolean ButtonIsPressed(uint8_t bt); 251 | boolean ButtonIsHolded(uint8_t bt); 252 | boolean PadIsPressed(uint8_t padIdx); 253 | 254 | #endif 255 | -------------------------------------------------------------------------------- /mod_kikpad_demo.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | KIKPAD - Alternative firmware for the Midiplus Smartpad. 8 | Copyright (C) 2020 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the KIKPAD distribution 12 | https://github.com/TheKikGen/kikpad 13 | Copyright (c) 2020 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | Disclaimer. 16 | 17 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 18 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 19 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 20 | 21 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 22 | commercial closed code solution without the licensor permission. 23 | 24 | You are free to copy and redistribute the material in any medium or format, 25 | adapt, transform, and build upon the material. 26 | 27 | You must give appropriate credit, a link to the github site 28 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 29 | and indicate if changes were made. You may do so in any reasonable manner, 30 | but not in any way that suggests the licensor endorses you or your use. 31 | 32 | You may not apply legal terms or technological measures that legally restrict 33 | others from doing anything the license permits. 34 | 35 | You do not have to comply with the license for elements of the material 36 | in the public domain or where your use is permitted by an applicable exception 37 | or limitation. 38 | 39 | No warranties are given. The license may not give you all of the permissions 40 | necessary for your intended use. This program is distributed in the hope that 41 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | */ 44 | 45 | // KIKPAD_DEMO : A Midi out only basic keyboard 46 | 47 | #ifndef _KIKPAD_MODULE_H_ 48 | #define _KIKPAD_MODULE_H_ 49 | 50 | /////////////////////////////////////////////////////////////////////////////// 51 | // PARSE A RECEIVED USB MIDI PACKET 52 | /////////////////////////////////////////////////////////////////////////////// 53 | void KikpadMod_USBMidiParse(midiPacket_t *pk) 54 | { 55 | return; 56 | } 57 | 58 | /////////////////////////////////////////////////////////////////////////////// 59 | // PARSE A RECEIVED USER EVENT 60 | /////////////////////////////////////////////////////////////////////////////// 61 | void KikpadMod_ProcessUserEvent(UserEvent_t *ev){ 62 | // 32 pads defined as midi keyboard. Offset + 32 63 | // -1 = No Note 64 | // -2 = transpose - 12 65 | // -3 = transpose + 12 66 | static const int8_t keyBoardMap[] { 67 | // C#4 D#4 F#4 G#4 A#4 68 | -1, 0x3D,0x3F, -1 ,0x42,0x44,0x46, -1, 69 | // C4 D4 E4 F4 G4 A4 B4 C5 70 | 0x3C,0x3E,0x40,0x41,0x43,0x45,0x47,0x48, 71 | // C#3 D#3 F#3 G#3 A#3 72 | -2, 0x31,0x33, -1 ,0x36,0x38,0x3A, -3, 73 | // C3 D3 E3 F3 G3 A3 B3 C4 74 | 0x30,0x32,0x34,0x35,0x37,0x39,0x3B,0x3C 75 | }; 76 | static const int8_t keyBoardPadColors[]{ 77 | BLACK, BLUE, BLUE, BLACK, BLUE, BLUE, BLUE, BLACK, 78 | WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, 79 | GREEN, BLUE, BLUE, BLACK, BLUE, BLUE, BLUE, GREEN, 80 | WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, 81 | }; 82 | 83 | static boolean setupMode = true; 84 | static uint8_t currentSendMode=0; 85 | static const uint8_t encoderCC[8]={CC_VOL,CC_EFFECT1,CC_EFFECT2,CC_PAN,CC_CUTOFF,CC_RESO,0,0}; 86 | static uint8_t encoderVal[8] = {0,0,0,0,0,0,0,0}; 87 | static uint8_t midiChTranspose = 0; 88 | static uint8_t midiChannel = 0; 89 | static int8_t midiNoteTranspose = 0; 90 | 91 | midiPacket_t pk2; 92 | 93 | if (setupMode ) { 94 | ButtonsLedStates[0] = BTMSK_VOLUME ; 95 | ButtonsLedStates[1] = BTMSK_MS1 ; 96 | PadColorsBackground(BLACK); 97 | // Copy Keyboard pad colors on the lower 32 pads 98 | memcpy(PadColorsCurrent+32,keyBoardPadColors,32); 99 | RGBMaskUpdateAll(); 100 | 101 | PadLedStates[0] = PadLedStates[1] = 0XFFFFFFFF ; 102 | currentSendMode = 0; 103 | midiChannel = 0; 104 | setupMode = false; 105 | } 106 | 107 | uint8_t idx = SCAN_IDX(ev->d1,ev->d2); 108 | 109 | switch (ev->ev) { 110 | 111 | // Encoders Clock wise 112 | case EV_EC_CW: 113 | if ( ++encoderVal[idx] > 127 ) encoderVal[idx] = 127; 114 | pk2.packet[0] = 0x0B; 115 | pk2.packet[1] = 0xB0 + idx + midiChTranspose; 116 | pk2.packet[2] = encoderCC[currentSendMode]; 117 | pk2.packet[3] = encoderVal[idx]; 118 | MidiUSB.writePacket(&pk2.i); 119 | 120 | break; 121 | 122 | // Encoders Counter Clock wise 123 | case EV_EC_CCW: 124 | if ( encoderVal[idx] > 0 ) encoderVal[idx]--; 125 | pk2.packet[0] = 0x0B; 126 | pk2.packet[1] = 0xB0 + idx + midiChTranspose; 127 | pk2.packet[2] = encoderCC[currentSendMode]; 128 | pk2.packet[3] = encoderVal[idx]; 129 | MidiUSB.writePacket(&pk2.i); 130 | 131 | 132 | break; 133 | 134 | // Pad pressed and not released 135 | case EV_PAD_PRESSED: 136 | // Keyboard : pad 32 to 63 137 | if ( idx >= 32 ) { 138 | uint8_t i = idx-32; 139 | if ( keyBoardMap[i] >= 0 ) { 140 | PadColorsBackup[idx] = PadColorsCurrent[idx]; 141 | PadSetColor(idx, RED); 142 | // Send Note On 143 | pk2.packet[0] = 0x09; 144 | pk2.packet[1] = 0x90 + midiChannel + midiChTranspose; 145 | pk2.packet[2] = keyBoardMap[i] + midiNoteTranspose; 146 | pk2.packet[3] = 0x7F; 147 | MidiUSB.writePacket(&pk2.i); 148 | } 149 | else if (keyBoardMap[i] == -2 ) { 150 | midiNoteTranspose -= 12; 151 | if ( midiNoteTranspose > 0) { 152 | PadSetColor(48, GREEN); 153 | PadSetColor(55, CYAN); 154 | } 155 | else if ( midiNoteTranspose == 0) { 156 | PadSetColor(48, GREEN); 157 | PadSetColor(55, GREEN); 158 | } 159 | else { 160 | PadSetColor(48, CYAN); 161 | PadSetColor(55, GREEN); 162 | } 163 | } 164 | } 165 | 166 | break; 167 | 168 | // Pad released 169 | case EV_PAD_RELEASED: 170 | // Keyboard : pad 32 to 63 171 | if ( idx >= 32 ) { 172 | uint8_t i = idx-32; 173 | if ( keyBoardMap[i] >= 0 ) { 174 | PadSetColor(idx, PadColorsBackup[idx]); 175 | // Send Note Off 176 | pk2.packet[0] = 0x08; 177 | pk2.packet[1] = 0x80 + midiChannel + midiChTranspose; 178 | pk2.packet[2] = keyBoardMap[i] + midiNoteTranspose; 179 | pk2.packet[3] = 0x40; 180 | MidiUSB.writePacket(&pk2.i); 181 | } 182 | else if (keyBoardMap[i] == -2 ) { 183 | midiNoteTranspose += 12; 184 | if ( midiNoteTranspose > 0) { 185 | PadSetColor(48, GREEN); 186 | PadSetColor(55, CYAN); 187 | } 188 | else if ( midiNoteTranspose == 0) { 189 | PadSetColor(48, GREEN); 190 | PadSetColor(55, GREEN); 191 | } 192 | else { 193 | PadSetColor(48, CYAN); 194 | PadSetColor(55, GREEN); 195 | } 196 | } 197 | } 198 | 199 | break; 200 | 201 | // Button pressed and not released 202 | case EV_BTN_PRESSED: 203 | 204 | 205 | break; 206 | 207 | // Button released 208 | case EV_BTN_RELEASED: 209 | if ( idx >= BT_VOLUME && idx <= BT_CONTROL4 ) { 210 | // Switch off all buttons of bank 0, left bar 211 | ButtonsLedStates[0] &= 0xFF000000; 212 | ButtonSetLed(idx, ON); 213 | currentSendMode = idx - BT_VOLUME; 214 | } 215 | else 216 | if ( idx >= BT_MS1 && idx <= BT_MS8 ) { 217 | // Switch off all buttons of bank 1, right bar 218 | ButtonsLedStates[1] = 0; 219 | midiChannel = idx - BT_MS1; 220 | ButtonSetLed(idx, ON); 221 | } 222 | else 223 | if ( idx == BT_UP ) { 224 | if ( midiChTranspose ) { 225 | midiChTranspose = 0; 226 | ButtonSetLed(idx, OFF); 227 | } else { 228 | midiChTranspose = 8; 229 | ButtonSetLed(idx, ON); 230 | } 231 | } 232 | 233 | break; 234 | 235 | // Button pressed and holded more than 2s 236 | case EV_BTN_HOLDED: 237 | 238 | break; 239 | 240 | } 241 | 242 | } 243 | 244 | /////////////////////////////////////////////////////////////////////////////// 245 | // Kikpad module setup 246 | /////////////////////////////////////////////////////////////////////////////// 247 | static void KikpadMod_Setup() { 248 | 249 | 250 | } 251 | /////////////////////////////////////////////////////////////////////////////// 252 | // Kikpad module loop 253 | /////////////////////////////////////////////////////////////////////////////// 254 | static void KikpadMod_Loop() { 255 | 256 | } 257 | 258 | 259 | #endif 260 | -------------------------------------------------------------------------------- /usb_midi_device.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | USBMIDIKLIK 4X4 - USB Midi advanced firmware for STM32F1 platform. 8 | Copyright (C) 2019 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the USBMIDIKLIK-4x4 distribution 12 | https://github.com/TheKikGen/USBMidiKliK4x4 13 | Copyright (c) 2019 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | USB MIDI LIBRARY adapted by TheKikGenLab from USB LeafLabs LLC. USB API : 16 | Perry Hung, Magnus Lundin,Donald Delmar Davis, Suspect Devices. 17 | GPL Licence. 18 | ----------------------------------------------------------------------------- 19 | Disclaimer. 20 | 21 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 22 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 23 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 24 | 25 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 26 | commercial closed code solution without the licensor permission. 27 | 28 | You are free to copy and redistribute the material in any medium or format, 29 | adapt, transform, and build upon the material. 30 | 31 | You must give appropriate credit, a link to the github site 32 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 33 | and indicate if changes were made. You may do so in any reasonable manner, 34 | but not in any way that suggests the licensor endorses you or your use. 35 | 36 | You may not apply legal terms or technological measures that legally restrict 37 | others from doing anything the license permits. 38 | 39 | You do not have to comply with the license for elements of the material 40 | in the public domain or where your use is permitted by an applicable exception 41 | or limitation. 42 | 43 | No warranties are given. The license may not give you all of the permissions 44 | necessary for your intended use. This program is distributed in the hope that 45 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 46 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 47 | */ 48 | #ifndef _USB_MIDI_DEVICE_H_ 49 | #define _USB_MIDI_DEVICE_H_ 50 | #pragma once 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | /* Private headers */ 59 | #include "usb_lib_globals.h" 60 | #include "usb_reg_map.h" 61 | 62 | /* usb_lib headers */ 63 | #include "usb_type.h" 64 | #include "usb_core.h" 65 | #include "usb_def.h" 66 | 67 | // EEPROM parameters 68 | //#include "EE_Prm.h" 69 | 70 | #ifdef __cplusplus 71 | extern "C" { 72 | #endif 73 | 74 | // -------------------------------------------------------------------------------------- 75 | // USB MIDI API Functions prototypes 76 | // -------------------------------------------------------------------------------------- 77 | 78 | void usb_midi_set_vid_pid(uint16_t vid, uint16_t pid); 79 | void usb_midi_set_product_string(const char stringDescriptor[]); 80 | 81 | void usb_midi_enable(gpio_dev *disc_dev, uint8_t disc_bit, uint8_t level); 82 | void usb_midi_disable(gpio_dev *disc_dev, uint8_t disc_bit, uint8_t level); 83 | 84 | void usb_midi_putc(char ch); 85 | uint32_t usb_midi_tx(const uint32* buf, uint32_t len); 86 | uint32_t usb_midi_rx(uint32* buf, uint32_t len); 87 | uint32_t usb_midi_peek(uint32* buf, uint32_t len); 88 | uint32_t usb_midi_mark_read(uint32_t n_copied) ; 89 | 90 | uint32_t usb_midi_data_available(void); /* in RX buffer */ 91 | uint16_t usb_midi_get_pending(void); 92 | uint8_t usb_midi_is_transmitting(void); 93 | 94 | // -------------------------------------------------------------------------------------- 95 | // GLOBAL USB CONFIGURATION 96 | // -------------------------------------------------------------------------------------- 97 | #define USB_MIDI_TIMEOUT 50 98 | 99 | // -------------------------------------------------------------------------------------- 100 | // MIDI PORTS 101 | // -------------------------------------------------------------------------------------- 102 | // To define the number of Midi ports, uncomment the right line below. 103 | //#define USB_MIDI_4X4 104 | //#define USB_MIDI_8X8 105 | //#define USB_MIDI_12X12 106 | //#define USB_MIDI_16X16 107 | 108 | #if defined(USB_MIDI_16X16) 109 | #define USB_MIDI_IO_PORT_NUM 16 110 | #warning "16 USB midi ports defined" 111 | #elif defined(USB_MIDI_12X12) 112 | #define USB_MIDI_IO_PORT_NUM 12 113 | #warning "12 USB midi ports defined" 114 | #elif defined(USB_MIDI_8X8) 115 | #define USB_MIDI_IO_PORT_NUM 8 116 | #warning "8 USB midi ports defined" 117 | #elif defined(USB_MIDI_4X4) 118 | #define USB_MIDI_IO_PORT_NUM 4 119 | #warning "4 USB midi ports defined" 120 | #else 121 | #define USB_MIDI_2X2 122 | #define USB_MIDI_IO_PORT_NUM 2 123 | #warning "2 USB midi ports defined by default. Please check usb_midi_device.h" 124 | #endif 125 | 126 | // -------------------------------------------------------------------------------------- 127 | // DESCRIPTOR IDS 128 | // -------------------------------------------------------------------------------------- 129 | #define USB_MIDI_VENDORID 0x2912 130 | #define USB_MIDI_PRODUCTID 0x1975 131 | 132 | // String buffer max Size in the descriptor whitout tailing zero. 133 | // Define here if product string, pid vid are dynamically set, 134 | // otherwise the descriptor will be static. 135 | #define USB_MIDI_PRODUCT_STRING_SIZE 32 136 | 137 | // -------------------------------------------------------------------------------------- 138 | // DESCRIPTORS TYPES 139 | // -------------------------------------------------------------------------------------- 140 | 141 | #define USB_DESCRIPTOR_TYPE_CS_INTERFACE 0x24 142 | #define USB_DESCRIPTOR_TYPE_CS_ENDPOINT 0x25 143 | 144 | #define USB_DEVICE_CLASS_UNDEFINED 0x00 145 | #define USB_DEVICE_CLASS_CDC 0x02 146 | #define USB_DEVICE_SUBCLASS_UNDEFINED 0x00 147 | 148 | #define USB_INTERFACE_CLASS_AUDIO 0x01 149 | #define USB_INTERFACE_SUBCLASS_UNDEFINED 0x00 150 | #define USB_INTERFACE_AUDIOCONTROL 0x01 151 | #define USB_INTERFACE_AUDIOSTREAMING 0x02 152 | #define USB_INTERFACE_MIDISTREAMING 0x03 153 | 154 | /* MIDI Streaming class specific interfaces */ 155 | #define MIDI_IN_JACK 0x02 156 | #define MIDI_OUT_JACK 0x03 157 | 158 | #define MIDI_JACK_EMBEDDED 0x01 159 | #define MIDI_JACK_EXTERNAL 0x02 160 | 161 | //#define MAX_POWER (100 >> 1) 162 | #define USB_MIDI_MAX_POWER (100 >> 1) 163 | 164 | // -------------------------------------------------------------------------------------- 165 | // ENDPOINTS 166 | // -------------------------------------------------------------------------------------- 167 | 168 | #define USB_MIDI_NUM_ENDPTS 0x04 169 | 170 | // buffer table base address 171 | #define USB_MIDI_BTABLE_ADDRESS 0x0000 172 | 173 | // Every USB device must provide at least one control endpoint at address 0 called the 174 | // default endpoint or Endpoint0. This endpoint is bidirectional. 175 | // that is, the host can send data to the endpoint and receive data from it within one transfer. 176 | // The purpose of a control transfer is to enable the host to obtain device information, 177 | // configure the device, or perform control operations that are unique to the device. 178 | // Control Endpoint 179 | 180 | #define USB_MIDI_MAX_PACKET_SIZE 16 /* 64B, maximum for USB FS Devices */ 181 | 182 | #define USB_MIDI_CTRL_ENDP USB_EP0 183 | #define USB_MIDI_CTRL_RX_ADDR 0x40 184 | #define USB_MIDI_CTRL_TX_ADDR 0x80 185 | 186 | // MIDI data endpoints are used for transferring data. They are unidirectional, 187 | // has a type (control, interrupt, bulk, isochronous) and other properties. 188 | // All those properties are described in an endpoint descriptor. 189 | // The direction of an endpoint is based on the host. Thus, IN always refers 190 | // to transfers to the host from a device and OUT always refers to transfers 191 | // from the host to a device. 192 | 193 | #define MIDI_STREAM_EPSIZE 16 194 | 195 | #define MIDI_STREAM_IN_ENDP USB_EP1 196 | #define MIDI_STREAM_IN_EPADDR 0xC0 197 | 198 | #define MIDI_STREAM_OUT_ENDP USB_EP2 199 | #define MIDI_STREAM_OUT_EPADDR 0x100 200 | 201 | // -------------------------------------------------------------------------------------- 202 | // MIDI DEVICE DESCRIPTOR STRUCTURES 203 | // -------------------------------------------------------------------------------------- 204 | 205 | #define AC_CS_INTERFACE_DESCRIPTOR_SIZE(DataSize) (8 + DataSize) 206 | #define AC_CS_INTERFACE_DESCRIPTOR(DataSize) \ 207 | struct { \ 208 | uint8_t bLength; \ 209 | uint8_t bDescriptorType; \ 210 | uint8_t SubType; \ 211 | uint16_t bcdADC; \ 212 | uint16_t wTotalLength; \ 213 | uint8_t bInCollection; \ 214 | uint8_t baInterfaceNr[DataSize]; \ 215 | } __packed 216 | 217 | typedef struct { 218 | uint8_t bLength; 219 | uint8_t bDescriptorType; 220 | uint8_t SubType; 221 | uint16_t bcdADC; 222 | uint16_t wTotalLength; 223 | } __packed MS_CS_INTERFACE_DESCRIPTOR; 224 | 225 | typedef struct { 226 | uint8_t bLength; 227 | uint8_t bDescriptorType; 228 | uint8_t SubType; 229 | uint8_t bJackType; 230 | uint8_t bJackId; 231 | uint8_t iJack; 232 | } __packed MIDI_IN_JACK_DESCRIPTOR; 233 | 234 | typedef struct { 235 | uint8_t bLength; 236 | uint8_t bDescriptorType; 237 | uint8_t bEndpointAddress; 238 | uint8_t bmAttributes; 239 | uint16_t wMaxPacketSize; 240 | uint8_t bInterval; 241 | uint8_t bRefresh; 242 | uint8_t bSynchAddress; 243 | } __packed MIDI_USB_DESCRIPTOR_ENDPOINT; 244 | 245 | #define MIDI_OUT_JACK_DESCRIPTOR_SIZE(DataSize) (7 + 2*DataSize) 246 | #define MIDI_OUT_JACK_DESCRIPTOR(DataSize) \ 247 | struct { \ 248 | uint8_t bLength; \ 249 | uint8_t bDescriptorType; \ 250 | uint8_t SubType; \ 251 | uint8_t bJackType; \ 252 | uint8_t bJackId; \ 253 | uint8_t bNrInputPins; \ 254 | uint8_t baSourceId[DataSize]; \ 255 | uint8_t baSourcePin[DataSize]; \ 256 | uint8_t iJack; \ 257 | } __packed 258 | 259 | 260 | #define MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(DataSize) (4 + DataSize) 261 | #define MS_CS_BULK_ENDPOINT_DESCRIPTOR(DataSize) \ 262 | struct { \ 263 | uint8_t bLength; \ 264 | uint8_t bDescriptorType; \ 265 | uint8_t SubType; \ 266 | uint8_t bNumEmbMIDIJack; \ 267 | uint8_t baAssocJackID[DataSize]; \ 268 | } __packed 269 | 270 | 271 | #ifdef __cplusplus 272 | } 273 | #endif 274 | 275 | #endif 276 | -------------------------------------------------------------------------------- /mod_kikpad_MPC.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | KIKPAD - Alternative firmware for the Midiplus Smartpad. 8 | Copyright (C) 2020 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the KIKPAD distribution 12 | https://github.com/TheKikGen/kikpad 13 | Copyright (c) 2020 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | Disclaimer. 16 | 17 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 18 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 19 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 20 | 21 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 22 | commercial closed code solution without the licensor permission. 23 | 24 | You are free to copy and redistribute the material in any medium or format, 25 | adapt, transform, and build upon the material. 26 | 27 | You must give appropriate credit, a link to the github site 28 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 29 | and indicate if changes were made. You may do so in any reasonable manner, 30 | but not in any way that suggests the licensor endorses you or your use. 31 | 32 | You may not apply legal terms or technological measures that legally restrict 33 | others from doing anything the license permits. 34 | 35 | You do not have to comply with the license for elements of the material 36 | in the public domain or where your use is permitted by an applicable exception 37 | or limitation. 38 | 39 | No warranties are given. The license may not give you all of the permissions 40 | necessary for your intended use. This program is distributed in the hope that 41 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | */ 44 | 45 | // KIKPAD_MPC : MPC control surface emulation 46 | 47 | #ifndef _KIKPAD_MODULE_H_ 48 | #define _KIKPAD_MODULE_H_ 49 | 50 | // 4 group of 4 Qlinks 51 | static uint8_t QLinkCurrentGroup = 0 ; 52 | // 2 master groups of 8 qlinks 53 | static uint8_t QLinkCurrentMasterGroup = 0 ; 54 | 55 | 56 | /////////////////////////////////////////////////////////////////////////////// 57 | // Set Qlink group by simulating Qlink select button press 58 | /////////////////////////////////////////////////////////////////////////////// 59 | static void SetQLinkCurrentGroup(uint8_t group) { 60 | 61 | midiPacket_t pk; 62 | 63 | if (group == QLinkCurrentGroup || group > 3 ) return; 64 | 65 | if ( group > QLinkCurrentGroup ) 66 | group -=QLinkCurrentGroup; 67 | else 68 | group = 4 - QLinkCurrentGroup + group; 69 | 70 | for ( uint8_t i = 0 ; i < group ; i++ ) { 71 | // Simulate QLink select button 72 | pk.packet[0] = 0x09 ; 73 | pk.packet[1] = 0x90 ; 74 | pk.packet[2] = 0X00 ; 75 | pk.packet[3] = 0x7F ; 76 | MidiUSB.writePacket(&pk.i); 77 | // Released 78 | pk.packet[3] = 0x00 ; 79 | MidiUSB.writePacket(&pk.i); 80 | } 81 | 82 | } 83 | 84 | /////////////////////////////////////////////////////////////////////////////// 85 | // PARSE A RECEIVED USB MIDI PACKET 86 | /////////////////////////////////////////////////////////////////////////////// 87 | void KikpadMod_USBMidiParse(midiPacket_t *pk) 88 | { 89 | 90 | if ( pk->packet[0] == 0x0B ) { 91 | 92 | if ( pk->packet[1] == 0xB0 ) { 93 | 94 | switch (pk->packet[2]) { 95 | 96 | // TAP led 97 | case 0x35: 98 | if ( pk->packet[3] == 0x03 ) PadSetColor(0, RED); 99 | else PadSetColor(0, BLACK); 100 | break; 101 | 102 | // QLink select led 103 | // We use QLink led # to get the current QLink set set 104 | case 0x5A: case 0x5B: case 0x5C: case 0x5D: 105 | if ( pk->packet[3] == 0x03 ) QLinkCurrentGroup = pk->packet[2] - 0x5A; 106 | break; 107 | } 108 | } 109 | } 110 | 111 | return; 112 | } 113 | 114 | /////////////////////////////////////////////////////////////////////////////// 115 | // PARSE A RECEIVED USER EVENT 116 | /////////////////////////////////////////////////////////////////////////////// 117 | void KikpadMod_ProcessUserEvent(UserEvent_t *ev){ 118 | 119 | // Compute time between 2 events 120 | static unsigned long lastEventMicros = micros(); 121 | unsigned long eventDelayMicros = micros() - lastEventMicros; 122 | lastEventMicros = micros(); 123 | 124 | // To keep track of last event 125 | static uint8_t lastEventId = EV_NONE; 126 | static uint8_t lastEventIdx = 0; 127 | 128 | enum MPCControls { 129 | // Pads first and enum starts at 0 ! 130 | MPAD1 , MPAD2 , MPAD3 , MPAD4 , 131 | MPAD5 , MPAD6 , MPAD7 , MPAD8 , 132 | MPAD9 , MPAD10, MPAD11, MPAD12, 133 | MPAD13 , MPAD14, MPAD15, MPAD16, 134 | _END_MPADS, 135 | // Buttons 136 | MSHIFT, MMENU, MMAIN, MUNDO, 137 | MCOPY , MTAP, MREC, MOVERDUB, 138 | MSTOP , MPLAY, MPLAY_START, MPLUS, 139 | MMINUS, MNOTE, MFULL_LEVEL, MLEVEL_16, 140 | MERASE, MPAD_BANKA, MPAD_BANKB, MPAD_BANKC, 141 | MPAD_BANKD, MQLINK_SELECT,SAMPLE_EDIT, PAD_MIXER, 142 | CH_MIXER, 143 | _END_MBUTTONS, 144 | 145 | MNONE = 0xFF, 146 | } ; 147 | 148 | // Pads & simulated buttons note on/off message value 149 | static const uint8_t MPCPadMidiValueMap[] = { 150 | // Pads 151 | 0x25, 0x24, 0x2A, 0X52, 152 | 0x28, 0x26, 0x2E, 0x2C, 153 | 0x30, 0x2F, 0x2D, 0x2B, 154 | 0x31, 0x37, 0x33, 0x35, 155 | 0xFF,// end pad 156 | 157 | // Buttons 158 | 0x31, 0x7B, 0x34, 0X43, 159 | 0x7A, 0x35, 0x49, 0X50, 160 | 0x51, 0x52, 0x53, 0x36, 161 | 0x37, 0x0B, 0x27, 0x28, 162 | 0x09, 0x23, 0x24, 0x25, 163 | 0x26, 0x00, 0x06, 0x73, 164 | 0x74, 165 | 0xFF, // end buttons 166 | }; 167 | 168 | // // Encoders touch note on/off midi msg from 1 to 8 169 | // static const uint8_t MPCEncTouchMidiValueMap[] = { 170 | // 0x54, 0x55, 0x56, 0x57, 171 | // 0x58, 0x59, 0x5A, 0x5B, 172 | // } 173 | 174 | // Kipad MPC control affectations 175 | static const uint8_t MPCPadsMap[] = { 176 | MNONE, MNONE, MNONE, MNONE, MNONE, SAMPLE_EDIT, PAD_MIXER, CH_MIXER, 177 | MNONE, MNONE, MNONE, MNONE, MNONE, MNONE, MNONE, MNONE, 178 | MNONE, MNONE, MNONE, MNONE, MNONE, MNONE, MNONE, MNONE, 179 | MNONE, MNONE, MNONE, MNONE, MNONE, MNONE, MNONE, MNONE, 180 | MPAD13, MPAD14, MPAD15, MPAD16, MNONE, MNONE, MNONE, MNONE, 181 | MPAD9, MPAD10, MPAD11, MPAD12, MNONE, MNONE, MNONE, MNONE, 182 | MPAD5, MPAD6, MPAD7, MPAD8, MNONE, MOVERDUB, MNONE, MPLAY_START, 183 | MPAD1, MPAD2, MPAD3, MPAD4, MNONE, MREC , MSTOP, MPLAY, 184 | }; 185 | 186 | // Corresponding colors 187 | static const int8_t MPCPadsColors[] = { 188 | BLACK,BLACK,BLACK,BLACK,BLACK,RED,YELLOW,BLUE, 189 | BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK, 190 | BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK, 191 | BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK, 192 | RED ,RED ,RED ,RED ,BLACK,BLACK,BLACK,BLACK, 193 | RED ,RED ,RED ,RED ,BLACK,BLACK,BLACK,BLACK, 194 | RED ,RED ,RED ,RED ,BLACK,MAGENTA,BLACK,YELLOW, 195 | RED ,RED ,RED ,RED ,BLACK,RED, BLUE, GREEN, 196 | }; 197 | 198 | static boolean setupMode = true; 199 | static uint8_t encoderVal[8] = {0,0,0,0,0,0,0,0}; 200 | 201 | midiPacket_t pk; 202 | 203 | if (setupMode ) { 204 | setupMode = false; 205 | QLinkCurrentMasterGroup = 0 ; 206 | ButtonsLedStates[1] = BTMSK_MS1 ; 207 | PadColorsBackground(BLACK); 208 | memcpy(PadColorsCurrent,MPCPadsColors,sizeof(MPCPadsColors)); 209 | RGBMaskUpdateAll(); 210 | 211 | PadLedStates[0] = PadLedStates[1] = 0XFFFFFFFF ; 212 | if ( !ev ) return; 213 | } 214 | 215 | uint8_t idx = SCAN_IDX(ev->d1,ev->d2); 216 | 217 | switch (ev->ev) { 218 | 219 | // Encoders Clock wise 220 | case EV_EC_CW: 221 | { 222 | if ( ++encoderVal[idx] > 127 ) encoderVal[idx] = 127; 223 | 224 | SetQLinkCurrentGroup(idx / 4 + QLinkCurrentMasterGroup * 2); 225 | uint8_t i = idx % 4; 226 | 227 | // MPC Encoder simulate touch 228 | pk.packet[0] = 0x09 ; 229 | pk.packet[1] = 0x90 ; 230 | pk.packet[2] = 0X54 + i ; 231 | pk.packet[3] = 0x7F ; 232 | MidiUSB.writePacket(&pk.i); 233 | 234 | // MPC Encoder rotate right : B0 10 01 235 | pk.packet[0] = 0x0B ; 236 | pk.packet[1] = 0xB0 ; 237 | pk.packet[2] = 0X10 + i ; 238 | pk.packet[3] = 0x01 ; 239 | MidiUSB.writePacket(&pk.i); 240 | 241 | // MPC Encoder simulate untouch 242 | pk.packet[0] = 0x09 ; 243 | pk.packet[1] = 0x90 ; 244 | pk.packet[2] = 0X54 + i ; 245 | pk.packet[3] = 0x00 ; 246 | MidiUSB.writePacket(&pk.i); 247 | 248 | break; 249 | } 250 | 251 | // Encoders Counter Clock wise 252 | case EV_EC_CCW: 253 | { 254 | if ( encoderVal[idx] > 0 ) encoderVal[idx]--; 255 | 256 | SetQLinkCurrentGroup( idx / 4 + QLinkCurrentMasterGroup * 2); 257 | uint8_t i = idx % 4; 258 | 259 | // MPC Encoder simulate touch 260 | pk.packet[0] = 0x09 ; 261 | pk.packet[1] = 0x90 ; 262 | pk.packet[2] = 0X54 + i ; 263 | pk.packet[3] = 0x7F ; 264 | MidiUSB.writePacket(&pk.i); 265 | 266 | // MPC Encoder rotate left : B0 10 7F 267 | pk.packet[0] = 0x0B ; 268 | pk.packet[1] = 0xB0 ; 269 | pk.packet[2] = 0X10 + i ; 270 | pk.packet[3] = 0x7F ; 271 | MidiUSB.writePacket(&pk.i); 272 | 273 | // MPC Encoder simulate untouch 274 | pk.packet[0] = 0x09 ; 275 | pk.packet[1] = 0x90 ; 276 | pk.packet[2] = 0X54 + i ; 277 | pk.packet[3] = 0x00 ; 278 | MidiUSB.writePacket(&pk.i); 279 | 280 | break; 281 | } 282 | 283 | // Pad pressed and not released 284 | case EV_PAD_PRESSED: 285 | if ( MPCPadsMap[idx] < _END_MBUTTONS ) { 286 | PadColorsBackup[idx] = PadColorsCurrent[idx]; 287 | PadSetColor(idx, WHITE); 288 | if ( MPCPadsMap[idx] < _END_MPADS ) 289 | pk.packet[1] = 0x99; 290 | else 291 | pk.packet[1] = 0x90; // Pad simulating a button 292 | 293 | pk.packet[0] = 0x09; 294 | pk.packet[2] = MPCPadMidiValueMap[MPCPadsMap[idx]]; 295 | pk.packet[3] = 0x7F; 296 | MidiUSB.writePacket(&pk.i); 297 | } 298 | break; 299 | 300 | // Pad released 301 | case EV_PAD_RELEASED: 302 | if ( MPCPadsMap[idx] < _END_MBUTTONS ) { 303 | PadSetColor(idx, PadColorsBackup[idx]); 304 | if ( MPCPadsMap[idx] < _END_MPADS ) 305 | pk.packet[1] = 0x89; 306 | else 307 | pk.packet[1] = 0x80; // Pad simulating a button 308 | 309 | pk.packet[0] = 0x08; 310 | pk.packet[2] = MPCPadMidiValueMap[MPCPadsMap[idx]]; 311 | pk.packet[3] = 0x00; 312 | MidiUSB.writePacket(&pk.i); 313 | } 314 | break; 315 | 316 | // Button pressed and not released 317 | case EV_BTN_PRESSED: 318 | 319 | 320 | break; 321 | 322 | // Button released 323 | case EV_BTN_RELEASED: 324 | if ( idx >= BT_VOLUME && idx <= BT_CONTROL4 ) { 325 | // Switch off all buttons of bank 0, left bar 326 | ButtonsLedStates[0] &= 0xFF000000; 327 | ButtonSetLed(idx, ON); 328 | } 329 | else 330 | if ( idx >= BT_MS1 && idx <= BT_MS8 ) { 331 | // Switch off all buttons of bank 1, right bar 332 | ButtonsLedStates[1] = 0; 333 | ButtonSetLed(idx, ON); 334 | 335 | if ( idx == BT_MS1 ) QLinkCurrentMasterGroup = 0; 336 | else if ( idx == BT_MS2 ) QLinkCurrentMasterGroup = 1; 337 | 338 | 339 | } 340 | else 341 | if ( idx == BT_UP ) { 342 | } 343 | 344 | break; 345 | 346 | // Button pressed and holded more than 2s 347 | case EV_BTN_HOLDED: 348 | 349 | break; 350 | 351 | } 352 | 353 | // Keep track of last event 354 | lastEventId = ev->ev; 355 | lastEventIdx = idx; 356 | 357 | } 358 | 359 | /////////////////////////////////////////////////////////////////////////////// 360 | // Kikpad module setup 361 | /////////////////////////////////////////////////////////////////////////////// 362 | static void KikpadMod_Setup() { 363 | 364 | 365 | } 366 | /////////////////////////////////////////////////////////////////////////////// 367 | // Kikpad module loop 368 | /////////////////////////////////////////////////////////////////////////////// 369 | static void KikpadMod_Loop() { 370 | 371 | } 372 | 373 | #endif 374 | -------------------------------------------------------------------------------- /mod_kikpad_MPCForce.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | KIKPAD - Alternative firmware for the Midiplus Smartpad. 8 | Copyright (C) 2020 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the KIKPAD distribution 12 | https://github.com/TheKikGen/kikpad 13 | Copyright (c) 2020 TheKikGen Labs team. 14 | ------------------------------------------------------------------------------ 15 | Disclaimer. 16 | 17 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 18 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 19 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 20 | 21 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 22 | commercial closed code solution without the licensor permission. 23 | 24 | You are free to copy and redistribute the material in any medium or format, 25 | adapt, transform, and build upon the material. 26 | 27 | You must give appropriate credit, a link to the github site 28 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 29 | and indicate if changes were made. You may do so in any reasonable manner, 30 | but not in any way that suggests the licensor endorses you or your use. 31 | 32 | You may not apply legal terms or technological measures that legally restrict 33 | others from doing anything the license permits. 34 | 35 | You do not have to comply with the license for elements of the material 36 | in the public domain or where your use is permitted by an applicable exception 37 | or limitation. 38 | 39 | No warranties are given. The license may not give you all of the permissions 40 | necessary for your intended use. This program is distributed in the hope that 41 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | */ 44 | 45 | // KIKPAD_MPCForce : Akai Force controller midi implementation 46 | 47 | #ifndef _KIKPAD_MODULE_H_ 48 | #define _KIKPAD_MODULE_H_ 49 | 50 | #define ENCODERS_RELATIVE 51 | #define KIPAD_PAD_DEFAULT_VELOCITY 0x7F 52 | #define FORCE_PAD_OFFSET 0x36 53 | 54 | enum ForceButtonsCtrlValue { 55 | FORCE_NAVIGATE = 0x00, FORCE_KNOBS = 0X01, FORCE_MENU = 0x02, FORCE_MATRIX = 0x03, 56 | FORCE_NOTE = 0x04, FORCE_MASTER = 0x05, FORCE_CLIP = 0x09, FORCE_MIXER = 0x0B, 57 | FORCE_LOAD = 0x23, FORCE_SAVE = 0x24, FORCE_EDIT = 0x25, FORCE_DELETE = 0x26, 58 | FORCE_SHIFT = 0x31, FORCE_SELECT = 0x34, FORCE_TAP = 0x35, FORCE_PLUS = 0x36, 59 | FORCE_MINUS = 0x37, FORCE_LAUNCH_1 = 0x38, FORCE_LAUNCH_2 = 0x39, FORCE_LAUNCH_3 = 0x3A, 60 | FORCE_LAUNCH_4 = 0x3B, FORCE_LAUNCH_5 = 0x3C, FORCE_LAUNCH_6 = 0x3D, FORCE_LAUNCH_7 = 0x3E, 61 | FORCE_LAUNCH_8 = 0x3F, FORCE_UNDO = 0x43, FORCE_REC = 0x49, FORCE_STOP = 0x51, 62 | FORCE_PLAY = 0x52, FORCE_MUTE = 0x5B, FORCE_SOLO = 0x5C, FORCE_REC_ARM = 0x5D, 63 | FORCE_CLIP_STOP = 0x5E, FORCE_STOP_ALL = 0x5F, FORCE_UP = 0x70, FORCE_DOWN = 0x71, 64 | FORCE_LEFT = 0x72, FORCE_RIGHT = 0x73, FORCE_LAUNCH = 0x74, FORCE_STEP_SEQ = 0x75, 65 | FORCE_ARP = 0x76, FORCE_COPY = 0x7A, FORCE_ASSIGN_A = 0x7B, FORCE_ASSIGN_B = 0x7C, 66 | }; 67 | 68 | // Control mapping 69 | typedef struct{ 70 | int8_t ctrl; 71 | uint8_t type; // 0 : Button. 1 : column pad 72 | } __packed ForceButtonsMap_t; 73 | 74 | // F0 47 7F [3B] 65 00 04 [Pad #] [R] [G] [B] F7 75 | const uint8_t MPCSysexPadColor[] = {0xF0,0x47,0x7F,0xFF,0x65,0x00,0x04,0xFF,0xFF,0xFF,0xFF,0xF7 }; 76 | 77 | const ForceButtonsMap_t ForceButtonsMap[] { 78 | { FORCE_LAUNCH_1, 0 }, // BT_MS1, 79 | { FORCE_LAUNCH_2, 0 }, // BT_MS2, 80 | { FORCE_LAUNCH_3, 0 }, // BT_MS3, 81 | { FORCE_LAUNCH_4, 0 }, // BT_MS4, 82 | { FORCE_LAUNCH_5, 0 }, // BT_MS5, 83 | { FORCE_LAUNCH_6, 0 }, // BT_MS6, 84 | { FORCE_LAUNCH_7, 0 }, // BT_MS7, 85 | { FORCE_LAUNCH_8, 0 }, // BT_MS8, 86 | { FORCE_UP, 0 }, // BT_UP, 87 | { FORCE_DOWN, 0 }, // BT_DOWN, 88 | { FORCE_LEFT, 0 }, // BT_LEFT, 89 | { FORCE_RIGHT, 0 }, // BT_RIGHT, 90 | { FORCE_CLIP, 0 }, // BT_CLIP, 91 | { -1, 0 }, // BT_MODE1, 92 | { -1, 0 }, // BT_MODE2, 93 | { -1, 0 }, // BT_SET, 94 | { FORCE_MENU, 0 }, // BT_VOLUME, 95 | { -1, 0 }, // BT_SENDA, 96 | { -1, 0 }, // BT_SENDB, 97 | { -1, 0 }, // BT_PAN, 98 | { -1, 0 }, // BT_CONTROL1, 99 | { -1, 0 }, // BT_CONTROL2, 100 | { -1, 0 }, // BT_CONTROL3, 101 | { -1, 0 }, // BT_CONTROL4, 102 | }; 103 | 104 | // Column Pads: 105 | // 90 60-67 00/7f 106 | // Mute Pads: 107 | // 90 29-30 00/7f 108 | 109 | 110 | // Force Pads . Offet from left top corner (Kikpad is also leftop) 111 | 112 | 113 | // // Encoders touch note on/off midi msg from 1 to 8 114 | // static const uint8_t MPCEncTouchMidiValueMap[] = { 115 | // 0x54, 0x55, 0x56, 0x57, 116 | // 0x58, 0x59, 0x5A, 0x5B, 117 | // } 118 | 119 | // Corresponding colors 120 | static const int8_t MPCPadsColors[] = { 121 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 122 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 123 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 124 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 125 | 126 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 127 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 128 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 129 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 130 | }; 131 | 132 | // 2 group of 4 Qlinks 133 | static uint8_t QLinkCurrentBank = 0 ; 134 | 135 | // Tap led count (it is our clock) 136 | static uint8_t TapLedCount = 0; 137 | 138 | /////////////////////////////////////////////////////////////////////////////// 139 | // SEND a MPC mapped ENCODER CC midi message 140 | /////////////////////////////////////////////////////////////////////////////// 141 | static void ForceQlinkSendCC(uint8_t encoder,uint8_t ccValue) { 142 | 143 | midiPacket_t pk = { .i = 0 }; 144 | 145 | // Simulate Touch 146 | pk.packet[0] = 0x09 ; 147 | pk.packet[1] = 0x90 ; 148 | pk.packet[2] = 0x53 + encoder ; 149 | pk.packet[3] = 0x7F ; 150 | MidiUSB.writePacket(&pk.i); 151 | 152 | // Send CC value 153 | pk.packet[0] = 0x0B ; 154 | pk.packet[1] = 0xB0 ; 155 | pk.packet[2] = 0x10 + encoder ; 156 | pk.packet[3] = ccValue ; 157 | MidiUSB.writePacket(&pk.i); 158 | 159 | // Simulate untouch 160 | pk.packet[0] = 0x08 ; 161 | pk.packet[1] = 0x80 ; 162 | pk.packet[2] = 0x53 + encoder ; 163 | pk.packet[3] = 00 ; 164 | MidiUSB.writePacket(&pk.i); 165 | } 166 | 167 | /////////////////////////////////////////////////////////////////////////////// 168 | // SEND A MPC Mapped Button Note ON on cable 0 169 | /////////////////////////////////////////////////////////////////////////////// 170 | static void ForceButtonSend(uint8_t value,boolean pressed) { 171 | 172 | midiPacket_t pk = { .i = 0 }; 173 | 174 | pk.packet[0] = 0x0B ; 175 | pk.packet[1] = 0xB0 ; 176 | pk.packet[2] = value ; 177 | pk.packet[3] = ( pressed ? 0x7F:0x00 ) ; 178 | 179 | MidiUSB.writePacket(&pk.i); 180 | } 181 | 182 | // Pad On: 183 | // 99 36-75 00-7f 184 | // Pad Off: 185 | // 89 36-75 00 186 | // Pad Pressure: 187 | // a9 36-75 00-7f 188 | 189 | /////////////////////////////////////////////////////////////////////////////// 190 | // Kikpad pads as Force pads 191 | /////////////////////////////////////////////////////////////////////////////// 192 | static void ForcePadSend(uint8_t value,boolean pressed) { 193 | 194 | midiPacket_t pk = { .i = 0 }; 195 | 196 | if ( pressed) { 197 | pk.packet[0] = 0x09 ; 198 | pk.packet[1] = 0x99 ; 199 | pk.packet[3] = KIPAD_PAD_DEFAULT_VELOCITY ; 200 | 201 | } else { 202 | 203 | pk.packet[0] = 0x08 ; 204 | pk.packet[1] = 0x89 ; 205 | pk.packet[3] = 0 ; 206 | 207 | } 208 | pk.packet[2] = value ; 209 | 210 | MidiUSB.writePacket(&pk.i); 211 | } 212 | 213 | /////////////////////////////////////////////////////////////////////////////// 214 | // SET A PAD COLOR FROM RGB values 215 | /////////////////////////////////////////////////////////////////////////////// 216 | void SetPadColorRGB(uint8_t pad, uint8_t r, uint8_t g, uint8_t b ) { 217 | //6 bits xxrgbRGB (EGA) 218 | // 128 / 4 values 219 | // R Value 00 to 11 220 | uint8_t R = r / (128/4) ; 221 | uint8_t G = g / (128/4) ; 222 | uint8_t B = b / (128/4) ; 223 | 224 | uint8_t c = ( ( R & 0B10 ) << 1 ) + ( R & 0B01 << 5 ) + 225 | ( ( G & 0B10 ) ) + ( G & 0B01 << 4 ) + 226 | ( ( B >> 1 ) ) + ( B & 0B01 << 3 ); 227 | 228 | PadSetColor( pad, c); 229 | 230 | } 231 | /////////////////////////////////////////////////////////////////////////////// 232 | // PARSE SYSEX 233 | /////////////////////////////////////////////////////////////////////////////// 234 | void SysEx_Parse(uint8_t byte) 235 | { 236 | static unsigned sxMsgIdx = 0; 237 | static uint8_t sysexBuff[32]; 238 | 239 | boolean match = false; 240 | 241 | match = ( byte == MPCSysexPadColor[sxMsgIdx] ) || ( MPCSysexPadColor[sxMsgIdx] == 0xFF ) || match ; 242 | 243 | if ( match ) { 244 | sysexBuff[sxMsgIdx] = byte; 245 | 246 | // F0 47 7F [3B] 65 00 04 [Pad #] [R] [G] [B] F7 247 | if ( ++sxMsgIdx == sizeof(MPCSysexPadColor) ) { 248 | SetPadColorRGB(sysexBuff[7],sysexBuff[8],sysexBuff[9],sysexBuff[10]); 249 | 250 | sxMsgIdx = 0; 251 | return; 252 | } 253 | 254 | } else sxMsgIdx = 0; 255 | 256 | 257 | } 258 | 259 | /////////////////////////////////////////////////////////////////////////////// 260 | // PARSE A RECEIVED USB MIDI PACKET 261 | /////////////////////////////////////////////////////////////////////////////// 262 | void KikpadMod_USBMidiParse(midiPacket_t *pk) 263 | { 264 | 265 | uint8_t cin = pk->packet[0] & 0x0F ; 266 | 267 | if ( cin == 0x0B ) { 268 | 269 | if ( pk->packet[1] == 0xB0 ) { 270 | 271 | switch (pk->packet[2]) { 272 | 273 | // TAP led 274 | case 0x35: 275 | // Tap led on = 1 beat 276 | if ( pk->packet[3] == 0x03 ) { 277 | ButtonSetLed(BT_VOLUME, ON); 278 | } 279 | else { 280 | ButtonSetLed(BT_VOLUME, OFF); 281 | } 282 | 283 | break; 284 | } 285 | } 286 | } 287 | else 288 | // SYSEX 289 | if ( cin > 3 && cin < 8 ) { 290 | // CIN 5 exception : tune request 291 | if (pk->packet[1] == 0XF6) return ; 292 | 293 | uint8_t pklen = ( cin == 4 ? 3 : cin - 4) ; 294 | for ( uint8_t i = 0 ; i< pklen ; i++ ) { 295 | SysEx_Parse(pk->packet[ i + 1 ]) ; 296 | } 297 | 298 | 299 | } 300 | 301 | return; 302 | 303 | } 304 | 305 | /////////////////////////////////////////////////////////////////////////////// 306 | // PARSE A RECEIVED USER EVENT 307 | /////////////////////////////////////////////////////////////////////////////// 308 | void KikpadMod_ProcessUserEvent(UserEvent_t *ev){ 309 | 310 | // Compute time between 2 events 311 | static unsigned long lastEventMicros = micros(); 312 | unsigned long eventDelayMicros = micros() - lastEventMicros; 313 | lastEventMicros = micros(); 314 | 315 | // To keep track of last event 316 | static uint8_t lastEventId = EV_NONE; 317 | static uint8_t lastEventIdx = 0; 318 | 319 | static uint8_t encoderVal[8] = {0,0,0,0,0,0,0,0}; 320 | 321 | midiPacket_t pk; 322 | 323 | 324 | uint8_t idx = SCAN_IDX(ev->d1,ev->d2); 325 | 326 | switch (ev->ev) { 327 | 328 | // Encoders Clock wise (turn right) 329 | case EV_EC_CW: 330 | // Encoders Counter Clock wise (turn left) 331 | case EV_EC_CCW: 332 | { 333 | // Compute accelerator factor regarding the turn speed. 334 | uint8_t accel = 50 * 1000 / eventDelayMicros ; 335 | 336 | // Send the relative value 337 | if ( ev->ev == EV_EC_CCW ) 338 | ForceQlinkSendCC(idx, 0x7f - accel); 339 | else 340 | ForceQlinkSendCC(idx, 1 + accel); 341 | 342 | break; 343 | } 344 | 345 | // Pad pressed and not released 346 | case EV_PAD_PRESSED: 347 | // Pad released 348 | case EV_PAD_RELEASED: 349 | { 350 | ForcePadSend( FORCE_PAD_OFFSET + idx, (ev->ev == EV_PAD_PRESSED) ); 351 | 352 | break; 353 | } 354 | 355 | // Button pressed and not released 356 | case EV_BTN_PRESSED: 357 | if ( idx == BT_SET ) PadColorsBackground(BLACK); 358 | 359 | break; 360 | 361 | // Button released 362 | case EV_BTN_RELEASED: 363 | 364 | if ( ForceButtonsMap[idx].ctrl >= 0 ) { 365 | if ( ForceButtonsMap[idx].type == 0 ) { // Button 366 | // Press and release 367 | ForceButtonSend(ForceButtonsMap[idx].ctrl,true); 368 | ForceButtonSend(ForceButtonsMap[idx].ctrl,false); 369 | } 370 | } 371 | 372 | break; 373 | 374 | // Button pressed and holded more than 2s 375 | case EV_BTN_HOLDED: 376 | 377 | break; 378 | 379 | } 380 | 381 | // Keep track of last event 382 | lastEventId = ev->ev; 383 | lastEventIdx = idx; 384 | 385 | } 386 | 387 | /////////////////////////////////////////////////////////////////////////////// 388 | // Kikpad module setup 389 | /////////////////////////////////////////////////////////////////////////////// 390 | static void KikpadMod_Setup() { 391 | 392 | 393 | ButtonsLedStates[1] = BTMSK_MS1 ; 394 | PadColorsBackground(BLACK); 395 | memcpy(PadColorsCurrent,MPCPadsColors,sizeof(MPCPadsColors)); 396 | RGBMaskUpdateAll(); 397 | 398 | PadLedStates[0] = PadLedStates[1] = 0XFFFFFFFF ; 399 | 400 | } 401 | /////////////////////////////////////////////////////////////////////////////// 402 | // Kikpad module loop 403 | /////////////////////////////////////////////////////////////////////////////// 404 | static void KikpadMod_Loop() { 405 | 406 | } 407 | #endif 408 | -------------------------------------------------------------------------------- /mod_kikpad_MPCClipsTest.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | KIKPAD - Alternative firmware for the Midiplus Smartpad. 8 | Copyright (C) 2020 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the KIKPAD distribution 12 | https://github.com/TheKikGen/kikpad 13 | Copyright (c) 2020 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | Disclaimer. 16 | 17 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 18 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 19 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 20 | 21 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 22 | commercial closed code solution without the licensor permission. 23 | 24 | You are free to copy and redistribute the material in any medium or format, 25 | adapt, transform, and build upon the material. 26 | 27 | You must give appropriate credit, a link to the github site 28 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 29 | and indicate if changes were made. You may do so in any reasonable manner, 30 | but not in any way that suggests the licensor endorses you or your use. 31 | 32 | You may not apply legal terms or technological measures that legally restrict 33 | others from doing anything the license permits. 34 | 35 | You do not have to comply with the license for elements of the material 36 | in the public domain or where your use is permitted by an applicable exception 37 | or limitation. 38 | 39 | No warranties are given. The license may not give you all of the permissions 40 | necessary for your intended use. This program is distributed in the hope that 41 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | */ 44 | 45 | // KIKPAD_MPCClips : MPC control surface emulation - 64 Clips launcher 46 | 47 | #ifndef _KIKPAD_MODULE_H_ 48 | #define _KIKPAD_MODULE_H_ 49 | 50 | 51 | enum MPCControls { 52 | // Pads first and enum starts at 0 ! 53 | MPAD1 , MPAD2 , MPAD3 , MPAD4 , 54 | MPAD5 , MPAD6 , MPAD7 , MPAD8 , 55 | MPAD9 , MPAD10, MPAD11, MPAD12, 56 | MPAD13 , MPAD14, MPAD15, MPAD16, 57 | _END_MPADS, 58 | // Buttons 59 | MSHIFT, MMENU, MMAIN, MUNDO, 60 | MCOPY , MTAP, MREC, MOVERDUB, 61 | MSTOP , MPLAY, MPLAY_START, MPLUS, 62 | MMINUS, MNOTE, MFULL_LEVEL, MLEVEL_16, 63 | MERASE, MPAD_BANKA, MPAD_BANKB, MPAD_BANKC, 64 | MPAD_BANKD, MQLINK_SELECT,SAMPLE_EDIT, PAD_MIXER, 65 | CH_MIXER, 66 | _END_MBUTTONS, 67 | 68 | MNONE = 0xFF, 69 | } ; 70 | 71 | // Pads & simulated buttons note on/off message value 72 | static const uint8_t MPCPadMidiValueMap[] = { 73 | // Pads. 0x99 74 | 0x25, 0x24, 0x2A, 0X52, 75 | 0x28, 0x26, 0x2E, 0x2C, 76 | 0x30, 0x2F, 0x2D, 0x2B, 77 | 0x31, 0x37, 0x33, 0x35, 78 | 0xFF,// end pad 79 | 80 | // Buttons. 0x90 81 | 0x31, 0x7B, 0x34, 0X43, 82 | 0x7A, 0x35, 0x49, 0X50, 83 | 0x51, 0x52, 0x53, 0x36, 84 | 0x37, 0x0B, 0x27, 0x28, 85 | 0x09, 0x23, 0x24, 0x25, 86 | 0x26, 0x00, 0x06, 0x73, 87 | 0x74, 88 | 0xFF, // end buttons 89 | }; 90 | 91 | // // Encoders touch note on/off midi msg from 1 to 8 92 | // static const uint8_t MPCEncTouchMidiValueMap[] = { 93 | // 0x54, 0x55, 0x56, 0x57, 94 | // 0x58, 0x59, 0x5A, 0x5B, 95 | // } 96 | 97 | // Kipad MPC control affectations 98 | static const uint8_t MPCPadsMap[] = { 99 | MPAD13, MPAD14, MPAD15, MPAD16, MPAD13, MPAD14, MPAD15, MPAD16, 100 | MPAD9, MPAD10, MPAD11, MPAD12, MPAD9, MPAD10, MPAD11, MPAD12, 101 | MPAD5, MPAD6, MPAD7, MPAD8, MPAD5, MPAD6, MPAD7, MPAD8, 102 | MPAD1, MPAD2, MPAD3, MPAD4, MPAD1, MPAD2, MPAD3, MPAD4, 103 | MPAD13, MPAD14, MPAD15, MPAD16, MPAD13, MPAD14, MPAD15, MPAD16, 104 | MPAD9, MPAD10, MPAD11, MPAD12, MPAD9, MPAD10, MPAD11, MPAD12, 105 | MPAD5, MPAD6, MPAD7, MPAD8, MPAD5, MPAD6, MPAD7, MPAD8, 106 | MPAD1, MPAD2, MPAD3, MPAD4, MPAD1, MPAD2, MPAD3, MPAD4, 107 | }; 108 | 109 | // Corresponding colors 110 | static const int8_t MPCPadsColors[] = { 111 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 112 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 113 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 114 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 115 | 116 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 117 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 118 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 119 | RED ,GREEN,MAGENTA, BLUE, RED ,GREEN,MAGENTA, BLUE, 120 | }; 121 | 122 | // 4 group of 4 Qlinks 123 | static uint8_t QLinkCurrentGroup = 0 ; 124 | // 2 master groups of 8 qlinks 125 | static uint8_t QLinkCurrentMasterGroup = 0 ; 126 | 127 | // 4 banks of 16 pads, upddated with LED msg 128 | static uint8_t PadCurrentBank = 0 ; 129 | 130 | // Tap led count (it is our clock) 131 | static uint8_t TapLedCount = 0; 132 | 133 | // Clips Status On/Off 134 | static uint8_t MPCClipsStatus[64]; 135 | 136 | /////////////////////////////////////////////////////////////////////////////// 137 | // Set Qlink group by simulating Qlink select button press 138 | /////////////////////////////////////////////////////////////////////////////// 139 | static void SetQLinkCurrentGroup(uint8_t group) { 140 | 141 | midiPacket_t pk; 142 | 143 | if (group == QLinkCurrentGroup || group > 3 ) return; 144 | 145 | if ( group > QLinkCurrentGroup ) 146 | group -=QLinkCurrentGroup; 147 | else 148 | group = 4 - QLinkCurrentGroup + group; 149 | 150 | for ( uint8_t i = 0 ; i < group ; i++ ) { 151 | // Simulate QLink select button 152 | pk.packet[0] = 0x09 ; 153 | pk.packet[1] = 0x90 ; 154 | pk.packet[2] = 0X00 ; 155 | pk.packet[3] = 0x7F ; 156 | MidiUSB.writePacket(&pk.i); 157 | // Released 158 | pk.packet[3] = 0x00 ; 159 | MidiUSB.writePacket(&pk.i); 160 | } 161 | 162 | } 163 | 164 | /////////////////////////////////////////////////////////////////////////////// 165 | // Set Pad Bank by simulating pad bank select button press 166 | /////////////////////////////////////////////////////////////////////////////// 167 | static void SetPadCurrentBank(uint8_t bank) { 168 | 169 | if ( bank == PadCurrentBank || bank > 7 ) return; 170 | 171 | midiPacket_t pk; 172 | 173 | // Compute the # of the bank button 174 | uint8_t bt = (bank > 3 ) ? bank - 4: bank; 175 | 176 | uint8_t i = 1; 177 | // Check if we need a "double press" 178 | if ( bank > 3 && PadCurrentBank < 4 ) i++; 179 | 180 | for ( ; i > 0 ; i-- ) { 181 | // Simulate Pad select Bank button 182 | // 90 [23-26] 7F-00 183 | pk.packet[0] = 0x09 ; 184 | pk.packet[1] = 0x90 ; 185 | pk.packet[2] = 0X23 + bt ; 186 | pk.packet[3] = 0x7F ; 187 | MidiUSB.writePacket(&pk.i); 188 | 189 | // Released 190 | pk.packet[3] = 0x00 ; 191 | MidiUSB.writePacket(&pk.i); 192 | } 193 | 194 | } 195 | 196 | 197 | /////////////////////////////////////////////////////////////////////////////// 198 | // Set Pad Bank by simulating pad bank select button press 199 | /////////////////////////////////////////////////////////////////////////////// 200 | static void ClipsPadAnimate(uint8_t on) { 201 | 202 | for ( uint8_t i = 0 ; i < 64 ; i++ ) { 203 | 204 | if ( MPCClipsStatus[i] ) { 205 | 206 | if ( on) PadSetColor(i, MPCPadsColors[i]); 207 | else PadSetColor(i, BLACK); 208 | } 209 | 210 | } 211 | 212 | } 213 | 214 | /////////////////////////////////////////////////////////////////////////////// 215 | // PARSE A RECEIVED USB MIDI PACKET 216 | /////////////////////////////////////////////////////////////////////////////// 217 | void KikpadMod_USBMidiParse(midiPacket_t *pk) 218 | { 219 | 220 | if ( pk->packet[0] == 0x0B ) { 221 | 222 | if ( pk->packet[1] == 0xB0 ) { 223 | 224 | switch (pk->packet[2]) { 225 | 226 | // TAP led 227 | case 0x35: 228 | // Tap led on = 1 beat 229 | if ( pk->packet[3] == 0x03 ) { 230 | if ( ++TapLedCount > 4) TapLedCount = 1; 231 | ButtonSetLed(BT_VOLUME, ON); 232 | if ( TapLedCount == 1 ) ClipsPadAnimate(1); 233 | } 234 | else { 235 | ButtonSetLed(BT_VOLUME, OFF); 236 | if ( TapLedCount == 3 ) ClipsPadAnimate(0); 237 | } 238 | 239 | break; 240 | 241 | // QLink select led 242 | // We use QLink led # to get the current QLink set set 243 | case 0x5A: case 0x5B: case 0x5C: case 0x5D: 244 | if ( pk->packet[3] == 0x03 ) QLinkCurrentGroup = pk->packet[2] - 0x5A; 245 | break; 246 | 247 | // Pad led bank 248 | case 0x23: case 0x24: case 0x25: case 0x26: 249 | // 00 : Off, 01:dim red, 02:dim orange, 03:red,04:orange 250 | if ( pk->packet[3] == 0x03 || pk->packet[3] == 0x04 ) 251 | PadCurrentBank = pk->packet[2] - 0x23 + 4 * (pk->packet[3] - 3 ) ; 252 | break; 253 | 254 | } 255 | } 256 | } 257 | 258 | return; 259 | } 260 | 261 | /////////////////////////////////////////////////////////////////////////////// 262 | // PARSE A RECEIVED USER EVENT 263 | /////////////////////////////////////////////////////////////////////////////// 264 | void KikpadMod_ProcessUserEvent(UserEvent_t *ev){ 265 | 266 | // Compute time between 2 events 267 | static unsigned long lastEventMicros = micros(); 268 | unsigned long eventDelayMicros = micros() - lastEventMicros; 269 | lastEventMicros = micros(); 270 | 271 | // To keep track of last event 272 | static uint8_t lastEventId = EV_NONE; 273 | static uint8_t lastEventIdx = 0; 274 | 275 | 276 | static boolean setupMode = true; 277 | static uint8_t encoderVal[8] = {0,0,0,0,0,0,0,0}; 278 | 279 | midiPacket_t pk; 280 | 281 | if (setupMode ) { 282 | setupMode = false; 283 | QLinkCurrentMasterGroup = 0 ; 284 | ButtonsLedStates[1] = BTMSK_MS1 ; 285 | PadColorsBackground(BLACK); 286 | memcpy(PadColorsCurrent,MPCPadsColors,sizeof(MPCPadsColors)); 287 | RGBMaskUpdateAll(); 288 | memset(MPCClipsStatus,0,sizeof(MPCClipsStatus)); 289 | 290 | PadLedStates[0] = PadLedStates[1] = 0XFFFFFFFF ; 291 | if ( !ev ) return; 292 | } 293 | 294 | uint8_t idx = SCAN_IDX(ev->d1,ev->d2); 295 | 296 | switch (ev->ev) { 297 | 298 | // Encoders Clock wise 299 | case EV_EC_CW: 300 | { 301 | if ( ++encoderVal[idx] > 127 ) encoderVal[idx] = 127; 302 | 303 | SetQLinkCurrentGroup(idx / 4 + QLinkCurrentMasterGroup * 2); 304 | uint8_t i = idx % 4; 305 | 306 | // MPC Encoder simulate touch 307 | pk.packet[0] = 0x09 ; 308 | pk.packet[1] = 0x90 ; 309 | pk.packet[2] = 0X54 + i ; 310 | pk.packet[3] = 0x7F ; 311 | MidiUSB.writePacket(&pk.i); 312 | 313 | // MPC Encoder rotate right : B0 10 01 314 | pk.packet[0] = 0x0B ; 315 | pk.packet[1] = 0xB0 ; 316 | pk.packet[2] = 0X10 + i ; 317 | pk.packet[3] = 0x01 ; 318 | MidiUSB.writePacket(&pk.i); 319 | 320 | // MPC Encoder simulate untouch 321 | pk.packet[0] = 0x09 ; 322 | pk.packet[1] = 0x90 ; 323 | pk.packet[2] = 0X54 + i ; 324 | pk.packet[3] = 0x00 ; 325 | MidiUSB.writePacket(&pk.i); 326 | 327 | break; 328 | } 329 | 330 | // Encoders Counter Clock wise 331 | case EV_EC_CCW: 332 | { 333 | if ( encoderVal[idx] > 0 ) encoderVal[idx]--; 334 | 335 | SetQLinkCurrentGroup( idx / 4 + QLinkCurrentMasterGroup * 2); 336 | uint8_t i = idx % 4; 337 | 338 | // MPC Encoder simulate touch 339 | pk.packet[0] = 0x09 ; 340 | pk.packet[1] = 0x90 ; 341 | pk.packet[2] = 0X54 + i ; 342 | pk.packet[3] = 0x7F ; 343 | MidiUSB.writePacket(&pk.i); 344 | 345 | // MPC Encoder rotate left : B0 10 7F 346 | pk.packet[0] = 0x0B ; 347 | pk.packet[1] = 0xB0 ; 348 | pk.packet[2] = 0X10 + i ; 349 | pk.packet[3] = 0x7F ; 350 | MidiUSB.writePacket(&pk.i); 351 | 352 | // MPC Encoder simulate untouch 353 | pk.packet[0] = 0x09 ; 354 | pk.packet[1] = 0x90 ; 355 | pk.packet[2] = 0X54 + i ; 356 | pk.packet[3] = 0x00 ; 357 | MidiUSB.writePacket(&pk.i); 358 | 359 | break; 360 | } 361 | 362 | // Pad pressed and not released 363 | case EV_PAD_PRESSED: 364 | { 365 | 366 | // Set the bank regarding the pad idx 367 | // 4x16 pads in the MPC 368 | uint8_t padBank = (idx % 8 > 3 ) ? 1 : 0 ; 369 | if ( idx < 32 ) padBank += 2; 370 | SetPadCurrentBank(padBank); 371 | 372 | // Send the pad value 373 | //PadColorsBackup[idx] = PadColorsCurrent[idx]; 374 | PadSetColor(idx, WHITE); 375 | pk.packet[0] = 0x09; 376 | pk.packet[1] = 0x99; 377 | pk.packet[2] = MPCPadMidiValueMap[MPCPadsMap[idx]]; 378 | pk.packet[3] = 0x7F; 379 | MidiUSB.writePacket(&pk.i); 380 | 381 | // Set the Clip status. Not synched wiht MPC pads... 382 | // Need to decode sysex...later 383 | MPCClipsStatus[idx] = ! MPCClipsStatus[idx]; 384 | 385 | break; 386 | } 387 | 388 | // Pad released 389 | case EV_PAD_RELEASED: 390 | //PadSetColor(idx, PadColorsBackup[idx]); 391 | PadSetColor(idx, MPCPadsColors[idx]); 392 | pk.packet[0] = 0x08; 393 | pk.packet[1] = 0x89; 394 | pk.packet[2] = MPCPadMidiValueMap[MPCPadsMap[idx]]; 395 | pk.packet[3] = 0x00; 396 | MidiUSB.writePacket(&pk.i); 397 | break; 398 | 399 | // Button pressed and not released 400 | case EV_BTN_PRESSED: 401 | 402 | 403 | break; 404 | 405 | // Button released 406 | case EV_BTN_RELEASED: 407 | if ( idx >= BT_VOLUME && idx <= BT_CONTROL4 ) { 408 | // Switch off all buttons of bank 0, left bar 409 | ButtonsLedStates[0] &= 0xFF000000; 410 | ButtonSetLed(idx, ON); 411 | } 412 | else 413 | if ( idx >= BT_MS1 && idx <= BT_MS8 ) { 414 | // Switch off all buttons of bank 1, right bar 415 | ButtonsLedStates[1] = 0; 416 | ButtonSetLed(idx, ON); 417 | 418 | if ( idx == BT_MS1 ) QLinkCurrentMasterGroup = 0; 419 | else if ( idx == BT_MS2 ) QLinkCurrentMasterGroup = 1; 420 | 421 | 422 | } 423 | else 424 | if ( idx == BT_UP ) { 425 | } 426 | 427 | break; 428 | 429 | // Button pressed and holded more than 2s 430 | case EV_BTN_HOLDED: 431 | 432 | break; 433 | 434 | } 435 | 436 | // Keep track of last event 437 | lastEventId = ev->ev; 438 | lastEventIdx = idx; 439 | 440 | } 441 | 442 | /////////////////////////////////////////////////////////////////////////////// 443 | // Kikpad module setup 444 | /////////////////////////////////////////////////////////////////////////////// 445 | static void KikpadMod_Setup() { 446 | 447 | 448 | } 449 | /////////////////////////////////////////////////////////////////////////////// 450 | // Kikpad module loop 451 | /////////////////////////////////////////////////////////////////////////////// 452 | static void KikpadMod_Loop() { 453 | 454 | } 455 | #endif 456 | -------------------------------------------------------------------------------- /mod_eeprom.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | USBMIDIKLIK 4X4 - USB Midi advanced firmware for STM32F1 platform. 8 | Copyright (C) 2019 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the USBMIDIKLIK-4x4 distribution 12 | https://github.com/TheKikGen/USBMidiKliK4x4 13 | Copyright (c) 2019 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | Disclaimer. 16 | 17 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 18 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 19 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 20 | 21 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 22 | commercial closed code solution without the licensor permission. 23 | 24 | You are free to copy and redistribute the material in any medium or format, 25 | adapt, transform, and build upon the material. 26 | 27 | You must give appropriate credit, a link to the github site 28 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 29 | and indicate if changes were made. You may do so in any reasonable manner, 30 | but not in any way that suggests the licensor endorses you or your use. 31 | 32 | You may not apply legal terms or technological measures that legally restrict 33 | others from doing anything the license permits. 34 | 35 | You do not have to comply with the license for elements of the material 36 | in the public domain or where your use is permitted by an applicable exception 37 | or limitation. 38 | 39 | No warranties are given. The license may not give you all of the permissions 40 | necessary for your intended use. This program is distributed in the hope that 41 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | 44 | */ 45 | /////////////////////////////////////////////////////////////////////////////// 46 | // EEPROM UTILITIES 47 | //----------------------------------------------------------------------------- 48 | // Due to the unusual make process of Arduino platform, this file is directly 49 | // included in the main ino one. 50 | // EEPROM library is not used anymore due to low frequenry rate of update of 51 | // setting. To avoid conflicts with that library, some core ST flash FUNCTIONS 52 | // are included here directly in the source code. 53 | //----------------------------------------------------------------------------- 54 | // The FPEC block handles the program and erase operations of the Flash memory. 55 | // The FPEC consists of seven 32-bit registers. 56 | // . FPEC key register (FLASH_KEYR) 57 | // . Option byte key register (FLASH_OPTKEYR) 58 | // . Flash control register (FLASH_CR) 59 | // . Flash status register (FLASH_SR) 60 | // . Flash address register (FLASH_AR) 61 | // . Option byte register (FLASH_OBR) 62 | // . Write protection register (FLASH_WRPR) 63 | // 64 | // After reset, the FPEC block and so the FLASH_CR register are locked. 65 | // To unlock the FPEC block, where two key values (KEY1 and KEY2) must be 66 | // written to the FLASH_KEYR. Any wrong sequence locks up the FPEC block 67 | // and FLASH_CR register until the next reset. 68 | // 69 | // FLASH_SR register : 70 | // Bits 31:6 Reserved, must be kept cleared. 71 | // Bit 5EOP: End of operation 72 | // Set by hardware when a Flash operation (programming / erase) is completed. Reset by 73 | // writing a 1 74 | // Note: EOP is asserted at the end of each successful program or erase operation 75 | // Bit 4WRPRTERR: Write protection error 76 | // Set by hardware when programming a write-protected address of the Flash memory. 77 | // Reset by writing 1. 78 | // Bit 3 Reserved, must be kept cleared. 79 | // Bit 2PGERR: Programming error 80 | // Set by hardware when an address to be programmed contains a value different from 81 | // '0xFFFF' before programming. 82 | // Reset by writing 1. 83 | // Note: The STRT bit in the FLASH_CR register should be reset before starting a programming 84 | // operation. 85 | // Bit 1 Reserved, must be kept cleared 86 | // Bit 0BSY: Busy 87 | // This indicates that a Flash operation is in progress. This is set on the beginning of a Flash 88 | // operation and reset when the operation finishes or when an error occurs 89 | /////////////////////////////////////////////////////////////////////////////// 90 | 91 | #pragma once 92 | 93 | typedef enum 94 | { 95 | FLASH_COMPLETE = 0, 96 | FLASH_BUSY, 97 | FLASH_ERROR_WRITE, 98 | FLASH_ERROR_PG, 99 | FLASH_ERROR_WRP, 100 | FLASH_ERROR_OPT, 101 | FLASH_ERROR_BAD_PAGE_SIZE, 102 | FLASH_BAD_ADDRESS 103 | } FLASH_Status; 104 | 105 | #define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x0807FFFF)) 106 | #define FLASH_KEY1 0x45670123 107 | #define FLASH_KEY2 0xCDEF89AB 108 | 109 | 110 | /////////////////////////////////////////////////////////////////////////////// 111 | // FUNCTIONS PROTOTYPES 112 | /////////////////////////////////////////////////////////////////////////////// 113 | // Low level functions 114 | void __O_SMALL FLASH_Unlock(); 115 | void __O_SMALL FLASH_Lock(); 116 | void __O_SMALL FLASH_WaitEndOfOperation(); 117 | FLASH_Status __O_SMALL FLASH_ErasePage(uint32); 118 | FLASH_Status __O_SMALL FLASH_ProgramHalfWord(uint32, uint16); 119 | FLASH_Status __O_SMALL FLASH_WritePage(uint8_t,uint8_t *,uint16_t); 120 | boolean __O_SMALL FLASH_DiffPage(uint8_t,uint8_t *,uint16_t); 121 | 122 | // High level functions 123 | void __O_SMALL EE_PrmLoad(); 124 | void __O_SMALL EE_PrmSave(); 125 | void __O_SMALL EE_PrmInit(bool factorySettings=false); 126 | 127 | // EEPROM emulation functions 128 | void __O_SMALL EEPROM_Update(uint8_t* ,uint16_t ); 129 | void __O_SMALL EEPROM_Get(uint8_t* ,uint16_t, uint16_t); 130 | void __O_SMALL EEPROM_Format(); 131 | void __O_SMALL EEPROM_FlashMemoryDump(uint8_t , uint8_t ); 132 | 133 | /////////////////////////////////////////////////////////////////////////////// 134 | // STM32F103 flash memory FUNCTIONS 135 | /////////////////////////////////////////////////////////////////////////////// 136 | 137 | /////////////////////////////////////////////////////////////////////////////// 138 | // Unlock FPEC block 139 | /////////////////////////////////////////////////////////////////////////////// 140 | void FLASH_Unlock() 141 | { 142 | // If already unlocked, do nothing 143 | if ( FLASH_BASE->CR & FLASH_CR_LOCK ) { 144 | FLASH_BASE->KEYR = FLASH_KEY1; 145 | FLASH_BASE->KEYR = FLASH_KEY2; 146 | } 147 | } 148 | 149 | /////////////////////////////////////////////////////////////////////////////// 150 | // lock FPEC block 151 | /////////////////////////////////////////////////////////////////////////////// 152 | void FLASH_Lock() 153 | { 154 | FLASH_BASE->CR |= FLASH_CR_LOCK; 155 | } 156 | 157 | /////////////////////////////////////////////////////////////////////////////// 158 | // Wait end of current flash operaiton. return false if timeout reached. 159 | /////////////////////////////////////////////////////////////////////////////// 160 | void FLASH_WaitEndOfOperation() { 161 | while ( (FLASH_BASE->SR & FLASH_SR_BSY) ) ; 162 | } 163 | 164 | /////////////////////////////////////////////////////////////////////////////// 165 | // Erase a flash memory page 166 | //----------------------------------------------------------------------------- 167 | // . Check the BSY bit in the FLASH_SR register 168 | // . Set the PER bit in the FLASH_CR register 169 | // . Program the FLASH_AR register to select a page to erase 170 | // . Set the STRT bit in the FLASH_CR register 171 | // . Wait for the BSY bit to be reset 172 | // . Read the erased page and verify (eventually) 173 | /////////////////////////////////////////////////////////////////////////////// 174 | FLASH_Status FLASH_ErasePage(uint32 pageAddress) 175 | { 176 | if ( ! IS_FLASH_ADDRESS(pageAddress) ) return FLASH_BAD_ADDRESS; 177 | 178 | FLASH_WaitEndOfOperation(); 179 | 180 | FLASH_Status status = FLASH_COMPLETE; 181 | 182 | FLASH_Unlock(); FLASH_WaitEndOfOperation(); 183 | 184 | // Erase the page 185 | FLASH_BASE->CR |= FLASH_CR_PER; 186 | FLASH_BASE->AR = pageAddress; 187 | FLASH_BASE->CR = FLASH_CR_STRT | FLASH_CR_PER;; 188 | FLASH_WaitEndOfOperation(); 189 | 190 | // PER bit in CR register must be cleared to allows other operations on flash to be run after that 191 | FLASH_BASE->CR &= ~FLASH_CR_PER; 192 | FLASH_BASE->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR); 193 | 194 | FLASH_Lock(); FLASH_WaitEndOfOperation(); 195 | 196 | return status; 197 | } 198 | 199 | /////////////////////////////////////////////////////////////////////////////// 200 | // Program a half word 16 bits value 201 | //----------------------------------------------------------------------------- 202 | // . Check the BSY bit in the FLASH_SR register. 203 | // . Set the PG bit in the FLASH_CR register. 204 | // . Perform the data write (half-word) at the desired address. 205 | // . Wait for the BSY bit to be reset. 206 | // . Read the programmed value and verify. 207 | /////////////////////////////////////////////////////////////////////////////// 208 | FLASH_Status FLASH_ProgramHalfWord(uint32 writeAddress, uint16 value ) 209 | { 210 | 211 | if ( ! IS_FLASH_ADDRESS(writeAddress) ) return FLASH_BAD_ADDRESS; 212 | 213 | // If value not changed, nothing to do... 214 | if ( value == *(uint16*)writeAddress ) return FLASH_COMPLETE; 215 | 216 | FLASH_WaitEndOfOperation(); 217 | 218 | FLASH_Status status = FLASH_COMPLETE; 219 | FLASH_Unlock(); FLASH_WaitEndOfOperation() ; 220 | 221 | FLASH_BASE->CR |= FLASH_CR_PG; 222 | *(uint16*)writeAddress = value; 223 | 224 | FLASH_WaitEndOfOperation(); 225 | if (FLASH_BASE->SR & FLASH_SR_PGERR) status = FLASH_ERROR_PG; 226 | else if ( FLASH_BASE->SR & FLASH_SR_WRPRTERR )status = FLASH_ERROR_WRP; 227 | //Verify the value written 228 | else if ( value != *(volatile uint16_t *)writeAddress ) status = FLASH_ERROR_WRITE; 229 | 230 | FLASH_BASE->CR &= ~FLASH_CR_PG; 231 | FLASH_BASE->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR); 232 | 233 | FLASH_Lock(); FLASH_WaitEndOfOperation(); 234 | 235 | return status; 236 | } 237 | 238 | /////////////////////////////////////////////////////////////////////////////// 239 | // Write bytes to a page of the flash memory 240 | ////////////////////////////////////////////////////////////////////////////// 241 | FLASH_Status FLASH_WritePage(uint8_t page, uint8_t *bloc,uint16_t sz) 242 | { 243 | // The page must be prepared before the call. 244 | if ( sz < 2 || sz > EE_PAGE_SIZE ) return FLASH_ERROR_BAD_PAGE_SIZE; 245 | 246 | // No necessary to write when no differences 247 | if ( ! FLASH_DiffPage(page, bloc,sz) ) return FLASH_COMPLETE; 248 | 249 | FLASH_WaitEndOfOperation(); 250 | 251 | uint32_t volatile addressWrite = (EE_FLASH_MEMORY_BASE + page * EE_PAGE_SIZE) ; 252 | 253 | FLASH_Status status = FLASH_ErasePage(addressWrite); 254 | if ( status != FLASH_COMPLETE ) return status; 255 | 256 | // A byte is 8 bits, but flash write operation is mandatory 16 bit. 257 | uint16_t *pAddress = (uint16_t *) addressWrite; 258 | uint16_t *pValue = (uint16_t *) bloc ; 259 | 260 | uint16_t size = sz / 2 + ( sz % 2 ? 1:0 ); 261 | boolean cleanLast = ( (size * 2) > sz ); 262 | 263 | while(size) { 264 | // Write only if differences to preserve the Flash memory 265 | if ( *pAddress != *pValue ) { 266 | uint16_t flashValue = 0XFF00; // 0xFF is the "format" value 267 | // Write our last value without out of bound garbage byte 268 | if (size == 1 && cleanLast ) flashValue += *( (uint8_t*)pValue ) ; 269 | else flashValue = *pValue; 270 | status = FLASH_ProgramHalfWord((uint32_t)pAddress, flashValue); 271 | if ( status != FLASH_COMPLETE ) return status ; 272 | } 273 | pAddress++; pValue++; size--; 274 | } 275 | return status; 276 | } 277 | 278 | /////////////////////////////////////////////////////////////////////////////// 279 | // Check if differences exist between a page and a buffer 280 | ////////////////////////////////////////////////////////////////////////////// 281 | boolean FLASH_DiffPage(uint8_t page, uint8_t *bloc,uint16_t sz) 282 | { 283 | uint32_t addressRead = EE_FLASH_MEMORY_BASE + page * EE_PAGE_SIZE ; 284 | return ( memcmp((void *)addressRead,(void *)bloc,sz) != 0 ) ; 285 | } 286 | 287 | /////////////////////////////////////////////////////////////////////////////// 288 | // HIGH LEVEL LOAD PARAMETERS FROM EEPROM 289 | //---------------------------------------------------------------------------- 290 | // High level abstraction parameters read function 291 | ////////////////////////////////////////////////////////////////////////////// 292 | void EE_PrmLoad() 293 | { 294 | EEPROM_Get((uint8*)&EE_Prm,sizeof(EEPROM_Prm_t),0); 295 | } 296 | 297 | /////////////////////////////////////////////////////////////////////////////// 298 | // HIGH LEVEL SAVE PARAMETERS TO EEPROM 299 | //---------------------------------------------------------------------------- 300 | // High level abstraction parameters read function 301 | ////////////////////////////////////////////////////////////////////////////// 302 | void EE_PrmSave() 303 | { 304 | EEPROM_Update((uint8*)&EE_Prm,sizeof(EEPROM_Prm_t)) ; 305 | } 306 | 307 | /////////////////////////////////////////////////////////////////////////////// 308 | // Intialize parameters stores in EEPROM 309 | //---------------------------------------------------------------------------- 310 | // Retrieve global parameters from EEPROM, or Initalize it 311 | // If factorySetting is true, all settings will be forced to factory default 312 | ////////////////////////////////////////////////////////////////////////////// 313 | void EE_PrmInit(bool factorySettings) 314 | { 315 | 316 | // Read the EEPROM parameters structure 317 | EE_PrmLoad(); 318 | 319 | // If the signature is not found, of not the same version of parameters structure, 320 | // or new version, or new size then initialize (factory settings) 321 | 322 | // New firmware uploaded 323 | if ( memcmp(EE_Prm.TimestampedVersion,TimestampedVersion,sizeof(EE_Prm.TimestampedVersion)) ) 324 | { 325 | // Update timestamp and activate 326 | 327 | if ( memcmp( EE_Prm.signature,EE_SIGNATURE,sizeof(EE_Prm.signature) ) 328 | || EE_Prm.prmVersion != EE_PRMVER 329 | || ( EE_Prm.majorVersion != VERSION_MAJOR && EE_Prm.minorVersion != VERSION_MINOR ) 330 | || EE_Prm.size != sizeof(EEPROM_Prm_t) 331 | ) factorySettings = true; 332 | else 333 | // New build only. We keep existing settings but reboot in config mode 334 | { 335 | memcpy( EE_Prm.TimestampedVersion,TimestampedVersion,sizeof(EE_Prm.TimestampedVersion) ); 336 | 337 | // Default boot mode when new firmware uploaded only for this session. 338 | bootMagicWord = BOOT_CONFIG_MAGIC; 339 | return; 340 | 341 | } 342 | 343 | } 344 | 345 | // Force factory setting 346 | if ( factorySettings ) 347 | { 348 | memset( &EE_Prm,0,sizeof(EEPROM_Prm_t) ); 349 | memcpy( EE_Prm.signature,EE_SIGNATURE,sizeof(EE_Prm.signature) ); 350 | 351 | EE_Prm.majorVersion = VERSION_MAJOR; 352 | EE_Prm.minorVersion = VERSION_MINOR; 353 | 354 | EE_Prm.prmVersion = EE_PRMVER; 355 | EE_Prm.size = sizeof(EEPROM_Prm_t); 356 | 357 | memcpy( EE_Prm.TimestampedVersion,TimestampedVersion,sizeof(EE_Prm.TimestampedVersion) ); 358 | 359 | // Default I2C Device ID and bus mode 360 | EE_Prm.I2C_DeviceId = B_MASTERID; 361 | EE_Prm.I2C_BusModeState = B_DISABLED; 362 | 363 | ResetMidiRoutingRules(ROUTING_RESET_ALL); 364 | 365 | // Set BPM and disable all clocks 366 | // MTC was initialized to false above with the first memset 367 | SetMidiBpmClock(0x7F,DEFAULT_BPM); 368 | SetMidiEnableClock(0x7F,false); 369 | 370 | 371 | EE_Prm.vendorID = USB_MIDI_VENDORID; 372 | EE_Prm.productID = USB_MIDI_PRODUCTID; 373 | 374 | memcpy(EE_Prm.productString,USB_MIDI_PRODUCT_STRING,sizeof(USB_MIDI_PRODUCT_STRING)); 375 | 376 | //Write the whole param struct 377 | EE_PrmSave(); 378 | 379 | // Default boot mode when new firmware uploaded (not saved as one shot mode) 380 | bootMagicWord = BOOT_CONFIG_MAGIC; 381 | } 382 | } 383 | 384 | /////////////////////////////////////////////////////////////////////////////// 385 | // Update a bytes buffer to FLASH EMULATED EEPROM avoiding writes if possible. 386 | // Use it with significant size buffer and low frequency updates rate. 387 | ////////////////////////////////////////////////////////////////////////////// 388 | void EEPROM_Update(uint8_t* bloc,uint16_t sz) 389 | { 390 | // Nothing to do if size above Capacity 391 | if ( sz <2 || sz > EE_CAPACITY ) return; 392 | 393 | uint16_t nbPageWrite = ( sz / EE_PAGE_SIZE ) + ( sz % EE_PAGE_SIZE ? 1:0); 394 | 395 | for ( uint16_t page = 0 ; page != nbPageWrite ; page++ ) { 396 | uint16_t NbBytesWrite = (sz > EE_PAGE_SIZE ? EE_PAGE_SIZE : sz); 397 | FLASH_WritePage(page + EE_PAGE_BASE, bloc, NbBytesWrite ); 398 | bloc += NbBytesWrite; 399 | sz -= NbBytesWrite; 400 | } 401 | } 402 | 403 | /////////////////////////////////////////////////////////////////////////////// 404 | // Get byte buffer from EEPROM 405 | ////////////////////////////////////////////////////////////////////////////// 406 | void EEPROM_Get(uint8_t* bloc,uint16_t sz, uint16_t offset) 407 | { 408 | // Check if size above Capacity 409 | if ( sz > EE_CAPACITY ) return ; 410 | 411 | uint32_t addressRead = EE_BASE + offset; 412 | 413 | for (uint16_t idx = 0 ; idx < sz; idx++) { 414 | *bloc++ = (*(__IO uint8*)addressRead++); 415 | } 416 | 417 | } 418 | 419 | /////////////////////////////////////////////////////////////////////////////// 420 | // Format EEPROM 421 | ////////////////////////////////////////////////////////////////////////////// 422 | void EEPROM_Format() { 423 | 424 | uint32_t addressWrite = EE_BASE; 425 | 426 | for (uint8_t i = 0; i != EE_NBPAGE ; i ++ ) { 427 | FLASH_ErasePage(addressWrite); 428 | addressWrite += EE_PAGE_SIZE; 429 | } 430 | } 431 | 432 | /////////////////////////////////////////////////////////////////////////////// 433 | // DUMP FLASH MEMORY BY PAGE (First page is 0) 434 | // To DUMP EEPROM, use EE_PAGE_BASE,EE_NBPAGE. 16 pages max at a time.. 435 | ////////////////////////////////////////////////////////////////////////////// 436 | void EEPROM_FlashMemoryDump(uint8_t startPage, uint8_t nbPage) { 437 | 438 | if (nbPage > 16 ) return; 439 | 440 | uint32_t addressRead = EE_FLASH_MEMORY_BASE + startPage * EE_PAGE_SIZE ; 441 | uint8_t b; 442 | char asciiBuff[17]; 443 | uint8_t c=0; 444 | 445 | for (uint16_t idx = 0 ; idx < (EE_PAGE_SIZE * nbPage); idx++) { 446 | if ( idx % EE_PAGE_SIZE == 0 ) 447 | SerialPrintf("Page %4d - EE_PAGE_SIZE = 0x%04x.%n", startPage++,EE_PAGE_SIZE); 448 | if (c == 0 ) SerialPrintf("%08x : ",addressRead); 449 | b = (*(__IO uint8*)addressRead++); 450 | SerialPrintf("%02x ",b); 451 | asciiBuff[c++] = ( b >= 0x20 && b< 127? b : '.' ) ; 452 | if ( c == 16 ) { 453 | asciiBuff[c] = 0; 454 | c = 0; 455 | SerialPrintf(" | %s%n", &asciiBuff[0]); 456 | } 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /mod_kikpad_LaunchPadMk3.h: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | KIKPAD - Alternative firmware for the Midiplus Smartpad. 8 | Copyright (C) 2020 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the KIKPAD distribution 12 | https://github.com/TheKikGen/kikpad 13 | Copyright (c) 2020 TheKikGen Labs team. 14 | ------------------------------------------------------------------------------ 15 | Disclaimer. 16 | 17 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 18 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 19 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 20 | 21 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 22 | commercial closed code solution without the licensor permission. 23 | 24 | You are free to copy and redistribute the material in any medium or format, 25 | adapt, transform, and build upon the material. 26 | 27 | You must give appropriate credit, a link to the github site 28 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 29 | and indicate if changes were made. You may do so in any reasonable manner, 30 | but not in any way that suggests the licensor endorses you or your use. 31 | 32 | You may not apply legal terms or technological measures that legally restrict 33 | others from doing anything the license permits. 34 | 35 | You do not have to comply with the license for elements of the material 36 | in the public domain or where your use is permitted by an applicable exception 37 | or limitation. 38 | 39 | No warranties are given. The license may not give you all of the permissions 40 | necessary for your intended use. This program is distributed in the hope that 41 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | */ 44 | 45 | // KIKPAD LaunchPadMK3 : Make the Smartpad a Launchpad MK3 46 | 47 | #ifndef _KIKPAD_MODULE_H_ 48 | #define _KIKPAD_MODULE_H_ 49 | 50 | // We have a pre setup in that module to change usb name 51 | #define MODULE_PRE_SETUP 52 | 53 | #define ENCODERS_RELATIVE 54 | #define KIPAD_PAD_DEFAULT_VELOCITY 0x7F 55 | 56 | #define LP_DEVICE_PRO_MK3 0x0E 57 | #define LP_DEVICE_MINI_MK3 0x0D 58 | #define LP_DEVICE LP_DEVICE_MINI_MK3 59 | 60 | // Usb settings 61 | // 1235:0113 Focusrite-Novation 62 | #define LP_VID 0x1235 63 | #define LP_PID 0x0113 64 | #define LP_PRODUCT_STRING "Launchpad Mini MK3" 65 | #define LP_SERIAL "AYCY4WP1B12345" 66 | 67 | #define LP_LAYOUT_PGM_MODE 0x7F 68 | #define LP_CC_FADER 0xB4 69 | #define LP_FADER_OFFSET 0x00 70 | 71 | #define LP_FIRMWARE_VERSION 0x00,0x03,0x04,0x01 72 | #define LP_SYSEX_HEADER 0xF0,0x00,0x20,0x29,0x02,LP_DEVICE 73 | 74 | static const uint8_t LpSx_UniversalDeviceInquiry[] = { 0xF0, 0x7E, 0x7F, 0x06, 0x01, 0xF7 }; 75 | static const uint8_t LpSx_DeviceInquiryReply[] { 0xF0, 0x7E, 0x00, 0x06, 0x02, 0x00, 0x20, 0x29, 0x13, 0x01, 0x00, 0x00, LP_FIRMWARE_VERSION, 0xF7 }; 76 | 77 | // Set Led color 78 | static const uint8_t LpSX_SetPadColorHeader[] = {LP_SYSEX_HEADER, 0x03 }; 79 | 80 | // Daw mode enable/disable 81 | static const uint8_t LpSX_EnableDawModeHeader[] = {LP_SYSEX_HEADER,0x10 } ; 82 | 83 | // Select Layout 84 | static const uint8_t LpSX_SelectLayoutHeader[] = {LP_SYSEX_HEADER,0x00 } ; 85 | 86 | // Standalone / Daw mode 87 | static uint8_t DawModeEnabled = 0; 88 | // Current Layout 89 | static uint8_t CurrentLayout = 0x7F; // Programmer mode 90 | 91 | 92 | // CC buttons around the pads. 93 | // CC are those of the Launchpad Mini remapped with the Smartpad when possible 94 | enum LaunchpadButtonsCtrlValue { 95 | // Top buttons line on the Mini 96 | LP_UP = 0x5B,LP_DOWN = 0x5C,LP_LEFT = 0x5D,LP_RIGHT = 0x5E,LP_SESSION = 0x5F,LP_DRUMS = 0x60,LP_KEYS = 0x61,LP_USER = 0x62, 97 | // Right vertical buttons line on the Mini 98 | LP_LAUNCH_1 = 0x59,LP_LAUNCH_2 = 0x4F, LP_LAUNCH_3 = 0x45,LP_LAUNCH_4 = 0x3B,LP_LAUNCH_5 = 0x31, LP_LAUNCH_6 = 0x27, LP_LAUNCH_7 = 0x1D, LP_LAUNCH_8 = 0x13, 99 | // LEft vertical buttons line on the Pro (nothing on the mini), from top to bottom 100 | LP_LEFT_1 = 0x50, LP_LEFT_2 = 0x46, LP_LEFT_3 = 0x3C, LP_LEFT_4 = 0x32, LP_LEFT_5 = 0x28, LP_LEFT_6 = 0x1E, LP_LEFT_7 = 0x14, LP_LEFT_8 = 0x0A, 101 | }; 102 | 103 | // Control mapping 104 | typedef struct{ 105 | int8_t ctrl; 106 | int8_t led; // Associated button led index if different 107 | } __packed LPButtonsMap_t; 108 | 109 | const LPButtonsMap_t LPButtonsMap[] { 110 | { LP_LAUNCH_1, -1 }, // BT_MS1, 111 | { LP_LAUNCH_2, -1 }, // BT_MS2, 112 | { LP_LAUNCH_3, -1 }, // BT_MS3, 113 | { LP_LAUNCH_4, -1 }, // BT_MS4, 114 | { LP_LAUNCH_5, -1 }, // BT_MS5, 115 | { LP_LAUNCH_6, -1 }, // BT_MS6, 116 | { LP_LAUNCH_7, -1 }, // BT_MS7, 117 | { LP_LAUNCH_8, -1 }, // BT_MS8, 118 | { LP_UP, -1 }, // BT_UP, 119 | { LP_DOWN, -1 }, // BT_DOWN, 120 | { LP_LEFT, -1 }, // BT_LEFT, 121 | { LP_RIGHT, -1 }, // BT_RIGHT, 122 | { LP_SESSION, -1 }, // BT_CLIP, 123 | { LP_DRUMS, -1 }, // BT_MODE1, 124 | { LP_KEYS, -1 }, // BT_MODE2, 125 | { LP_USER, -1 }, // BT_SET, 126 | { LP_LEFT_1, -1 }, // BT_VOLUME, 127 | { LP_LEFT_2, -1 }, // BT_SENDA, 128 | { LP_LEFT_3, -1 }, // BT_SENDB, 129 | { LP_LEFT_4, -1 }, // BT_PAN, 130 | { LP_LEFT_5, -1 }, // BT_CONTROL1, 131 | { LP_LEFT_6, -1 }, // BT_CONTROL2, 132 | { LP_LEFT_7, -1 }, // BT_CONTROL3, 133 | { LP_LEFT_8, -1 }, // BT_CONTROL4, 134 | }; 135 | // Inverted table 136 | uint8_t LPButtonsMap_Inv[128]; 137 | 138 | ////////////////////////////////////////////////////////////////////////////// 139 | // Send a SYSEX midi message to USB Cable n 140 | /////////////////////////////////////////////////////////////////////////////// 141 | boolean USBMidi_SendSysExMsg( uint8_t cable, const uint8_t sxBuff[],uint16_t sz) 142 | { 143 | midiPacket_t pk { .i = 0}; 144 | uint8_t b=0; 145 | bool startSx=false; 146 | bool endSx=false; 147 | 148 | if (cable > 0x0F) return false; 149 | if ( sxBuff[0] != 0xF0 || sxBuff[sz-1] != 0xF7) return false; 150 | 151 | // Build sysex packets 152 | // Multiple Sysyex messages can be embedded in the buffer : 153 | // F0 nn ... nn F7 F0 nn ... nn F7 so we have to care about that. 154 | 155 | for ( uint16_t i = 0; i != sz ; i ++ ) { 156 | // Check integrity 157 | if ( sxBuff[i] == 0xF0) startSx = true; 158 | if ( sxBuff[i] == 0xF7) endSx = true; 159 | if (startSx) { 160 | pk.packet[++b] = sxBuff[i]; 161 | if ( b == 3 || endSx ) { 162 | pk.packet[0] = (endSx ? b + 4 : 4 ) + (cable << 4); 163 | MidiUSB.writePacket(&pk.i); 164 | if (endSx) startSx = endSx = false; 165 | b=0; pk.i = 0; 166 | } 167 | } 168 | } 169 | 170 | return true; 171 | } 172 | 173 | /////////////////////////////////////////////////////////////////////////////// 174 | // SEND a knob fader mapped CC midi message 175 | /////////////////////////////////////////////////////////////////////////////// 176 | // 8 faders. 177 | static void LPKnobSendCC(uint8_t knob,uint8_t ccValue) { 178 | 179 | midiPacket_t pk = { .i = 0 }; 180 | 181 | // Send CC value 182 | pk.packet[0] = 0x0B ; 183 | pk.packet[1] = LP_CC_FADER ; 184 | pk.packet[2] = LP_FADER_OFFSET + knob ; 185 | pk.packet[3] = ccValue ; 186 | MidiUSB.writePacket(&pk.i); 187 | } 188 | 189 | /////////////////////////////////////////////////////////////////////////////// 190 | // SEND A mapped Button Note ON on cable 0 191 | /////////////////////////////////////////////////////////////////////////////// 192 | static void LPButtonSend(uint8_t value,boolean pressed) { 193 | 194 | midiPacket_t pk = { .i = 0 }; 195 | 196 | pk.packet[0] = 0x0B ; 197 | pk.packet[1] = 0xB0 ; 198 | pk.packet[2] = value ; 199 | pk.packet[3] = ( pressed ? 0x7F:0x00 ) ; 200 | 201 | MidiUSB.writePacket(&pk.i); 202 | } 203 | 204 | /////////////////////////////////////////////////////////////////////////////// 205 | // Kikpad pads as LP pads 206 | /////////////////////////////////////////////////////////////////////////////// 207 | static void LPPadSend(uint8_t value,boolean pressed) { 208 | 209 | midiPacket_t pk = { .i = 0 }; 210 | 211 | if ( pressed) { 212 | pk.packet[0] = 0x09 ; 213 | pk.packet[1] = 0x90 ; 214 | pk.packet[3] = KIPAD_PAD_DEFAULT_VELOCITY ; 215 | 216 | } else { 217 | 218 | pk.packet[0] = 0x08 ; 219 | pk.packet[1] = 0x80 ; 220 | pk.packet[3] = 0 ; 221 | 222 | } 223 | pk.packet[2] = value ; 224 | 225 | MidiUSB.writePacket(&pk.i); 226 | } 227 | 228 | /////////////////////////////////////////////////////////////////////////////// 229 | // SET A PAD COLOR FROM RGB values 230 | /////////////////////////////////////////////////////////////////////////////// 231 | void SetPadColorRGB(uint8_t pad, uint8_t r, uint8_t g, uint8_t b ) { 232 | // 6 bits xxrgbRGB (EGA) 233 | // Value are from 0 to 127 => 0 to 3 234 | 235 | r /= 42.333 ; 236 | g /= 42.333 ; 237 | b /= 42.333 ; 238 | 239 | r = ( ( r & 0B10 ) << 1 ) + ( ( r & 0B1 ) << 5 ); 240 | g = ( ( g & 0B10 ) ) + ( ( g & 0B1 ) << 4 ); 241 | b = ( ( b & 0B10 ) >> 1 ) + ( ( b & 0B1 ) << 3 ); 242 | 243 | 244 | PadSetColor( pad, r + g + b); 245 | 246 | } 247 | /////////////////////////////////////////////////////////////////////////////// 248 | // PARSE SYSEX 249 | /////////////////////////////////////////////////////////////////////////////// 250 | void SysEx_Parse(uint8_t byte) 251 | { 252 | static unsigned sxLen = 0; 253 | 254 | static unsigned sxMsgIdx = 0 ; 255 | static uint8_t sysexBuff[64]; 256 | 257 | // First byte ? 258 | if ( byte == 0xF0) sxLen = 0 ; 259 | 260 | 261 | // Overflow. Drop everything until the next sysex start 262 | if ( sxLen >= sizeof(sysexBuff) ) return ; 263 | 264 | sysexBuff[sxLen++] = byte; 265 | 266 | // was Last byte ? 267 | if ( byte != 0xF7) return ; 268 | 269 | // Device inquiry (test optimized). Reply to the 2 ports 270 | if ( sysexBuff[1] == 0x7E && memcmp( sysexBuff, LpSx_UniversalDeviceInquiry, sizeof(LpSx_UniversalDeviceInquiry) ) == 0 ) { 271 | USBMidi_SendSysExMsg( 0, LpSx_DeviceInquiryReply,sizeof(LpSx_DeviceInquiryReply) ); 272 | USBMidi_SendSysExMsg( 1, LpSx_DeviceInquiryReply,sizeof(LpSx_DeviceInquiryReply) ); 273 | sxLen = 0 ; 274 | } 275 | 276 | // Daw mode 277 | else 278 | if ( sysexBuff[6] == 0x10 && memcmp( sysexBuff, LpSX_EnableDawModeHeader, sizeof(LpSX_EnableDawModeHeader) ) == 0 ) { 279 | 280 | // Request current mode ? 281 | if ( sysexBuff[7] == 0xF7 ) { 282 | sysexBuff[7] = DawModeEnabled; 283 | sysexBuff[8] = 0xF7; 284 | USBMidi_SendSysExMsg( 0, sysexBuff,sxLen + 1 ); 285 | USBMidi_SendSysExMsg( 0, sysexBuff,sxLen + 1 ); 286 | } 287 | // Set Mode 288 | else { 289 | DawModeEnabled = sysexBuff[7] ; 290 | } 291 | sxLen = 0 ; 292 | } 293 | 294 | // Select Layout 295 | else 296 | if ( sysexBuff[6] == 0x00 && memcmp( sysexBuff, LpSX_SelectLayoutHeader, sizeof(LpSX_SelectLayoutHeader) ) == 0 ) { 297 | // Request current layout ? 298 | if ( sysexBuff[7] == 0xF7 ) { 299 | sysexBuff[7] = CurrentLayout; 300 | sysexBuff[8] = 0xF7; 301 | USBMidi_SendSysExMsg( 0, sysexBuff,sxLen + 1 ); 302 | USBMidi_SendSysExMsg( 0, sysexBuff,sxLen + 1 ); 303 | } 304 | // Set Mode 305 | else { 306 | CurrentLayout = sysexBuff[7] ; 307 | } 308 | sxLen = 0 ; 309 | } 310 | 311 | // Set Led Color 312 | // F0h 00h 20h 29h 02h 0Eh 03h [ [_] ] F7h 313 | // The is structured as follows: 314 | // - Lighting type (1 byte) LED index (1 byte) Lighting data (1 – 3 bytes) 315 | // 00h : 0 --- Static colour from palette, Lighting data is 1 byte specifying palette entry. 316 | // 01h : 1 --- Flashing colour, Lighting data is 2 bytes specifying Colour B and Colour A. 317 | // 02h : 2 --- Pulsing colour, Lighting data is 1 byte specifying palette entry. 318 | // 03h : 3 --- RGB colour, Lighting data is 3 bytes for Red, Green and Blue (127:Max, 0: Min). 319 | // F0 00 20 29 02 0D 03 03 0B 7F 7F 7F F7 320 | 321 | //"F0 00 20 29 02 0D 03 00 3A 7F 01 3A 40 41 02 3A 41 03 0B 7F 7F 7F F7" 322 | else 323 | if ( sysexBuff[6] == 0x03 && memcmp( sysexBuff, LpSX_SetPadColorHeader, sizeof(LpSX_SetPadColorHeader) ) == 0 ) { 324 | 325 | uint8_t idx = 7; // Start at 326 | while ( idx < sxLen ) { 327 | // Button or pad led ? 328 | uint8_t padL = sysexBuff[idx + 1 ] / 10 ; 329 | uint8_t padC = sysexBuff[idx + 1 ] % 10 ; 330 | bool isPad = ( padL > 0 && padL < 9 && padC > 0 && padC < 9); 331 | 332 | if ( isPad) { 333 | padL-- ; padC-- ; 334 | uint8_t pad = ( 7 - padL ) * 8 + padC; 335 | switch (sysexBuff[idx]) { 336 | case 0: // Static color 337 | PadSetColor( pad, sysexBuff[idx + 2] ); 338 | idx += 3; 339 | break; 340 | case 1: // Flash 341 | idx += 4; 342 | break; 343 | case 2: // Pulsing 344 | idx += 3; 345 | break; 346 | case 3: // RGB 347 | SetPadColorRGB(pad,sysexBuff[idx + 2],sysexBuff[idx + 3],sysexBuff[idx + 4]); 348 | idx += 5; 349 | break; 350 | } 351 | } 352 | // Button Led mapped ? 353 | else { 354 | int16_t b = LPButtonsMap_Inv[ sysexBuff[idx + 1 ] ]; 355 | // The Smartpad has no colors for buttons. Only on/off 356 | switch (sysexBuff[idx]) { 357 | case 0: // Static color 358 | if ( b >= 0 ) ButtonSetLed( b , sysexBuff[idx+2] == 0 ? OFF : ON); 359 | idx += 3; 360 | break; 361 | case 1: // Flash 362 | idx += 4; 363 | break; 364 | case 2: // Pulsing 365 | idx += 3; 366 | break; 367 | case 3: // RGB 368 | if ( b >= 0 ) { 369 | if ( (sysexBuff[idx + 2] + sysexBuff[idx + 3] + sysexBuff[idx + 4]) != 0 ) { 370 | ButtonSetLed( b , ON); 371 | } 372 | else ButtonSetLed( b , OFF); 373 | } 374 | idx += 5; 375 | break; 376 | } 377 | } 378 | if ( sysexBuff[idx] == 0xF7 ) break; 379 | } 380 | 381 | sxLen = 0 ; 382 | } 383 | 384 | } 385 | 386 | /////////////////////////////////////////////////////////////////////////////// 387 | // PARSE A RECEIVED USB MIDI PACKET 388 | /////////////////////////////////////////////////////////////////////////////// 389 | void KikpadMod_USBMidiParse(midiPacket_t *pk) 390 | { 391 | uint8_t cin = pk->packet[0] & 0x0F ; 392 | 393 | // Lighting leds - Pads 394 | if ( cin == 0x09 ) { 395 | 396 | uint8_t padL = pk->packet[2] / 10 ; 397 | uint8_t padC = pk->packet[2] % 10 ; 398 | bool isPad = ( padL > 0 && padL < 9 && padC > 0 && padC < 9); 399 | if ( isPad ) { 400 | padL -- ; padC -- ; 401 | uint8_t pad = ( 7 - padL ) * 8 + padC; 402 | // Static colour 403 | if ( pk->packet[1] == 0x90 ) { 404 | PadSetColor( pad, pk->packet[3] ); 405 | } 406 | // Flashing colour 407 | else if ( pk->packet[1] == 0x91 ) { 408 | } 409 | // Pulsing Colour 410 | else if ( pk->packet[1] == 0x92 ) { 411 | } 412 | } 413 | } 414 | 415 | // Lighting Buttons 416 | else 417 | if ( cin == 0x0B ) { 418 | uint8_t padL = pk->packet[2] / 10 ; 419 | uint8_t padC = pk->packet[2] % 10 ; 420 | bool isPad = ( padL > 0 && padL < 9 && padC > 0 && padC < 9); 421 | if ( !isPad ) { 422 | int16_t b = LPButtonsMap_Inv[ pk->packet[2] ]; 423 | // Static colour 424 | if ( pk->packet[1] == 0xB0 ) { 425 | if ( b >= 0 ) ButtonSetLed( b , pk->packet[3] != 0 ? ON:OFF); 426 | } 427 | // Flashing colour 428 | else if ( pk->packet[1] == 0xB1 ) { 429 | } 430 | // Pulsing Colour 431 | else if ( pk->packet[1] == 0xB2 ) { 432 | } 433 | } 434 | } 435 | 436 | // SYSEX 437 | else 438 | if ( cin > 3 && cin < 8 ) { 439 | // CIN 5 exception : tune request 440 | if (pk->packet[1] == 0XF6) return ; 441 | 442 | uint8_t pklen = ( cin == 4 ? 3 : cin - 4) ; 443 | for ( uint8_t i = 0 ; i< pklen ; i++ ) { 444 | SysEx_Parse(pk->packet[ i + 1 ]) ; 445 | } 446 | } 447 | 448 | } 449 | 450 | /////////////////////////////////////////////////////////////////////////////// 451 | // PARSE A RECEIVED USER EVENT 452 | /////////////////////////////////////////////////////////////////////////////// 453 | void KikpadMod_ProcessUserEvent(UserEvent_t *ev){ 454 | 455 | // Compute time between 2 events 456 | static unsigned long lastEventMicros = micros(); 457 | unsigned long eventDelayMicros = micros() - lastEventMicros; 458 | lastEventMicros = micros(); 459 | 460 | // To keep track of last event 461 | static uint8_t lastEventId = EV_NONE; 462 | static uint8_t lastEventIdx = 0; 463 | 464 | static uint8_t encoderVal[8] = {0,0,0,0,0,0,0,0}; 465 | 466 | midiPacket_t pk; 467 | 468 | 469 | uint8_t idx = SCAN_IDX(ev->d1,ev->d2); 470 | 471 | switch (ev->ev) { 472 | 473 | // Encoders Clock wise (turn right) 474 | case EV_EC_CW: 475 | // Encoders Counter Clock wise (turn left) 476 | case EV_EC_CCW: 477 | { 478 | // Compute accelerator factor regarding the turn speed. 479 | uint8_t accel = 50 * 1000 / eventDelayMicros ; 480 | 481 | // Send the relative value 482 | if ( ev->ev == EV_EC_CCW ) 483 | LPKnobSendCC(idx, 0x7f - accel); 484 | else 485 | LPKnobSendCC(idx, 1 + accel); 486 | 487 | break; 488 | } 489 | 490 | // Pad pressed and not released 491 | case EV_PAD_PRESSED: 492 | // Pad released 493 | case EV_PAD_RELEASED: 494 | { 495 | // Kipad number from top left 496 | // Lauchpad from bottom left to up 497 | uint8_t pad = ( 7 - idx / 8 )*10 + idx % 8 + 0x0B ; 498 | 499 | LPPadSend( pad, (ev->ev == EV_PAD_PRESSED) ); 500 | 501 | 502 | break; 503 | } 504 | 505 | // Button pressed and not released 506 | case EV_BTN_PRESSED: 507 | // Button released 508 | case EV_BTN_RELEASED: 509 | 510 | //if ( idx == BT_SET ) PadColorsBackground(BLACK); 511 | LPButtonSend(LPButtonsMap[idx].ctrl,(ev->ev == EV_BTN_PRESSED)); 512 | 513 | break; 514 | 515 | // Button pressed and holded more than 2s 516 | case EV_BTN_HOLDED: 517 | 518 | break; 519 | 520 | } 521 | 522 | // Keep track of last event 523 | lastEventId = ev->ev; 524 | lastEventIdx = idx; 525 | 526 | } 527 | 528 | /////////////////////////////////////////////////////////////////////////////// 529 | // Kikpad module setup 530 | /////////////////////////////////////////////////////////////////////////////// 531 | 532 | static void KikpadMod_Pre_Setup() { 533 | usb_midi_set_product_string(LP_PRODUCT_STRING); 534 | usb_midi_set_vid_pid(LP_VID, LP_PID); 535 | } 536 | 537 | // Post Main Kikpad setup (after usb init) 538 | static void KikpadMod_Setup() { 539 | 540 | PadColorsBackground(BLACK); 541 | 542 | RGBMaskUpdateAll(); 543 | 544 | //PadLedStates[0] = PadLedStates[1] = 0XFFFFFFF ; 545 | 546 | // Build button map inverted table 547 | for ( uint8_t i = 0 ; i < sizeof(LPButtonsMap_Inv) ; i++ ) { 548 | LPButtonsMap_Inv[ i ] = -1; 549 | } 550 | for ( uint8_t i = 0 ; i < sizeof(LPButtonsMap) / sizeof(LPButtonsMap_t) ; i++ ) { 551 | LPButtonsMap_Inv[ LPButtonsMap[i].ctrl ] = i; 552 | } 553 | 554 | } 555 | /////////////////////////////////////////////////////////////////////////////// 556 | // Kikpad module loop 557 | /////////////////////////////////////////////////////////////////////////////// 558 | static void KikpadMod_Loop() { 559 | 560 | } 561 | #endif 562 | -------------------------------------------------------------------------------- /usb_midi_device.c: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | USBMIDIKLIK 4X4 - USB Midi advanced firmware for STM32F1 platform. 8 | Copyright (C) 2019 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the USBMIDIKLIK-4x4 distribution 12 | https://github.com/TheKikGen/USBMidiKliK4x4 13 | Copyright (c) 2019 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | USB MIDI LIBRARY adapted by TheKikGenLab from USB LeafLabs LLC. USB API : 16 | Perry Hung, Magnus Lundin,Donald Delmar Davis, Suspect Devices. 17 | GPL Licence. 18 | ----------------------------------------------------------------------------- 19 | Disclaimer. 20 | 21 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 22 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 23 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 24 | 25 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 26 | commercial closed code solution without the licensor permission. 27 | 28 | You are free to copy and redistribute the material in any medium or format, 29 | adapt, transform, and build upon the material. 30 | 31 | You must give appropriate credit, a link to the github site 32 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 33 | and indicate if changes were made. You may do so in any reasonable manner, 34 | but not in any way that suggests the licensor endorses you or your use. 35 | 36 | You may not apply legal terms or technological measures that legally restrict 37 | others from doing anything the license permits. 38 | 39 | You do not have to comply with the license for elements of the material 40 | in the public domain or where your use is permitted by an applicable exception 41 | or limitation. 42 | 43 | No warranties are given. The license may not give you all of the permissions 44 | necessary for your intended use. This program is distributed in the hope that 45 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 46 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 47 | */ 48 | #ifndef USB_MIDI_DEVICE_H 49 | #define USB_MIDI_DEVICE_H 50 | #include 51 | #include "hardware_config.h" 52 | #include "usb_midi_device.h" 53 | 54 | #include 55 | #include 56 | #include 57 | 58 | /* Private headers */ 59 | #include "usb_lib_globals.h" 60 | #include "usb_reg_map.h" 61 | 62 | /* usb_lib headers */ 63 | #include "usb_type.h" 64 | #include "usb_core.h" 65 | #include "usb_def.h" 66 | 67 | #include "usb_midi_descriptor.c" 68 | 69 | static void usb_midi_DataTxCb(void); 70 | static void usb_midi_DataRxCb(void); 71 | static void usb_midi_Init(void); 72 | static void usb_midi_Reset(void); 73 | static RESULT usb_midi_DataSetup(uint8_t request); 74 | static RESULT usb_midi_NoDataSetup(uint8_t request); 75 | static RESULT usb_midi_GetInterfaceSetting(uint8_t interface, uint8_t alt_setting); 76 | static uint8* usb_midi_GetDeviceDescriptor(uint16_t length); 77 | static uint8* usb_midi_GetConfigDescriptor(uint16_t length); 78 | static uint8* usb_midi_GetStringDescriptor(uint16_t length); 79 | static void usb_midi_SetConfiguration(void); 80 | static void usb_midi_SetDeviceAddress(void); 81 | 82 | /* 83 | * Etc. 84 | */ 85 | 86 | /* I/O state */ 87 | 88 | /* Received data */ 89 | static volatile uint32_t midiBufferRx[MIDI_STREAM_EPSIZE/4]; 90 | /* Read index into midiBufferRx */ 91 | static volatile uint32_t rx_offset = 0; 92 | /* Transmit data */ 93 | static volatile uint32_t midiBufferTx[MIDI_STREAM_EPSIZE/4]; 94 | /* Write index into midiBufferTx */ 95 | static volatile uint32_t tx_offset = 0; 96 | /* Number of bytes left to transmit */ 97 | static volatile uint32_t n_unsent_packets = 0; 98 | /* Are we currently sending an IN packet? */ 99 | static volatile uint8_t transmitting = 0; 100 | /* Number of unread bytes */ 101 | static volatile uint32_t n_unread_packets = 0; 102 | 103 | 104 | // -------------------------------------------------------------------------------------- 105 | // ENDPOINTS CALLBACKS TABLES 106 | // -------------------------------------------------------------------------------------- 107 | void (*usb_midi_ep_int_in[7])(void) = 108 | {usb_midi_DataTxCb, 109 | NOP_Process, 110 | NOP_Process, 111 | NOP_Process, 112 | NOP_Process, 113 | NOP_Process, 114 | NOP_Process}; 115 | 116 | void (*usb_midi_ep_int_out[7])(void) = 117 | {NOP_Process, 118 | usb_midi_DataRxCb, 119 | NOP_Process, 120 | NOP_Process, 121 | NOP_Process, 122 | NOP_Process, 123 | NOP_Process}; 124 | 125 | // -------------------------------------------------------------------------------------- 126 | // Globals required by usb_lib. 127 | // These override core USB functionality which was declared __weak. 128 | // -------------------------------------------------------------------------------------- 129 | 130 | // 131 | // DEVICE Device_Table = { 132 | // .Total_Endpoint = USB_MIDI_NUM_ENDPTS, 133 | // .Total_Configuration = 1 134 | // }; 135 | // 136 | // 137 | // DEVICE_PROP Device_Property = { 138 | // .Init = usb_midi_Init, 139 | // .Reset = usb_midi_Reset, 140 | // .Process_Status_IN = NOP_Process, 141 | // .Process_Status_OUT = NOP_Process, 142 | // .Class_Data_Setup = usb_midi_DataSetup, 143 | // .Class_NoData_Setup = usb_midi_NoDataSetup, 144 | // .Class_Get_Interface_Setting = usb_midi_GetInterfaceSetting, 145 | // .GetDeviceDescriptor = usb_midi_GetDeviceDescriptor, 146 | // .GetConfigDescriptor = usb_midi_GetConfigDescriptor, 147 | // .GetStringDescriptor = usb_midi_GetStringDescriptor, 148 | // .RxEP_buffer = NULL, 149 | // .MaxPacketSize = USB_MIDI_MAX_PACKET_SIZE 150 | // }; 151 | // 152 | // USER_STANDARD_REQUESTS User_Standard_Requests = { 153 | // .User_GetConfiguration = NOP_Process, 154 | // .User_SetConfiguration = usb_midi_SetConfiguration, 155 | // .User_GetInterface = NOP_Process, 156 | // .User_SetInterface = NOP_Process, 157 | // .User_GetStatus = NOP_Process, 158 | // .User_ClearFeature = NOP_Process, 159 | // .User_SetEndPointFeature = NOP_Process, 160 | // .User_SetDeviceFeature = NOP_Process, 161 | // .User_SetDeviceAddress = usb_midi_SetDeviceAddress 162 | // }; 163 | 164 | 165 | // -------------------------------------------------------------------------------------- 166 | // DEVICE DESCRIPTOR MANIPULATION (IF NOT READ ONLY) 167 | // -------------------------------------------------------------------------------------- 168 | 169 | #ifdef USB_MIDI_PRODUCT_STRING_SIZE 170 | 171 | void usb_midi_set_vid_pid(uint16_t vid, uint16_t pid) { 172 | usbMIDIDescriptor_Device.idVendor = vid; 173 | usbMIDIDescriptor_Device.idProduct = pid; 174 | 175 | } 176 | 177 | void usb_midi_set_product_string(const char stringDescriptor[]) { 178 | 179 | // Check the true string descriptor size allocated by manual declaration 180 | // in usb_midi_descriptor.c. It is critical !!! 181 | 182 | // fill the existing string descriptor with 0 183 | memset(&usbMIDIDescriptor_iProduct.bString,0, (USB_MIDI_PRODUCT_STRING_SIZE)*2); 184 | 185 | // Copy string to the descriptor. The input string must be zero ending !!! 186 | uint8_t i = 0; 187 | while ( stringDescriptor[i] != 0 ) { 188 | // The string is wide characters type. 2 bytes / char. 189 | usbMIDIDescriptor_iProduct.bString[i*2] = stringDescriptor[i]; 190 | if (++i >= USB_MIDI_PRODUCT_STRING_SIZE ) break; 191 | } 192 | 193 | // Adjust the length 194 | usbMIDIDescriptor_iProduct.bLength = i*2+2; 195 | usbMIDIString_Descriptor[usbMIDIDescriptor_Device.iProduct].Descriptor_Size = i*2+2; 196 | } 197 | 198 | #endif 199 | // -------------------------------------------------------------------------------------- 200 | // ENABLE / DISABLE / POWERDOWN MIDI DEVICE 201 | // -------------------------------------------------------------------------------------- 202 | void usb_midi_enable(gpio_dev *disc_dev, uint8_t disc_bit, uint8_t level) { 203 | /* Present ourselves to the host. Writing 0 to "disc" pin must 204 | * pull USB_DP pin up while leaving USB_DM pulled down by the 205 | * transceiver. See USB 2.0 spec, section 7.1.7.3. 206 | * 207 | * FT : The function was modified to support a new "level" parameter, 208 | * to set the 0 or 1 regarding the logic level used by the DISC pin. 209 | * 210 | */ 211 | 212 | // USB MIDI Device setup. We dont redeclare Device_Table 213 | 214 | Device_Table.Total_Endpoint = USB_MIDI_NUM_ENDPTS; 215 | Device_Table.Total_Configuration = 1; 216 | 217 | Device_Property.Init = usb_midi_Init; 218 | Device_Property.Reset = usb_midi_Reset; 219 | Device_Property.Process_Status_IN = NOP_Process; 220 | Device_Property.Process_Status_OUT = NOP_Process; 221 | Device_Property.Class_Data_Setup = usb_midi_DataSetup; 222 | Device_Property.Class_NoData_Setup = usb_midi_NoDataSetup; 223 | Device_Property.Class_Get_Interface_Setting = usb_midi_GetInterfaceSetting; 224 | Device_Property.GetDeviceDescriptor = usb_midi_GetDeviceDescriptor; 225 | Device_Property.GetConfigDescriptor = usb_midi_GetConfigDescriptor; 226 | Device_Property.GetStringDescriptor = usb_midi_GetStringDescriptor; 227 | Device_Property.RxEP_buffer = NULL; 228 | Device_Property.MaxPacketSize = USB_MIDI_MAX_PACKET_SIZE; 229 | 230 | User_Standard_Requests.User_GetConfiguration = NOP_Process; 231 | User_Standard_Requests.User_SetConfiguration = usb_midi_SetConfiguration; 232 | User_Standard_Requests.User_GetInterface = NOP_Process; 233 | User_Standard_Requests.User_SetInterface = NOP_Process; 234 | User_Standard_Requests.User_GetStatus = NOP_Process; 235 | User_Standard_Requests.User_ClearFeature = NOP_Process; 236 | User_Standard_Requests.User_SetEndPointFeature = NOP_Process; 237 | User_Standard_Requests.User_SetDeviceFeature = NOP_Process; 238 | User_Standard_Requests.User_SetDeviceAddress = usb_midi_SetDeviceAddress; 239 | 240 | if (disc_dev != NULL) { 241 | gpio_set_mode(disc_dev, disc_bit, GPIO_OUTPUT_PP); 242 | gpio_write_bit(disc_dev, disc_bit, level); 243 | } 244 | 245 | /* Initialize the USB peripheral. */ 246 | usb_init_usblib(USBLIB, usb_midi_ep_int_in, usb_midi_ep_int_out); 247 | } 248 | 249 | void usb_midi_disable(gpio_dev *disc_dev, uint8_t disc_bit, uint8_t level) { 250 | /* Turn off the interrupt and signal disconnect (see e.g. USB 2.0 251 | * spec, section 7.1.7.3). 252 | * 253 | * FT : The function was modified to support a new "level" parameter, 254 | * to set the 0 or 1 regarding the logic level used by the DISC pin. 255 | */ 256 | 257 | nvic_irq_disable(NVIC_USB_LP_CAN_RX0); 258 | if (disc_dev != NULL) { 259 | gpio_write_bit(disc_dev, disc_bit, level); 260 | } 261 | usb_power_off(); 262 | } 263 | 264 | 265 | // -------------------------------------------------------------------------------------- 266 | // USB BUFFERS I/O 267 | // -------------------------------------------------------------------------------------- 268 | /* TODO these could use some improvement; they're fairly 269 | * straightforward ports of the analogous ST code. The PMA blit 270 | * routines in particular are obvious targets for performance 271 | * measurement and tuning. */ 272 | static void usb_copy_to_pma(const uint8_t *buf, uint16_t len, uint16_t pma_offset) { 273 | uint16_t *dst = (uint16*)usb_pma_ptr(pma_offset); 274 | uint16_t n = len >> 1; 275 | uint16_t i; 276 | for (i = 0; i < n; i++) { 277 | *dst = (uint16)(*buf) | *(buf + 1) << 8; 278 | buf += 2; 279 | dst += 2; 280 | } 281 | if (len & 1) { 282 | *dst = *buf; 283 | } 284 | } 285 | 286 | static void usb_copy_from_pma(uint8_t *buf, uint16_t len, uint16_t pma_offset) { 287 | uint32_t *src = (uint32*)usb_pma_ptr(pma_offset); 288 | uint16_t *dst = (uint16*)buf; 289 | uint16_t n = len >> 1; 290 | uint16_t i; 291 | for (i = 0; i < n; i++) { 292 | *dst++ = *src++; 293 | } 294 | if (len & 1) { 295 | *dst = *src & 0xFF; 296 | } 297 | } 298 | 299 | 300 | // -------------------------------------------------------------------------------------- 301 | // USB TX / RX / PEEK 302 | // -------------------------------------------------------------------------------------- 303 | 304 | /* This function is non-blocking. 305 | * 306 | * It copies data from a usercode buffer into the USB peripheral TX 307 | * buffer, and returns the number of bytes copied. */ 308 | uint32_t usb_midi_tx(const uint32* buf, uint32_t packets) { 309 | uint32_t bytes=packets*4; 310 | /* Last transmission hasn't finished, so abort. */ 311 | if (usb_midi_is_transmitting()) { 312 | /* Copy to TxBuffer */ 313 | 314 | return 0; /* return len */ 315 | } 316 | 317 | /* We can only put USB_MIDI_TX_EPSIZE bytes in the buffer. */ 318 | if (bytes > MIDI_STREAM_EPSIZE) { 319 | bytes = MIDI_STREAM_EPSIZE; 320 | packets=bytes/4; 321 | } 322 | 323 | /* Queue bytes for sending. */ 324 | if (packets) { 325 | usb_copy_to_pma((uint8_t *)buf, bytes, MIDI_STREAM_IN_EPADDR); 326 | } 327 | // We still need to wait for the interrupt, even if we're sending 328 | // zero bytes. (Sending zero-size packets is useful for flushing 329 | // host-side buffers.) 330 | usb_set_ep_tx_count(MIDI_STREAM_IN_ENDP, bytes); 331 | n_unsent_packets = packets; 332 | transmitting = 1; 333 | usb_set_ep_tx_stat(MIDI_STREAM_IN_ENDP, USB_EP_STAT_TX_VALID); 334 | 335 | return packets; 336 | } 337 | 338 | /* Nonblocking byte receive. 339 | * 340 | * Copies up to len bytes from our private data buffer (*NOT* the PMA) 341 | * into buf and deq's the FIFO. */ 342 | uint32_t usb_midi_rx(uint32* buf, uint32_t packets) { 343 | /* Copy bytes to buffer. */ 344 | uint32_t n_copied = usb_midi_peek(buf, packets); 345 | 346 | usb_midi_mark_read(n_copied); 347 | 348 | // /* Mark bytes as read. */ 349 | // n_unread_packets -= n_copied; 350 | // rx_offset += n_copied; 351 | // 352 | // /* If all bytes have been read, re-enable the RX endpoint, which 353 | // * was set to NAK when the current batch of bytes was received. */ 354 | // if (n_unread_packets == 0) { 355 | // usb_set_ep_rx_count(MIDI_STREAM_OUT_ENDP, MIDI_STREAM_EPSIZE); 356 | // usb_set_ep_rx_stat(MIDI_STREAM_OUT_ENDP, USB_EP_STAT_RX_VALID); 357 | // rx_offset = 0; 358 | // } 359 | 360 | return n_copied; 361 | } 362 | 363 | /* Nonblocking byte lookahead. 364 | * 365 | * Looks at unread bytes without marking them as read. */ 366 | uint32_t usb_midi_peek(uint32* buf, uint32_t packets) { 367 | uint16_t i; 368 | if (packets > n_unread_packets) { 369 | packets = n_unread_packets; 370 | } 371 | 372 | for (i = 0; i < packets; i++) { 373 | buf[i] = midiBufferRx[i + rx_offset]; 374 | } 375 | 376 | return packets; 377 | } 378 | 379 | /* Nonblocking byte receive. 380 | * Mark n packets as read when they have been peeked 381 | * Warning : this call must only follow a peek !! 382 | * Use readPacket instead if you need to read and mark 383 | */ 384 | 385 | uint32_t usb_midi_mark_read(uint32_t n_copied) { 386 | /* Mark bytes as read. */ 387 | n_unread_packets -= n_copied; 388 | rx_offset += n_copied; 389 | 390 | /* If all bytes have been read, re-enable the RX endpoint, which 391 | * was set to NAK when the current batch of bytes was received. */ 392 | if (n_unread_packets == 0) { 393 | usb_set_ep_rx_count(MIDI_STREAM_OUT_ENDP, MIDI_STREAM_EPSIZE); 394 | usb_set_ep_rx_stat(MIDI_STREAM_OUT_ENDP, USB_EP_STAT_RX_VALID); 395 | rx_offset = 0; 396 | } 397 | 398 | return n_copied; 399 | } 400 | 401 | 402 | // -------------------------------------------------------------------------------------- 403 | // USB MIDI STATE 404 | // -------------------------------------------------------------------------------------- 405 | 406 | uint32_t usb_midi_data_available(void) { 407 | return n_unread_packets; 408 | } 409 | 410 | uint8_t usb_midi_is_transmitting(void) { 411 | return transmitting; 412 | } 413 | 414 | uint16_t usb_midi_get_pending(void) { 415 | return n_unsent_packets; 416 | } 417 | 418 | // -------------------------------------------------------------------------------------- 419 | // ENDPOINTS CALLBACKS 420 | // -------------------------------------------------------------------------------------- 421 | 422 | static void usb_midi_DataTxCb(void) { 423 | n_unsent_packets = 0; 424 | transmitting = 0; 425 | } 426 | 427 | static void usb_midi_DataRxCb(void) { 428 | usb_set_ep_rx_stat(MIDI_STREAM_OUT_ENDP, USB_EP_STAT_RX_NAK); 429 | n_unread_packets = usb_get_ep_rx_count(MIDI_STREAM_OUT_ENDP) / 4; 430 | /* This copy won't overwrite unread bytes, since we've set the RX 431 | * endpoint to NAK, and will only set it to VALID when all bytes 432 | * have been read. */ 433 | 434 | usb_copy_from_pma((uint8*)midiBufferRx, n_unread_packets * 4, 435 | MIDI_STREAM_OUT_EPADDR); 436 | 437 | if (n_unread_packets == 0) { 438 | usb_set_ep_rx_count(MIDI_STREAM_OUT_ENDP, MIDI_STREAM_EPSIZE); 439 | usb_set_ep_rx_stat(MIDI_STREAM_OUT_ENDP, USB_EP_STAT_RX_VALID); 440 | rx_offset = 0; 441 | } 442 | 443 | } 444 | 445 | // -------------------------------------------------------------------------------------- 446 | // USB User functions 447 | // -------------------------------------------------------------------------------------- 448 | 449 | /* NOTE: Nothing specific to this device class in this function, move to usb_lib */ 450 | static void usb_midi_Init(void) { 451 | pInformation->Current_Configuration = 0; 452 | 453 | USB_BASE->CNTR = USB_CNTR_FRES; 454 | 455 | USBLIB->irq_mask = 0; 456 | USB_BASE->CNTR = USBLIB->irq_mask; 457 | USB_BASE->ISTR = 0; 458 | USBLIB->irq_mask = USB_CNTR_RESETM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; 459 | USB_BASE->CNTR = USBLIB->irq_mask; 460 | 461 | USB_BASE->ISTR = 0; 462 | USBLIB->irq_mask = USB_ISR_MSK; 463 | USB_BASE->CNTR = USBLIB->irq_mask; 464 | 465 | nvic_irq_enable(NVIC_USB_LP_CAN_RX0); 466 | USBLIB->state = USB_UNCONNECTED; 467 | } 468 | 469 | 470 | static void usb_midi_Reset(void) { 471 | pInformation->Current_Configuration = 0; 472 | 473 | /* current feature is current bmAttributes */ 474 | pInformation->Current_Feature = (USB_CONFIG_ATTR_BUSPOWERED | 475 | USB_CONFIG_ATTR_SELF_POWERED); 476 | 477 | USB_BASE->BTABLE = USB_MIDI_BTABLE_ADDRESS; 478 | 479 | // Setup control endpoint */ 480 | usb_set_ep_type (USB_MIDI_CTRL_ENDP, USB_EP_EP_TYPE_CONTROL); 481 | usb_set_ep_tx_stat (USB_MIDI_CTRL_ENDP, USB_EP_STAT_TX_STALL ); 482 | usb_set_ep_rx_addr (USB_MIDI_CTRL_ENDP, USB_MIDI_CTRL_RX_ADDR ); 483 | usb_set_ep_tx_addr (USB_MIDI_CTRL_ENDP, USB_MIDI_CTRL_TX_ADDR ); 484 | 485 | usb_clear_status_out(USB_MIDI_CTRL_ENDP ); 486 | usb_set_ep_rx_count (USB_MIDI_CTRL_ENDP, pProperty->MaxPacketSize); 487 | usb_set_ep_rx_stat (USB_MIDI_CTRL_ENDP, USB_EP_STAT_RX_VALID); 488 | 489 | /* TODO figure out differences in style between RX/TX EP setup */ 490 | 491 | /* set up data endpoint OUT (RX) */ 492 | usb_set_ep_type (MIDI_STREAM_OUT_ENDP, USB_EP_EP_TYPE_BULK ); 493 | usb_set_ep_rx_addr (MIDI_STREAM_OUT_ENDP, MIDI_STREAM_OUT_EPADDR); 494 | usb_set_ep_rx_count (MIDI_STREAM_OUT_ENDP, MIDI_STREAM_EPSIZE ); 495 | usb_set_ep_rx_stat (MIDI_STREAM_OUT_ENDP, USB_EP_STAT_RX_VALID ); 496 | 497 | /* set up data endpoint IN (TX) */ 498 | usb_set_ep_type (MIDI_STREAM_IN_ENDP, USB_EP_EP_TYPE_BULK ); 499 | usb_set_ep_tx_addr (MIDI_STREAM_IN_ENDP, MIDI_STREAM_IN_EPADDR ); 500 | usb_set_ep_tx_stat (MIDI_STREAM_IN_ENDP, USB_EP_STAT_TX_NAK ); 501 | usb_set_ep_rx_stat (MIDI_STREAM_IN_ENDP, USB_EP_STAT_RX_DISABLED); 502 | 503 | USBLIB->state = USB_ATTACHED; 504 | SetDeviceAddress(0); 505 | 506 | /* Reset the RX/TX state */ 507 | n_unread_packets = 0; 508 | n_unsent_packets = 0; 509 | rx_offset = 0; 510 | } 511 | 512 | static RESULT usb_midi_DataSetup(uint8_t request) { 513 | uint8* (*CopyRoutine)(uint16) = 0; 514 | 515 | // if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { 516 | // 517 | // } 518 | 519 | if (CopyRoutine == NULL) { 520 | return USB_UNSUPPORT; 521 | } 522 | 523 | pInformation->Ctrl_Info.CopyData = CopyRoutine; 524 | pInformation->Ctrl_Info.Usb_wOffset = 0; 525 | (*CopyRoutine)(0); 526 | return USB_SUCCESS; 527 | } 528 | 529 | static RESULT usb_midi_NoDataSetup(uint8_t request) { 530 | RESULT ret = USB_UNSUPPORT; 531 | 532 | // if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { 533 | // } 534 | return ret; 535 | } 536 | 537 | static RESULT usb_midi_GetInterfaceSetting(uint8_t interface, uint8_t alt_setting) { 538 | if (alt_setting > 0) { 539 | return USB_UNSUPPORT; 540 | } else if (interface > 1) { 541 | return USB_UNSUPPORT; 542 | } 543 | 544 | return USB_SUCCESS; 545 | } 546 | 547 | static uint8* usb_midi_GetDeviceDescriptor(uint16_t length) { 548 | return Standard_GetDescriptorData(length, &usbMidiDevice_Descriptor); 549 | } 550 | 551 | static uint8* usb_midi_GetConfigDescriptor(uint16_t length) { 552 | return Standard_GetDescriptorData(length, &usbMidiConfig_Descriptor); 553 | } 554 | 555 | static uint8* usb_midi_GetStringDescriptor(uint16_t length) { 556 | uint8_t wValue0 = pInformation->USBwValue0; 557 | 558 | if (wValue0 >= sizeof(usbMIDIString_Descriptor) ) { 559 | return NULL; 560 | } 561 | return Standard_GetDescriptorData(length, &usbMIDIString_Descriptor[wValue0]); 562 | } 563 | 564 | static void usb_midi_SetConfiguration(void) { 565 | if (pInformation->Current_Configuration != 0) { 566 | USBLIB->state = USB_CONFIGURED; 567 | } 568 | } 569 | 570 | static void usb_midi_SetDeviceAddress(void) { 571 | USBLIB->state = USB_ADDRESSED; 572 | } 573 | #endif 574 | -------------------------------------------------------------------------------- /KIKPAD.xmm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 43 | 44 | 45 | 46 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | 63 | 64 | 65 | 66 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 76 | 78 | 79 | 80 | 81 | 83 | 84 | 85 | 86 | 88 | 89 | 90 | 91 | 93 | 94 | 95 | 96 | 98 | 99 | 100 | 101 | 103 | 104 | 105 | 106 | 108 | 109 | 110 | 111 | 113 | 114 | 115 | 116 | 118 | 119 | 120 | 121 | 123 | 124 | 125 | 126 | 128 | 129 | 130 | 131 | 133 | 134 | 135 | 136 | 138 | 139 | 140 | 141 | 143 | 144 | 145 | 146 | 148 | 149 | 150 | 151 | 153 | 154 | 155 | 156 | 158 | 159 | 160 | 161 | 163 | 164 | 165 | 166 | 168 | 169 | 170 | 171 | 173 | 174 | 175 | 176 | 178 | 179 | 180 | 181 | 183 | 184 | 185 | 186 | 188 | 189 | 190 | 191 | 193 | 194 | 195 | 196 | 198 | 199 | 200 | 201 | 203 | 204 | 205 | 206 | 208 | 209 | 210 | 211 | 213 | 214 | 215 | 216 | 218 | 219 | 220 | 221 | 223 | 224 | 225 | 226 | 228 | 229 | 230 | 231 | 233 | 234 | 235 | 236 | 238 | 239 | 240 | 241 | 243 | 244 | 245 | 246 | 248 | 249 | 250 | 251 | 253 | 254 | 255 | 256 | 258 | 259 | 260 | 261 | 263 | 264 | 265 | 266 | 268 | 269 | 270 | 271 | 273 | 274 | 275 | 276 | 278 | 279 | 280 | 281 | 283 | 284 | 285 | 286 | 288 | 289 | 290 | 291 | 293 | 294 | 295 | 296 | 298 | 299 | 300 | 301 | 303 | 304 | 305 | 306 | 308 | 309 | 310 | 311 | 313 | 314 | 315 | 316 | 318 | 319 | 320 | 321 | 323 | 324 | 325 | 326 | 328 | 329 | 330 | 331 | 333 | 334 | 335 | 336 | 338 | 339 | 340 | 341 | 343 | 344 | 345 | 346 | 348 | 349 | 350 | 351 | 353 | 354 | 355 | 356 | 358 | 359 | 360 | 361 | 363 | 364 | 365 | 366 | 368 | 369 | 370 | 371 | 373 | 374 | 375 | 376 | 378 | 379 | 380 | 381 | 383 | 384 | 385 | 386 | 388 | 389 | 390 | 391 | 393 | 394 | 395 | 396 | 398 | 399 | 400 | 401 | 403 | 404 | 405 | 406 | 408 | 409 | 410 | 411 | 413 | 414 | 415 | 416 | 418 | 419 | 420 | 421 | 423 | 424 | 425 | 426 | 428 | 429 | 430 | 431 | 433 | 434 | 435 | 436 | 438 | 439 | 440 | 441 | 443 | 444 | 445 | 446 | 448 | 449 | 450 | 451 | 453 | 454 | 455 | 456 | 458 | 459 | 460 | 461 | 463 | 464 | 465 | 466 | 468 | 469 | 470 | 471 | 473 | 474 | 475 | 476 | 478 | 479 | 480 | 481 | 483 | 484 | 485 | 486 | 488 | 489 | 490 | 491 | 493 | 494 | 495 | 496 | 498 | 499 | 500 | 501 | 503 | 504 | 505 | 506 | 508 | 509 | 510 | 511 | 513 | 514 | 515 | 516 | 518 | 519 | 520 | 521 | 523 | 524 | 525 | 526 | 528 | 529 | 530 | 531 | 533 | 534 | 535 | 536 | 538 | 539 | 540 | 541 | 543 | 544 | 545 | 546 | 548 | 549 | 550 | 551 | 553 | 554 | 555 | 556 | 558 | 559 | 560 | 561 | 563 | 564 | 565 | 566 | 568 | 569 | 570 | 571 | 573 | 574 | 575 | 576 | 578 | 579 | 580 | 581 | 583 | 584 | 585 | 586 | 588 | 589 | 590 | 591 | 593 | 594 | 595 | -------------------------------------------------------------------------------- /kikpad.ino: -------------------------------------------------------------------------------- 1 | /* 2 | __ __| | | /_) | ___| | | 3 | | __ \ _ \ ' / | | / | _ \ __ \ | _` | __ \ __| 4 | | | | | __/ . \ | < | | __/ | | | ( | | |\__ \ 5 | _| _| |_|\___| _|\_\_|_|\_\\____|\___|_| _| _____|\__,_|_.__/ ____/ 6 | ----------------------------------------------------------------------------- 7 | KIKPAD - Alternative firmware for the Midiplus Smartpad. 8 | Copyright (C) 2020 by The KikGen labs. 9 | LICENCE CREATIVE COMMONS - Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 10 | 11 | This file is part of the KIKPAD distribution 12 | https://github.com/TheKikGen/kikpad 13 | Copyright (c) 2020 TheKikGen Labs team. 14 | ----------------------------------------------------------------------------- 15 | Disclaimer. 16 | 17 | This work is licensed under the Creative Commons Attribution-NonCommercial 4.0 International License. 18 | To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/ 19 | or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 20 | 21 | NON COMMERCIAL - PERSONAL USE ONLY : You may not use the material for pure 22 | commercial closed code solution without the licensor permission. 23 | 24 | You are free to copy and redistribute the material in any medium or format, 25 | adapt, transform, and build upon the material. 26 | 27 | You must give appropriate credit, a link to the github site 28 | https://github.com/TheKikGen/USBMidiKliK4x4 , provide a link to the license, 29 | and indicate if changes were made. You may do so in any reasonable manner, 30 | but not in any way that suggests the licensor endorses you or your use. 31 | 32 | You may not apply legal terms or technological measures that legally restrict 33 | others from doing anything the license permits. 34 | 35 | You do not have to comply with the license for elements of the material 36 | in the public domain or where your use is permitted by an applicable exception 37 | or limitation. 38 | 39 | No warranties are given. The license may not give you all of the permissions 40 | necessary for your intended use. This program is distributed in the hope that 41 | it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | */ 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | // KIKPAD SYSTEM KEYS 47 | //------------------------------------------------------------------------------ 48 | // RESET = HOLD BT_CONTROL4 & MASTER8 THEN PRESS SET 49 | // BOOTLOADER MODE = HOLD BT_CONTROL4 & MASTER7 THEN PRESS SET 50 | //////////////////////////////////////////////////////////////////////////////// 51 | 52 | 53 | #include 54 | #include 55 | #include 56 | #include "libmaple/flash.h" 57 | #include "libmaple/pwr.h" 58 | #include "libmaple/rcc.h" 59 | #include "libmaple/bkp.h" 60 | 61 | #include "kikpad.h" 62 | 63 | #include "usb_midi.h" 64 | #include "usb_midi_device.h" 65 | 66 | #include "ringbuffer.h" 67 | #include "Rotary.h" 68 | #include 69 | 70 | /////////////////////////////////////////////////////////////////////////////// 71 | // GLOBALS 72 | /////////////////////////////////////////////////////////////////////////////// 73 | 74 | // TIMER 75 | HardwareTimer RGBRefreshTim2(2); 76 | HardwareTimer UserEventsTim3(3); 77 | 78 | // Queue (ring buffer) for User events 79 | RingBuffer UserEventQueue; 80 | 81 | // Multi purpose data buffer. 82 | uint8_t globalDataBuffer[GLOBAL_DATA_BUFF_SIZE] ; 83 | 84 | // Serial interfaces Array 85 | HardwareSerial * serialHw = &Serial1; 86 | 87 | // USB Midi object & globals 88 | USBMidi MidiUSB; 89 | bool midiUSBCx = false ; 90 | bool midiUSBIdle = false ; 91 | bool isSerialBusy = false ; 92 | 93 | // MIDI Parsers for serial 1 94 | midiXparser midiSerial; 95 | 96 | 97 | // Buttons Pad & bars scan lines 98 | const uint8_t ScanButtonsRows[] = {PC8,PC9,PC10,PC11,PC12,PC13,PC14,PC15}; 99 | const uint8_t ScanButtonsColumns[] = {PB0,PB1,PB2,PB3,PB4,PB5,PB6,PB7,PB8,PB9,PB10}; 100 | 101 | // Encoders 102 | Rotary SPRotary[8]; 103 | 104 | // Led bank color map, in the same order than enum LedBankIds 105 | const ledColor_t PadLedBanksColorMap[] = { BLUE,RED,GREEN,BLUE,RED,GREEN,}; 106 | 107 | // Color RGB quantization masks 3 colors x 6 banks 108 | static volatile uint32_t PadLedBanksRGBMsk[PAD_COLOR_DEPTH][LED_BANK_MAX-2]; 109 | 110 | // Buttons Banks and mask settings 111 | const uint32_t ButtonLedBankMsk[] = { 112 | BTMSK_MS1, BTMSK_MS2, BTMSK_MS3, BTMSK_MS4, BTMSK_MS5, BTMSK_MS6, BTMSK_MS7, BTMSK_MS8, 113 | BTMSK_UP, BTMSK_DOWN, BTMSK_LEFT, BTMSK_RIGHT,BTMSK_CLIP, BTMSK_MODE1, BTMSK_MODE2, BTMSK_SET, 114 | BTMSK_VOLUME,BTMSK_SENDA,BTMSK_SENDB,BTMSK_PAN, BTMSK_CONTROL1,BTMSK_CONTROL2,BTMSK_CONTROL3,BTMSK_CONTROL4, 115 | }; 116 | 117 | const uint8_t ButtonLedBankMap[] = { 118 | 1,1,1,1,1,1,1,1, 119 | 0,0,0,0,0,0,0,0, 120 | 0,0,0,0,0,0,0,0, 121 | }; 122 | 123 | // Current led states (ON/Off) 124 | // Pads : Higer / Lower 125 | volatile uint32_t PadLedStates[] = { LED_BK_PATTERN1, LED_BK_PATTERN1 }; 126 | // Buttons : Left, bottom / Right 127 | volatile uint32_t ButtonsLedStates[] = { 0, 0 }; 128 | 129 | // Current pad colors set 130 | uint8_t PadColorsCurrent[] { 131 | BLACK, RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, WHITE, 132 | RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, WHITE, BLACK, 133 | GREEN, BLUE, YELLOW, MAGENTA, CYAN, WHITE, BLACK, RED, 134 | BLUE, YELLOW,MAGENTA,CYAN, WHITE, BLACK, RED, GREEN, 135 | 136 | BLACK, RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, WHITE, 137 | RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, WHITE, BLACK, 138 | GREEN, BLUE, YELLOW, MAGENTA, CYAN, WHITE, BLACK, RED, 139 | BLUE, YELLOW,MAGENTA,CYAN, WHITE, BLACK, RED, GREEN 140 | }; 141 | 142 | const uint8_t KikGenLogo[] { 143 | WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, 144 | WHITE, BLUE , ORANGE, ORANGE, ORANGE, BLUE , ORANGE, WHITE, 145 | WHITE, BLUE , ORANGE, ORANGE, BLUE , ORANGE, ORANGE, WHITE, 146 | WHITE, BLUE , BLUE , BLUE , ORANGE, ORANGE, ORANGE, WHITE, 147 | WHITE, BLUE , BLUE , BLUE , ORANGE, ORANGE, ORANGE, WHITE, 148 | WHITE, BLUE , ORANGE, ORANGE, BLUE , ORANGE, ORANGE, WHITE, 149 | WHITE, BLUE , ORANGE, ORANGE, ORANGE, BLUE , ORANGE, WHITE, 150 | WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, 151 | }; 152 | 153 | 154 | // Temporary pad color save 155 | uint8_t PadColorsBackup[PAD_SIZE]; 156 | 157 | // Pad & buttons scan lines states 158 | volatile uint16_t BtnScanStates[8][11] = { 159 | {0,0,0,0,0,0,0,0,0,0,0}, 160 | {0,0,0,0,0,0,0,0,0,0,0}, 161 | {0,0,0,0,0,0,0,0,0,0,0}, 162 | {0,0,0,0,0,0,0,0,0,0,0}, 163 | {0,0,0,0,0,0,0,0,0,0,0}, 164 | {0,0,0,0,0,0,0,0,0,0,0}, 165 | {0,0,0,0,0,0,0,0,0,0,0}, 166 | {0,0,0,0,0,0,0,0,0,0,0}, 167 | }; 168 | 169 | /////////////////////////////////////////////////////////////////////////////// 170 | // CODE MODULES 171 | //----------------------------------------------------------------------------- 172 | // Due to the unusual make process of Arduino platform, modules are included 173 | // directly here as "h" type. This allows a better code separation and readability. 174 | /////////////////////////////////////////////////////////////////////////////// 175 | // DO NOT REMOVE OR CHANGE THE ORDER ! 176 | 177 | 178 | // Kikpad functionnal module.Uncomment only one. 179 | 180 | //#include "mod_kikpad_demo.h" 181 | //#include "mod_kikpad_MPC.h" 182 | //#include "mod_kikpad_MPCClipsTest.h" 183 | //#include "mod_kikpad_MPCClipLauncher.h" 184 | //#include "mod_kikpad_MPCForce.h" 185 | #include "mod_kikpad_LaunchPadMk3.h" 186 | 187 | /////////////////////////////////////////////////////////////////////////////// 188 | // CORE FUNCTIONS 189 | /////////////////////////////////////////////////////////////////////////////// 190 | 191 | /////////////////////////////////////////////////////////////////////////////// 192 | // SerialPrintf : a light printf like to Serial. 193 | // %% : % 194 | // %n : new line 195 | // %s : strings 196 | // %m : string minor case 197 | // %M : string major case 198 | // %y : string beautify (fist letter major case) 199 | // %c : character 200 | // %x : hexa 201 | // %d : integer 202 | // %b : binary 203 | // %(n)s : print n char from string or char array 204 | // %(nn)d/x/b : pad space left until size n 205 | // %(0n)d/x/b : pad 0 left untile size n (nb for b : n=32 bits max). 206 | /////////////////////////////////////////////////////////////////////////////// 207 | #define _PRINT_OUT Serial.print 208 | void SerialPrintf(const char *format, ...) 209 | { 210 | va_list varg; 211 | va_start(varg, format); 212 | 213 | while ( *format != 0 ) { 214 | if (*format == '%') { 215 | format++; 216 | if (*format == '%') _PRINT_OUT(*format); 217 | else if (*format == 'b') _PRINT_OUT(va_arg(varg, long),BIN); // Binary 218 | else if (*format == 'c') _PRINT_OUT((char)va_arg(varg, int)); // Char (must be casted to int) 219 | else if (*format == 'd') _PRINT_OUT(va_arg(varg, long )); // long int 220 | else if (*format == 'u') _PRINT_OUT(va_arg(varg, unsigned long)); // u long int 221 | else if (*format == 'x') _PRINT_OUT(va_arg(varg, long),HEX); // hexa 222 | else if (*format == 's' || *format == 'M' || *format == 'm' || *format == 'y') { 223 | char * str=va_arg(varg, char*); 224 | uint16_t i = 0; 225 | while (*str) { 226 | char c = *str; 227 | if ( c >= 'A' && c <= 'Z' ) { 228 | if ( *format == 'm' || (*format == 'y' && i > 0) ) c += 0x20; 229 | } else 230 | if ( c >= 'a' && c <= 'z') { 231 | if ( *format == 'M' || (*format == 'y' && i == 0) ) c -= 0x20; 232 | } 233 | _PRINT_OUT(c); str++; i++; 234 | } 235 | } 236 | else if (*format >= '0' && *format <= '9' ) { 237 | char p =' '; 238 | if ( *format == '0') { format++; p = '0';} 239 | if ( *format >='1' && *format <= '9' ) { 240 | uint8_t pad = *format - '0'; 241 | format++; 242 | if ( *format >='0' && *format <= '9' ) pad = pad*10 + *(format++) - '0'; 243 | if ( *format == 'c' ) { 244 | while (--pad) _PRINT_OUT(p); 245 | _PRINT_OUT((char)va_arg(varg, int)); 246 | } 247 | else if ( *format == 's' || *format == 'M' || *format == 'm' || *format == 'y') { 248 | char * str=va_arg(varg, char*); 249 | uint16_t i = 0; 250 | while (pad--) { 251 | if (*str) { 252 | char c = *str; 253 | if ( c >= 'A' && c <= 'Z' ) { 254 | if ( *format == 'm' || (*format == 'y' && i > 0) ) c += 0x20; 255 | } else 256 | if ( c >= 'a' && c <= 'z') { 257 | if ( *format == 'M' || (*format == 'y' && i == 0) ) c -= 0x20; 258 | } 259 | _PRINT_OUT(c); str++; i++; 260 | } 261 | else break; 262 | } 263 | } 264 | else { 265 | uint8_t base = 0; 266 | if ( *format == 'd' ) base = 10; 267 | else if ( *format =='x' ) base = 16; 268 | else if ( *format =='b' ) { 269 | base = 2; 270 | if ( pad > 32 ) pad = 8; // 32 bits max 271 | } 272 | if (base) { 273 | int value = va_arg(varg, long); 274 | uint32_t pw = base; 275 | while (pad--) { 276 | if ( value < (int)pw ) while (pad) { _PRINT_OUT(p);pad--;} 277 | else pw *= base; 278 | } 279 | if (base == 16) _PRINT_OUT(value,HEX); 280 | else if (base == 2) _PRINT_OUT(value,BIN); 281 | else _PRINT_OUT(value); 282 | } 283 | } 284 | } else return; 285 | } 286 | else if (*format == 'n') Serial.println(); // new line 287 | else return; 288 | } else Serial.print(*format); 289 | format++; 290 | } 291 | va_end(varg); 292 | } 293 | /////////////////////////////////////////////////////////////////////////////// 294 | // DMC 13C LED Driver management 295 | //----------------------------------------------------------------------------- 296 | // 2 16 bits DMC 13 are cascaded in the SMARTPAD, so 32 bits can be written. 297 | // Bits are sent serialized from 0 to 31 with a clock synchronization. 298 | // End of transmission is done by raising LAT(ch) 299 | /////////////////////////////////////////////////////////////////////////////// 300 | static void WriteDMC(uint32_t mask) { 301 | 302 | for ( uint8_t i = 0; i!= 32 ; i++ ) { 303 | 304 | FAST_DIGITAL_WRITE(DMC_DAI, mask & 1 ); 305 | FAST_DIGITAL_WRITE(DMC_DCK,1); 306 | FAST_DIGITAL_WRITE(DMC_DCK,0); 307 | mask >>= 1; 308 | } 309 | FAST_DIGITAL_WRITE(DMC_LAT,1); 310 | FAST_DIGITAL_WRITE(DMC_LAT,0); 311 | 312 | } 313 | 314 | /////////////////////////////////////////////////////////////////////////////// 315 | // Operate the LS138 BRG colors LED multiplexer and buttons LEDs 316 | //----------------------------------------------------------------------------- 317 | // 8 address can be set : 318 | // . 0-2 are used by higher pads, respectively for colors Blue, Red, Green 319 | // . 3-5 are used by lower pads, respectively for colors Blue, Red, Green 320 | // . 6-7 are used by buttons left+bottom and right 321 | /////////////////////////////////////////////////////////////////////////////// 322 | static boolean LedBankSet(uint8_t addr ) { 323 | static uint8_t lastAddr = 0xFF; 324 | if ( addr == lastAddr ) return false; 325 | 326 | lastAddr = addr; 327 | 328 | FAST_DIGITAL_WRITE(LS_A0, addr & 1 ); 329 | FAST_DIGITAL_WRITE(LS_A1, ( addr & 2 ) >> 1 ); 330 | FAST_DIGITAL_WRITE(LS_A2, ( addr & 4 ) >> 2 ) ; 331 | 332 | return true; 333 | } 334 | 335 | /////////////////////////////////////////////////////////////////////////////// 336 | // Update the RGB colors mask for one pad color only 337 | /////////////////////////////////////////////////////////////////////////////// 338 | void RGBMaskUpdate(uint8_t padIdx) { 339 | if (padIdx >= PAD_SIZE) return; 340 | uint8_t idx,ofs,st; 341 | 342 | if ( padIdx < 32 ) { idx = 31-padIdx; ofs = 0; st = 0; } 343 | else { idx = 63-padIdx; ofs = 3; st = 1; } 344 | 345 | for ( uint8_t b= ofs ; b != ofs+3 ; b++) { 346 | for (uint8_t colorQ = 0; colorQ != PAD_COLOR_DEPTH ; colorQ++) { 347 | uint8_t colorShift = colorQ < 2 ? 0:3; // Manage msb/lsb 348 | if ( ( (PadColorsCurrent[padIdx]>>colorShift) & PadLedBanksColorMap[b] ) && (PadLedStates[st] & (1<< idx) ) ) 349 | PadLedBanksRGBMsk[colorQ][b] |= 1<>colorShift) & PadLedBanksColorMap[b] ) 374 | && (PadLedStates[idx] & (1<> 1 ); 432 | FAST_DIGITAL_WRITE(EC_LS_A2, ( ecAddr & 4 ) >> 2 ); 433 | 434 | // mandatory delay to debounce 435 | delayMicroseconds(4); 436 | 437 | // Read the direction or nothing... 438 | uint8_t state = SPRotary[ecAddr].read(); 439 | if (state == Rotary::dirCw ) { 440 | // Simulate a line 0, col = addr 441 | UserEvent_t ev = { .ev = EV_EC_CW, .d1 = 0 , .d2 = ecAddr } ; 442 | UserEventQueue.write((uint8_t*)&ev,sizeof(UserEvent_t)); 443 | 444 | } else if (state == Rotary::dirCCw ) { 445 | UserEvent_t ev = { .ev = EV_EC_CCW, .d1 = 0, .d2 = ecAddr } ; 446 | UserEventQueue.write((uint8_t*)&ev,sizeof(UserEvent_t)); 447 | } 448 | 449 | if (++ecAddr == 8) ecAddr = 0; 450 | 451 | // Scan lines //////////////////////////////////////////////////////////////// 452 | static uint8_t c = 0; 453 | //for ( uint8_t c = 0 ; c!= sizeof(ScanButtonsColumns); c++ ) { 454 | for ( uint8_t r = 0 ; r !=sizeof(ScanButtonsRows) ;r++ ) { 455 | FAST_DIGITAL_WRITE(ScanButtonsRows[r],1); 456 | FAST_DIGITAL_WRITE(ScanButtonsColumns[c],0); 457 | if ( FAST_DIGITAL_READ(ScanButtonsRows[r]) == 0 ) { 458 | if (BtnScanStates[r][c] == 0 ) { 459 | BtnScanStates[r][c] = 1; 460 | UserEvent_t ev; 461 | // NB : scan rows are columns on the pad matrix ! 462 | // So to match the visual matrix, r and c are inversed. 463 | // The same for buttons, more convenient with 8 columns instead 464 | // of 8 lines/3 columns. Better match with pads. 465 | if ( c < 8 ) ev = { .ev = EV_PAD_PRESSED, .d1 = c, .d2 = r } ; 466 | else ev = { .ev = EV_BTN_PRESSED, .d1 = c-8 , .d2 = r } ; 467 | UserEventQueue.write((uint8_t*)&ev,sizeof(UserEvent_t)); 468 | } 469 | else { 470 | if ( c >= 8 ) { 471 | if ( BtnScanStates[r][c] < BT_HOLD_THRESHOLD ) BtnScanStates[r][c]++; 472 | else if ( BtnScanStates[r][c] == BT_HOLD_THRESHOLD ) { 473 | BtnScanStates[r][c] ++; 474 | UserEvent_t ev = { .ev = EV_BTN_HOLDED, .d1 = c-8 , .d2 = r } ; 475 | UserEventQueue.write((uint8_t*)&ev,sizeof(UserEvent_t)); 476 | } 477 | } 478 | } // else 479 | } 480 | else if ( BtnScanStates[r][c] ) { 481 | BtnScanStates[r][c] = 0; 482 | UserEvent_t ev; 483 | if ( c < 8 ) ev = { .ev = EV_PAD_RELEASED, .d1 = c, .d2 = r } ; 484 | else ev = { .ev = EV_BTN_RELEASED, .d1 = c-8 , .d2 = r } ; 485 | UserEventQueue.write((uint8_t*)&ev,sizeof(UserEvent_t)); 486 | } 487 | FAST_DIGITAL_WRITE(ScanButtonsColumns[c],1); 488 | } 489 | 490 | if (++c == sizeof(ScanButtonsColumns) ) c =0; 491 | } 492 | 493 | /////////////////////////////////////////////////////////////////////////////// 494 | // Set a pad color (0-63) 495 | /////////////////////////////////////////////////////////////////////////////// 496 | void PadSetColor(uint8_t padIdx,uint8_t color) { 497 | if (padIdx >= PAD_SIZE) return; 498 | if (color == PadColorsCurrent[padIdx]) return; 499 | 500 | PadColorsCurrent[padIdx] = color ; 501 | RGBMaskUpdate(padIdx); 502 | } 503 | 504 | /////////////////////////////////////////////////////////////////////////////// 505 | // Temporary save the full pad current color set to a temporary set 506 | /////////////////////////////////////////////////////////////////////////////// 507 | void PadColorsSave() { 508 | memcpy(PadColorsBackup,PadColorsCurrent,PAD_SIZE); ; 509 | } 510 | 511 | /////////////////////////////////////////////////////////////////////////////// 512 | // Restore a temporary saved full pad color set in the current color set 513 | /////////////////////////////////////////////////////////////////////////////// 514 | void PadColorsRestore(uint8_t padIdx) { 515 | memcpy(PadColorsCurrent,PadColorsBackup,PAD_SIZE); 516 | RGBMaskUpdateAll(); 517 | } 518 | 519 | /////////////////////////////////////////////////////////////////////////////// 520 | // Set the pads background 521 | /////////////////////////////////////////////////////////////////////////////// 522 | void PadColorsBackground(uint8_t color) { 523 | memset(PadColorsCurrent,color,PAD_SIZE); 524 | RGBMaskUpdateAll(); 525 | } 526 | 527 | /////////////////////////////////////////////////////////////////////////////// 528 | // Set pads color row 529 | //----------------------------------------------------------------------------- 530 | // Mode 0 COLOR_LINE : Draw a column c at padIdx 531 | // Mode 1 COLOR_COL : Draw a line l at padIdx 532 | // Mode 3 COLOR_CROSS : Draw a cross at padIdx 533 | /////////////////////////////////////////////////////////////////////////////// 534 | void PadColorsRow(uint8_t mode, uint8_t padIdx,uint8_t color) { 535 | if (padIdx >= PAD_SIZE) return; 536 | uint8_t l = padIdx / 8; 537 | uint8_t c = padIdx - 8*l; 538 | 539 | for (uint8_t i=0 ; i != 8 ; i++ ) { 540 | if (mode == COLOR_LINE || mode == COLOR_CROSS) PadSetColor(l*8+i,color); 541 | if (mode >= COLOR_COL || mode == COLOR_CROSS) PadSetColor(c+8*i,color); 542 | } 543 | } 544 | 545 | /////////////////////////////////////////////////////////////////////////////// 546 | // Set a PAD led ON/OFF 547 | /////////////////////////////////////////////////////////////////////////////// 548 | void PadSetLed(uint8_t padIdx,uint8_t state) { 549 | 550 | if (padIdx >= PAD_SIZE) return; 551 | 552 | uint8_t i,idx; 553 | if ( padIdx < 32 ) { i = 0 ; idx = 31 - padIdx; } 554 | else { i = 1; idx = 63 - padIdx; } 555 | 556 | if (state == ON) PadLedStates[i] |= 1 << idx ; 557 | else PadLedStates[i] &= ~( 1<< idx) ; 558 | 559 | } 560 | 561 | /////////////////////////////////////////////////////////////////////////////// 562 | // Set a button LED ON/OFF 563 | //----------------------------------------------------------------------------- 564 | // Bank 0 is 16 bits. First is Set (bit 16), Last is volume (bit 31) 565 | // Bank 1 is 8 bits. First is 8. Bit 24. 566 | /////////////////////////////////////////////////////////////////////////////// 567 | void ButtonSetLed(uint8_t bt,uint8_t state) { 568 | if (bt >= BT_NB_MAX ) return; 569 | 570 | if (state == ON) 571 | ButtonsLedStates[ButtonLedBankMap[bt]] |= ButtonLedBankMsk[bt]; 572 | else ButtonsLedStates[ButtonLedBankMap[bt]] &= ~ButtonLedBankMsk[bt]; 573 | } 574 | 575 | /////////////////////////////////////////////////////////////////////////////// 576 | // Get the led state ON/OFF of a button 577 | /////////////////////////////////////////////////////////////////////////////// 578 | uint8_t ButtonGetLed(uint8_t bt) { 579 | if (bt >= BT_NB_MAX ) return OFF; 580 | 581 | return (ButtonsLedStates[ButtonLedBankMap[bt]] & ButtonLedBankMsk[bt] ? ON:OFF); 582 | 583 | } 584 | 585 | /////////////////////////////////////////////////////////////////////////////// 586 | // Set a buttons bar mask 587 | /////////////////////////////////////////////////////////////////////////////// 588 | void ButtonsBarSetLedMsk(uint8_t btBar,uint32_t bitsMsk) { 589 | 590 | if (btBar == BT_BAR_MASTER ) { 591 | ButtonsLedStates[1] = bitsMsk ; 592 | } 593 | else if ( btBar == BT_BAR_MODES) { 594 | ButtonsLedStates[0] &= ( 0x00FFFFFF | bitsMsk) ; 595 | } 596 | else if ( btBar == BT_BAR_CONTROLS) { 597 | ButtonsLedStates[0] &= 0xFF00FFFF | bitsMsk; 598 | } 599 | } 600 | 601 | /////////////////////////////////////////////////////////////////////////////// 602 | // Get a buttons bar mask 603 | /////////////////////////////////////////////////////////////////////////////// 604 | uint32_t ButtonsBarGetLedMsk(uint8_t btBar) { 605 | 606 | if (btBar == BT_BAR_MASTER ) { 607 | return ButtonsLedStates[1] ; 608 | } 609 | else if ( btBar == BT_BAR_MODES || btBar == BT_BAR_CONTROLS) { 610 | return ButtonsLedStates[0] ; 611 | } 612 | return 0; 613 | } 614 | 615 | /////////////////////////////////////////////////////////////////////////////// 616 | // Get the current pressed state of a button (not a pad !) 617 | /////////////////////////////////////////////////////////////////////////////// 618 | boolean ButtonIsPressed(uint8_t bt) { 619 | if (bt >= BT_NB_MAX ) return false; 620 | // r and c are inversed in the scan array 621 | uint8_t r = bt/8 ; 622 | uint8_t c = bt - 8*r ; 623 | return ( BtnScanStates[c][r+8] ? true:false ); 624 | } 625 | 626 | /////////////////////////////////////////////////////////////////////////////// 627 | // Get the current holded state of a button (not a pad !) 628 | /////////////////////////////////////////////////////////////////////////////// 629 | boolean ButtonIsHolded(uint8_t bt) { 630 | if (bt >= BT_NB_MAX ) return false; 631 | // r and c are inversed in the scan array 632 | uint8_t r = bt/8 ; 633 | uint8_t c = bt - 8*r ; 634 | return ( BtnScanStates[c][r+8] > BT_HOLD_THRESHOLD ? true:false ); 635 | } 636 | 637 | /////////////////////////////////////////////////////////////////////////////// 638 | // Get the current pressed state of a pad (not a button !) 639 | /////////////////////////////////////////////////////////////////////////////// 640 | boolean PadIsPressed(uint8_t padIdx) { 641 | if (padIdx >= PAD_SIZE ) return false; 642 | // r and c are inversed in the scan array 643 | uint8_t r = padIdx/8 ; 644 | uint8_t c = padIdx - 8*r ; 645 | return ( BtnScanStates[c][r] ? true:false ); 646 | } 647 | 648 | /////////////////////////////////////////////////////////////////////////////// 649 | // Set magic bootloader mode 650 | /////////////////////////////////////////////////////////////////////////////// 651 | void BootLoaderMode() 652 | { 653 | // Write the Magic word bootloader 654 | 655 | RCC_BASE->APB1ENR |= (RCC_APB1ENR_BKPEN | RCC_APB1ENR_PWREN) ; 656 | // Enable write access to the backup registers and the RTC 657 | PWR_BASE->CR |= PWR_CR_DBP; 658 | 659 | // write register 660 | BKP_BASE->BOOT_BTL_REGISTER = BOOT_BTL_MAGIC; 661 | 662 | // Disable write 663 | PWR_BASE->CR &= ~PWR_CR_DBP; 664 | RCC_BASE->APB1ENR &= ~(RCC_APB1ENR_BKPEN | RCC_APB1ENR_PWREN) ; 665 | 666 | // Reset 667 | nvic_sys_reset(); 668 | } 669 | 670 | /////////////////////////////////////////////////////////////////////////////// 671 | // Process users events (pads & buttons + encoders) 672 | //----------------------------------------------------------------------------- 673 | // Events are stored in a queue (a ring buffer in fact) 674 | // Event will contain the eventId, row & column 675 | /////////////////////////////////////////////////////////////////////////////// 676 | static void ProcessUserEvent(UserEvent_t *ev){ 677 | 678 | uint8_t idx = SCAN_IDX(ev->d1,ev->d2); 679 | 680 | switch (ev->ev) { 681 | 682 | // Button pressed and not released 683 | case EV_BTN_PRESSED: 684 | // Reset 685 | if ( idx == BT_SET && ButtonIsPressed(BT_CONTROL4) ) { 686 | // RESET = HOLD BT_CONTROL4 & MASTER8 THEN PRESS SET 687 | if ( ButtonIsPressed(BT_MS8) ) { 688 | // All pads off 689 | PadLedStates[0] = PadLedStates[1] = ButtonsLedStates[0] = ButtonsLedStates[1] = 0; 690 | delay(100); 691 | nvic_sys_reset(); 692 | } 693 | else 694 | 695 | // UPDATE = HOLD MODE2 & MASTER7 THEN PRESS SET 696 | if ( ButtonIsPressed(BT_MS7) ) { 697 | PadLedStates[0] = PadLedStates[1] = ButtonsLedStates[0] = ButtonsLedStates[1] = 0; 698 | delay(100); 699 | // Show our logo 700 | memcpy(PadColorsCurrent,KikGenLogo,sizeof(PadColorsCurrent)); 701 | RGBMaskUpdateAll(); 702 | delay(100); 703 | 704 | BootLoaderMode(); // Do a reset 705 | } 706 | } 707 | break; 708 | } 709 | 710 | KikpadMod_ProcessUserEvent(ev); 711 | 712 | } 713 | 714 | /////////////////////////////////////////////////////////////////////////////// 715 | // MIDI USB Loop Process 716 | /////////////////////////////////////////////////////////////////////////////// 717 | static void USBMidi_Process() 718 | { 719 | 720 | // Retrieve from unconnected state or first call 721 | if ( MidiUSB.isConnected() ) { 722 | 723 | // Do we have a MIDI USB packet available ? 724 | if ( MidiUSB.available() ) { 725 | midiPacket_t pk ; 726 | pk.i = MidiUSB.readPacket(); 727 | KikpadMod_USBMidiParse(&pk); 728 | } 729 | } 730 | 731 | } 732 | /////////////////////////////////////////////////////////////////////////////// 733 | // SETUP 734 | /////////////////////////////////////////////////////////////////////////////// 735 | void setup() { 736 | 737 | // Module pre_setup 738 | #ifdef MODULE_PRE_SETUP 739 | KikpadMod_Pre_Setup(); 740 | #endif 741 | 742 | Serial.end(); 743 | 744 | // Disable JTAG to free pins 745 | afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY); 746 | 747 | // USB DISC PIN 748 | pinMode(PA8, OUTPUT); 749 | FAST_DIGITAL_WRITE(PA8,1); 750 | 751 | 752 | // Leds 753 | pinMode(DMC_EN,OUTPUT_OPEN_DRAIN); 754 | FAST_DIGITAL_WRITE(PA2,1); 755 | pinMode(LS_A0,OUTPUT); 756 | pinMode(LS_A1,OUTPUT); 757 | pinMode(LS_A2,OUTPUT); 758 | 759 | pinMode(LS_EN,OUTPUT_OPEN_DRAIN); 760 | FAST_DIGITAL_WRITE(LS_EN,1); 761 | pinMode(DMC_DAI,OUTPUT); 762 | pinMode(DMC_DCK,OUTPUT); 763 | pinMode(DMC_LAT,OUTPUT); 764 | 765 | // BUTTONS 766 | 767 | for (uint8_t i = 0; i< sizeof(ScanButtonsColumns) ; i++ ) { 768 | pinMode(ScanButtonsColumns[i],OUTPUT_OPEN_DRAIN); 769 | FAST_DIGITAL_WRITE(ScanButtonsColumns[i],1); 770 | } 771 | 772 | for (uint8_t i = 0; i< sizeof(ScanButtonsRows) ; i++ ) { 773 | pinMode(ScanButtonsRows[i],INPUT_PULLUP ); 774 | FAST_DIGITAL_WRITE(ScanButtonsRows[i],1); 775 | } 776 | 777 | // Encoders 778 | pinMode(EC_LS_A0,OUTPUT); 779 | pinMode(EC_LS_A1,OUTPUT); 780 | pinMode(EC_LS_A2,OUTPUT); 781 | 782 | for (uint8_t i=0; i!=8 ; i++ ) SPRotary[i].begin(EC_KA,EC_KB); 783 | 784 | // Timer 2 for RGB pad colors, 785 | // Timer 3 for buttons , and encoders 786 | 787 | RGBRefreshTim2.pause(); 788 | UserEventsTim3.pause(); 789 | 790 | RGBRefreshTim2.setPeriod(TIMER_RGB_PERIOD); // in microseconds 791 | RGBRefreshTim2.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); 792 | RGBRefreshTim2.setCompare(TIMER_CH1, 1); 793 | RGBRefreshTim2.attachInterrupt(TIMER_CH1, RGBTim2Handler); 794 | 795 | UserEventsTim3.setPeriod(TIMER_USER_EVENTS_PERIOD); // in microseconds 796 | UserEventsTim3.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); 797 | UserEventsTim3.setCompare(TIMER_CH1, 1); 798 | UserEventsTim3.attachInterrupt(TIMER_CH1, UserEventsTim3Handler); 799 | 800 | RGBRefreshTim2.refresh(); 801 | UserEventsTim3.refresh(); 802 | 803 | // Reset all leds 804 | WriteDMC(0); 805 | LedBankSet(LED_BANK_BT2); 806 | FAST_DIGITAL_WRITE(DMC_EN,0); 807 | FAST_DIGITAL_WRITE(LS_EN,0); 808 | 809 | // Start timer 810 | UserEventsTim3.resume(); 811 | RGBRefreshTim2.resume(); 812 | 813 | // All Leds On 814 | PadLedStates[0] = PadLedStates[1] = ButtonsLedStates[0] = ButtonsLedStates[1] = 0xFFFFFFFF; 815 | 816 | // Show all colors 817 | for (uint8_t i=0; i != 64 ; i ++ ) PadColorsCurrent[i]=i; 818 | RGBMaskUpdateAll(); 819 | 820 | // Start USB Midi 821 | MidiUSB.begin() ; 822 | 823 | // Retrieve from unconnected state or first call 824 | while (! MidiUSB.isConnected() ) delay(500); 825 | 826 | // All buttons Leds Off 827 | ButtonsLedStates[0] = ButtonsLedStates[1] = 0; 828 | 829 | // Show our logo 830 | memcpy(PadColorsCurrent,KikGenLogo,sizeof(PadColorsCurrent)); 831 | RGBMaskUpdateAll(); 832 | delay(1000); 833 | 834 | // Final module initialize 835 | KikpadMod_Setup(); 836 | 837 | } 838 | /////////////////////////////////////////////////////////////////////////////// 839 | // LOOP 840 | /////////////////////////////////////////////////////////////////////////////// 841 | void loop() { 842 | USBMidi_Process(); 843 | 844 | if (UserEventQueue.available() >= sizeof(UserEvent_t) ) { 845 | UserEvent_t ev; 846 | UserEventQueue.readBytes((uint8_t *)&ev,sizeof(UserEvent_t)); 847 | ProcessUserEvent(&ev); 848 | } 849 | 850 | KikpadMod_Loop(); 851 | 852 | } 853 | --------------------------------------------------------------------------------