├── .gitignore ├── case ├── case.pdf ├── case.png ├── smallCase.pdf └── smallCase.svg ├── doc ├── emptyBox.jpg ├── overview.odg ├── overview.png ├── pinsUSB.png ├── result.jpg ├── pinsAtmega328.png ├── pinsSwitches.png ├── pinsRotatoryPot.png └── bootloaderFlashingSetup.png ├── hardware ├── board.fzz ├── board.png ├── boardIRL.jpg ├── schematic.png └── usbCableIRL.jpg ├── .gitmodules ├── lib └── MidiUSB │ ├── Multiplexer.h │ ├── usbdrvasm.asm │ ├── Multiplexer.cpp │ ├── oddebug.c │ ├── MidiUSB.h │ ├── oddebug.h │ ├── usbportability.h │ ├── asmcommon.inc │ ├── usbconfig.h │ ├── usbdrvasm.S │ ├── usbdrvasm16.inc │ ├── MidiUSB.cpp │ ├── usbdrvasm20.inc │ ├── usbdrvasm12.inc │ ├── usbdrvasm165.inc │ ├── usbdrvasm15.inc │ └── usbconfig-prototype.h ├── main.cpp ├── Makefile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.hex 3 | .lib 4 | .dep 5 | -------------------------------------------------------------------------------- /case/case.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/case/case.pdf -------------------------------------------------------------------------------- /case/case.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/case/case.png -------------------------------------------------------------------------------- /doc/emptyBox.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/doc/emptyBox.jpg -------------------------------------------------------------------------------- /doc/overview.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/doc/overview.odg -------------------------------------------------------------------------------- /doc/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/doc/overview.png -------------------------------------------------------------------------------- /doc/pinsUSB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/doc/pinsUSB.png -------------------------------------------------------------------------------- /doc/result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/doc/result.jpg -------------------------------------------------------------------------------- /case/smallCase.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/case/smallCase.pdf -------------------------------------------------------------------------------- /hardware/board.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/hardware/board.fzz -------------------------------------------------------------------------------- /hardware/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/hardware/board.png -------------------------------------------------------------------------------- /doc/pinsAtmega328.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/doc/pinsAtmega328.png -------------------------------------------------------------------------------- /doc/pinsSwitches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/doc/pinsSwitches.png -------------------------------------------------------------------------------- /hardware/boardIRL.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/hardware/boardIRL.jpg -------------------------------------------------------------------------------- /doc/pinsRotatoryPot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/doc/pinsRotatoryPot.png -------------------------------------------------------------------------------- /hardware/schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/hardware/schematic.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bootloader"] 2 | path = bootloader 3 | url = https://github.com/baerwolf/USBaspLoader 4 | -------------------------------------------------------------------------------- /hardware/usbCableIRL.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/hardware/usbCableIRL.jpg -------------------------------------------------------------------------------- /doc/bootloaderFlashingSetup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexAubin/customMidiController/HEAD/doc/bootloaderFlashingSetup.png -------------------------------------------------------------------------------- /lib/MidiUSB/Multiplexer.h: -------------------------------------------------------------------------------- 1 | #ifndef MULTIPLEXER_H_ 2 | #define MULTIPLEXER_H_ 3 | 4 | #include "Arduino.h" 5 | 6 | class Multiplexer 7 | { 8 | public : 9 | 10 | Multiplexer(int pin, int controlPinA, int controlPinB, int controlPinC); 11 | 12 | void init(); 13 | 14 | void select(unsigned char id); 15 | int getPin(); 16 | 17 | private : 18 | 19 | int pin; 20 | int controlPinA; 21 | int controlPinB; 22 | int controlPinC; 23 | 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /lib/MidiUSB/usbdrvasm.asm: -------------------------------------------------------------------------------- 1 | /* Name: usbdrvasm.asm 2 | * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2006-03-01 5 | * Tabsize: 4 6 | * Copyright: (c) 2006 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | */ 9 | 10 | /* 11 | General Description: 12 | The IAR compiler/assembler system prefers assembler files with file extension 13 | ".asm". We simply provide this file as an alias for usbdrvasm.S. 14 | 15 | Thanks to Oleg Semyonov for his help with the IAR tools port! 16 | */ 17 | 18 | #include "usbdrvasm.S" 19 | 20 | end 21 | -------------------------------------------------------------------------------- /lib/MidiUSB/Multiplexer.cpp: -------------------------------------------------------------------------------- 1 | #include "Multiplexer.h" 2 | 3 | Multiplexer::Multiplexer(int pin, int controlPinA, int controlPinB, int controlPinC) 4 | { 5 | this->pin = pin; 6 | this->controlPinA = controlPinA; 7 | this->controlPinB = controlPinB; 8 | this->controlPinC = controlPinC; 9 | } 10 | 11 | void Multiplexer::init() 12 | { 13 | pinMode(pin, INPUT); 14 | pinMode(controlPinA, OUTPUT); 15 | pinMode(controlPinB, OUTPUT); 16 | pinMode(controlPinC, OUTPUT); 17 | } 18 | 19 | void Multiplexer::select(unsigned char id) 20 | { 21 | if ((id < 0) || (id >= 8)) return; 22 | 23 | delay(1); 24 | digitalWrite(controlPinA, id & (1 << 0)); 25 | digitalWrite(controlPinB, id & (1 << 1)); 26 | digitalWrite(controlPinC, id & (1 << 2)); 27 | } 28 | 29 | int Multiplexer::getPin() 30 | { 31 | return pin; 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "lib/MidiUSB/MidiUSB.h" 4 | 5 | MIDIController midiController; 6 | 7 | 8 | Multiplexer mux1(A5, 11, 10, 9); 9 | 10 | void setup() 11 | { 12 | 13 | mux1.init(); 14 | 15 | midiController.initUSB(); 16 | //midiController.mapPinToNote(12,60); 17 | midiController.mapPinToControl(&mux1, 0, 0); 18 | midiController.mapPinToControl(&mux1, 1, 1); 19 | midiController.mapPinToControl(&mux1, 2, 2); 20 | midiController.mapPinToControl(&mux1, 3, 3); 21 | midiController.mapPinToControl(&mux1, 4, 4); 22 | midiController.mapPinToControl(&mux1, 5, 5); 23 | midiController.mapPinToControl(&mux1, 6, 6); 24 | midiController.mapPinToControl(&mux1, 7, 7); 25 | 26 | } 27 | 28 | void loop() 29 | { 30 | 31 | midiController.update(); 32 | 33 | delay(10); 34 | 35 | } 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /lib/MidiUSB/oddebug.c: -------------------------------------------------------------------------------- 1 | /* Name: oddebug.c 2 | * Project: AVR library 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2005-01-16 5 | * Tabsize: 4 6 | * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | */ 9 | 10 | #include "oddebug.h" 11 | 12 | #if DEBUG_LEVEL > 0 13 | 14 | #warning "Never compile production devices with debugging enabled" 15 | 16 | static void uartPutc(char c) 17 | { 18 | while(!(ODDBG_USR & (1 << ODDBG_UDRE))); /* wait for data register empty */ 19 | ODDBG_UDR = c; 20 | } 21 | 22 | static uchar hexAscii(uchar h) 23 | { 24 | h &= 0xf; 25 | if(h >= 10) 26 | h += 'a' - (uchar)10 - '0'; 27 | h += '0'; 28 | return h; 29 | } 30 | 31 | static void printHex(uchar c) 32 | { 33 | uartPutc(hexAscii(c >> 4)); 34 | uartPutc(hexAscii(c)); 35 | } 36 | 37 | void odDebug(uchar prefix, uchar *data, uchar len) 38 | { 39 | printHex(prefix); 40 | uartPutc(':'); 41 | while(len--){ 42 | uartPutc(' '); 43 | printHex(*data++); 44 | } 45 | uartPutc('\r'); 46 | uartPutc('\n'); 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /lib/MidiUSB/MidiUSB.h: -------------------------------------------------------------------------------- 1 | #ifndef _MIDIUSB_H_ 2 | #define _MIDIUSB_H_ 3 | 4 | #include "Multiplexer.h" 5 | 6 | #define DEBOUNCE_PARAMETER 5 7 | #define MAX_NUMBER_OF_ELEMENTS 32 8 | 9 | class MIDIController 10 | { 11 | 12 | public : 13 | 14 | MIDIController(); 15 | 16 | int initUSB(); 17 | void mapPinToNote ( int pinNumber, char noteValue); 18 | void mapPinToNote (Multiplexer* mux, int pinNumber, char noteValue); 19 | void mapPinToControl( int pinNumber, char controlId); 20 | void mapPinToControl(Multiplexer* mux, int pinNumber, char controlId); 21 | void update(); 22 | void readElement(int i); 23 | void updateElement(int i); 24 | void sendMidiMessage(char a, char b, char c); 25 | 26 | private : 27 | 28 | void initElement(int pinNumber, char function, char functionParameter, Multiplexer* mux = 0); 29 | 30 | int numberOfElements; 31 | int elementPin [MAX_NUMBER_OF_ELEMENTS]; 32 | char elementFunction [MAX_NUMBER_OF_ELEMENTS]; 33 | char elementFunctionParameter[MAX_NUMBER_OF_ELEMENTS]; 34 | char elementLastState [MAX_NUMBER_OF_ELEMENTS]; 35 | char elementDebounceCount [MAX_NUMBER_OF_ELEMENTS]; 36 | char elementStatus [MAX_NUMBER_OF_ELEMENTS]; 37 | 38 | 39 | Multiplexer* elementMux [MAX_NUMBER_OF_ELEMENTS]; 40 | 41 | unsigned char usbMessage[8]; 42 | 43 | }; 44 | 45 | 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /lib/MidiUSB/oddebug.h: -------------------------------------------------------------------------------- 1 | /* Name: oddebug.h 2 | * Project: AVR library 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2005-01-16 5 | * Tabsize: 4 6 | * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | */ 9 | 10 | #ifndef __oddebug_h_included__ 11 | #define __oddebug_h_included__ 12 | 13 | /* 14 | General Description: 15 | This module implements a function for debug logs on the serial line of the 16 | AVR microcontroller. Debugging can be configured with the define 17 | 'DEBUG_LEVEL'. If this macro is not defined or defined to 0, all debugging 18 | calls are no-ops. If it is 1, DBG1 logs will appear, but not DBG2. If it is 19 | 2, DBG1 and DBG2 logs will be printed. 20 | 21 | A debug log consists of a label ('prefix') to indicate which debug log created 22 | the output and a memory block to dump in hex ('data' and 'len'). 23 | */ 24 | 25 | 26 | #ifndef F_CPU 27 | # define F_CPU 12000000 /* 12 MHz */ 28 | #endif 29 | 30 | /* make sure we have the UART defines: */ 31 | #include "usbportability.h" 32 | 33 | #ifndef uchar 34 | # define uchar unsigned char 35 | #endif 36 | 37 | #if DEBUG_LEVEL > 0 && !(defined TXEN || defined TXEN0) /* no UART in device */ 38 | # warning "Debugging disabled because device has no UART" 39 | # undef DEBUG_LEVEL 40 | #endif 41 | 42 | #ifndef DEBUG_LEVEL 43 | # define DEBUG_LEVEL 0 44 | #endif 45 | 46 | /* ------------------------------------------------------------------------- */ 47 | 48 | #if DEBUG_LEVEL > 0 49 | # define DBG1(prefix, data, len) odDebug(prefix, data, len) 50 | #else 51 | # define DBG1(prefix, data, len) 52 | #endif 53 | 54 | #if DEBUG_LEVEL > 1 55 | # define DBG2(prefix, data, len) odDebug(prefix, data, len) 56 | #else 57 | # define DBG2(prefix, data, len) 58 | #endif 59 | 60 | /* ------------------------------------------------------------------------- */ 61 | 62 | #if DEBUG_LEVEL > 0 63 | extern void odDebug(uchar prefix, uchar *data, uchar len); 64 | 65 | /* Try to find our control registers; ATMEL likes to rename these */ 66 | 67 | #if defined UBRR 68 | # define ODDBG_UBRR UBRR 69 | #elif defined UBRRL 70 | # define ODDBG_UBRR UBRRL 71 | #elif defined UBRR0 72 | # define ODDBG_UBRR UBRR0 73 | #elif defined UBRR0L 74 | # define ODDBG_UBRR UBRR0L 75 | #endif 76 | 77 | #if defined UCR 78 | # define ODDBG_UCR UCR 79 | #elif defined UCSRB 80 | # define ODDBG_UCR UCSRB 81 | #elif defined UCSR0B 82 | # define ODDBG_UCR UCSR0B 83 | #endif 84 | 85 | #if defined TXEN 86 | # define ODDBG_TXEN TXEN 87 | #else 88 | # define ODDBG_TXEN TXEN0 89 | #endif 90 | 91 | #if defined USR 92 | # define ODDBG_USR USR 93 | #elif defined UCSRA 94 | # define ODDBG_USR UCSRA 95 | #elif defined UCSR0A 96 | # define ODDBG_USR UCSR0A 97 | #endif 98 | 99 | #if defined UDRE 100 | # define ODDBG_UDRE UDRE 101 | #else 102 | # define ODDBG_UDRE UDRE0 103 | #endif 104 | 105 | #if defined UDR 106 | # define ODDBG_UDR UDR 107 | #elif defined UDR0 108 | # define ODDBG_UDR UDR0 109 | #endif 110 | 111 | static inline void odDebugInit(void) 112 | { 113 | ODDBG_UCR |= (1< 38 | #ifndef __IAR_SYSTEMS_ASM__ 39 | # include 40 | #endif 41 | 42 | #define __attribute__(arg) /* not supported on IAR */ 43 | 44 | #ifdef __IAR_SYSTEMS_ASM__ 45 | # define __ASSEMBLER__ /* IAR does not define standard macro for asm */ 46 | #endif 47 | 48 | #ifdef __HAS_ELPM__ 49 | # define PROGMEM __farflash 50 | #else 51 | # define PROGMEM __flash 52 | #endif 53 | 54 | #define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) 55 | 56 | /* The following definitions are not needed by the driver, but may be of some 57 | * help if you port a gcc based project to IAR. 58 | */ 59 | #define cli() __disable_interrupt() 60 | #define sei() __enable_interrupt() 61 | #define wdt_reset() __watchdog_reset() 62 | #define _BV(x) (1 << (x)) 63 | 64 | /* assembler compatibility macros */ 65 | #define nop2 rjmp $+2 /* jump to next instruction */ 66 | #define XL r26 67 | #define XH r27 68 | #define YL r28 69 | #define YH r29 70 | #define ZL r30 71 | #define ZH r31 72 | #define lo8(x) LOW(x) 73 | #define hi8(x) (((x)>>8) & 0xff) /* not HIGH to allow XLINK to make a proper range check */ 74 | 75 | /* Depending on the device you use, you may get problems with the way usbdrv.h 76 | * handles the differences between devices. Since IAR does not use #defines 77 | * for MCU registers, we can't check for the existence of a particular 78 | * register with an #ifdef. If the autodetection mechanism fails, include 79 | * definitions for the required USB_INTR_* macros in your usbconfig.h. See 80 | * usbconfig-prototype.h and usbdrv.h for details. 81 | */ 82 | 83 | /* ------------------------------------------------------------------------- */ 84 | #elif __CODEVISIONAVR__ /* check for CodeVision AVR */ 85 | /* ------------------------------------------------------------------------- */ 86 | /* This port is not working (yet) */ 87 | 88 | /* #define F_CPU _MCU_CLOCK_FREQUENCY_ seems to be defined automatically */ 89 | 90 | #include 91 | #include 92 | 93 | #define __attribute__(arg) /* not supported on IAR */ 94 | 95 | #define PROGMEM __flash 96 | #define USB_READ_FLASH(addr) (*(PROGMEM char *)(addr)) 97 | 98 | #ifndef __ASSEMBLER__ 99 | static inline void cli(void) 100 | { 101 | #asm("cli"); 102 | } 103 | static inline void sei(void) 104 | { 105 | #asm("sei"); 106 | } 107 | #endif 108 | #define _delay_ms(t) delay_ms(t) 109 | #define _BV(x) (1 << (x)) 110 | #define USB_CFG_USE_SWITCH_STATEMENT 1 /* macro for if() cascase fails for unknown reason */ 111 | 112 | #define macro .macro 113 | #define endm .endmacro 114 | #define nop2 rjmp .+0 /* jump to next instruction */ 115 | 116 | /* ------------------------------------------------------------------------- */ 117 | #else /* default development environment is avr-gcc/avr-libc */ 118 | /* ------------------------------------------------------------------------- */ 119 | 120 | #include 121 | #ifdef __ASSEMBLER__ 122 | # define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */ 123 | #else 124 | # include 125 | #endif 126 | 127 | #if USB_CFG_DRIVER_FLASH_PAGE 128 | # define USB_READ_FLASH(addr) pgm_read_byte_far(((long)USB_CFG_DRIVER_FLASH_PAGE << 16) | (long)(addr)) 129 | #else 130 | # define USB_READ_FLASH(addr) pgm_read_byte(addr) 131 | #endif 132 | 133 | #define macro .macro 134 | #define endm .endm 135 | #define nop2 rjmp .+0 /* jump to next instruction */ 136 | 137 | #endif /* development environment */ 138 | 139 | /* for conveniecne, ensure that PRG_RDB exists */ 140 | #ifndef PRG_RDB 141 | # define PRG_RDB(addr) USB_READ_FLASH(addr) 142 | #endif 143 | #endif /* __usbportability_h_INCLUDED__ */ 144 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # 3 | # Based on Arduino Makefile http://ed.am/dev/make/arduino-mk 4 | # Customized by Alex. 5 | # 6 | ############################################################################### 7 | 8 | MAIN_FILE = main.cpp 9 | 10 | # Microcontroller configuration : check this match yours ! 11 | 12 | BOARD_BUILD_MCU = atmega328p 13 | BOARD_BUILD_FCPU = 12000000 14 | BOARD_BUILD_VARIANT = standard 15 | 16 | # Uploader configuration 17 | 18 | UPLOAD_PROTOCOL = usbasp 19 | UPLOAD_SPEED = 19200 20 | UPLOAD_PORT = usb 21 | 22 | # Compiler 23 | 24 | CC = avr-gcc 25 | CXX = avr-g++ 26 | AR = avr-ar 27 | OBJCOPY = avr-objcopy 28 | AVRDUDE = avrdude 29 | AVRSIZE = avr-size 30 | 31 | # Arduino library stuff 32 | 33 | ARDUINODIR = /usr/share/arduino/ 34 | 35 | ############################################################################### 36 | # Pathes, flags and stuff 37 | ############################################################################### 38 | 39 | # Default arduino version 40 | 41 | ARDUINOCONST = 100 42 | 43 | # Target and sources 44 | 45 | TARGET := $(basename $(MAIN_FILE)) 46 | SOURCES := $(wildcard *.c *.cc *.cpp lib/*/*.cpp lib/*/*.c lib/*/*.S) 47 | 48 | # Automatically determine included libraries 49 | 50 | LIBRARIES := $(filter $(notdir $(wildcard $(ARDUINODIR)/libraries/*)), \ 51 | $(shell sed -ne "s/^ *\# *include *[<\"]\(.*\)\.h[>\"]/\1/p" $(SOURCES))) 52 | 53 | 54 | # Check arduino folder is correct 55 | 56 | ifeq "$(wildcard $(ARDUINODIR)/hardware/arduino/boards.txt)" "" 57 | $(error ARDUINODIR is not set correctly; arduino software not found) 58 | endif 59 | 60 | # Default path for avr tools 61 | 62 | AVRTOOLSPATH := $(subst :, , $(PATH)) 63 | AVRTOOLSPATH += $(ARDUINODIR)/hardware/tools 64 | AVRTOOLSPATH += $(ARDUINODIR)/hardware/tools/avr/bin 65 | 66 | # Files 67 | 68 | TARGET := $(if $(TARGET),$(TARGET),a.out) 69 | OBJECTS := $(addsuffix .o, $(basename $(SOURCES))) 70 | DEPFILES := $(patsubst %, .dep/%.dep, $(SOURCES)) 71 | ARDUINOCOREDIR := $(ARDUINODIR)/hardware/arduino/cores/arduino 72 | ARDUINOLIB := .lib/arduino.a 73 | ARDUINOLIBLIBSDIR := $(ARDUINODIR)/libraries 74 | ARDUINOLIBLIBSPATH := $(foreach lib, $(LIBRARIES), \ 75 | $(ARDUINODIR)/libraries/$(lib)/ $(ARDUINODIR)/libraries/$(lib)/utility/ ) 76 | ARDUINOLIBOBJS := $(foreach dir, $(ARDUINOCOREDIR) $(ARDUINOLIBLIBSPATH), \ 77 | $(patsubst %, .lib/%.o, $(wildcard $(addprefix $(dir)/, *.c *.cpp)))) 78 | 79 | # Flags 80 | 81 | OPTIMIZATION := -O1 82 | CPPFLAGS := $(OPTIMIZATION) -Wall -fno-exceptions -ffunction-sections -fdata-sections 83 | CPPFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums 84 | CPPFLAGS += -mmcu=$(BOARD_BUILD_MCU) 85 | CPPFLAGS += -DF_CPU=$(BOARD_BUILD_FCPU) -DARDUINO=$(ARDUINOCONST) 86 | CPPFLAGS += -I. -I./lib/*/ -I$(ARDUINOCOREDIR) 87 | CPPFLAGS += -I$(ARDUINODIR)/hardware/arduino/variants/$(BOARD_BUILD_VARIANT)/ 88 | CPPFLAGS += $(addprefix -I$(ARDUINODIR)/libraries/, $(LIBRARIES)) 89 | CPPFLAGS += $(patsubst %, -I$(ARDUINODIR)/libraries/%/utility, $(LIBRARIES)) 90 | CPPDEPFLAGS = -MMD -MP -MF .dep/$<.dep 91 | CPPINOFLAGS := -x c++ -include $(ARDUINOCOREDIR)/Arduino.h 92 | AVRDUDEFLAGS := $(addprefix -C , $(AVRDUDECONF)) -DV 93 | AVRDUDEFLAGS += -p $(BOARD_BUILD_MCU) -c $(UPLOAD_PROTOCOL) -b $(UPLOAD_SPEED) -P $(UPLOAD_PORT) 94 | LINKFLAGS := $(OPTIMIZATION) -Wl,--gc-sections -mmcu=$(BOARD_BUILD_MCU) 95 | 96 | # Include dependencies 97 | 98 | ifneq "$(MAKECMDGOALS)" "clean" 99 | -include $(DEPFILES) 100 | endif 101 | 102 | ############################################################################### 103 | # Targets 104 | ############################################################################### 105 | 106 | .PHONY: default everything all hex upload clean bootloader bootloader-flash 107 | 108 | default: hex 109 | 110 | everything: clean hex upload 111 | 112 | all: everything 113 | 114 | hex: $(TARGET).hex 115 | 116 | upload: 117 | @echo " " 118 | @echo "#######################" 119 | @echo " Uploading to board..." 120 | @echo "#######################" 121 | @echo " " 122 | su root --command "$(AVRDUDE) $(AVRDUDEFLAGS) -v -F -B 1 -U flash:w:$(TARGET).hex" 123 | 124 | clean: 125 | @echo " " 126 | @echo "#######################" 127 | @echo " Cleaning stuff..." 128 | @echo "#######################" 129 | @echo " " 130 | rm -f $(OBJECTS) $(TARGET).elf $(TARGET).hex $(ARDUINOLIB) *~ 131 | rm -rf .lib .dep 132 | 133 | bootloader: 134 | @echo " " 135 | @echo "#######################" 136 | @echo " Compiling USBasp bootloader..." 137 | @echo " Note : this Makefile passed the following parameters to the bootloader's Makefile :" 138 | @echo " DEVICE = $(BOARD_BUILD_MCU)" 139 | @echo " F_CPU = $(BOARD_BUILD_FCPU) (Hz)" 140 | @echo "#######################" 141 | @echo " " 142 | $(MAKE) -C bootloader clean 143 | $(MAKE) -C bootloader firmware F_CPU=$(BOARD_BUILD_FCPU) DEVICE=$(BOARD_BUILD_MCU) 144 | 145 | bootloader-flash: 146 | @echo " " 147 | @echo "#######################" 148 | @echo " Attempting to flash bootloader using Arduino as in-situ programmer (AVRisp)..." 149 | @echo "#######################" 150 | @echo " " 151 | $(MAKE) -C bootloader fuse DEVICE=$(BOARD_BUILD_MCU) PROGRAMMER='-c avrisp -P `ls /dev/ttyACM*` -b $(UPLOAD_SPEED)' LOCKOPT='-U lock:w:0xcf:m' 152 | $(MAKE) -C bootloader flash DEVICE=$(BOARD_BUILD_MCU) PROGRAMMER='-c avrisp -P `ls /dev/ttyACM*` -b $(UPLOAD_SPEED)' LOCKOPT='-U lock:w:0xcf:m' 153 | 154 | ############################################################################### 155 | # Building rules 156 | ############################################################################### 157 | 158 | $(TARGET).hex: $(TARGET).elf 159 | $(OBJCOPY) -O ihex -R .eeprom $< $@ 160 | 161 | .INTERMEDIATE: $(TARGET).elf 162 | 163 | $(TARGET).elf: $(ARDUINOLIB) $(OBJECTS) 164 | $(CC) $(LINKFLAGS) $(OBJECTS) $(ARDUINOLIB) -o $@ 165 | 166 | %.o: %.c 167 | mkdir -p .dep/$(dir $<) 168 | $(COMPILE.cpp) $(CPPDEPFLAGS) -o $@ $< 169 | 170 | %.o: %.cpp 171 | mkdir -p .dep/$(dir $<) 172 | $(COMPILE.cpp) $(CPPDEPFLAGS) -o $@ $< 173 | 174 | %.o: %.cc 175 | mkdir -p .dep/$(dir $<) 176 | $(COMPILE.cpp) $(CPPDEPFLAGS) -o $@ $< 177 | 178 | %.o: %.C 179 | mkdir -p .dep/$(dir $<) 180 | $(COMPILE.cpp) $(CPPDEPFLAGS) -o $@ $< 181 | 182 | %.o: %.ino 183 | mkdir -p .dep/$(dir $<) 184 | $(COMPILE.cpp) $(CPPDEPFLAGS) -o $@ $(CPPINOFLAGS) $< 185 | 186 | %.o: %.pde 187 | mkdir -p .dep/$(dir $<) 188 | $(COMPILE.cpp) $(CPPDEPFLAGS) -o $@ -x c++ -include $(ARDUINOCOREDIR)/Arduino.h $< 189 | 190 | %.o: %.S 191 | $(COMPILE.c) $(CPPDEPFLAGS) -x assembler-with-cpp -c $< -o $@ 192 | 193 | # Rules for the Arduino lib 194 | 195 | $(ARDUINOLIB): $(ARDUINOLIBOBJS) 196 | $(AR) rcs $@ $? 197 | 198 | .lib/%.c.o: %.c 199 | mkdir -p $(dir $@) 200 | $(COMPILE.c) -o $@ $< 201 | 202 | .lib/%.cpp.o: %.cpp 203 | mkdir -p $(dir $@) 204 | $(COMPILE.cpp) -o $@ $< 205 | 206 | .lib/%.cc.o: %.cc 207 | mkdir -p $(dir $@) 208 | $(COMPILE.cpp) -o $@ $< 209 | 210 | .lib/%.C.o: %.C 211 | mkdir -p $(dir $@) 212 | $(COMPILE.cpp) -o $@ $< 213 | 214 | -------------------------------------------------------------------------------- /lib/MidiUSB/asmcommon.inc: -------------------------------------------------------------------------------- 1 | /* Name: asmcommon.inc 2 | * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers 3 | * Author: Christian Starkjohann 4 | * Creation Date: 2007-11-05 5 | * Tabsize: 4 6 | * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | */ 9 | 10 | /* Do not link this file! Link usbdrvasm.S instead, which includes the 11 | * appropriate implementation! 12 | */ 13 | 14 | /* 15 | General Description: 16 | This file contains assembler code which is shared among the USB driver 17 | implementations for different CPU cocks. Since the code must be inserted 18 | in the middle of the module, it's split out into this file and #included. 19 | 20 | Jump destinations called from outside: 21 | sofError: Called when no start sequence was found. 22 | se0: Called when a package has been successfully received. 23 | overflow: Called when receive buffer overflows. 24 | doReturn: Called after sending data. 25 | 26 | Outside jump destinations used by this module: 27 | waitForJ: Called to receive an already arriving packet. 28 | sendAckAndReti: 29 | sendNakAndReti: 30 | sendCntAndReti: 31 | usbSendAndReti: 32 | 33 | The following macros must be defined before this file is included: 34 | .macro POP_STANDARD 35 | .endm 36 | .macro POP_RETI 37 | .endm 38 | */ 39 | 40 | #define token x1 41 | 42 | overflow: 43 | ldi x2, 1< 10.6666666 cycles per bit, 85.333333333 cycles per byte 29 | ; Numbers in brackets are clocks counted from center of last sync bit 30 | ; when instruction starts 31 | 32 | USB_INTR_VECTOR: 33 | ;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt 34 | push YL ;[-25] push only what is necessary to sync with edge ASAP 35 | in YL, SREG ;[-23] 36 | push YL ;[-22] 37 | push YH ;[-20] 38 | ;---------------------------------------------------------------------------- 39 | ; Synchronize with sync pattern: 40 | ;---------------------------------------------------------------------------- 41 | ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 42 | ;sync up with J to K edge during sync pattern -- use fastest possible loops 43 | ;The first part waits at most 1 bit long since we must be in sync pattern. 44 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 45 | ;waitForJ, ensure that this prerequisite is met. 46 | waitForJ: 47 | inc YL 48 | sbis USBIN, USBMINUS 49 | brne waitForJ ; just make sure we have ANY timeout 50 | waitForK: 51 | ;The following code results in a sampling window of < 1/4 bit which meets the spec. 52 | sbis USBIN, USBMINUS ;[-15] 53 | rjmp foundK ;[-14] 54 | sbis USBIN, USBMINUS 55 | rjmp foundK 56 | sbis USBIN, USBMINUS 57 | rjmp foundK 58 | sbis USBIN, USBMINUS 59 | rjmp foundK 60 | sbis USBIN, USBMINUS 61 | rjmp foundK 62 | sbis USBIN, USBMINUS 63 | rjmp foundK 64 | #if USB_COUNT_SOF 65 | lds YL, usbSofCount 66 | inc YL 67 | sts usbSofCount, YL 68 | #endif /* USB_COUNT_SOF */ 69 | #ifdef USB_SOF_HOOK 70 | USB_SOF_HOOK 71 | #endif 72 | rjmp sofError 73 | foundK: ;[-12] 74 | ;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] 75 | ;we have 1 bit time for setup purposes, then sample again. Numbers in brackets 76 | ;are cycles from center of first sync (double K) bit after the instruction 77 | push bitcnt ;[-12] 78 | ; [---] ;[-11] 79 | lds YL, usbInputBufOffset;[-10] 80 | ; [---] ;[-9] 81 | clr YH ;[-8] 82 | subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] 83 | sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] 84 | push shift ;[-5] 85 | ; [---] ;[-4] 86 | ldi bitcnt, 0x55 ;[-3] [rx loop init] 87 | sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) 88 | rjmp haveTwoBitsK ;[-1] 89 | pop shift ;[0] undo the push from before 90 | pop bitcnt ;[2] undo the push from before 91 | rjmp waitForK ;[4] this was not the end of sync, retry 92 | ; The entire loop from waitForK until rjmp waitForK above must not exceed two 93 | ; bit times (= 21 cycles). 94 | 95 | ;---------------------------------------------------------------------------- 96 | ; push more registers and initialize values while we sample the first bits: 97 | ;---------------------------------------------------------------------------- 98 | haveTwoBitsK: 99 | push x1 ;[1] 100 | push x2 ;[3] 101 | push x3 ;[5] 102 | ldi shift, 0 ;[7] 103 | ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that 104 | push x4 ;[9] == leap 105 | 106 | in x1, USBIN ;[11] <-- sample bit 0 107 | andi x1, USBMASK ;[12] 108 | bst x1, USBMINUS ;[13] 109 | bld shift, 7 ;[14] 110 | push cnt ;[15] 111 | ldi leap, 0 ;[17] [rx loop init] 112 | ldi cnt, USB_BUFSIZE;[18] [rx loop init] 113 | rjmp rxbit1 ;[19] arrives at [21] 114 | 115 | ;---------------------------------------------------------------------------- 116 | ; Receiver loop (numbers in brackets are cycles within byte after instr) 117 | ;---------------------------------------------------------------------------- 118 | 119 | ; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" 120 | ; accordingly to approximate this value in the long run. 121 | 122 | unstuff6: 123 | andi x2, USBMASK ;[03] 124 | ori x3, 1<<6 ;[04] will not be shifted any more 125 | andi shift, ~0x80;[05] 126 | mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 127 | subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 128 | rjmp didUnstuff6 ;[08] 129 | 130 | unstuff7: 131 | ori x3, 1<<7 ;[09] will not be shifted any more 132 | in x2, USBIN ;[00] [10] re-sample bit 7 133 | andi x2, USBMASK ;[01] 134 | andi shift, ~0x80;[02] 135 | subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 136 | rjmp didUnstuff7 ;[04] 137 | 138 | unstuffEven: 139 | ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 140 | in x1, USBIN ;[00] [10] 141 | andi shift, ~0x80;[01] 142 | andi x1, USBMASK ;[02] 143 | breq se0 ;[03] 144 | subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 145 | nop2 ;[05] 146 | rjmp didUnstuffE ;[06] 147 | 148 | unstuffOdd: 149 | ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 150 | in x2, USBIN ;[00] [10] 151 | andi shift, ~0x80;[01] 152 | andi x2, USBMASK ;[02] 153 | breq se0 ;[03] 154 | subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 155 | nop2 ;[05] 156 | rjmp didUnstuffO ;[06] 157 | 158 | rxByteLoop: 159 | andi x1, USBMASK ;[03] 160 | eor x2, x1 ;[04] 161 | subi leap, 1 ;[05] 162 | brpl skipLeap ;[06] 163 | subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte 164 | nop ;1 165 | skipLeap: 166 | subi x2, 1 ;[08] 167 | ror shift ;[09] 168 | didUnstuff6: 169 | cpi shift, 0xfc ;[10] 170 | in x2, USBIN ;[00] [11] <-- sample bit 7 171 | brcc unstuff6 ;[01] 172 | andi x2, USBMASK ;[02] 173 | eor x1, x2 ;[03] 174 | subi x1, 1 ;[04] 175 | ror shift ;[05] 176 | didUnstuff7: 177 | cpi shift, 0xfc ;[06] 178 | brcc unstuff7 ;[07] 179 | eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others 180 | st y+, x3 ;[09] store data 181 | rxBitLoop: 182 | in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 183 | andi x1, USBMASK ;[01] 184 | eor x2, x1 ;[02] 185 | andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 186 | subi x2, 1 ;[04] 187 | ror shift ;[05] 188 | cpi shift, 0xfc ;[06] 189 | brcc unstuffEven ;[07] 190 | didUnstuffE: 191 | lsr x3 ;[08] 192 | lsr x3 ;[09] 193 | rxbit1: 194 | in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 195 | andi x2, USBMASK ;[01] 196 | breq se0 ;[02] 197 | eor x1, x2 ;[03] 198 | subi x1, 1 ;[04] 199 | ror shift ;[05] 200 | cpi shift, 0xfc ;[06] 201 | brcc unstuffOdd ;[07] 202 | didUnstuffO: 203 | subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 204 | brcs rxBitLoop ;[09] 205 | 206 | subi cnt, 1 ;[10] 207 | in x1, USBIN ;[00] [11] <-- sample bit 6 208 | brcc rxByteLoop ;[01] 209 | rjmp overflow 210 | 211 | macro POP_STANDARD ; 14 cycles 212 | pop cnt 213 | pop x4 214 | pop x3 215 | pop x2 216 | pop x1 217 | pop shift 218 | pop bitcnt 219 | endm 220 | macro POP_RETI ; 7 cycles 221 | pop YH 222 | pop YL 223 | out SREG, YL 224 | pop YL 225 | endm 226 | 227 | #include "asmcommon.inc" 228 | 229 | ; USB spec says: 230 | ; idle = J 231 | ; J = (D+ = 0), (D- = 1) 232 | ; K = (D+ = 1), (D- = 0) 233 | ; Spec allows 7.5 bit times from EOP to SOP for replies 234 | 235 | bitstuffN: 236 | eor x1, x4 ;[5] 237 | ldi x2, 0 ;[6] 238 | nop2 ;[7] 239 | nop ;[9] 240 | out USBOUT, x1 ;[10] <-- out 241 | rjmp didStuffN ;[0] 242 | 243 | bitstuff6: 244 | eor x1, x4 ;[5] 245 | ldi x2, 0 ;[6] Carry is zero due to brcc 246 | rol shift ;[7] compensate for ror shift at branch destination 247 | rjmp didStuff6 ;[8] 248 | 249 | bitstuff7: 250 | ldi x2, 0 ;[2] Carry is zero due to brcc 251 | rjmp didStuff7 ;[3] 252 | 253 | 254 | sendNakAndReti: 255 | ldi x3, USBPID_NAK ;[-18] 256 | rjmp sendX3AndReti ;[-17] 257 | sendAckAndReti: 258 | ldi cnt, USBPID_ACK ;[-17] 259 | sendCntAndReti: 260 | mov x3, cnt ;[-16] 261 | sendX3AndReti: 262 | ldi YL, 20 ;[-15] x3==r20 address is 20 263 | ldi YH, 0 ;[-14] 264 | ldi cnt, 2 ;[-13] 265 | ; rjmp usbSendAndReti fallthrough 266 | 267 | ;usbSend: 268 | ;pointer to data in 'Y' 269 | ;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] 270 | ;uses: x1...x4, btcnt, shift, cnt, Y 271 | ;Numbers in brackets are time since first bit of sync pattern is sent 272 | ;We don't match the transfer rate exactly (don't insert leap cycles every third 273 | ;byte) because the spec demands only 1.5% precision anyway. 274 | usbSendAndReti: ; 12 cycles until SOP 275 | in x2, USBDDR ;[-12] 276 | ori x2, USBMASK ;[-11] 277 | sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) 278 | in x1, USBOUT ;[-8] port mirror for tx loop 279 | out USBDDR, x2 ;[-7] <- acquire bus 280 | ; need not init x2 (bitstuff history) because sync starts with 0 281 | ldi x4, USBMASK ;[-6] exor mask 282 | ldi shift, 0x80 ;[-5] sync byte is first byte sent 283 | txByteLoop: 284 | ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 285 | txBitLoop: 286 | sbrs shift, 0 ;[-3] [7] 287 | eor x1, x4 ;[-2] [8] 288 | out USBOUT, x1 ;[-1] [9] <-- out N 289 | ror shift ;[0] [10] 290 | ror x2 ;[1] 291 | didStuffN: 292 | cpi x2, 0xfc ;[2] 293 | brcc bitstuffN ;[3] 294 | lsr bitcnt ;[4] 295 | brcc txBitLoop ;[5] 296 | brne txBitLoop ;[6] 297 | 298 | sbrs shift, 0 ;[7] 299 | eor x1, x4 ;[8] 300 | didStuff6: 301 | out USBOUT, x1 ;[-1] [9] <-- out 6 302 | ror shift ;[0] [10] 303 | ror x2 ;[1] 304 | cpi x2, 0xfc ;[2] 305 | brcc bitstuff6 ;[3] 306 | ror shift ;[4] 307 | didStuff7: 308 | ror x2 ;[5] 309 | sbrs x2, 7 ;[6] 310 | eor x1, x4 ;[7] 311 | nop ;[8] 312 | cpi x2, 0xfc ;[9] 313 | out USBOUT, x1 ;[-1][10] <-- out 7 314 | brcc bitstuff7 ;[0] [11] 315 | ld shift, y+ ;[1] 316 | dec cnt ;[3] 317 | brne txByteLoop ;[4] 318 | ;make SE0: 319 | cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] 320 | lds x2, usbNewDeviceAddr;[6] 321 | lsl x2 ;[8] we compare with left shifted address 322 | subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 323 | sbci YH, 0 ;[10] 324 | out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle 325 | ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: 326 | ;set address only after data packet was sent, not after handshake 327 | breq skipAddrAssign ;[0] 328 | sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer 329 | skipAddrAssign: 330 | ;end of usbDeviceAddress transfer 331 | ldi x2, 1< 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "MidiUSB.h" 11 | #include "usbdrv.h" 12 | 13 | // ########################################################################### 14 | // # USB Descriptor and HID stuff # 15 | // ########################################################################### 16 | 17 | //MIDI Adapter Device Descriptor (MIDI10.pdf Appendix B.1) 18 | static const PROGMEM char deviceDescrMIDI[] = { 19 | 18, /* length of descriptor in bytes */ 20 | 1, /* descriptor type */ 21 | 0x10, 0x01, /* USB version supported */ 22 | 0, /* device class: defined at interface level */ 23 | 0, /* subclass */ 24 | 0, /* protocol */ 25 | 8, /* max packet size */ 26 | USB_CFG_VENDOR_ID, /* Vendor ID */ 27 | USB_CFG_DEVICE_ID, /* Product ID */ 28 | USB_CFG_DEVICE_VERSION, /* Device Release Code */ 29 | 1, /* manufacturer string index */ 30 | 2, /* product string index */ 31 | 0, /* serial number string index */ 32 | 1, /* number of configurations */ 33 | }; 34 | 35 | //MIDI Adapter Configuration Descriptor (MIDI10.pdf Appendix B.2) 36 | static const PROGMEM char configDescrMIDI[] = { 37 | 9, /* sizeof(usbDescrConfig): length of descriptor in bytes */ 38 | USBDESCR_CONFIG, /* descriptor type 2: CONFIGURATION*/ 39 | 101, 0, /* total length of data returned (including inlined descriptors) */ 40 | 2, /* number of interfaces in this configuration */ 41 | 1, /* index of this configuration */ 42 | 0, /* configuration name string index */ 43 | USBATTR_BUSPOWER,/* attributes */ 44 | USB_CFG_MAX_BUS_POWER/2,/* max USB current in 2mA units */ 45 | 46 | //MIDI Adapter Standard AC Interface Descriptor (MIDI10.pdf Appendix B.3.1) 47 | 9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ 48 | USBDESCR_INTERFACE, /* descriptor type 4: INTERFACE*/ 49 | 0, /* index of this interface */ 50 | 0, /* alternate setting for this interface */ 51 | 0, /* endpoints excl 0: number of endpoint descriptors to follow */ 52 | 1, /* */ 53 | 1, /* */ 54 | 0, /* */ 55 | 0, /* string index for interface */ 56 | 57 | //MIDI Adapter Class-specific AC Interface Descriptor (MIDI10.pdf Appendix B.3.2) 58 | 9, /* sizeof(usbDescrCDC_HeaderFn): length of descriptor in bytes */ 59 | 36, /* descriptor type 0x24: CS_INTERFACE - special to USB, so not defined in usbdrv.h */ 60 | 1, /* header functional descriptor */ 61 | 0x0, 0x01, /* bcdADC */ 62 | 9, 0, /* wTotalLength */ 63 | 1, /* */ 64 | 1, /* */ 65 | 66 | //Standard MIDIStreaming Interface Descriptor (MIDI10.pdf Appendix B.3.1) 67 | 9, /* length of descriptor in bytes */ 68 | USBDESCR_INTERFACE, /* descriptor type */ 69 | 1, /* index of this interface */ 70 | 0, /* alternate setting for this interface */ 71 | 2, /* endpoints excl 0: number of endpoint descriptors to follow */ 72 | 1, /* AUDIO */ 73 | 3, /* MS */ 74 | 0, /* unused */ 75 | 0, /* string index for interface */ 76 | 77 | //Class-specific MIDIStreaming Interface Descriptor (MIDI10.pdf Appendix B.4.2) 78 | 7, /* length of descriptor in bytes */ 79 | 36, /* descriptor type 0x24: CS_INTERFACE */ 80 | 1, /* header functional descriptor */ 81 | 0x0, 0x01, /* bcdADC */ 82 | 65, 0, /* wTotalLength */ 83 | 84 | //MIDI IN Jack Descriptor (MIDI10.pdf Appendix B.4.3) 85 | 6, /* bLength */ 86 | 36, /* descriptor type 0x24: CS_INTERFACE */ 87 | 2, /* MIDI_IN_JACK desc subtype */ 88 | 1, /* EMBEDDED bJackType */ 89 | 1, /* bJackID */ 90 | 0, /* iJack */ 91 | 92 | 6, /* bLength */ 93 | 36, /* descriptor type 0x24: CS_INTERFACE */ 94 | 2, /* MIDI_IN_JACK desc subtype */ 95 | 2, /* EXTERNAL bJackType */ 96 | 2, /* bJackID */ 97 | 0, /* iJack */ 98 | 99 | //MIDI OUT Jack Descriptor (MIDI10.pdf Appendix B.4.4) 100 | 9, /* length of descriptor in bytes */ 101 | 36, /* descriptor type 0x24: CS_INTERFACE */ 102 | 3, /* MIDI_OUT_JACK descriptor */ 103 | 1, /* EMBEDDED bJackType */ 104 | 3, /* bJackID */ 105 | 1, /* No of input pins */ 106 | 2, /* BaSourceID */ 107 | 1, /* BaSourcePin */ 108 | 0, /* iJack */ 109 | 110 | 9, /* bLength of descriptor in bytes */ 111 | 36, /* bDescriptorType */ 112 | 3, /* MIDI_OUT_JACK bDescriptorSubtype */ 113 | 2, /* EXTERNAL bJackType */ 114 | 4, /* bJackID */ 115 | 1, /* bNrInputPins */ 116 | 1, /* baSourceID (0) */ 117 | 1, /* baSourcePin (0) */ 118 | 0, /* iJack */ 119 | 120 | //Standard Bulk OUT Endpoint Descriptor (MIDI10.pdf Appendix B.5.1) 121 | 9, /* bLenght */ 122 | USBDESCR_ENDPOINT, /* bDescriptorType = endpoint */ 123 | 0x1, /* bEndpointAddress OUT endpoint number 1 */ 124 | 3, /* bmAttributes: 2:Bulk, 3:Interrupt endpoint */ 125 | 8, 0, /* wMaxPacketSize */ 126 | 10, /* bIntervall in ms */ 127 | 0, /* bRefresh */ 128 | 0, /* bSyncAddress */ 129 | 130 | //Class-specific MS Bulk OUT Endpoint (MIDI10.pdf Appendix Descriptor B.5.2) 131 | 5, /* bLength of descriptor in bytes */ 132 | 37, /* bDescriptorType 0x25: CS_ENDPOINT */ 133 | 1, /* bDescriptorSubtype */ 134 | 1, /* bNumEmbMIDIJack */ 135 | 1, /* baAssocJackID (0) */ 136 | 137 | //Standard Bulk IN Endpoint Descriptor (MIDI10.pdf Appendix Descriptor B.6.1) 138 | 9, /* bLenght */ 139 | USBDESCR_ENDPOINT, /* bDescriptorType 0x05: ENDPOINT */ 140 | 0x81, /* bEndpointAddress IN endpoint number 1 */ 141 | 3, /* bmAttributes: 2: Bulk, 3: Interrupt endpoint */ 142 | 8, 0, /* wMaxPacketSize */ 143 | 10, /* bIntervall in ms */ 144 | 0, /* bRefresh */ 145 | 0, /* bSyncAddress */ 146 | 147 | //Class-specific MS Bulk IN Endpoint Descriptor (MIDI10.pdf Appendix Descriptor B.6.2) 148 | 5, /* bLength of descriptor in bytes */ 149 | 37, /* bDescriptorType 0x37: CS_ENDPOINT */ 150 | 1, /* bDescriptorSubtype */ 151 | 1, /* bNumEmbMIDIJack (0) */ 152 | 3, /* baAssocJackID (0) */ 153 | }; 154 | 155 | // USB HID Setup 156 | // ----------------------------------------------------------------------------- 157 | 158 | uchar usbFunctionDescriptor(usbRequest_t * rq) 159 | { 160 | if (rq->wValue.bytes[1] == USBDESCR_DEVICE) 161 | { 162 | usbMsgPtr = (uchar *) deviceDescrMIDI; 163 | return sizeof(deviceDescrMIDI); 164 | } 165 | 166 | if (rq->wValue.bytes[1] == USBDESCR_CONFIG) 167 | { 168 | usbMsgPtr = (uchar *) configDescrMIDI; 169 | return sizeof(configDescrMIDI); 170 | } 171 | 172 | return 0; 173 | } 174 | 175 | uchar usbFunctionSetup(uchar data[8]) 176 | { 177 | usbRequest_t *rq = (usbRequest_t *) data; 178 | 179 | // class request type 180 | if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) 181 | { 182 | // Prepare bulk-in endpoint to respond to early termination 183 | if ((rq->bmRequestType & USBRQ_DIR_MASK) == 184 | USBRQ_DIR_HOST_TO_DEVICE) {}; 185 | } 186 | 187 | return 0xff; 188 | } 189 | 190 | uchar usbFunctionRead(uchar * data, uchar len) 191 | { 192 | 193 | data[0] = 0; 194 | data[1] = 0; 195 | data[2] = 0; 196 | data[3] = 0; 197 | data[4] = 0; 198 | data[5] = 0; 199 | data[6] = 0; 200 | 201 | return 7; 202 | } 203 | 204 | uchar usbFunctionWrite(uchar * data, uchar len) 205 | { 206 | return 1; 207 | } 208 | 209 | void usbFunctionWriteOut(uchar * data, uchar len) 210 | { 211 | } 212 | 213 | // Calibrate (DO NOTHING) when ready 214 | void usbEventResetReady(void) 215 | { 216 | } 217 | 218 | // ############################################################################ 219 | // # MIDI controller # 220 | // ############################################################################ 221 | 222 | enum ElementFunction { NOTE, CONTROL, PLAY, PAUSE, STOP }; 223 | enum ElementStatus { UP_TO_DATE, TO_BE_UPDATED }; 224 | 225 | enum MidiMessageType 226 | { 227 | MIDI_INVALID_TYPE = 0x00, ///< For notifying errors 228 | MIDI_NOTE_OFF = 0x80, ///< Note Off 229 | MIDI_NOTE_ON = 0x90, ///< Note On 230 | MIDI_AFTERTOUCH_POLY = 0xA0, ///< Polyphonic AfterTouch 231 | MIDI_CONTROL_CHANGE = 0xB0, ///< Control Change / Channel Mode 232 | MIDI_PROGRAM_CHANGE = 0xC0, ///< Program Change 233 | MIDI_AFTERTOUCH_CHANNEL = 0xD0, ///< Channel (monophonic) AfterTouch 234 | MIDI_PITCH_BEND = 0xE0, ///< Pitch Bend 235 | MIDI_SYSTEM_EXCLUSIVE = 0xF0, ///< System Exclusive 236 | MIDI_TIME_CODE_QUARTER_FRAME = 0xF1, ///< System Common - MIDI Time Code Quarter Frame 237 | MIDI_SONG_POSITION = 0xF2, ///< System Common - Song Position Pointer 238 | MIDI_SONG_SELECT = 0xF3, ///< System Common - Song Select 239 | MIDI_TUNE_REQUEST = 0xF6, ///< System Common - Tune Request 240 | MIDI_CLOCK = 0xF8, ///< System Real Time - Timing Clock 241 | MIDI_START = 0xFA, ///< System Real Time - Start 242 | MIDI_CONTINUE = 0xFB, ///< System Real Time - Continue 243 | MIDI_STOP = 0xFC, ///< System Real Time - Stop 244 | MIDI_ACTIVE_SENSING = 0xFE, ///< System Real Time - Active Sensing 245 | MIDI_SYSTEM_RESET = 0xFF, ///< System Real Time - System Reset 246 | }; 247 | 248 | MIDIController::MIDIController() 249 | { 250 | numberOfElements = 0; 251 | }; 252 | 253 | int MIDIController::initUSB() 254 | { 255 | 256 | // Disconnection-reconnection-enumeration dance 257 | 258 | wdt_enable(WDTO_4S); // enable 1s watchdog timer 259 | 260 | usbInit(); 261 | usbDeviceDisconnect(); 262 | 263 | // 600 ms disconnect 264 | uchar i; 265 | for(i=0 ; i < 60 ; i++) 266 | { 267 | wdt_reset(); 268 | _delay_ms(15); 269 | } 270 | 271 | usbDeviceConnect(); 272 | sei(); 273 | 274 | return 0; 275 | } 276 | 277 | void MIDIController::mapPinToNote(int pinNumber, char noteValue) 278 | { 279 | pinMode(pinNumber, INPUT); 280 | initElement(pinNumber, NOTE, noteValue); 281 | }; 282 | 283 | void MIDIController::mapPinToNote(Multiplexer* mux, int pinNumber, char noteValue) 284 | { 285 | initElement(pinNumber, NOTE, noteValue, mux); 286 | }; 287 | 288 | void MIDIController::mapPinToControl(int pinNumber, char controlId) 289 | { 290 | pinMode(pinNumber, INPUT); 291 | initElement(pinNumber, CONTROL, controlId); 292 | }; 293 | 294 | void MIDIController::mapPinToControl(Multiplexer* mux, int pinNumber, char controlId) 295 | { 296 | initElement(pinNumber, CONTROL, controlId, mux); 297 | }; 298 | 299 | void MIDIController::initElement(int pinNumber, char function, char functionParameter, Multiplexer* mux) 300 | { 301 | elementPin[numberOfElements] = pinNumber; 302 | elementFunction[numberOfElements] = function; 303 | elementFunctionParameter[numberOfElements] = functionParameter; 304 | elementLastState[numberOfElements] = 0; 305 | elementDebounceCount[numberOfElements] = 0; 306 | elementStatus[numberOfElements] = UP_TO_DATE; 307 | elementMux[numberOfElements] = mux; 308 | numberOfElements++; 309 | }; 310 | 311 | 312 | void MIDIController::update() 313 | { 314 | usbPoll(); 315 | 316 | wdt_reset(); // keep the watchdog happy 317 | 318 | for (int i = 0 ; i < numberOfElements ; i++) 319 | { 320 | readElement(i); 321 | } 322 | 323 | for (int i = 0 ; i < numberOfElements ; i++) 324 | { 325 | if ((elementStatus[i] == TO_BE_UPDATED) && (usbInterruptIsReady())) 326 | { 327 | updateElement(i); 328 | break; 329 | } 330 | } 331 | 332 | }; 333 | 334 | void MIDIController::readElement(int i) 335 | { 336 | if (elementDebounceCount[i] != 0) 337 | { 338 | elementDebounceCount[i]--; 339 | return; 340 | } 341 | 342 | int pin = elementPin[i]; 343 | char currentValue = -1; 344 | char oldValue = elementLastState[i]; 345 | Multiplexer* mux = elementMux[i]; 346 | char changeDetected = 0; 347 | 348 | switch(elementFunction[i]) 349 | { 350 | case NOTE : 351 | { 352 | if (mux) currentValue = digitalRead(mux->getPin()); 353 | else currentValue = digitalRead(pin); 354 | if (currentValue != oldValue) changeDetected = 1; 355 | break; 356 | } 357 | case CONTROL : 358 | { 359 | if (mux) 360 | { 361 | mux->select(pin); 362 | delay(1); 363 | currentValue = analogRead(mux->getPin()) / 8; 364 | delay(1); 365 | } 366 | else 367 | { 368 | delay(1); 369 | currentValue = analogRead(pin) / 8; 370 | delay(1); 371 | } 372 | 373 | if (abs(currentValue - oldValue) > 1) changeDetected = 1; 374 | 375 | break; 376 | } 377 | } 378 | 379 | if (changeDetected != 0) 380 | { 381 | elementLastState[i] = currentValue; 382 | elementStatus[i] = TO_BE_UPDATED; 383 | elementDebounceCount[i] = DEBOUNCE_PARAMETER; 384 | } 385 | } 386 | 387 | void MIDIController::updateElement(int i) 388 | { 389 | 390 | switch(elementFunction[i]) 391 | { 392 | case NOTE : 393 | { 394 | char note = elementFunctionParameter[i]; 395 | if (elementLastState[i] == HIGH) sendMidiMessage(MIDI_NOTE_OFF, note, 0x00); 396 | else sendMidiMessage(MIDI_NOTE_ON, note, 0x7f); 397 | break; 398 | } 399 | case CONTROL : 400 | { 401 | char id = elementFunctionParameter[i]; 402 | char value = elementLastState[i]; 403 | sendMidiMessage(MIDI_CONTROL_CHANGE, id, value); 404 | break; 405 | } 406 | } 407 | 408 | elementStatus[i] = UP_TO_DATE; 409 | } 410 | 411 | void MIDIController::sendMidiMessage(char a, char b, char c) 412 | { 413 | usbMessage[0] = 0x08; 414 | usbMessage[1] = a; 415 | usbMessage[2] = b; 416 | usbMessage[3] = c; 417 | usbMessage[4] = 0x00; 418 | usbMessage[5] = 0x00; 419 | usbMessage[6] = 0x00; 420 | usbMessage[7] = 0x00; 421 | usbSetInterrupt(usbMessage, sizeof(usbMessage)); 422 | } 423 | 424 | 425 | 426 | -------------------------------------------------------------------------------- /lib/MidiUSB/usbdrvasm20.inc: -------------------------------------------------------------------------------- 1 | /* Name: usbdrvasm20.inc 2 | * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers 3 | * Author: Jeroen Benschop 4 | * Based on usbdrvasm16.inc from Christian Starkjohann 5 | * Creation Date: 2008-03-05 6 | * Tabsize: 4 7 | * Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH 8 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 9 | */ 10 | 11 | /* Do not link this file! Link usbdrvasm.S instead, which includes the 12 | * appropriate implementation! 13 | */ 14 | 15 | /* 16 | General Description: 17 | This file is the 20 MHz version of the asssembler part of the USB driver. It 18 | requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC 19 | oscillator). 20 | 21 | See usbdrv.h for a description of the entire driver. 22 | 23 | Since almost all of this code is timing critical, don't change unless you 24 | really know what you are doing! Many parts require not only a maximum number 25 | of CPU cycles, but even an exact number of cycles! 26 | */ 27 | 28 | #define leap2 x3 29 | #ifdef __IAR_SYSTEMS_ASM__ 30 | #define nextInst $+2 31 | #else 32 | #define nextInst .+0 33 | #endif 34 | 35 | ;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes 36 | ;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte 37 | ; Numbers in brackets are clocks counted from center of last sync bit 38 | ; when instruction starts 39 | ;register use in receive loop: 40 | ; shift assembles the byte currently being received 41 | ; x1 holds the D+ and D- line state 42 | ; x2 holds the previous line state 43 | ; x4 (leap) is used to add a leap cycle once every three bytes received 44 | ; X3 (leap2) is used to add a leap cycle once every three stuff bits received 45 | ; bitcnt is used to determine when a stuff bit is due 46 | ; cnt holds the number of bytes left in the receive buffer 47 | 48 | USB_INTR_VECTOR: 49 | ;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt 50 | push YL ;[-28] push only what is necessary to sync with edge ASAP 51 | in YL, SREG ;[-26] 52 | push YL ;[-25] 53 | push YH ;[-23] 54 | ;---------------------------------------------------------------------------- 55 | ; Synchronize with sync pattern: 56 | ;---------------------------------------------------------------------------- 57 | ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 58 | ;sync up with J to K edge during sync pattern -- use fastest possible loops 59 | ;The first part waits at most 1 bit long since we must be in sync pattern. 60 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 61 | ;waitForJ, ensure that this prerequisite is met. 62 | waitForJ: 63 | inc YL 64 | sbis USBIN, USBMINUS 65 | brne waitForJ ; just make sure we have ANY timeout 66 | waitForK: 67 | ;The following code results in a sampling window of < 1/4 bit which meets the spec. 68 | sbis USBIN, USBMINUS ;[-19] 69 | rjmp foundK ;[-18] 70 | sbis USBIN, USBMINUS 71 | rjmp foundK 72 | sbis USBIN, USBMINUS 73 | rjmp foundK 74 | sbis USBIN, USBMINUS 75 | rjmp foundK 76 | sbis USBIN, USBMINUS 77 | rjmp foundK 78 | sbis USBIN, USBMINUS 79 | rjmp foundK 80 | sbis USBIN, USBMINUS 81 | rjmp foundK 82 | sbis USBIN, USBMINUS 83 | rjmp foundK 84 | sbis USBIN, USBMINUS 85 | rjmp foundK 86 | #if USB_COUNT_SOF 87 | lds YL, usbSofCount 88 | inc YL 89 | sts usbSofCount, YL 90 | #endif /* USB_COUNT_SOF */ 91 | #ifdef USB_SOF_HOOK 92 | USB_SOF_HOOK 93 | #endif 94 | rjmp sofError 95 | foundK: ;[-16] 96 | ;{3, 5} after falling D- edge, average delay: 4 cycles 97 | ;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample 98 | ;use 1 bit time for setup purposes, then sample again. Numbers in brackets 99 | ;are cycles from center of first sync (double K) bit after the instruction 100 | push bitcnt ;[-16] 101 | ; [---] ;[-15] 102 | lds YL, usbInputBufOffset;[-14] 103 | ; [---] ;[-13] 104 | clr YH ;[-12] 105 | subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] 106 | sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] 107 | push shift ;[-9] 108 | ; [---] ;[-8] 109 | ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected 110 | nop2 ;[-6] 111 | ; [---] ;[-5] 112 | ldi bitcnt, 5 ;[-4] [rx loop init] 113 | sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) 114 | rjmp haveTwoBitsK ;[-2] 115 | pop shift ;[-1] undo the push from before 116 | pop bitcnt ;[1] 117 | rjmp waitForK ;[3] this was not the end of sync, retry 118 | ; The entire loop from waitForK until rjmp waitForK above must not exceed two 119 | ; bit times (= 27 cycles). 120 | 121 | ;---------------------------------------------------------------------------- 122 | ; push more registers and initialize values while we sample the first bits: 123 | ;---------------------------------------------------------------------------- 124 | haveTwoBitsK: 125 | push x1 ;[0] 126 | push x2 ;[2] 127 | push x3 ;[4] (leap2) 128 | ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit 129 | push x4 ;[7] == leap 130 | ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received 131 | push cnt ;[10] 132 | ldi cnt, USB_BUFSIZE ;[12] [rx loop init] 133 | ldi x2, 1< max 25 cycles interrupt disable 38 | ;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes 39 | ;Numbers in brackets are maximum cycles since SOF. 40 | USB_INTR_VECTOR: 41 | ;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt 42 | push YL ;2 [35] push only what is necessary to sync with edge ASAP 43 | in YL, SREG ;1 [37] 44 | push YL ;2 [39] 45 | ;---------------------------------------------------------------------------- 46 | ; Synchronize with sync pattern: 47 | ;---------------------------------------------------------------------------- 48 | ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 49 | ;sync up with J to K edge during sync pattern -- use fastest possible loops 50 | ;The first part waits at most 1 bit long since we must be in sync pattern. 51 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 52 | ;waitForJ, ensure that this prerequisite is met. 53 | waitForJ: 54 | inc YL 55 | sbis USBIN, USBMINUS 56 | brne waitForJ ; just make sure we have ANY timeout 57 | waitForK: 58 | ;The following code results in a sampling window of 1/4 bit which meets the spec. 59 | sbis USBIN, USBMINUS 60 | rjmp foundK 61 | sbis USBIN, USBMINUS 62 | rjmp foundK 63 | sbis USBIN, USBMINUS 64 | rjmp foundK 65 | sbis USBIN, USBMINUS 66 | rjmp foundK 67 | sbis USBIN, USBMINUS 68 | rjmp foundK 69 | #if USB_COUNT_SOF 70 | lds YL, usbSofCount 71 | inc YL 72 | sts usbSofCount, YL 73 | #endif /* USB_COUNT_SOF */ 74 | #ifdef USB_SOF_HOOK 75 | USB_SOF_HOOK 76 | #endif 77 | rjmp sofError 78 | foundK: 79 | ;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] 80 | ;we have 1 bit time for setup purposes, then sample again. Numbers in brackets 81 | ;are cycles from center of first sync (double K) bit after the instruction 82 | push YH ;2 [2] 83 | lds YL, usbInputBufOffset;2 [4] 84 | clr YH ;1 [5] 85 | subi YL, lo8(-(usbRxBuf));1 [6] 86 | sbci YH, hi8(-(usbRxBuf));1 [7] 87 | 88 | sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] 89 | rjmp haveTwoBitsK ;2 [10] 90 | pop YH ;2 [11] undo the push from before 91 | rjmp waitForK ;2 [13] this was not the end of sync, retry 92 | haveTwoBitsK: 93 | ;---------------------------------------------------------------------------- 94 | ; push more registers and initialize values while we sample the first bits: 95 | ;---------------------------------------------------------------------------- 96 | push shift ;2 [16] 97 | push x1 ;2 [12] 98 | push x2 ;2 [14] 99 | 100 | in x1, USBIN ;1 [17] <-- sample bit 0 101 | ldi shift, 0xff ;1 [18] 102 | bst x1, USBMINUS ;1 [19] 103 | bld shift, 0 ;1 [20] 104 | push x3 ;2 [22] 105 | push cnt ;2 [24] 106 | 107 | in x2, USBIN ;1 [25] <-- sample bit 1 108 | ser x3 ;1 [26] [inserted init instruction] 109 | eor x1, x2 ;1 [27] 110 | bst x1, USBMINUS ;1 [28] 111 | bld shift, 1 ;1 [29] 112 | ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] 113 | rjmp rxbit2 ;2 [32] 114 | 115 | ;---------------------------------------------------------------------------- 116 | ; Receiver loop (numbers in brackets are cycles within byte after instr) 117 | ;---------------------------------------------------------------------------- 118 | 119 | unstuff0: ;1 (branch taken) 120 | andi x3, ~0x01 ;1 [15] 121 | mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit 122 | in x2, USBIN ;1 [17] <-- sample bit 1 again 123 | ori shift, 0x01 ;1 [18] 124 | rjmp didUnstuff0 ;2 [20] 125 | 126 | unstuff1: ;1 (branch taken) 127 | mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit 128 | andi x3, ~0x02 ;1 [22] 129 | ori shift, 0x02 ;1 [23] 130 | nop ;1 [24] 131 | in x1, USBIN ;1 [25] <-- sample bit 2 again 132 | rjmp didUnstuff1 ;2 [27] 133 | 134 | unstuff2: ;1 (branch taken) 135 | andi x3, ~0x04 ;1 [29] 136 | ori shift, 0x04 ;1 [30] 137 | mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit 138 | nop ;1 [32] 139 | in x2, USBIN ;1 [33] <-- sample bit 3 140 | rjmp didUnstuff2 ;2 [35] 141 | 142 | unstuff3: ;1 (branch taken) 143 | in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] 144 | andi x3, ~0x08 ;1 [35] 145 | ori shift, 0x08 ;1 [36] 146 | rjmp didUnstuff3 ;2 [38] 147 | 148 | unstuff4: ;1 (branch taken) 149 | andi x3, ~0x10 ;1 [40] 150 | in x1, USBIN ;1 [41] <-- sample stuffed bit 4 151 | ori shift, 0x10 ;1 [42] 152 | rjmp didUnstuff4 ;2 [44] 153 | 154 | unstuff5: ;1 (branch taken) 155 | andi x3, ~0x20 ;1 [48] 156 | in x2, USBIN ;1 [49] <-- sample stuffed bit 5 157 | ori shift, 0x20 ;1 [50] 158 | rjmp didUnstuff5 ;2 [52] 159 | 160 | unstuff6: ;1 (branch taken) 161 | andi x3, ~0x40 ;1 [56] 162 | in x1, USBIN ;1 [57] <-- sample stuffed bit 6 163 | ori shift, 0x40 ;1 [58] 164 | rjmp didUnstuff6 ;2 [60] 165 | 166 | ; extra jobs done during bit interval: 167 | ; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] 168 | ; bit 1: se0 check 169 | ; bit 2: overflow check 170 | ; bit 3: recovery from delay [bit 0 tasks took too long] 171 | ; bit 4: none 172 | ; bit 5: none 173 | ; bit 6: none 174 | ; bit 7: jump, eor 175 | rxLoop: 176 | eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others 177 | in x1, USBIN ;1 [1] <-- sample bit 0 178 | st y+, x3 ;2 [3] store data 179 | ser x3 ;1 [4] 180 | nop ;1 [5] 181 | eor x2, x1 ;1 [6] 182 | bst x2, USBMINUS;1 [7] 183 | bld shift, 0 ;1 [8] 184 | in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) 185 | andi x2, USBMASK ;1 [10] 186 | breq se0 ;1 [11] SE0 check for bit 1 187 | andi shift, 0xf9 ;1 [12] 188 | didUnstuff0: 189 | breq unstuff0 ;1 [13] 190 | eor x1, x2 ;1 [14] 191 | bst x1, USBMINUS;1 [15] 192 | bld shift, 1 ;1 [16] 193 | rxbit2: 194 | in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) 195 | andi shift, 0xf3 ;1 [18] 196 | breq unstuff1 ;1 [19] do remaining work for bit 1 197 | didUnstuff1: 198 | subi cnt, 1 ;1 [20] 199 | brcs overflow ;1 [21] loop control 200 | eor x2, x1 ;1 [22] 201 | bst x2, USBMINUS;1 [23] 202 | bld shift, 2 ;1 [24] 203 | in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) 204 | andi shift, 0xe7 ;1 [26] 205 | breq unstuff2 ;1 [27] 206 | didUnstuff2: 207 | eor x1, x2 ;1 [28] 208 | bst x1, USBMINUS;1 [29] 209 | bld shift, 3 ;1 [30] 210 | didUnstuff3: 211 | andi shift, 0xcf ;1 [31] 212 | breq unstuff3 ;1 [32] 213 | in x1, USBIN ;1 [33] <-- sample bit 4 214 | eor x2, x1 ;1 [34] 215 | bst x2, USBMINUS;1 [35] 216 | bld shift, 4 ;1 [36] 217 | didUnstuff4: 218 | andi shift, 0x9f ;1 [37] 219 | breq unstuff4 ;1 [38] 220 | nop2 ;2 [40] 221 | in x2, USBIN ;1 [41] <-- sample bit 5 222 | eor x1, x2 ;1 [42] 223 | bst x1, USBMINUS;1 [43] 224 | bld shift, 5 ;1 [44] 225 | didUnstuff5: 226 | andi shift, 0x3f ;1 [45] 227 | breq unstuff5 ;1 [46] 228 | nop2 ;2 [48] 229 | in x1, USBIN ;1 [49] <-- sample bit 6 230 | eor x2, x1 ;1 [50] 231 | bst x2, USBMINUS;1 [51] 232 | bld shift, 6 ;1 [52] 233 | didUnstuff6: 234 | cpi shift, 0x02 ;1 [53] 235 | brlo unstuff6 ;1 [54] 236 | nop2 ;2 [56] 237 | in x2, USBIN ;1 [57] <-- sample bit 7 238 | eor x1, x2 ;1 [58] 239 | bst x1, USBMINUS;1 [59] 240 | bld shift, 7 ;1 [60] 241 | didUnstuff7: 242 | cpi shift, 0x04 ;1 [61] 243 | brsh rxLoop ;2 [63] loop control 244 | unstuff7: 245 | andi x3, ~0x80 ;1 [63] 246 | ori shift, 0x80 ;1 [64] 247 | in x2, USBIN ;1 [65] <-- sample stuffed bit 7 248 | nop ;1 [66] 249 | rjmp didUnstuff7 ;2 [68] 250 | 251 | macro POP_STANDARD ; 12 cycles 252 | pop cnt 253 | pop x3 254 | pop x2 255 | pop x1 256 | pop shift 257 | pop YH 258 | endm 259 | macro POP_RETI ; 5 cycles 260 | pop YL 261 | out SREG, YL 262 | pop YL 263 | endm 264 | 265 | #include "asmcommon.inc" 266 | 267 | ;---------------------------------------------------------------------------- 268 | ; Transmitting data 269 | ;---------------------------------------------------------------------------- 270 | 271 | txByteLoop: 272 | txBitloop: 273 | stuffN1Delay: ; [03] 274 | ror shift ;[-5] [11] [59] 275 | brcc doExorN1 ;[-4] [60] 276 | subi x4, 1 ;[-3] 277 | brne commonN1 ;[-2] 278 | lsl shift ;[-1] compensate ror after rjmp stuffDelay 279 | nop ;[00] stuffing consists of just waiting 8 cycles 280 | rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear 281 | 282 | sendNakAndReti: ;0 [-19] 19 cycles until SOP 283 | ldi x3, USBPID_NAK ;1 [-18] 284 | rjmp usbSendX3 ;2 [-16] 285 | sendAckAndReti: ;0 [-19] 19 cycles until SOP 286 | ldi x3, USBPID_ACK ;1 [-18] 287 | rjmp usbSendX3 ;2 [-16] 288 | sendCntAndReti: ;0 [-17] 17 cycles until SOP 289 | mov x3, cnt ;1 [-16] 290 | usbSendX3: ;0 [-16] 291 | ldi YL, 20 ;1 [-15] 'x3' is R20 292 | ldi YH, 0 ;1 [-14] 293 | ldi cnt, 2 ;1 [-13] 294 | ; rjmp usbSendAndReti fallthrough 295 | 296 | ; USB spec says: 297 | ; idle = J 298 | ; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 299 | ; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 300 | ; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) 301 | 302 | ;usbSend: 303 | ;pointer to data in 'Y' 304 | ;number of bytes in 'cnt' -- including sync byte 305 | ;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt] 306 | ;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) 307 | usbSendAndReti: 308 | in x2, USBDDR ;[-12] 12 cycles until SOP 309 | ori x2, USBMASK ;[-11] 310 | sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) 311 | out USBDDR, x2 ;[-8] <--- acquire bus 312 | in x1, USBOUT ;[-7] port mirror for tx loop 313 | ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror) 314 | ldi x2, USBMASK ;[-5] 315 | push x4 ;[-4] 316 | doExorN1: 317 | eor x1, x2 ;[-2] [06] [62] 318 | ldi x4, 6 ;[-1] [07] [63] 319 | commonN1: 320 | stuffN2Delay: 321 | out USBOUT, x1 ;[00] [08] [64] <--- set bit 322 | ror shift ;[01] 323 | brcc doExorN2 ;[02] 324 | subi x4, 1 ;[03] 325 | brne commonN2 ;[04] 326 | lsl shift ;[05] compensate ror after rjmp stuffDelay 327 | rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear 328 | doExorN2: 329 | eor x1, x2 ;[04] [12] 330 | ldi x4, 6 ;[05] [13] 331 | commonN2: 332 | nop ;[06] [14] 333 | subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1 334 | out USBOUT, x1 ;[08] [16] <--- set bit 335 | brcs txBitloop ;[09] [25] [41] 336 | 337 | stuff6Delay: 338 | ror shift ;[42] [50] 339 | brcc doExor6 ;[43] 340 | subi x4, 1 ;[44] 341 | brne common6 ;[45] 342 | lsl shift ;[46] compensate ror after rjmp stuffDelay 343 | nop ;[47] stuffing consists of just waiting 8 cycles 344 | rjmp stuff6Delay ;[48] after ror, C bit is reliably clear 345 | doExor6: 346 | eor x1, x2 ;[45] [53] 347 | ldi x4, 6 ;[46] 348 | common6: 349 | stuff7Delay: 350 | ror shift ;[47] [55] 351 | out USBOUT, x1 ;[48] <--- set bit 352 | brcc doExor7 ;[49] 353 | subi x4, 1 ;[50] 354 | brne common7 ;[51] 355 | lsl shift ;[52] compensate ror after rjmp stuffDelay 356 | rjmp stuff7Delay ;[53] after ror, C bit is reliably clear 357 | doExor7: 358 | eor x1, x2 ;[51] [59] 359 | ldi x4, 6 ;[52] 360 | common7: 361 | ld shift, y+ ;[53] 362 | tst cnt ;[55] 363 | out USBOUT, x1 ;[56] <--- set bit 364 | brne txByteLoop ;[57] 365 | 366 | ;make SE0: 367 | cbr x1, USBMASK ;[58] prepare SE0 [spec says EOP may be 15 to 18 cycles] 368 | lds x2, usbNewDeviceAddr;[59] 369 | lsl x2 ;[61] we compare with left shifted address 370 | subi YL, 2 + 20 ;[62] Only assign address on data packets, not ACK/NAK in x3 371 | sbci YH, 0 ;[63] 372 | out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle 373 | ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: 374 | ;set address only after data packet was sent, not after handshake 375 | breq skipAddrAssign ;[01] 376 | sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer 377 | skipAddrAssign: 378 | ;end of usbDeviceAddress transfer 379 | ldi x2, 1< max 52 cycles interrupt disable 31 | ;max stack usage: [ret(2), r0, SREG, YL, YH, shift, x1, x2, x3, x4, cnt] = 12 bytes 32 | ;nominal frequency: 16.5 MHz -> 11 cycles per bit 33 | ; 16.3125 MHz < F_CPU < 16.6875 MHz (+/- 1.1%) 34 | ; Numbers in brackets are clocks counted from center of last sync bit 35 | ; when instruction starts 36 | 37 | 38 | USB_INTR_VECTOR: 39 | ;order of registers pushed: YL, SREG [sofError], r0, YH, shift, x1, x2, x3, x4, cnt 40 | push YL ;[-23] push only what is necessary to sync with edge ASAP 41 | in YL, SREG ;[-21] 42 | push YL ;[-20] 43 | ;---------------------------------------------------------------------------- 44 | ; Synchronize with sync pattern: 45 | ;---------------------------------------------------------------------------- 46 | ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 47 | ;sync up with J to K edge during sync pattern -- use fastest possible loops 48 | ;The first part waits at most 1 bit long since we must be in sync pattern. 49 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 50 | ;waitForJ, ensure that this prerequisite is met. 51 | waitForJ: 52 | inc YL 53 | sbis USBIN, USBMINUS 54 | brne waitForJ ; just make sure we have ANY timeout 55 | waitForK: 56 | ;The following code results in a sampling window of < 1/4 bit which meets the spec. 57 | sbis USBIN, USBMINUS ;[-15] 58 | rjmp foundK ;[-14] 59 | sbis USBIN, USBMINUS 60 | rjmp foundK 61 | sbis USBIN, USBMINUS 62 | rjmp foundK 63 | sbis USBIN, USBMINUS 64 | rjmp foundK 65 | sbis USBIN, USBMINUS 66 | rjmp foundK 67 | sbis USBIN, USBMINUS 68 | rjmp foundK 69 | #if USB_COUNT_SOF 70 | lds YL, usbSofCount 71 | inc YL 72 | sts usbSofCount, YL 73 | #endif /* USB_COUNT_SOF */ 74 | #ifdef USB_SOF_HOOK 75 | USB_SOF_HOOK 76 | #endif 77 | rjmp sofError 78 | foundK: ;[-12] 79 | ;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] 80 | ;we have 1 bit time for setup purposes, then sample again. Numbers in brackets 81 | ;are cycles from center of first sync (double K) bit after the instruction 82 | push r0 ;[-12] 83 | ; [---] ;[-11] 84 | push YH ;[-10] 85 | ; [---] ;[-9] 86 | lds YL, usbInputBufOffset;[-8] 87 | ; [---] ;[-7] 88 | clr YH ;[-6] 89 | subi YL, lo8(-(usbRxBuf));[-5] [rx loop init] 90 | sbci YH, hi8(-(usbRxBuf));[-4] [rx loop init] 91 | mov r0, x2 ;[-3] [rx loop init] 92 | sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) 93 | rjmp haveTwoBitsK ;[-1] 94 | pop YH ;[0] undo the pushes from before 95 | pop r0 ;[2] 96 | rjmp waitForK ;[4] this was not the end of sync, retry 97 | ; The entire loop from waitForK until rjmp waitForK above must not exceed two 98 | ; bit times (= 22 cycles). 99 | 100 | ;---------------------------------------------------------------------------- 101 | ; push more registers and initialize values while we sample the first bits: 102 | ;---------------------------------------------------------------------------- 103 | haveTwoBitsK: ;[1] 104 | push shift ;[1] 105 | push x1 ;[3] 106 | push x2 ;[5] 107 | push x3 ;[7] 108 | ldi shift, 0xff ;[9] [rx loop init] 109 | ori x3, 0xff ;[10] [rx loop init] == ser x3, clear zero flag 110 | 111 | in x1, USBIN ;[11] <-- sample bit 0 112 | bst x1, USBMINUS ;[12] 113 | bld shift, 0 ;[13] 114 | push x4 ;[14] == phase 115 | ; [---] ;[15] 116 | push cnt ;[16] 117 | ; [---] ;[17] 118 | ldi phase, 0 ;[18] [rx loop init] 119 | ldi cnt, USB_BUFSIZE;[19] [rx loop init] 120 | rjmp rxbit1 ;[20] 121 | ; [---] ;[21] 122 | 123 | ;---------------------------------------------------------------------------- 124 | ; Receiver loop (numbers in brackets are cycles within byte after instr) 125 | ;---------------------------------------------------------------------------- 126 | /* 127 | byte oriented operations done during loop: 128 | bit 0: store data 129 | bit 1: SE0 check 130 | bit 2: overflow check 131 | bit 3: catch up 132 | bit 4: rjmp to achieve conditional jump range 133 | bit 5: PLL 134 | bit 6: catch up 135 | bit 7: jump, fixup bitstuff 136 | ; 87 [+ 2] cycles 137 | ------------------------------------------------------------------ 138 | */ 139 | continueWithBit5: 140 | in x2, USBIN ;[055] <-- bit 5 141 | eor r0, x2 ;[056] 142 | or phase, r0 ;[057] 143 | sbrc phase, USBMINUS ;[058] 144 | lpm ;[059] optional nop3; modifies r0 145 | in phase, USBIN ;[060] <-- phase 146 | eor x1, x2 ;[061] 147 | bst x1, USBMINUS ;[062] 148 | bld shift, 5 ;[063] 149 | andi shift, 0x3f ;[064] 150 | in x1, USBIN ;[065] <-- bit 6 151 | breq unstuff5 ;[066] *** unstuff escape 152 | eor phase, x1 ;[067] 153 | eor x2, x1 ;[068] 154 | bst x2, USBMINUS ;[069] 155 | bld shift, 6 ;[070] 156 | didUnstuff6: ;[ ] 157 | in r0, USBIN ;[071] <-- phase 158 | cpi shift, 0x02 ;[072] 159 | brlo unstuff6 ;[073] *** unstuff escape 160 | didUnstuff5: ;[ ] 161 | nop2 ;[074] 162 | ; [---] ;[075] 163 | in x2, USBIN ;[076] <-- bit 7 164 | eor x1, x2 ;[077] 165 | bst x1, USBMINUS ;[078] 166 | bld shift, 7 ;[079] 167 | didUnstuff7: ;[ ] 168 | eor r0, x2 ;[080] 169 | or phase, r0 ;[081] 170 | in r0, USBIN ;[082] <-- phase 171 | cpi shift, 0x04 ;[083] 172 | brsh rxLoop ;[084] 173 | ; [---] ;[085] 174 | unstuff7: ;[ ] 175 | andi x3, ~0x80 ;[085] 176 | ori shift, 0x80 ;[086] 177 | in x2, USBIN ;[087] <-- sample stuffed bit 7 178 | nop ;[088] 179 | rjmp didUnstuff7 ;[089] 180 | ; [---] ;[090] 181 | ;[080] 182 | 183 | unstuff5: ;[067] 184 | eor phase, x1 ;[068] 185 | andi x3, ~0x20 ;[069] 186 | ori shift, 0x20 ;[070] 187 | in r0, USBIN ;[071] <-- phase 188 | mov x2, x1 ;[072] 189 | nop ;[073] 190 | nop2 ;[074] 191 | ; [---] ;[075] 192 | in x1, USBIN ;[076] <-- bit 6 193 | eor r0, x1 ;[077] 194 | or phase, r0 ;[078] 195 | eor x2, x1 ;[079] 196 | bst x2, USBMINUS ;[080] 197 | bld shift, 6 ;[081] no need to check bitstuffing, we just had one 198 | in r0, USBIN ;[082] <-- phase 199 | rjmp didUnstuff5 ;[083] 200 | ; [---] ;[084] 201 | ;[074] 202 | 203 | unstuff6: ;[074] 204 | andi x3, ~0x40 ;[075] 205 | in x1, USBIN ;[076] <-- bit 6 again 206 | ori shift, 0x40 ;[077] 207 | nop2 ;[078] 208 | ; [---] ;[079] 209 | rjmp didUnstuff6 ;[080] 210 | ; [---] ;[081] 211 | ;[071] 212 | 213 | unstuff0: ;[013] 214 | eor r0, x2 ;[014] 215 | or phase, r0 ;[015] 216 | andi x2, USBMASK ;[016] check for SE0 217 | in r0, USBIN ;[017] <-- phase 218 | breq didUnstuff0 ;[018] direct jump to se0 would be too long 219 | andi x3, ~0x01 ;[019] 220 | ori shift, 0x01 ;[020] 221 | mov x1, x2 ;[021] mov existing sample 222 | in x2, USBIN ;[022] <-- bit 1 again 223 | rjmp didUnstuff0 ;[023] 224 | ; [---] ;[024] 225 | ;[014] 226 | 227 | unstuff1: ;[024] 228 | eor r0, x1 ;[025] 229 | or phase, r0 ;[026] 230 | andi x3, ~0x02 ;[027] 231 | in r0, USBIN ;[028] <-- phase 232 | ori shift, 0x02 ;[029] 233 | mov x2, x1 ;[030] 234 | rjmp didUnstuff1 ;[031] 235 | ; [---] ;[032] 236 | ;[022] 237 | 238 | unstuff2: ;[035] 239 | eor r0, x2 ;[036] 240 | or phase, r0 ;[037] 241 | andi x3, ~0x04 ;[038] 242 | in r0, USBIN ;[039] <-- phase 243 | ori shift, 0x04 ;[040] 244 | mov x1, x2 ;[041] 245 | rjmp didUnstuff2 ;[042] 246 | ; [---] ;[043] 247 | ;[033] 248 | 249 | unstuff3: ;[043] 250 | in x2, USBIN ;[044] <-- bit 3 again 251 | eor r0, x2 ;[045] 252 | or phase, r0 ;[046] 253 | andi x3, ~0x08 ;[047] 254 | ori shift, 0x08 ;[048] 255 | nop ;[049] 256 | in r0, USBIN ;[050] <-- phase 257 | rjmp didUnstuff3 ;[051] 258 | ; [---] ;[052] 259 | ;[042] 260 | 261 | unstuff4: ;[053] 262 | andi x3, ~0x10 ;[054] 263 | in x1, USBIN ;[055] <-- bit 4 again 264 | ori shift, 0x10 ;[056] 265 | rjmp didUnstuff4 ;[057] 266 | ; [---] ;[058] 267 | ;[048] 268 | 269 | rxLoop: ;[085] 270 | eor x3, shift ;[086] reconstruct: x3 is 0 at bit locations we changed, 1 at others 271 | in x1, USBIN ;[000] <-- bit 0 272 | st y+, x3 ;[001] 273 | ; [---] ;[002] 274 | eor r0, x1 ;[003] 275 | or phase, r0 ;[004] 276 | eor x2, x1 ;[005] 277 | in r0, USBIN ;[006] <-- phase 278 | ser x3 ;[007] 279 | bst x2, USBMINUS ;[008] 280 | bld shift, 0 ;[009] 281 | andi shift, 0xf9 ;[010] 282 | rxbit1: ;[ ] 283 | in x2, USBIN ;[011] <-- bit 1 284 | breq unstuff0 ;[012] *** unstuff escape 285 | andi x2, USBMASK ;[013] SE0 check for bit 1 286 | didUnstuff0: ;[ ] Z only set if we detected SE0 in bitstuff 287 | breq se0 ;[014] 288 | eor r0, x2 ;[015] 289 | or phase, r0 ;[016] 290 | in r0, USBIN ;[017] <-- phase 291 | eor x1, x2 ;[018] 292 | bst x1, USBMINUS ;[019] 293 | bld shift, 1 ;[020] 294 | andi shift, 0xf3 ;[021] 295 | didUnstuff1: ;[ ] 296 | in x1, USBIN ;[022] <-- bit 2 297 | breq unstuff1 ;[023] *** unstuff escape 298 | eor r0, x1 ;[024] 299 | or phase, r0 ;[025] 300 | subi cnt, 1 ;[026] overflow check 301 | brcs overflow ;[027] 302 | in r0, USBIN ;[028] <-- phase 303 | eor x2, x1 ;[029] 304 | bst x2, USBMINUS ;[030] 305 | bld shift, 2 ;[031] 306 | andi shift, 0xe7 ;[032] 307 | didUnstuff2: ;[ ] 308 | in x2, USBIN ;[033] <-- bit 3 309 | breq unstuff2 ;[034] *** unstuff escape 310 | eor r0, x2 ;[035] 311 | or phase, r0 ;[036] 312 | eor x1, x2 ;[037] 313 | bst x1, USBMINUS ;[038] 314 | in r0, USBIN ;[039] <-- phase 315 | bld shift, 3 ;[040] 316 | andi shift, 0xcf ;[041] 317 | didUnstuff3: ;[ ] 318 | breq unstuff3 ;[042] *** unstuff escape 319 | nop ;[043] 320 | in x1, USBIN ;[044] <-- bit 4 321 | eor x2, x1 ;[045] 322 | bst x2, USBMINUS ;[046] 323 | bld shift, 4 ;[047] 324 | didUnstuff4: ;[ ] 325 | eor r0, x1 ;[048] 326 | or phase, r0 ;[049] 327 | in r0, USBIN ;[050] <-- phase 328 | andi shift, 0x9f ;[051] 329 | breq unstuff4 ;[052] *** unstuff escape 330 | rjmp continueWithBit5;[053] 331 | ; [---] ;[054] 332 | 333 | macro POP_STANDARD ; 16 cycles 334 | pop cnt 335 | pop x4 336 | pop x3 337 | pop x2 338 | pop x1 339 | pop shift 340 | pop YH 341 | pop r0 342 | endm 343 | macro POP_RETI ; 5 cycles 344 | pop YL 345 | out SREG, YL 346 | pop YL 347 | endm 348 | 349 | #include "asmcommon.inc" 350 | 351 | 352 | ; USB spec says: 353 | ; idle = J 354 | ; J = (D+ = 0), (D- = 1) 355 | ; K = (D+ = 1), (D- = 0) 356 | ; Spec allows 7.5 bit times from EOP to SOP for replies 357 | 358 | bitstuff7: 359 | eor x1, x4 ;[4] 360 | ldi x2, 0 ;[5] 361 | nop2 ;[6] C is zero (brcc) 362 | rjmp didStuff7 ;[8] 363 | 364 | bitstuffN: 365 | eor x1, x4 ;[5] 366 | ldi x2, 0 ;[6] 367 | lpm ;[7] 3 cycle NOP, modifies r0 368 | out USBOUT, x1 ;[10] <-- out 369 | rjmp didStuffN ;[0] 370 | 371 | #define bitStatus x3 372 | 373 | sendNakAndReti: 374 | ldi cnt, USBPID_NAK ;[-19] 375 | rjmp sendCntAndReti ;[-18] 376 | sendAckAndReti: 377 | ldi cnt, USBPID_ACK ;[-17] 378 | sendCntAndReti: 379 | mov r0, cnt ;[-16] 380 | ldi YL, 0 ;[-15] R0 address is 0 381 | ldi YH, 0 ;[-14] 382 | ldi cnt, 2 ;[-13] 383 | ; rjmp usbSendAndReti fallthrough 384 | 385 | ;usbSend: 386 | ;pointer to data in 'Y' 387 | ;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] 388 | ;uses: x1...x4, shift, cnt, Y 389 | ;Numbers in brackets are time since first bit of sync pattern is sent 390 | usbSendAndReti: ; 12 cycles until SOP 391 | in x2, USBDDR ;[-12] 392 | ori x2, USBMASK ;[-11] 393 | sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) 394 | in x1, USBOUT ;[-8] port mirror for tx loop 395 | out USBDDR, x2 ;[-7] <- acquire bus 396 | ; need not init x2 (bitstuff history) because sync starts with 0 397 | ldi x4, USBMASK ;[-6] exor mask 398 | ldi shift, 0x80 ;[-5] sync byte is first byte sent 399 | ldi bitStatus, 0xff ;[-4] init bit loop counter, works for up to 12 bytes 400 | byteloop: 401 | bitloop: 402 | sbrs shift, 0 ;[8] [-3] 403 | eor x1, x4 ;[9] [-2] 404 | out USBOUT, x1 ;[10] [-1] <-- out 405 | ror shift ;[0] 406 | ror x2 ;[1] 407 | didStuffN: 408 | cpi x2, 0xfc ;[2] 409 | brcc bitstuffN ;[3] 410 | nop ;[4] 411 | subi bitStatus, 37 ;[5] 256 / 7 ~=~ 37 412 | brcc bitloop ;[6] when we leave the loop, bitStatus has almost the initial value 413 | sbrs shift, 0 ;[7] 414 | eor x1, x4 ;[8] 415 | ror shift ;[9] 416 | didStuff7: 417 | out USBOUT, x1 ;[10] <-- out 418 | ror x2 ;[0] 419 | cpi x2, 0xfc ;[1] 420 | brcc bitstuff7 ;[2] 421 | ld shift, y+ ;[3] 422 | dec cnt ;[5] 423 | brne byteloop ;[6] 424 | ;make SE0: 425 | cbr x1, USBMASK ;[7] prepare SE0 [spec says EOP may be 21 to 25 cycles] 426 | lds x2, usbNewDeviceAddr;[8] 427 | lsl x2 ;[10] we compare with left shifted address 428 | out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle 429 | ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: 430 | ;set address only after data packet was sent, not after handshake 431 | subi YL, 2 ;[0] Only assign address on data packets, not ACK/NAK in r0 432 | sbci YH, 0 ;[1] 433 | breq skipAddrAssign ;[2] 434 | sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer 435 | skipAddrAssign: 436 | ;end of usbDeviceAddress transfer 437 | ldi x2, 1< 34 | 1. General overview 35 | ------------------- 36 | 37 | The general architecture is shown below. The core of the controller is an Atmega 38 | chip. A special bootloader is used to allow the chip to be reprogrammed directly 39 | through USB. The user program can also communicate with the computer directly 40 | through USB, and actually be recognized as any USB device you would like, 41 | including HID (Human Interface Device), and in our case, a MIDI controller. You 42 | can then interface your controller with any music software speaking MIDI. 43 | 44 | On the Atmega chip, potentiometers and switches can be connected, and mapped in 45 | the software to generate corresponding MIDI signals (control, notes, play/pause, 46 | ...). 47 | 48 | Note that the Atmega alone accepts only 6 analog entries (i.e. needed to interface 49 | potentiometers). However it is relatively simple to add one or several 50 | multiplexers to extend the capabilities (8 entries mapped to 1 entry, i.e. up to 51 | 48 analog entries). 52 | 53 | ![](./doc/overview.png) 54 | 55 | 56 | 2. Shopping for hardware 57 | ------------------------ 58 | 59 | ### The core 60 | 61 | - 1 × Atmega328P-PU ([Product on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=1715487)) Careful : need *exactly* the 328**P-PU** version 62 | - 1 × Socket for 28 pin chip ([Product on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2445626)) Optionnal, but recommended 63 | - 1 × USB connector ([Product on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=1696544)) (or an old USB cable with male plug, to be cut to access the wires) 64 | - 1 × 12MHz crystal ([Product on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2508453)) 65 | - 2 × 3.6V zener diode ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=1861480)) 66 | - 2 × 22pF ceramic capacitor ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=9411674)) 67 | - 1 × 100nF ceramic capacitor ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=9411887)) 68 | - 1 × 4.7μF electrolytic capacitor ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=9451471)) 69 | - 1 × 10~50kΩ resistor ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2329609)) 70 | - 1 × 1~2kΩ resistor ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2329629)) 71 | - 2 × 68Ω resistor ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2329701)) 72 | - 1 × mini push button switch ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2468762)) 73 | - 1 × mini toggle switch ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2320018)) 74 | 75 | - Some colored wires such as [this](http://www.robotshop.com/eu/en/elenco-22-gauge-black-25-ft.html), or any wire you can find. Just don't use fancy jumper wires if you intend to solder ! 76 | - Single-sided prototyping board ([example](http://www.robotshop.com/eu/en/prototyping-board.html)), or a breadboard if you don't want to solder right away. 77 | 78 | Total should be around 6€, the Atmega chip being the most expensive part. 79 | 80 | ### Actual controller stuff 81 | 82 | Feel free to look for other potentiometers and switches ! Just make sure that 83 | the knobs/caps you take are compatible with the potentiometers you choose. Also 84 | make sure you know how you plan to fix them on your final box. I'm just showing 85 | those I took below. Total for rotatory pots was around 1.5€ each (including the 86 | knob). Linear pots were around 2€ each (including the cursor). 87 | 88 | - On/off switches ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=9473378)) 89 | - Push-button / keys ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2079605)) 90 | - Rotatory potentiometers ([Product on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=1760794)) 91 | - Knob for rotatory potentiometers ([Example on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2473099)) 92 | - Linear potentiometers (sliders/faders) (Product on Farnell : [45mm](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=1688415), [60mm](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=1688411)) 93 | - Cursor for linear potentiometers ([Product on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=1440016)) 94 | - 100 screws for linear potentiomeneters ([Product on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=507118)) 95 | 96 | Total for 4 rotatory, 4 linear and 4 push-buttons is around 15~20€. 97 | 98 | ### Tools 99 | 100 | - A soldering iron (and some solder material) ; 101 | - A stripper ; 102 | - Some scissors or small cutters to cut wire excess after soldering ; 103 | - Ideally, a multimeter - or anything to at least something to check there's no short between GND and 5V ! 104 | - A small knife if you need to remove the chip from the support. 105 | 106 | ### Optional - Analog multiplexers (if you aim to control more than 6 analog entries) : 107 | 108 | These will allow you to map 8 analog inputs to 1 input 109 | 110 | - 1-to-8 analog multiplexer ([Product on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=1236279)) 111 | - Socket for 16 pin chip ([Product on Farnell](http://fr.farnell.com/webapp/wcs/stores/servlet/Search?exaMfpn=true&mfpn=2445622)) Optionnal, but recommended 112 | 113 | 114 | 3. Making a USB cable 115 | --------------------- 116 | 117 | The first step is to build ourselves a USB cable. This will be the interface 118 | between the computer and our MIDI controller, and in particular the Atmega chip. 119 | 120 | There are many ways to do this. For instance, you can cut an old USB cable (e.g. 121 | from a broken USB mouse) to access the wires. You can also just solder a male or 122 | female connector directly on the board next to your Atmega, and use a cable like 123 | [this](http://fr.farnell.com/productimages/standard/en_US/95W0184-40.jpg). 124 | Personnally I built a cable using a male USB connector and some wires. Make sure 125 | you wires are at least ~0.5m long (mines were 20 cm long and it's clearly too short :)) 126 | 127 | The pinning of the A plug is shown below. It is good practice to use red/black 128 | for the 5V/GND pins , and white/green for the D-/D+ pins. 129 | 130 | ![](./doc/pinsUSB.png) 131 | 132 | ![](./hardware/usbCableIRL.jpg) 133 | 134 | 135 | 4. Building the core (standalone Atmega on USB) 136 | ----------------------------------------------- 137 | 138 | The core of the controller is heavily based on the [USnooBie kit](http://eleccelerator.com/usnoobie/index.php). 139 | The design of the kit allows a Atmega to communicate and be reprogrammed 140 | directly through USB, and, if you want to, to be recognized as an actual USB 141 | device of your choice. It's also relatively cheap (you may build one for 5-6€). 142 | On the other hand, it doesn't give you an easy access to i/o pins like the 143 | Arduino does with its headers numbered 1 to 13. I also don't know yet how to 144 | get an equivalent of the Serial library for debugging purposes. 145 | 146 | The design is centered on an Atmega328P-PU, and a few parts to ensure good 147 | operation of the chip and working USB communication. Here are the major stuff : 148 | 149 | - Capacitors to mitigate fluctuation on the power supply (5V / GND) ; 150 | - 12 MHz crystal (and two 22pF friends) as an external clock for the chip ; 151 | - 3.6V zener diodes and a few resistors for the USB data lines to work properly ; 152 | - A reset switch to reboot the Atmega when needed ; 153 | - A bootloader switch to choose between bootloader/reprogram mode, or "use current program" mode ; 154 | - (Optionnal) A led to show the board is powered on. 155 | 156 | More details on the role of each components can be found on the 157 | [USnooBie assembly page](http://eleccelerator.com/usnoobie/assembly.php). 158 | You might also be interested in keeping the [Atmega pin map](./doc/pinsAtmega328.png) 159 | around, it's always handy at some point. 160 | 161 | I suggest this order when soldering : 162 | 1. The socket for the chip, around the middle of your board ; 163 | 2. The crystal and the two 22pF capacitors ; 164 | 3. The 100 nF capacitor, near the crystal ; 165 | 4. The two 68Ω and zener diodes for D+/D- ; 166 | 5. The switches and pull-up resistors ; 167 | 6. The 4.7μF capacitor ; 168 | 7. The USB cable (and other wires) ; 169 | 170 | PLEASE NOTE that I did not include any protection in my design. It's essentially 171 | because the board is not meant to have any 9V around that could inadvertently 172 | damage the USB ports it's plugged in. There's still a risk of short between GND 173 | and 5V on your board. However I found out from experience that my laptop's USB 174 | ports were not damaged because of such shorts - the kernel was just unhappy for 175 | a few minutes, then it was working again. 176 | 177 | It's a good practice, though, to check after any change in the board, that 178 | there's at least no short between 5V and GND before plugging the controller back 179 | in your laptop. You can do that with the "beep mode" of a multimeter. Ideally, 180 | you should also check for instance that the total resistance between, e.g. the 181 | D+ and D- pins, directly on the back of the USB connector, and the corresponding 182 | pins on the chip socket, is around 68Ω (I had 65, good enough). More generally, 183 | you can do some continuity checks (the "beep" thing) between the various component 184 | legs and pins that should be connected together, to remove suspicions about 185 | solders being bad and so on. 186 | 187 | ![](./hardware/schematic.png) 188 | 189 | ![](./hardware/board.png) 190 | 191 | ![](./hardware/boardIRL.jpg) 192 | 193 | 194 | 5. Flashing the USBasp/V-USB bootloader 195 | --------------------------------------- 196 | 197 | For the Atmega to be able to communicate directly through the USB (allowing it 198 | to be recognized as a USB device and to reprogram it at will), we use a custom 199 | bootloader from V-USB. The bootloader is the small piece of software, pretty 200 | similar to the BIOS in computer, which initiates the rest of the software meant 201 | to be run. 202 | 203 | In our case, if the "bootloader switch" is activated when the Atmega boot, it will 204 | emulate a "USBasp" programmer and you'll be able to upload to change the program. 205 | If the "bootloader switch" is in the other position, the bootloader will launch 206 | the program you uploaded on the Atmega - which can be as simple as blinking a LED, 207 | or acting as a USB device (mouse, keyboard, MIDI, ...). 208 | 209 | Flashing the bootloader is a sort of egg-and-chicken situation : to flash the 210 | chip for the first time, you need another chip. This can be done for instance 211 | using a Arduino UNO board. Fortunately, this is done once and for all, assuming 212 | the flashing is succesful ! The bootloader flashing setup is the following : 213 | 214 | ![](./doc/bootloaderFlashingSetup.png) 215 | 216 | Once this setup is ready, you should flash a particular program "ArduinoISP" 217 | (in-situ programming) on the Arduino board. It is available in the Arduino IDE 218 | under File > Examples. Then you need to switch the Programmer in the IDE to 219 | "Arduino as ISP" (Tools > Programmer > Arduino as ISP). 220 | 221 | If we wanted to flash the regular Arduino bootloader, we would now go to Tools > 222 | Flash bootloader. However, we want to flash our particular V-USB bootloader. 223 | Therefore, we use the Makefile in this directory to do so : 224 | 225 | ```bash 226 | # Compile 227 | make bootloader 228 | # Flash 229 | make bootloader-flash 230 | ``` 231 | 232 | Once this is done, you should put back your Atmega on your board and test if it 233 | worked. To do so, you can set your board in "Bootloader mode ON". In a terminal, 234 | type : 235 | 236 | ``` 237 | dmesg -e -w 238 | ``` 239 | 240 | to display new messages from the kernel. Plug your board in. If it's working, 241 | you should see some blabbling about a new USB device called USBasp (meaning it's 242 | recognized as a USB chip programmer). 243 | 244 | 245 | 6. Testing with a simple Midi Controller firmware 246 | -------------------------------------------- 247 | 248 | Now you might want to flash a simple / test firmware to see your device being a 249 | MIDI controller. To do so, you might want to add a small switch for instance on 250 | pin 13 of your Atmega (the other pin of the switch goes to GND). You might want 251 | to add a pull-up resistor (something between 10k and 50k should do the trick) 252 | between pin 13 and VCC (see 'Switches' in next section). 253 | 254 | Then, edit the `setup()` and `loop()` of the `main.cpp` to something like this : 255 | 256 | ```C++ 257 | void setup() 258 | { 259 | midiController.initUSB(); 260 | midiController.mapPinToNote(13, 60); 261 | } 262 | 263 | void loop() 264 | { 265 | midiController.update(); 266 | delay(10); 267 | } 268 | ``` 269 | 270 | In this example, we map what's on pin 13 to the note 60 ("Middle C", or C5 in 271 | music notation). (The full map of MIDI notes can be found for instance 272 | [here](http://www.electronics.dit.ie/staff/tscarff/Music_technology/midi/midi_note_numbers_for_octaves.htm)). 273 | 274 | Compile the code using `make hex`, then upload it using `make upload` ! If 275 | compiling and uploading worked, you can flip the bootloader switch to the other 276 | position, such that the device now use the program you just flashed. Reboot or 277 | replug the board. In `dmesg -e -w`, you should now see the board being 278 | recognized as some sort of 'CustomMidiController'. Try `amidi -l` to confirm 279 | that the OS correctly sees it as a MIDI controller. Then try `amidi -p hw:1,0,0 280 | -d` to listen to incoming MIDI signals. Now if you press the switch on pin 13, 281 | it should emit a MIDI signal that you can see on your computer. 282 | 283 | 284 | 7. Adding switches and potentiometers 285 | ------------------------------------- 286 | 287 | Next step is to add as many switches and potentiometers as you'd like (modulo 288 | the fact that you'll need multiplexing if you want more than 6 potentiometers). 289 | Each time you add a new element, you should a corresponding line in your 290 | `setup()` in `main.cpp` : 291 | 292 | ```C++ 293 | // For a 'key' 294 | // SOME_NOTE_NUMBER designates the music note you want 295 | midiController.mapPinToNote(SOME_PIN_NUMBER, SOME_NOTE_NUMBER); 296 | // For a control (knobs / faders). 297 | // SOME_CONTROL_NUMBER is just an identifier you choose, starting at 0 298 | midiController.mapPinToControl(SOME_OTHER_PIN_NUMBER, SOME_CONTROL_NUMBER); 299 | ``` 300 | 301 | ### Linear potentiometer ([datasheet](http://www.farnell.com/datasheets/1874874.pdf)) 302 | 303 | Look for the « 1 », « 2 » and « 3 » numbers near the pins of the potentiometer. 304 | 305 | - Pin 1 goes to 5V ; 306 | - Pin 2 (the signal) goes to the analog input of your choice ; 307 | - Pin 3 goes to GND. 308 | 309 | ### Rotatory potentiometer ([datasheet](http://www.farnell.com/datasheets/90780.pdf)) 310 | 311 | The pin mapping is the following : 312 | 313 | ![](./doc/pinsRotatoryPot.png) 314 | 315 | ### Switches 316 | 317 | For the switches (any types), you'll probably need to add a pull-up resistor, 318 | which yields a schematic like this : 319 | 320 | ![](./doc/pinsSwitches.png) 321 | 322 | 323 | 324 | 8. (Optionnal) Add multiplexers 325 | ------------------------------- 326 | 327 | 328 | 9. Put all this in a nice case 329 | ------------------------------ 330 | 331 | I used the website [MakerCase](http://www.makercase.com/) to automatically 332 | generate the basis of my box/case. I then added other rectangles and circles to 333 | be able to mount my potentiometers and switches. The relevant dimensions are 334 | given in the datasheets of the potentiometers. 335 | 336 | ![](./case/case.png) 337 | 338 | ![](./doc/emptyBox.jpg) 339 | 340 | 341 | 10. Profit ! 342 | ----------- 343 | 344 | ![](./doc/result.jpg) 345 | 346 | Programming using the Arduino IDE 347 | --------------------------------- 348 | 349 | Haven't tested this already ! 350 | 351 | Instructions are available on this [USnooBie kit page](http://eleccelerator.com/usnoobie/arduino.php), 352 | regarding how to program the core board with the Arduino IDE. In the context of 353 | this MIDI Controller, you will also need to include the MIDI USB lib thing 354 | somehow. 355 | 356 | References, inspiration, credits, links 357 | --------------------------------------- 358 | 359 | - USBaspLoader https://github.com/baerwolf/USBaspLoader 360 | - V-USB https://www.obdev.at/products/vusb/index.html 361 | 362 | - USnooBie kit http://www.eleccelerator.com/usnoobie/index.php 363 | - More on USnooBie kit http://wiki.seeedstudio.com/wiki/Usnoobie_Kit 364 | 365 | - http://www.instructables.com/id/Custom-Arduino-MIDI-Controller/?ALLSTEPS 366 | - http://www.instructables.com/id/USB-MIDI-Controller/?ALLSTEPS 367 | 368 | - http://codeandlife.com/2012/01/22/avr-attiny-usb-tutorial-part-1/ 369 | 370 | -------------------------------------------------------------------------------- /lib/MidiUSB/usbdrvasm15.inc: -------------------------------------------------------------------------------- 1 | /* Name: usbdrvasm15.inc 2 | * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers 3 | * Author: contributed by V. Bosch 4 | * Creation Date: 2007-08-06 5 | * Tabsize: 4 6 | * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH 7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) 8 | */ 9 | 10 | /* Do not link this file! Link usbdrvasm.S instead, which includes the 11 | * appropriate implementation! 12 | */ 13 | 14 | /* 15 | General Description: 16 | This file is the 15 MHz version of the asssembler part of the USB driver. It 17 | requires a 15 MHz crystal (not a ceramic resonator and not a calibrated RC 18 | oscillator). 19 | 20 | See usbdrv.h for a description of the entire driver. 21 | 22 | Since almost all of this code is timing critical, don't change unless you 23 | really know what you are doing! Many parts require not only a maximum number 24 | of CPU cycles, but even an exact number of cycles! 25 | */ 26 | 27 | ;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes 28 | ;nominal frequency: 15 MHz -> 10.0 cycles per bit, 80.0 cycles per byte 29 | ; Numbers in brackets are clocks counted from center of last sync bit 30 | ; when instruction starts 31 | 32 | ;---------------------------------------------------------------------------- 33 | ; order of registers pushed: 34 | ; YL, SREG [sofError] YH, shift, x1, x2, x3, bitcnt, cnt, x4 35 | ;---------------------------------------------------------------------------- 36 | USB_INTR_VECTOR: 37 | push YL ;2 push only what is necessary to sync with edge ASAP 38 | in YL, SREG ;1 39 | push YL ;2 40 | ;---------------------------------------------------------------------------- 41 | ; Synchronize with sync pattern: 42 | ; 43 | ; sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] 44 | ; sync up with J to K edge during sync pattern -- use fastest possible loops 45 | ;The first part waits at most 1 bit long since we must be in sync pattern. 46 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to 47 | ;waitForJ, ensure that this prerequisite is met. 48 | waitForJ: 49 | inc YL 50 | sbis USBIN, USBMINUS 51 | brne waitForJ ; just make sure we have ANY timeout 52 | ;------------------------------------------------------------------------------- 53 | ; The following code results in a sampling window of < 1/4 bit 54 | ; which meets the spec. 55 | ;------------------------------------------------------------------------------- 56 | waitForK: ;- 57 | sbis USBIN, USBMINUS ;1 [00] <-- sample 58 | rjmp foundK ;2 [01] 59 | sbis USBIN, USBMINUS ; <-- sample 60 | rjmp foundK 61 | sbis USBIN, USBMINUS ; <-- sample 62 | rjmp foundK 63 | sbis USBIN, USBMINUS ; <-- sample 64 | rjmp foundK 65 | sbis USBIN, USBMINUS ; <-- sample 66 | rjmp foundK 67 | sbis USBIN, USBMINUS ; <-- sample 68 | rjmp foundK 69 | #if USB_COUNT_SOF 70 | lds YL, usbSofCount 71 | inc YL 72 | sts usbSofCount, YL 73 | #endif /* USB_COUNT_SOF */ 74 | #ifdef USB_SOF_HOOK 75 | USB_SOF_HOOK 76 | #endif 77 | rjmp sofError 78 | ;------------------------------------------------------------------------------ 79 | ; {3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for 80 | ; center sampling] 81 | ; we have 1 bit time for setup purposes, then sample again. 82 | ; Numbers in brackets are cycles from center of first sync (double K) 83 | ; bit after the instruction 84 | ;------------------------------------------------------------------------------ 85 | foundK: ;- [02] 86 | lds YL, usbInputBufOffset;2 [03+04] tx loop 87 | push YH ;2 [05+06] 88 | clr YH ;1 [07] 89 | subi YL, lo8(-(usbRxBuf)) ;1 [08] [rx loop init] 90 | sbci YH, hi8(-(usbRxBuf)) ;1 [09] [rx loop init] 91 | push shift ;2 [10+11] 92 | ser shift ;1 [12] 93 | sbis USBIN, USBMINUS ;1 [-1] [13] <--sample:we want two bits K (sample 1 cycle too early) 94 | rjmp haveTwoBitsK ;2 [00] [14] 95 | pop shift ;2 [15+16] undo the push from before 96 | pop YH ;2 [17+18] undo the push from before 97 | rjmp waitForK ;2 [19+20] this was not the end of sync, retry 98 | ; The entire loop from waitForK until rjmp waitForK above must not exceed two 99 | ; bit times (= 20 cycles). 100 | 101 | ;---------------------------------------------------------------------------- 102 | ; push more registers and initialize values while we sample the first bits: 103 | ;---------------------------------------------------------------------------- 104 | haveTwoBitsK: ;- [01] 105 | push x1 ;2 [02+03] 106 | push x2 ;2 [04+05] 107 | push x3 ;2 [06+07] 108 | push bitcnt ;2 [08+09] 109 | in x1, USBIN ;1 [00] [10] <-- sample bit 0 110 | bst x1, USBMINUS ;1 [01] 111 | bld shift, 0 ;1 [02] 112 | push cnt ;2 [03+04] 113 | ldi cnt, USB_BUFSIZE ;1 [05] 114 | push x4 ;2 [06+07] tx loop 115 | rjmp rxLoop ;2 [08] 116 | ;---------------------------------------------------------------------------- 117 | ; Receiver loop (numbers in brackets are cycles within byte after instr) 118 | ;---------------------------------------------------------------------------- 119 | unstuff0: ;- [07] (branch taken) 120 | andi x3, ~0x01 ;1 [08] 121 | mov x1, x2 ;1 [09] x2 contains last sampled (stuffed) bit 122 | in x2, USBIN ;1 [00] [10] <-- sample bit 1 again 123 | andi x2, USBMASK ;1 [01] 124 | breq se0Hop ;1 [02] SE0 check for bit 1 125 | ori shift, 0x01 ;1 [03] 0b00000001 126 | nop ;1 [04] 127 | rjmp didUnstuff0 ;2 [05] 128 | ;----------------------------------------------------- 129 | unstuff1: ;- [05] (branch taken) 130 | mov x2, x1 ;1 [06] x1 contains last sampled (stuffed) bit 131 | andi x3, ~0x02 ;1 [07] 132 | ori shift, 0x02 ;1 [08] 0b00000010 133 | nop ;1 [09] 134 | in x1, USBIN ;1 [00] [10] <-- sample bit 2 again 135 | andi x1, USBMASK ;1 [01] 136 | breq se0Hop ;1 [02] SE0 check for bit 2 137 | rjmp didUnstuff1 ;2 [03] 138 | ;----------------------------------------------------- 139 | unstuff2: ;- [05] (branch taken) 140 | andi x3, ~0x04 ;1 [06] 141 | ori shift, 0x04 ;1 [07] 0b00000100 142 | mov x1, x2 ;1 [08] x2 contains last sampled (stuffed) bit 143 | nop ;1 [09] 144 | in x2, USBIN ;1 [00] [10] <-- sample bit 3 145 | andi x2, USBMASK ;1 [01] 146 | breq se0Hop ;1 [02] SE0 check for bit 3 147 | rjmp didUnstuff2 ;2 [03] 148 | ;----------------------------------------------------- 149 | unstuff3: ;- [00] [10] (branch taken) 150 | in x2, USBIN ;1 [01] [11] <-- sample stuffed bit 3 one cycle too late 151 | andi x2, USBMASK ;1 [02] 152 | breq se0Hop ;1 [03] SE0 check for stuffed bit 3 153 | andi x3, ~0x08 ;1 [04] 154 | ori shift, 0x08 ;1 [05] 0b00001000 155 | rjmp didUnstuff3 ;2 [06] 156 | ;---------------------------------------------------------------------------- 157 | ; extra jobs done during bit interval: 158 | ; 159 | ; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs], 160 | ; overflow check, jump to the head of rxLoop 161 | ; bit 1: SE0 check 162 | ; bit 2: SE0 check, recovery from delay [bit 0 tasks took too long] 163 | ; bit 3: SE0 check, recovery from delay [bit 0 tasks took too long] 164 | ; bit 4: SE0 check, none 165 | ; bit 5: SE0 check, none 166 | ; bit 6: SE0 check, none 167 | ; bit 7: SE0 check, reconstruct: x3 is 0 at bit locations we changed, 1 at others 168 | ;---------------------------------------------------------------------------- 169 | rxLoop: ;- [09] 170 | in x2, USBIN ;1 [00] [10] <-- sample bit 1 (or possibly bit 0 stuffed) 171 | andi x2, USBMASK ;1 [01] 172 | brne SkipSe0Hop ;1 [02] 173 | se0Hop: ;- [02] 174 | rjmp se0 ;2 [03] SE0 check for bit 1 175 | SkipSe0Hop: ;- [03] 176 | ser x3 ;1 [04] 177 | andi shift, 0xf9 ;1 [05] 0b11111001 178 | breq unstuff0 ;1 [06] 179 | didUnstuff0: ;- [06] 180 | eor x1, x2 ;1 [07] 181 | bst x1, USBMINUS ;1 [08] 182 | bld shift, 1 ;1 [09] 183 | in x1, USBIN ;1 [00] [10] <-- sample bit 2 (or possibly bit 1 stuffed) 184 | andi x1, USBMASK ;1 [01] 185 | breq se0Hop ;1 [02] SE0 check for bit 2 186 | andi shift, 0xf3 ;1 [03] 0b11110011 187 | breq unstuff1 ;1 [04] do remaining work for bit 1 188 | didUnstuff1: ;- [04] 189 | eor x2, x1 ;1 [05] 190 | bst x2, USBMINUS ;1 [06] 191 | bld shift, 2 ;1 [07] 192 | nop2 ;2 [08+09] 193 | in x2, USBIN ;1 [00] [10] <-- sample bit 3 (or possibly bit 2 stuffed) 194 | andi x2, USBMASK ;1 [01] 195 | breq se0Hop ;1 [02] SE0 check for bit 3 196 | andi shift, 0xe7 ;1 [03] 0b11100111 197 | breq unstuff2 ;1 [04] 198 | didUnstuff2: ;- [04] 199 | eor x1, x2 ;1 [05] 200 | bst x1, USBMINUS ;1 [06] 201 | bld shift, 3 ;1 [07] 202 | didUnstuff3: ;- [07] 203 | andi shift, 0xcf ;1 [08] 0b11001111 204 | breq unstuff3 ;1 [09] 205 | in x1, USBIN ;1 [00] [10] <-- sample bit 4 206 | andi x1, USBMASK ;1 [01] 207 | breq se0Hop ;1 [02] SE0 check for bit 4 208 | eor x2, x1 ;1 [03] 209 | bst x2, USBMINUS ;1 [04] 210 | bld shift, 4 ;1 [05] 211 | didUnstuff4: ;- [05] 212 | andi shift, 0x9f ;1 [06] 0b10011111 213 | breq unstuff4 ;1 [07] 214 | nop2 ;2 [08+09] 215 | in x2, USBIN ;1 [00] [10] <-- sample bit 5 216 | andi x2, USBMASK ;1 [01] 217 | breq se0 ;1 [02] SE0 check for bit 5 218 | eor x1, x2 ;1 [03] 219 | bst x1, USBMINUS ;1 [04] 220 | bld shift, 5 ;1 [05] 221 | didUnstuff5: ;- [05] 222 | andi shift, 0x3f ;1 [06] 0b00111111 223 | breq unstuff5 ;1 [07] 224 | nop2 ;2 [08+09] 225 | in x1, USBIN ;1 [00] [10] <-- sample bit 6 226 | andi x1, USBMASK ;1 [01] 227 | breq se0 ;1 [02] SE0 check for bit 6 228 | eor x2, x1 ;1 [03] 229 | bst x2, USBMINUS ;1 [04] 230 | bld shift, 6 ;1 [05] 231 | didUnstuff6: ;- [05] 232 | cpi shift, 0x02 ;1 [06] 0b00000010 233 | brlo unstuff6 ;1 [07] 234 | nop2 ;2 [08+09] 235 | in x2, USBIN ;1 [00] [10] <-- sample bit 7 236 | andi x2, USBMASK ;1 [01] 237 | breq se0 ;1 [02] SE0 check for bit 7 238 | eor x1, x2 ;1 [03] 239 | bst x1, USBMINUS ;1 [04] 240 | bld shift, 7 ;1 [05] 241 | didUnstuff7: ;- [05] 242 | cpi shift, 0x04 ;1 [06] 0b00000100 243 | brlo unstuff7 ;1 [07] 244 | eor x3, shift ;1 [08] reconstruct: x3 is 0 at bit locations we changed, 1 at others 245 | nop ;1 [09] 246 | in x1, USBIN ;1 [00] [10] <-- sample bit 0 247 | st y+, x3 ;2 [01+02] store data 248 | eor x2, x1 ;1 [03] 249 | bst x2, USBMINUS ;1 [04] 250 | bld shift, 0 ;1 [05] 251 | subi cnt, 1 ;1 [06] 252 | brcs overflow ;1 [07] 253 | rjmp rxLoop ;2 [08] 254 | ;----------------------------------------------------- 255 | unstuff4: ;- [08] 256 | andi x3, ~0x10 ;1 [09] 257 | in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 4 258 | andi x1, USBMASK ;1 [01] 259 | breq se0 ;1 [02] SE0 check for stuffed bit 4 260 | ori shift, 0x10 ;1 [03] 261 | rjmp didUnstuff4 ;2 [04] 262 | ;----------------------------------------------------- 263 | unstuff5: ;- [08] 264 | ori shift, 0x20 ;1 [09] 265 | in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 5 266 | andi x2, USBMASK ;1 [01] 267 | breq se0 ;1 [02] SE0 check for stuffed bit 5 268 | andi x3, ~0x20 ;1 [03] 269 | rjmp didUnstuff5 ;2 [04] 270 | ;----------------------------------------------------- 271 | unstuff6: ;- [08] 272 | andi x3, ~0x40 ;1 [09] 273 | in x1, USBIN ;1 [00] [10] <-- sample stuffed bit 6 274 | andi x1, USBMASK ;1 [01] 275 | breq se0 ;1 [02] SE0 check for stuffed bit 6 276 | ori shift, 0x40 ;1 [03] 277 | rjmp didUnstuff6 ;2 [04] 278 | ;----------------------------------------------------- 279 | unstuff7: ;- [08] 280 | andi x3, ~0x80 ;1 [09] 281 | in x2, USBIN ;1 [00] [10] <-- sample stuffed bit 7 282 | andi x2, USBMASK ;1 [01] 283 | breq se0 ;1 [02] SE0 check for stuffed bit 7 284 | ori shift, 0x80 ;1 [03] 285 | rjmp didUnstuff7 ;2 [04] 286 | 287 | macro POP_STANDARD ; 16 cycles 288 | pop x4 289 | pop cnt 290 | pop bitcnt 291 | pop x3 292 | pop x2 293 | pop x1 294 | pop shift 295 | pop YH 296 | endm 297 | macro POP_RETI ; 5 cycles 298 | pop YL 299 | out SREG, YL 300 | pop YL 301 | endm 302 | 303 | #include "asmcommon.inc" 304 | 305 | ;--------------------------------------------------------------------------- 306 | ; USB spec says: 307 | ; idle = J 308 | ; J = (D+ = 0), (D- = 1) 309 | ; K = (D+ = 1), (D- = 0) 310 | ; Spec allows 7.5 bit times from EOP to SOP for replies 311 | ;--------------------------------------------------------------------------- 312 | bitstuffN: ;- [04] 313 | eor x1, x4 ;1 [05] 314 | clr x2 ;1 [06] 315 | nop ;1 [07] 316 | rjmp didStuffN ;1 [08] 317 | ;--------------------------------------------------------------------------- 318 | bitstuff6: ;- [04] 319 | eor x1, x4 ;1 [05] 320 | clr x2 ;1 [06] 321 | rjmp didStuff6 ;1 [07] 322 | ;--------------------------------------------------------------------------- 323 | bitstuff7: ;- [02] 324 | eor x1, x4 ;1 [03] 325 | clr x2 ;1 [06] 326 | nop ;1 [05] 327 | rjmp didStuff7 ;1 [06] 328 | ;--------------------------------------------------------------------------- 329 | sendNakAndReti: ;- [-19] 330 | ldi x3, USBPID_NAK ;1 [-18] 331 | rjmp sendX3AndReti ;1 [-17] 332 | ;--------------------------------------------------------------------------- 333 | sendAckAndReti: ;- [-17] 334 | ldi cnt, USBPID_ACK ;1 [-16] 335 | sendCntAndReti: ;- [-16] 336 | mov x3, cnt ;1 [-15] 337 | sendX3AndReti: ;- [-15] 338 | ldi YL, 20 ;1 [-14] x3==r20 address is 20 339 | ldi YH, 0 ;1 [-13] 340 | ldi cnt, 2 ;1 [-12] 341 | ; rjmp usbSendAndReti fallthrough 342 | ;--------------------------------------------------------------------------- 343 | ;usbSend: 344 | ;pointer to data in 'Y' 345 | ;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] 346 | ;uses: x1...x4, btcnt, shift, cnt, Y 347 | ;Numbers in brackets are time since first bit of sync pattern is sent 348 | ;We need not to match the transfer rate exactly because the spec demands 349 | ;only 1.5% precision anyway. 350 | usbSendAndReti: ;- [-13] 13 cycles until SOP 351 | in x2, USBDDR ;1 [-12] 352 | ori x2, USBMASK ;1 [-11] 353 | sbi USBOUT, USBMINUS ;2 [-09-10] prepare idle state; D+ and D- must have been 0 (no pullups) 354 | in x1, USBOUT ;1 [-08] port mirror for tx loop 355 | out USBDDR, x2 ;1 [-07] <- acquire bus 356 | ; need not init x2 (bitstuff history) because sync starts with 0 357 | ldi x4, USBMASK ;1 [-06] exor mask 358 | ldi shift, 0x80 ;1 [-05] sync byte is first byte sent 359 | ldi bitcnt, 6 ;1 [-04] 360 | txBitLoop: ;- [-04] [06] 361 | sbrs shift, 0 ;1 [-03] [07] 362 | eor x1, x4 ;1 [-02] [08] 363 | ror shift ;1 [-01] [09] 364 | didStuffN: ;- [09] 365 | out USBOUT, x1 ;1 [00] [10] <-- out N 366 | ror x2 ;1 [01] 367 | cpi x2, 0xfc ;1 [02] 368 | brcc bitstuffN ;1 [03] 369 | dec bitcnt ;1 [04] 370 | brne txBitLoop ;1 [05] 371 | sbrs shift, 0 ;1 [06] 372 | eor x1, x4 ;1 [07] 373 | ror shift ;1 [08] 374 | didStuff6: ;- [08] 375 | nop ;1 [09] 376 | out USBOUT, x1 ;1 [00] [10] <-- out 6 377 | ror x2 ;1 [01] 378 | cpi x2, 0xfc ;1 [02] 379 | brcc bitstuff6 ;1 [03] 380 | sbrs shift, 0 ;1 [04] 381 | eor x1, x4 ;1 [05] 382 | ror shift ;1 [06] 383 | ror x2 ;1 [07] 384 | didStuff7: ;- [07] 385 | ldi bitcnt, 6 ;1 [08] 386 | cpi x2, 0xfc ;1 [09] 387 | out USBOUT, x1 ;1 [00] [10] <-- out 7 388 | brcc bitstuff7 ;1 [01] 389 | ld shift, y+ ;2 [02+03] 390 | dec cnt ;1 [04] 391 | brne txBitLoop ;1 [05] 392 | makeSE0: 393 | cbr x1, USBMASK ;1 [06] prepare SE0 [spec says EOP may be 19 to 23 cycles] 394 | lds x2, usbNewDeviceAddr;2 [07+08] 395 | lsl x2 ;1 [09] we compare with left shifted address 396 | ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: 397 | ;set address only after data packet was sent, not after handshake 398 | out USBOUT, x1 ;1 [00] [10] <-- out SE0-- from now 2 bits==20 cycl. until bus idle 399 | subi YL, 20 + 2 ;1 [01] Only assign address on data packets, not ACK/NAK in x3 400 | sbci YH, 0 ;1 [02] 401 | breq skipAddrAssign ;1 [03] 402 | sts usbDeviceAddr, x2 ;2 [04+05] if not skipped: SE0 is one cycle longer 403 | ;---------------------------------------------------------------------------- 404 | ;end of usbDeviceAddress transfer 405 | skipAddrAssign: ;- [03/04] 406 | ldi x2, 1< 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 29 | 31 | 59 | 69 | 77 | 81 | 85 | 89 | 93 | 97 | 101 | 105 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 118 | 125 | 135 | 145 | 152 | 162 | 172 | 182 | 189 | 190 | 197 | 200 | 204 | 205 | 208 | 212 | 213 | 216 | 220 | 221 | 224 | 228 | 229 | 232 | 236 | 237 | 240 | 244 | 245 | 250 | 253 | 260 | 270 | 280 | 287 | 297 | 307 | 317 | 324 | 325 | 326 | --------------------------------------------------------------------------------