; PIC 16F628 MCV628 MIDI to CV Gate interface ; auteur :Marc Bareille ; contact m.bareille@free.fr ; web site http:\\m.bareille.free.fr ; 9/11/2002 implant ; 20/01/2003 add table notes sysex ... ; 29/01/2003 korg special version ( gate inverse) ; 01/02/2003 too lazy to optimize the code now ... released to the web site ;----------------------------------------------------------------- ; status = debut ;----------------------------------------------------------------- ;Autolearn sw sur RA0 ;Gate sur RB0 ;sel DAC A = RA4 ;sel DAC B = RB3 ; ; #define __16F628 #include __CONFIG _HS_OSC & _WDT_OFF & _PWRTE_OFF & _CP_OFF & _BODEN_OFF & _LVP_OFF radix dec ERRORLEVEL -302 ; SUPPRESS BANK SELECTION MESSAGES ERRORLEVEL -307 ; SUPPRESS PCLATH SELECTION MESSAGES ERRORLEVEL -306 ; SUPPRESS PCLATH SELECTION MESSAGES #define _GATE_PLUS 1 ; definit la compilation pour Gate + ou Gate- (korg) ;--------------------------------------------------------------------------------------- ; macros & defines utiles... #define LED PORTB,0 #define BOUTON PORTA,0 #define Bank0 bcf STATUS,RP0 #define Bank1 bsf STATUS,RP0 #if _GATE_PLUS == 1 #define LEDON bsf LED #define LEDOF bcf LED #else #define LEDON bcf LED #define LEDOF bsf LED #endif #define EECANAL 0x7F ; offset @ R/W en EEprom #define EENOTE 0x7E #define EECTRLE 0x7D #define EECONFIG 0x7C TEMP0 equ 0x07A ; 2 octet buffers temporaires TEMP1 equ 0x07B ; BUFFER equ 0x022 ; midi buffer OLDSB equ 0x023 ; dernier status byte MIDI recu NSPCTP equ 0x02E ; compteur pour maj NSPTR equ 0x02F ; pointeur de la pile notes MIDI NSP0 equ 0x030 ; pile des notes a traiter... de @30 a @39 NSP1 equ 0x031 NSPDEL equ 0x03A ; note a effacer ds la Pile NOTE0 equ 0x040 ; table RAM des 48 valeurs DAC /notes NOTE16 equ 0x050 NOTE32 equ 0x060 NOTE47 equ 0x06F CONFIG equ 0x07C ; octet de config ; bit 4 = CV0 LIN / table ; bit 5 = CV1 Velo/Ctrl ; bit 6 = flag Autolearn ; bit 3 = Gate Status CTRLRF equ 0x07D ; ctrl de reference NOTERF equ 0x07E ; note de reference CANAL equ 0x07F ; canal Midi ;--------------------------------------------------------------------------------- org 0 goto Initial org 0x05 ;--------------------------------------------------------------------------------- Initial clrf PORTA ; raz ports A et B clrf PORTB movlw 0x07 movwf CMCON ; port A en mode I/O bcf STATUS,RP1 ; force bank 0/1 PORT_Init Bank1 clrf TRISB ; B en OUT bsf TRISB,1 ; sauf RX en IN clrf TRISA ; A en out Bank0 ; UART_Init Bank1 movlw 0x09 movwf SPBRG ; init BRG 31250 bauds bcf TXSTA,SYNC ; maj usart reg. pour MIDI rx/tx Bank0 Memory_Init clrf OLDSB call VideNSP Eeprom_Init movlw EECANAL ; adresse eeprom ou est le canal midi call LireEE ; movwf CANAL ; cpy ds canal... movlw EENOTE ; adresse eeprom ou est la note de ref call LireEE ;---------*------ movwf NOTERF ; cpy ds note ref movlw EECTRLE ; adresse eeprom ou est le ctrl de ref call LireEE ; movwf CTRLRF ; cpy ds ctrl ref movlw EECONFIG ; adresse eeprom ou est l'octet de config call LireEE ; movwf CONFIG ; cpy ds config var ; ----------------- Lire table des notes en EEPROM movlw 0x2F ; nb de notes ds la table -1 movwf TEMP0 ; ds le compteur temporaire movlw 0x6F ; adresse de base de la derniere note movwf FSR ; adresse de base ds FSR InitNTB movf TEMP0,W ; place adresse EEProm call LireEE ; lit l'EEprom movwf INDF ; stocke ds la table en RAM par @ indirect decf FSR,F ; decfsz TEMP0,F ; skippe si temp0=0 goto InitNTB ; movf TEMP0,W ; place adresse EEProm - lecture de la note num 0... call LireEE ; lit l'EEprom movwf INDF ; stocke ds la table en RAM par @ indirect InitDACs clrf TEMP1 ; raz des 2 DAC call WriteDAC0 clrf BUFFER call AuxDAC1 call Clignote3 ; tout est ok movlw 0x18 movwf PORTA bsf RCSTA,SPEN ; demarre midi rx bsf RCSTA,CREN ;--------------------------------------------------------------------------------- Depart btfss PIR1,RCIF ;teste si besoin de lecture 1 octet sur le MIDI goto LireSwitch ; sinon teste si le switch est ok? movf RCREG,W ; un octet a lire sur le MIDI movwf BUFFER ; ds buffer call NSPmaj bsf NSPDEL,7 ; force note invalide ds buffer note a effacer NSP btfss BUFFER,7 ;teste si c'est un Running Status goto RunStatus ; movf BUFFER,W ; sinon on copie l'octet ds un buffer temporaire movwf TEMP0 andlw 0xF0 ; et teste si c un msg systeme xorlw 0xF0 bnz CalCanal ; sinon c'est un Status byte common - det canal movf TEMP0,W ; sinon teste si c'est un exclusive xorlw 0xF0 bz TraiteSysEx goto Depart ;-------------------------------------------------------------------------------- LireSwitch Bank1 ; passe port A en IN sur bit 0 clrf TRISA bsf TRISA,0 Bank0 btfsc BOUTON ; si btn pas active ( logique -) call SwitchOn Bank1 clrf TRISA ; repasse port A en out Bank0 goto Depart SwitchOn btfsc BOUTON goto SwitchOn ; attente relachement du sw LEDON ; allume la led GATE bsf CONFIG,6 ; flag Autolearn activé return ;--------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------- CalCanal movf BUFFER,W andlw 0x0F ;w contient le num de canal MIDI btfss CONFIG,6 ; teste si mode Learn activé goto TestCan ; si non , continue a tester le canal ; xorwf CANAL,W ; si canal identique , on skippe bz TestCan0 movlw 0x0F ; extraire le canal MIDI andwf BUFFER,W ; w contient le num de canal MIDI movwf CANAL ; si oui : modifie le canal midi -> ecriture var global Bank1 movwf EEDATA movlw EECANAL ; @ canal midi en eeprom movwf EEADR ; ds regitre adresse Bank0 call EcrireEE ; bcf CONFIG,6 ; desactive flag switch LEDOF ; eteind la led Gate goto Depart TestCan0 movf CANAL,W ; force canal TestCan xorwf CANAL,W bnz DelOldSB swapf BUFFER,W ; sinon det le type de msg MIDI recu andlw 0x0F movwf BUFFER movlw 8 subwf BUFFER,W ; branche sur la routine de gestion msg correspdt addwf PCL,F ; par ajout d'offset sur le PCL goto TraiteNoteOff ; 1000 note off goto TraiteNoteOn ; 1001 note on goto DelOldSB ; 1010 poly key pressure NI goto TraiteCtrl ; 1011 control change goto DelOldSB ; 1100 program change NI goto DelOldSB ; 1101 overall key pressure NI goto DelOldSB ; 1110 pitch wheel NI DelOldSB clrf OLDSB ; efface le dernier status byte goto Depart RunStatus movf OLDSB,W ; place le dernier status byte ds temp1 pour manips... buffer pas modifié par cette routine movwf TEMP1 movlw 0x0F ; extraire le canal MIDI andwf TEMP1,W xorwf CANAL,W ; retour au debut si pas bon canal bnz Depart swapf TEMP1,F ; sinon det le type de msg MIDI recu movlw 0x0F andwf TEMP1,F movf TEMP1,W ; branche sur la routine de gestion msg correspdt addwf PCL,F ; par ajout d'offset sur le PCL goto Depart ; retour au depart si OLDSB contient 0 nop nop nop nop nop nop nop goto TraiteRSNoteOff ; 1000 note off goto TraiteRSNoteOn ; 1001 note on goto Depart ; 1010 poly key pressure NI goto TraiteRSCtrl ; 1011 control change goto Depart ; 1100 program change NI goto Depart ; 1101 overall key pressure NI goto Depart ; 1110 pitch wheel NI goto Depart ; 1111 SYSTEM MESSAGES NI ;--------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------- TraiteNoteOn call LireMidi ; lecture octet2 ( valeur note 0-127) btfsc BUFFER,7 ; teste si octet lu est pas un System Real-Time message goto TraiteNoteOn ; sinon recommence... movf TEMP0,W ; restaure status byte ds w movwf OLDSB ; et maj du dernier status byte lu TraiteRSNoteOn movf BUFFER,W ; restaure octet lu ( valeur note) ds w movwf TEMP1 ; place W (note val) ds TEMP1 LireVelo call LireMidi ; lecture octet3 ( valeur note velocity 0-127) btfsc BUFFER,7 ; teste si octet lu est pas un System Real-Time message goto LireVelo ; sinon recommence... movf BUFFER,W ; teste si la velocité est 0 bz ActiveNoteOff ; si oui branche sur gestion Note Off btfsc CONFIG,5 ; teste flag DAC1 -> Velo goto NoteLearn ; si c po le cas , on jumpe call AuxDAC1 NoteLearn btfss CONFIG,6 ; teste si mode Learn activé goto ActiveNoteOn ; si non , continue movf TEMP1,W ; restaure ( valeur note) ds w movwf NOTERF ; si oui : modifie note reference -> ecriture var global Bank1 movwf EEDATA movlw EENOTE ; @ noteref en eeprom movwf EEADR ; ds regitre adresse Bank0 call EcrireEE ; bcf CONFIG,6 ; desactive flag switch LEDOF ; Gate off -> eteind la led Gate goto Depart ; reset... ActiveNoteOn incf NSPTR,F ; inc le ptr NSP movf NSPTR,W ; place l'@ courante ptr NSP ds FSR movwf FSR movf TEMP1,W ; place note val ds W movwf INDF ; ecrit note val ds la pile a l'@ courante... movf NOTERF,W ; note ref ds W subwf TEMP1,F ; calc ( Note Midi - Note ref ) btfss STATUS,C ; si res >=0 skippe clrf TEMP1 ; sinon note en dessous de note ref --> val = 0 btfsc CONFIG,4 ; teste mode CV0 : Table=1 LIN=0 call RelNote ; mode Tables de notes... btfss CONFIG,4 ; rlf TEMP1,W ; LIN mode val x2 ... cause conversion DAC = 8 bit... call WriteDAC0 ; ecrit valeur note (temp1) ds DAC0 LEDON ; gate on movlw 0x39 ; teste debordements de la pile des notes NSP xorwf NSPTR,W bnz Depart ; si l'@39 est vide ,retour au depart movlw 0x80 ; sinon vide la pile par le bas @31 movwf NSP1 ; ecrit note vide en bas de pile decf NSPTR,F ; dec le ptr de pile goto Depart RelNote movf TEMP1,W addlw 0x40 ; ajoute offset table notes a la val note (0..48 en theorie ) = adresse movwf FSR ; copie l'adresse note a lire ds le FSR movf INDF,W ; lit la valeur DAC de la note ds la table , movwf TEMP1 ; retour par W return ;--------------------------------------------------------------------------------- TraiteNoteOff call LireMidi ;lire octet 2 ( note val) btfsc BUFFER,7 goto TraiteNoteOff ; anti RT msg movf TEMP0,W ; restaure status byte ds w movwf OLDSB ; et maj du dernier status byte lu TraiteRSNoteOff movf BUFFER,W ; restaure octet lu ( valeur note) ds w movwf TEMP1 ; le tout ds TEMP1 LireVelo2 call LireMidi ; lecture octet3 ( valeur note velocity 0-127) btfsc BUFFER,7 goto LireVelo2 ActiveNoteOff movf NSPTR,W ; @ courante ptr pile NSP ds W movwf FSR ; et w ds FSR pour un futur adress ind .... movf TEMP1,W ; copie TEMP1 (num de la note a muter) ds le buffer note a effacer movwf NSPDEL ; pour effacement futur... xorwf INDF,W ; compare val courante note avec note a effacer bnz Depart ; si pas eq alors retour au depart movlw 0x30 ; sinon test si debut de pile atteind ... xorwf FSR,W bz Depart ; si oui retour au depart , y plus rien a jouer ... gate deja Off decf FSR,W ; decrementre le ptr pile NSP xorlw 0x30 bz GateOff2 ; teste si on est en bas de la pile NSP , si oui GATE->off ; lecture de la note precedente vu k'il reste des notes dans ce cas decf FSR,F ; decale le ptr de pile movf INDF,W ; lire la valeur de note precedente dans la pile movwf TEMP1 ; la place ds temp1 movf NOTERF,W ; note ref ds W subwf TEMP1,F ; calc ( Note Midi - Note ref ) btfss STATUS,C ; si res >=0 skippe clrf TEMP1 ; sinon note en dessous de note ref --> val = 0 btfsc CONFIG,4 ; teste mode CV0 : Table=1 LIN=0 call RelNote ; mode Tables de notes... btfss CONFIG,4 ; rlf TEMP1,W ; LIN mode val x2 ... cause conversion DAC = 8 bit... call WriteDAC0 ; ecrit valeur note (temp1) ds DAC0 goto Depart ;--------------------------------------------------------------------------------- GateOff2 LEDOF ; sinon effectue un allNoteOff: vide pile notes + gate off goto Depart ;--------------------------------------------------------------------------------- TraiteCtrl call LireMidi btfsc BUFFER,7 goto TraiteCtrl movf TEMP0,W ; restaure status byte ds w movwf OLDSB ; et maj du dernier status byte lu TraiteRSCtrl movf BUFFER,W movwf TEMP1 LireValCtrl call LireMidi ; lire octet 3 ( ctrl val ) btfsc BUFFER,7 ; BUFFER contient la val du ctrl goto LireValCtrl ; anti RT msg ; ------------------------ALL NOTE OFF msg-------------------- Ctrl123 movlw 123 ; val 123 ds w xorwf TEMP1,W ; compare w avec TEMP1 num de ctrl bnz CtrlDAC1 movf BUFFER,F ; sinon replace buffer dans F bnz Depart ; retour si note of invalide ( val !=0) call VideNSP ; sinon effectue un allNoteOff: vide pile notes + gate off goto GateOff2 CtrlDAC1 btfss CONFIG,6 ; teste si mode Learn activé goto finDAC1 ; si non , continue movf TEMP1,W ; place num de ctrl ds w movwf CTRLRF ; si oui : modifie ctrl reference -> ecriture var global ;movf CTRLRF,W ; copie ctrle ref val ds EEDATA reg Bank1 movwf EEDATA movlw EECTRLE ; @ ctrlref en eeprom movwf EEADR ; ds regitre adresse Bank0 call EcrireEE ; bcf CONFIG,6 ; desactive flag switch LEDOF ; Gate off -> eteind la led Gate ; finDAC1 btfss CONFIG,5 ; teste DAC1 assigné a Ctrl goto Depart movf CTRLRF,W xorwf TEMP1,W ; compare avec num actuel bnz Depart ; sinon ctrl suivant call AuxDAC1 goto Depart ;--------------------------------------------------------------------------------- TraiteSysEx call LireMidi ; lecture octet2 ( id constructeur == 70h) btfsc BUFFER,7 ; teste si octet lu est pas un System Real-Time message goto FinEx ; sinon annule tout movlw 0x70 xorwf BUFFER,W btfss STATUS,Z ; si id construct ok on skippe goto FinEx call LireMidi ; lire octet3 (canal MIDI) btfsc BUFFER,7 ; teste si octet lu est pas un System Real-Time message goto FinEx ; sinon annule tout movf CANAL,W xorwf BUFFER,W ; compare avec canal actuel btfss STATUS,Z ; si ok va allumer la led et lire la suite goto FinEx ; sinon annule tout LEDON ; allume la led Gate call LireMidi ; lire octet4 ( offset adresse data ) btfsc BUFFER,7 ; teste si octet lu est pas un System Real-Time message goto FinEx ; sinon annule tout movf BUFFER,W movwf TEMP0 ; memo val note ds temp0 call LireMidi ; lire octet5 ( valeur data LSB ) btfsc BUFFER,7 ; teste si octet lu est pas un System Real-Time message goto FinEx ; sinon annule tout movf BUFFER,W movwf TEMP1 ; memo LSB data ds temp1 call LireMidi ; lire octet6 ( valeur data MSB) btfsc BUFFER,7 ; teste si octet lu est pas un System Real-Time message goto FinEx ; sinon annule tout movf BUFFER,W movwf OLDSB ; memo MSB data ds Old Status var ki sert a rien ici call LireMidi ; lire octet7 ( EOX ) btfss BUFFER,7 ; teste si octet lu est pas un System Real-Time message goto FinEx ; sinon annule tout ;------------- reconstitution de la valeur DATA = aaaabbbb recue en 0aaaa et 0bbbb swapf OLDSB,W ; 0aaaa * 16 == aaaa0 addwf TEMP1,F ; aaaa0+0bbbb == aaaabbbb movf TEMP0,W ; dump exclusive ok, adresse ds temp0, val DAC ds temp1 -> ecriture en RAM et EEprom Bank1 movwf EEADR ; ecriture en EEprom movf TEMP1,W movwf EEDATA Bank0 call EcrireEE movf TEMP0,W ; restaure l'offset sublw 0x30 ; teste si 30H- offset>0 btfss STATUS, C ; result >0, on skippe, c'est une val de note goto SetConf ; sinon c un octet de config movf TEMP0,W ; restaure l'offset addlw 0x40 ; ajoute adresse de base NTB a l'offset movwf FSR ; le tout ds FSR movf TEMP1,W ; restaure valeur Data movwf INDF ; ecrit la valeur en RAM goto FinEx1 ; on va eteindre la led - tout est ok... SetConf movf TEMP0,W ; offset octet de config movwf FSR ; le tout ds FSR movf TEMP1,W ; restaure valeur Data movwf INDF ; ecrit la valeur en RAM FinEx1 LEDOF ; eteind la led Gate : fin reception msg exclusif FinEx goto Depart ; retour au debut ;--------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------- LireMidi btfss PIR1,RCIF ; lecture 1 octet sur le MIDI avec attente goto LireMidi movf RCREG,W ; valeur de retour ds W movwf BUFFER return ;--------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------- AuxDAC1 rlf BUFFER,W andlw 0xF8 btfsc PORTB,0 addlw 1 ; keep GATE status movwf PORTB rlf BUFFER,W ; x2 andlw 7 addlw 8 movwf PORTA ; ecrit nibble bas nop bsf PORTA,4 return ;--------------------------------------------------------------------------------- WriteDAC0 andlw 0xF8 btfsc PORTB,0 addlw 1 ; keep GATE status movwf PORTB ; ecrit les 5 bit MSB sur port B rlf TEMP1,W andlw 7 addlw 16 movwf PORTA ; ecrit nibble bas + pulse ON nop bsf PORTA,3 return ;------------------------------lecture/ecriture 1 octet en EEPROM----------------- LireEE Bank1 movwf EEADR ; place adresse (w) ds reg @ indirect bsf EECON1,RD ; active bit de lecture eeprom movf EEDATA,W ; place l'octet lu dans w Bank0 return ;adresse est ds EEADR et donnee est ds EEDATA EcrireEE Bank1 bsf EECON1,WREN ; active wren eeprom bcf INTCON,GIE movlw 0x55 movwf EECON2 ; ecrit 55h movlw 0xAA movwf EECON2 ; ecrit AAh bsf EECON1,WR ; valide ecriture eeprom bcf EECON1,WREN ; desactive wren eeprom bsf INTCON,GIE Bank0 return ;--------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------- NSPmaj bcf STATUS,C ; raz carry movlw 0x80 ; ecrit 10000000 ds w et buffer movwf NSPCTP movlw 0x31 ;adress de maj NSP ds le FSR movwf FSR Loop movf INDF,W ; lire la val de la note en bas de pile xorwf NSPDEL,W ; ou ex pour voir si la note est deja effacée movlw 0x80 ; restaure w avec 0x80 bnz Check ; si Z =0 ( note pas effacée ) on checke movwf INDF ; ecrit 0x80 ds la pile decf NSPTR,F ; note effacée , decremente le ptr NSP ; Check xorwf INDF,W ; teste si le ptr NSP est sur une note vide bnz FinLp ; si l'@ de ptr NSP contient deja une note, on jumpe... incf FSR,F ; sinon on decale la pile NSP, inc l'@ ptr movf INDF,W ; lit la valeur decf FSR,F ; dec l'@ ptr movwf INDF ; copie la valeur incf FSR,F ; inc a nouveau l'@ ptr NSP movlw 0x80 ; efface la note a cet emplacement movwf INDF decf FSR,F ; restaure l'@ courante ptr NSP FinLp incf FSR,F ; incremente a nouveau l'@ NSP rrf NSPCTP,F ; rotation a droite du buffer compteur... bnc Loop ; tq le marqueur n'est pas dans la CARRY cad 8 fois return ; et c fini... ;--------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------- VideNSP movlw 0x80 ; vide la pile des notes movwf 0x30 movwf 0x31 movwf 0x32 movwf 0x33 movwf 0x34 movwf 0x35 movwf 0x36 movwf 0x37 movwf 0x38 movwf 0x39 movlw 0xFF ;pas de notes a effacer -> num de note impossible movwf NSPDEL movlw 0x30 movwf NSPTR ;ptr pile de notes au debut de la pile return ;--------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------- Clignote3 LEDON call Pause LEDOF call Pause LEDON call Pause LEDOF call Pause LEDON call Pause LEDOF return ;--------------------------------------------------------------------------------- Pause movlw 255 movwf TEMP1 loop1 movlw 255 movwf TEMP0 looP nop nop nop nop nop nop nop nop nop nop nop decfsz TEMP0,F goto looP decfsz TEMP1,F goto loop1 return end