<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://www.sequencer.de/synth/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fetz</id>
	<title>Synthesizer Wiki - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://www.sequencer.de/synth/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fetz"/>
	<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php/Spezial:Beitr%C3%A4ge/Fetz"/>
	<updated>2026-10-28T22:00:41Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.44.2</generator>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7327</id>
		<title>MCV876 Controller Update: File Converter</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7327"/>
		<updated>2011-06-27T21:08:08Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Datenformat */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Worum geht&#039;s? = &lt;br /&gt;
Der File Konverter ist ein kleines Programm, dass vom Compiler/Linker erzeugte Binär-Datei (im [http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format])) in ein Datenformat wandelt, dass per Midi übertragen werden kann und das der Controller so versteht, dass er sich selber updaten kann. &lt;br /&gt;
&lt;br /&gt;
== Benutzen == &lt;br /&gt;
Der Linker der Entwicklungsumgebung erzeugt ein .hex File. Dieses File zieht man einfach auf das Icon der [http://www.kanowski.de/nordcore/sys876.exe sys876.exe]. Kurz darauf taucht im gleichen Verzeichnis wie die .hex Datei eine .syx Datei sonst gleichen Namens auf. Das war es schon - diese Datei ist das Sys-Ex Update File, was man per Midi an den Controller schickt, worauf er dann sofort mit diesem neuen Programm los rennt.&lt;br /&gt;
&lt;br /&gt;
== Datenformat == &lt;br /&gt;
Die Programm-Daten werden in 64-Byte Nutzdatenhäppchen eingepackt. Diese Pakete sind Brutto 84 Bytes lang. &lt;br /&gt;
Die Daten müssen für Midi umkodiert werden, da Midi nur 7 Bit Nutz-Daten übertragen kann. Dazu werden einfach immer die nächsten 7 bit aus dem 8-Bit breiten Datenstrom entnommen. Das gibt völlig undurchsichtige Werte, geht erst nach 7x8=56 Bits auf und ist ansonsten recht kurzer Code, der kompakte Daten erzeugt. Um Datenübertragungsfehler zu erkennen, bekommt jedes Paket eine 16 Bit CRC als Prüfsumme. Und damit es als Midi durchgeht, kommen noch ein paar Bytes (&amp;quot;hallo hier kommt ein SysEx&amp;quot;) davor und ein Ende Byte dahinter. &lt;br /&gt;
&lt;br /&gt;
Die 64 Byte sind eine für Midi, Sys-Ex und das Controller-RAM noch verträgliche Datensatzlänge. Und der Controller schreibt seinen Programm-Speicher in genau solchen 64 Byte Blöcken. &lt;br /&gt;
&lt;br /&gt;
Vor die eigentlich Progamm-Daten werden noch zwei Pakete vorangestellt: &lt;br /&gt;
&lt;br /&gt;
Das erste schaltet einen in normalem Midi-Betrieb laufenden Controller auf den Boot-Lader um. Nach dem eigentlichen, kurzen Kommando, das den Normalbetrieb beendet, folgen 70 Nullen, die dem Controller mindestens 22ms Luft verschaffen, um auf den Bootlader zu wechseln. (Ist der Controller bereits im Bootlader wird dieses Paket nebst den Nullen schlichtweg überlesen/ignoriert. ) &lt;br /&gt;
&lt;br /&gt;
Das zweite Paket enthält nur die Info, dass es a)das erste Update-Pakte ist (d.h. es setzt den Empfänger auf &amp;quot;jetzt geht es los&amp;quot; zurück), und b)die Zahl der folgenden Blöcke (damit Vollständigkeit geprüft werden kann). &lt;br /&gt;
&lt;br /&gt;
Dann kommen die eigentlichen Programmdaten.&lt;br /&gt;
&lt;br /&gt;
= Quelltext =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
	Programm zum Erzeugen von Midi-Sys-Ex Software Update Files aus einer HEX-Datei &lt;br /&gt;
	für das MCV-876 Midi Interface mit ATMEL ATMEGA 88 Controller. &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*	&lt;br /&gt;
	Aufruf: &lt;br /&gt;
	sys676 avr-studio-compiler-out.hex &lt;br /&gt;
	&lt;br /&gt;
	das erzeugt dann eine Datei &lt;br /&gt;
	sys676 avr-studio-comipler-out.syx&lt;br /&gt;
	&lt;br /&gt;
	die mit Midi-OX (oder einen andern &amp;quot;ich kann Midi senden&amp;quot; Programm) an das &lt;br /&gt;
	Interface übertragen wird. &lt;br /&gt;
	&lt;br /&gt;
	Unter Windows kann man das übrigens bequem per &amp;quot;Drag and Drop&amp;quot; bedienen, einfach die &lt;br /&gt;
	.hex Datei auf die sys876.exe draufziehen, das Ergebnis taucht dann im gleichen Order wie &lt;br /&gt;
	die .hex Datei auf. (Man sieht halt keine Fehlermeldungen... ) &lt;br /&gt;
&lt;br /&gt;
	Erfolgreich übersetzt mit Visual Studio .net 2003 auf Windows XP &lt;br /&gt;
	Leeres Konsolenprojekt Namens syx876 erzeugen, diesen Quelltext in das .cpp &lt;br /&gt;
	File kopieren und bauen lassen. Ggfls. auf &amp;quot;Release&amp;quot; umstellen, im Release Ordner &lt;br /&gt;
	findet sich dann die fertige Applikation. &lt;br /&gt;
	&lt;br /&gt;
	Sollte sich nach marginalen Änderungen allerdings mit jedem Compiler übersetzen lassen. &lt;br /&gt;
	&lt;br /&gt;
*/&lt;br /&gt;
// für Windows =======================&lt;br /&gt;
#include &amp;quot;stdafx.h&amp;quot;&lt;br /&gt;
#define main _tmain &lt;br /&gt;
&lt;br /&gt;
// für normale Compiler =============&lt;br /&gt;
/*&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
*/&lt;br /&gt;
//===================================&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define VERSION &amp;quot;1.00&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DATA 8192				// netto data size (ATMEGA 88 Flash Rom size) &lt;br /&gt;
#define MAX_SYX_DATA (2*MAX_DATA)	// max. complete syx-ex size&lt;br /&gt;
#define MAX_SYX_BLOCK (2*64)		// one Sys-Ex Block of 64 bytes netto data &lt;br /&gt;
&lt;br /&gt;
unsigned char data[MAX_DATA];	// holds binary program data &lt;br /&gt;
&lt;br /&gt;
// Convert one HEX-ASCII character to its numerical value, or -1 if illegal value &lt;br /&gt;
int conf( char c)&lt;br /&gt;
{&lt;br /&gt;
	switch (c)&lt;br /&gt;
	{&lt;br /&gt;
		case &#039;0&#039;: return 0;&lt;br /&gt;
		case &#039;1&#039;: return 1;&lt;br /&gt;
		case &#039;2&#039;: return 2;&lt;br /&gt;
		case &#039;3&#039;: return 3;&lt;br /&gt;
		case &#039;4&#039;: return 4;&lt;br /&gt;
		case &#039;5&#039;: return 5;&lt;br /&gt;
		case &#039;6&#039;: return 6;&lt;br /&gt;
		case &#039;7&#039;: return 7;&lt;br /&gt;
		case &#039;8&#039;: return 8;&lt;br /&gt;
		case &#039;9&#039;: return 9;&lt;br /&gt;
		case &#039;a&#039;: case &#039;A&#039;: return 10;&lt;br /&gt;
		case &#039;b&#039;: case &#039;B&#039;: return 11;&lt;br /&gt;
		case &#039;c&#039;: case &#039;C&#039;: return 12;&lt;br /&gt;
		case &#039;d&#039;: case &#039;D&#039;: return 13;&lt;br /&gt;
		case &#039;e&#039;: case &#039;E&#039;: return 14;&lt;br /&gt;
		case &#039;f&#039;: case &#039;F&#039;: return 15;&lt;br /&gt;
	}&lt;br /&gt;
	return (-1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate 16 bit crc from previous crc and new data byte, initial (first) crc should be 0xffff, returns new crc &lt;br /&gt;
unsigned int crc16(unsigned int crc, unsigned char byte)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	crc^=byte;&lt;br /&gt;
	for(i=0;i&amp;lt;8;++i)&lt;br /&gt;
	{&lt;br /&gt;
	    if (crc&amp;amp;1)&lt;br /&gt;
			crc=(crc&amp;gt;&amp;gt;1)^0xA001;&lt;br /&gt;
		else&lt;br /&gt;
			crc&amp;gt;&amp;gt;=1;&lt;br /&gt;
	}&lt;br /&gt;
	return crc &amp;amp; 0xffff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// generates first Sys-EX Block, this switches the interface from operation mode to boot-load mode&lt;br /&gt;
int makePrgPrep(unsigned char *str)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	&lt;br /&gt;
	unsigned char a[] = { 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7 }; // the switch command string (see makeSysEx for byte value explanation) &lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;sizeof(a);i++)	&lt;br /&gt;
		*str++=a[i];&lt;br /&gt;
	for(;i&amp;lt;70;i++)	// 70 bytes * 0,32ms for midi data transmission gives the interface the time to start the bootloader &lt;br /&gt;
	{&lt;br /&gt;
		*str++=0;&lt;br /&gt;
	}&lt;br /&gt;
	return i; &lt;br /&gt;
}&lt;br /&gt;
// make a sys-ex data block with proper midi framing and crc from binary data in &amp;quot;block&amp;quot; &lt;br /&gt;
int makeSysEx(unsigned char *block, int len, unsigned char *syxTarget)&lt;br /&gt;
{&lt;br /&gt;
	int crc=0xffff;&lt;br /&gt;
	unsigned int out=0;&lt;br /&gt;
	int bits=0;&lt;br /&gt;
	int i;&lt;br /&gt;
	unsigned char c;  &lt;br /&gt;
	&lt;br /&gt;
	unsigned char *syxStr=syxTarget;&lt;br /&gt;
&lt;br /&gt;
	// make CRC &lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];&lt;br /&gt;
		crc=crc16(crc, c);&lt;br /&gt;
	}&lt;br /&gt;
	block[len++]= ( crc)&amp;amp;0xff;&lt;br /&gt;
	block[len++]= (crc &amp;gt;&amp;gt;8 ) &amp;amp;0xff;&lt;br /&gt;
	&lt;br /&gt;
	*syxStr++ = (0xF0);	// start sys ex &lt;br /&gt;
	*syxStr++ = (0x70);	// manufacturer id &lt;br /&gt;
	*syxStr++ = (0x7D);	// unit type  &lt;br /&gt;
	*syxStr++ = (0x00); // unit id &lt;br /&gt;
	*syxStr++ = (0x15);	// for normal sysEx it is a adress, here magic byte 1 &lt;br /&gt;
	*syxStr++ = (0x55); // magic byte 2 &lt;br /&gt;
		&lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)	&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];	// data byte 	&lt;br /&gt;
		/*&lt;br /&gt;
			Midi data is 7 bit only, as MSB is command/data bit. &lt;br /&gt;
			So data must be converted to 7 bit. &lt;br /&gt;
			Each source data byte gives 8/7 target bytes, &lt;br /&gt;
			as soon as there is one complete byte more (which happens every 7 input bytes)&lt;br /&gt;
			two target bytes will be generated.&lt;br /&gt;
		*/&lt;br /&gt;
		out&amp;lt;&amp;lt;=8;	&lt;br /&gt;
		out|=c;&lt;br /&gt;
		bits+=8;&lt;br /&gt;
		while(bits&amp;gt;=7)&lt;br /&gt;
		{&lt;br /&gt;
			*syxStr++ = ((out&amp;gt;&amp;gt;(bits-7)) &amp;amp; 0x7f);&lt;br /&gt;
			bits-=7;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if(bits)	// don&#039;t forget about the last bits of the last data byte &lt;br /&gt;
	{&lt;br /&gt;
		out&amp;lt;&amp;lt;= 7-bits;  // this will pad last byte with up to 6 zero bits. &lt;br /&gt;
		*syxStr++ =  (out &amp;amp; 0x7f);&lt;br /&gt;
	}&lt;br /&gt;
	*syxStr++ = 0xF7;	// Sys-Ex end &lt;br /&gt;
&lt;br /&gt;
	return (int) (syxStr - syxTarget); // lenght of generated data &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// argv[1] is filename to be converted &lt;br /&gt;
int main(int argc, _TCHAR* argv[])&lt;br /&gt;
{&lt;br /&gt;
	FILE *fp; &lt;br /&gt;
	int minAdr=0xffff;&lt;br /&gt;
	int maxAdr=0;&lt;br /&gt;
	int chars=0;&lt;br /&gt;
	int error=0;&lt;br /&gt;
	int c; &lt;br /&gt;
	int val;&lt;br /&gt;
	int field=-1;&lt;br /&gt;
	int adress=0;&lt;br /&gt;
	int cnt; &lt;br /&gt;
	int sum=0; &lt;br /&gt;
	int field_len=0; &lt;br /&gt;
	int type=99;&lt;br /&gt;
	int line=0;&lt;br /&gt;
&lt;br /&gt;
	// the controller allways flashes 64 byte blocks, so the last block is padded with arbitrary data &lt;br /&gt;
	int padit[]= {110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
              110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
			  110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43 } ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	printf(&amp;quot;syx876 Version &amp;quot; VERSION &amp;quot;\nHex-File to Sys-Ex Converter for ATMega88 on MCV876\n&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
	// &#039;check&#039; for proper input filename (it must have a &amp;quot;three byte extension&amp;quot; )&lt;br /&gt;
	//  *not* fool proof, try &amp;quot;/../a&amp;quot; and you&#039;ll get interesting results&lt;br /&gt;
	size_t l=strlen(argv[1]);&lt;br /&gt;
&lt;br /&gt;
	if(argc !=2 || l&amp;lt;5 || *(argv[1]+l-4)!=&#039;.&#039; )&lt;br /&gt;
	{&lt;br /&gt;
		printf(&amp;quot;\nUsage: syx876 filename.hex\n&amp;quot;);&lt;br /&gt;
		return -1;&lt;br /&gt;
	}&lt;br /&gt;
	l -= 3; &lt;br /&gt;
	&lt;br /&gt;
	fp=fopen (argv[1],&amp;quot;rb&amp;quot;);	// open input files &lt;br /&gt;
	&lt;br /&gt;
	if(fp)&lt;br /&gt;
	{&lt;br /&gt;
		// make output file name &lt;br /&gt;
		*(argv[1]+l)=0;	&lt;br /&gt;
		strcat (argv[1],&amp;quot;syx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		while( (c=fgetc(fp)) != EOF  ) // read input hex file &lt;br /&gt;
		{&lt;br /&gt;
			chars++;&lt;br /&gt;
			 &lt;br /&gt;
			if(c==&#039;:&#039;)&lt;br /&gt;
			{&lt;br /&gt;
				cnt=0;&lt;br /&gt;
				field=0;&lt;br /&gt;
				sum=0;&lt;br /&gt;
				line++;&lt;br /&gt;
			}&lt;br /&gt;
			else if ( conf( (unsigned char) c ) &amp;gt;=0 )&lt;br /&gt;
			{&lt;br /&gt;
				if(cnt==0)&lt;br /&gt;
					val=conf( (unsigned char) c );&lt;br /&gt;
				else &lt;br /&gt;
				{&lt;br /&gt;
					val&amp;lt;&amp;lt;=4;&lt;br /&gt;
					val += conf( (unsigned char) c );&lt;br /&gt;
				}&lt;br /&gt;
				cnt++;&lt;br /&gt;
				&lt;br /&gt;
				if(cnt==2)	// add up checksum&lt;br /&gt;
					sum+= val;&lt;br /&gt;
				if( cnt==4)&lt;br /&gt;
					sum += val &amp;amp; 0xff;&lt;br /&gt;
				&lt;br /&gt;
				if(field ==0 &amp;amp;&amp;amp; cnt==2)&lt;br /&gt;
				{	// byte count &lt;br /&gt;
					field_len=val; &lt;br /&gt;
					field =1;&lt;br /&gt;
					cnt=0;				&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==1 &amp;amp;&amp;amp; cnt == 4)&lt;br /&gt;
				{	// adress&lt;br /&gt;
					adress=val;&lt;br /&gt;
					field=2;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==2 &amp;amp;&amp;amp; cnt == 2)&lt;br /&gt;
				{	// type &lt;br /&gt;
					type=val;&lt;br /&gt;
					field=4;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len&amp;gt;0)&lt;br /&gt;
				{	// data &lt;br /&gt;
					field_len--;&lt;br /&gt;
					&lt;br /&gt;
					if(adress &amp;lt; minAdr)&lt;br /&gt;
						minAdr=adress;&lt;br /&gt;
					if(adress&amp;gt;maxAdr)&lt;br /&gt;
						maxAdr=adress;&lt;br /&gt;
&lt;br /&gt;
					if(type==0 &amp;amp;&amp;amp; adress &amp;lt; MAX_DATA)&lt;br /&gt;
						data[adress]=val;&lt;br /&gt;
					adress++;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len == 0)&lt;br /&gt;
				{	// checksum &lt;br /&gt;
					cnt=0;&lt;br /&gt;
					sum &amp;amp;=0xff;&lt;br /&gt;
					if(sum)&lt;br /&gt;
					{&lt;br /&gt;
						error++;&lt;br /&gt;
						printf(&amp;quot;Line %d Checksum error (sum=%d)\n&amp;quot;,line, sum); &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else if( c !=10 &amp;amp;&amp;amp; c !=13)&lt;br /&gt;
			{&lt;br /&gt;
				error ++;&lt;br /&gt;
				printf( &amp;quot;Line %d unexpected character (%c)\n&amp;quot;,line, c); // characters I don&#039;t know  &lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		printf( &amp;quot;\nread %d charactes, from adress %d to %d with %d errors\n&amp;quot;, chars, minAdr, maxAdr,error); &lt;br /&gt;
		&lt;br /&gt;
		// generate output data &lt;br /&gt;
		if(error==0 &amp;amp;&amp;amp; minAdr==0) &lt;br /&gt;
		{&lt;br /&gt;
			int blocks; &lt;br /&gt;
			int blockNr; &lt;br /&gt;
			unsigned char block[MAX_SYX_BLOCK];	// buffer to collect/prepare one binary data block &lt;br /&gt;
			unsigned char stp[MAX_SYX_DATA]; 	//  output data buffer, collects the sysEx data&lt;br /&gt;
			unsigned char *str;		// current write pointer &lt;br /&gt;
			int adr; &lt;br /&gt;
			int padded;&lt;br /&gt;
			FILE *wp;&lt;br /&gt;
			int i;&lt;br /&gt;
			&lt;br /&gt;
			str=stp; &lt;br /&gt;
			&lt;br /&gt;
			blocks= (((maxAdr)/64)+1);	// total number of blocks &lt;br /&gt;
						&lt;br /&gt;
			str+= makePrgPrep(str);		// generate sys-ex header (=switch to bootloader command) &lt;br /&gt;
			&lt;br /&gt;
			// first block is a spezial block - it tells controller number of blocks he has to expect. &lt;br /&gt;
			blockNr=0;&lt;br /&gt;
			block[0]=blockNr;&lt;br /&gt;
			block[1]=blocks;&lt;br /&gt;
			block[2]=0;	// reserved &lt;br /&gt;
			block[3]=0;	// reserved &lt;br /&gt;
			str += makeSysEx(block,4,str);	// generate SysEx from block data &lt;br /&gt;
			&lt;br /&gt;
			adr=minAdr;&lt;br /&gt;
			// make flash data blocks &lt;br /&gt;
			// flash data blocks are 65 bytes, 1 Byte Block number, 64 data bytes&lt;br /&gt;
			// the controller allways writes 64 byte blocks to flash, so this format &lt;br /&gt;
			// helps to keep the controller program straight. &lt;br /&gt;
			while (blockNr&amp;lt;blocks)&lt;br /&gt;
			{&lt;br /&gt;
				blockNr++;&lt;br /&gt;
				block[0]=blockNr;	// first data byte in block is block number (so controller sees, when a block is lost) &lt;br /&gt;
				padded=0; &lt;br /&gt;
				for(i=0;i&amp;lt;64;i++)&lt;br /&gt;
				{&lt;br /&gt;
					if(adr&amp;gt;maxAdr)	 // it beyond highest adresses from hex file:  last block is(must be) padded to 64 bytes &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=padit[padded];&lt;br /&gt;
						padded++;&lt;br /&gt;
					}&lt;br /&gt;
					else	// write programm data &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=data[adr++];					&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				str +=makeSysEx(block,65,str);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			// write output file &lt;br /&gt;
			wp=fopen(argv[1],&amp;quot;wb&amp;quot;);&lt;br /&gt;
			if(wp)&lt;br /&gt;
			{&lt;br /&gt;
				fwrite ((void *) stp ,sizeof(unsigned char), str-stp, wp);&lt;br /&gt;
				fclose(wp);&lt;br /&gt;
				&lt;br /&gt;
				printf(&amp;quot;%d sysEx data blocks in %d bytes written\n&amp;quot;,blocks, str-stp);&lt;br /&gt;
			}&lt;br /&gt;
			else &lt;br /&gt;
				printf(&amp;quot;Cant open output file %s\n&amp;quot;,argv[1]);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			if(minAdr)&lt;br /&gt;
				printf(&amp;quot;Lowest adress %d is not zero!\n&amp;quot;,minAdr);&lt;br /&gt;
			return -2;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
		printf (&amp;quot;Input file %s not found\n&amp;quot;,argv[1]);&lt;br /&gt;
&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7326</id>
		<title>MCV876 Controller Update: File Converter</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7326"/>
		<updated>2011-06-27T21:07:17Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Datenformat */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Worum geht&#039;s? = &lt;br /&gt;
Der File Konverter ist ein kleines Programm, dass vom Compiler/Linker erzeugte Binär-Datei (im [http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format])) in ein Datenformat wandelt, dass per Midi übertragen werden kann und das der Controller so versteht, dass er sich selber updaten kann. &lt;br /&gt;
&lt;br /&gt;
== Benutzen == &lt;br /&gt;
Der Linker der Entwicklungsumgebung erzeugt ein .hex File. Dieses File zieht man einfach auf das Icon der [http://www.kanowski.de/nordcore/sys876.exe sys876.exe]. Kurz darauf taucht im gleichen Verzeichnis wie die .hex Datei eine .syx Datei sonst gleichen Namens auf. Das war es schon - diese Datei ist das Sys-Ex Update File, was man per Midi an den Controller schickt, worauf er dann sofort mit diesem neuen Programm los rennt.&lt;br /&gt;
&lt;br /&gt;
== Datenformat == &lt;br /&gt;
Die Programm-Daten werden in 64-Byte Nutzdatenhäppchen eingepackt. Diese Pakete sind Brutto 84 Bytes lang. &lt;br /&gt;
Die Daten müssen für Midi umkodiert werden, da Midi nur 7 Bit Nutz-Daten übertragen kann. Dazu werden einfach immer die nächsten 7 bit aus dem 8-Bit breiten Datenstrom entnommen. Das gibt völlig undurchsichtige Werte, geht erst nach 7x8=56 Bits auf und ist ansonsten recht einfacher Code, der kompakte Daten erzeugt. Um Datenübertragungsfehler zu erkennen, bekommt jedes Paket eine 16 Bit CRC als Prüfsumme. Und damit es als Midi durchgeht, kommen noch ein paar Bytes (&amp;quot;hallo hier kommt ein SysEx&amp;quot;) davor und ein Ende Byte dahinter. &lt;br /&gt;
&lt;br /&gt;
Die 64 Byte sind eine für Midi, Sys-Ex und das Controller-RAM noch verträgliche Datensatzlänge. Und der Controller schreibt seinen Programm-Speicher in genau solchen 64 Byte Blöcken. &lt;br /&gt;
&lt;br /&gt;
Vor die eigentlich Progamm-Daten werden noch zwei Pakete vorangestellt: &lt;br /&gt;
&lt;br /&gt;
Das erste schaltet einen in normalem Midi-Betrieb laufenden Controller auf den Boot-Lader um. Nach dem eigentlichen, kurzen Kommando, das den Normalbetrieb beendet, folgen 70 Nullen, die dem Controller mindestens 22ms Luft verschaffen, um auf den Bootlader zu wechseln. (Ist der Controller bereits im Bootlader wird dieses Paket nebst den Nullen schlichtweg überlesen/ignoriert. ) &lt;br /&gt;
&lt;br /&gt;
Das zweite Paket enthält nur die Info, dass es a)das erste Update-Pakte ist (d.h. es setzt den Empfänger auf &amp;quot;jetzt geht es los&amp;quot; zurück), und b)die Zahl der folgenden Blöcke (damit Vollständigkeit geprüft werden kann). &lt;br /&gt;
&lt;br /&gt;
Dann kommen die eigentlichen Programmdaten.&lt;br /&gt;
&lt;br /&gt;
= Quelltext =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
	Programm zum Erzeugen von Midi-Sys-Ex Software Update Files aus einer HEX-Datei &lt;br /&gt;
	für das MCV-876 Midi Interface mit ATMEL ATMEGA 88 Controller. &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*	&lt;br /&gt;
	Aufruf: &lt;br /&gt;
	sys676 avr-studio-compiler-out.hex &lt;br /&gt;
	&lt;br /&gt;
	das erzeugt dann eine Datei &lt;br /&gt;
	sys676 avr-studio-comipler-out.syx&lt;br /&gt;
	&lt;br /&gt;
	die mit Midi-OX (oder einen andern &amp;quot;ich kann Midi senden&amp;quot; Programm) an das &lt;br /&gt;
	Interface übertragen wird. &lt;br /&gt;
	&lt;br /&gt;
	Unter Windows kann man das übrigens bequem per &amp;quot;Drag and Drop&amp;quot; bedienen, einfach die &lt;br /&gt;
	.hex Datei auf die sys876.exe draufziehen, das Ergebnis taucht dann im gleichen Order wie &lt;br /&gt;
	die .hex Datei auf. (Man sieht halt keine Fehlermeldungen... ) &lt;br /&gt;
&lt;br /&gt;
	Erfolgreich übersetzt mit Visual Studio .net 2003 auf Windows XP &lt;br /&gt;
	Leeres Konsolenprojekt Namens syx876 erzeugen, diesen Quelltext in das .cpp &lt;br /&gt;
	File kopieren und bauen lassen. Ggfls. auf &amp;quot;Release&amp;quot; umstellen, im Release Ordner &lt;br /&gt;
	findet sich dann die fertige Applikation. &lt;br /&gt;
	&lt;br /&gt;
	Sollte sich nach marginalen Änderungen allerdings mit jedem Compiler übersetzen lassen. &lt;br /&gt;
	&lt;br /&gt;
*/&lt;br /&gt;
// für Windows =======================&lt;br /&gt;
#include &amp;quot;stdafx.h&amp;quot;&lt;br /&gt;
#define main _tmain &lt;br /&gt;
&lt;br /&gt;
// für normale Compiler =============&lt;br /&gt;
/*&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
*/&lt;br /&gt;
//===================================&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define VERSION &amp;quot;1.00&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DATA 8192				// netto data size (ATMEGA 88 Flash Rom size) &lt;br /&gt;
#define MAX_SYX_DATA (2*MAX_DATA)	// max. complete syx-ex size&lt;br /&gt;
#define MAX_SYX_BLOCK (2*64)		// one Sys-Ex Block of 64 bytes netto data &lt;br /&gt;
&lt;br /&gt;
unsigned char data[MAX_DATA];	// holds binary program data &lt;br /&gt;
&lt;br /&gt;
// Convert one HEX-ASCII character to its numerical value, or -1 if illegal value &lt;br /&gt;
int conf( char c)&lt;br /&gt;
{&lt;br /&gt;
	switch (c)&lt;br /&gt;
	{&lt;br /&gt;
		case &#039;0&#039;: return 0;&lt;br /&gt;
		case &#039;1&#039;: return 1;&lt;br /&gt;
		case &#039;2&#039;: return 2;&lt;br /&gt;
		case &#039;3&#039;: return 3;&lt;br /&gt;
		case &#039;4&#039;: return 4;&lt;br /&gt;
		case &#039;5&#039;: return 5;&lt;br /&gt;
		case &#039;6&#039;: return 6;&lt;br /&gt;
		case &#039;7&#039;: return 7;&lt;br /&gt;
		case &#039;8&#039;: return 8;&lt;br /&gt;
		case &#039;9&#039;: return 9;&lt;br /&gt;
		case &#039;a&#039;: case &#039;A&#039;: return 10;&lt;br /&gt;
		case &#039;b&#039;: case &#039;B&#039;: return 11;&lt;br /&gt;
		case &#039;c&#039;: case &#039;C&#039;: return 12;&lt;br /&gt;
		case &#039;d&#039;: case &#039;D&#039;: return 13;&lt;br /&gt;
		case &#039;e&#039;: case &#039;E&#039;: return 14;&lt;br /&gt;
		case &#039;f&#039;: case &#039;F&#039;: return 15;&lt;br /&gt;
	}&lt;br /&gt;
	return (-1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate 16 bit crc from previous crc and new data byte, initial (first) crc should be 0xffff, returns new crc &lt;br /&gt;
unsigned int crc16(unsigned int crc, unsigned char byte)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	crc^=byte;&lt;br /&gt;
	for(i=0;i&amp;lt;8;++i)&lt;br /&gt;
	{&lt;br /&gt;
	    if (crc&amp;amp;1)&lt;br /&gt;
			crc=(crc&amp;gt;&amp;gt;1)^0xA001;&lt;br /&gt;
		else&lt;br /&gt;
			crc&amp;gt;&amp;gt;=1;&lt;br /&gt;
	}&lt;br /&gt;
	return crc &amp;amp; 0xffff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// generates first Sys-EX Block, this switches the interface from operation mode to boot-load mode&lt;br /&gt;
int makePrgPrep(unsigned char *str)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	&lt;br /&gt;
	unsigned char a[] = { 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7 }; // the switch command string (see makeSysEx for byte value explanation) &lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;sizeof(a);i++)	&lt;br /&gt;
		*str++=a[i];&lt;br /&gt;
	for(;i&amp;lt;70;i++)	// 70 bytes * 0,32ms for midi data transmission gives the interface the time to start the bootloader &lt;br /&gt;
	{&lt;br /&gt;
		*str++=0;&lt;br /&gt;
	}&lt;br /&gt;
	return i; &lt;br /&gt;
}&lt;br /&gt;
// make a sys-ex data block with proper midi framing and crc from binary data in &amp;quot;block&amp;quot; &lt;br /&gt;
int makeSysEx(unsigned char *block, int len, unsigned char *syxTarget)&lt;br /&gt;
{&lt;br /&gt;
	int crc=0xffff;&lt;br /&gt;
	unsigned int out=0;&lt;br /&gt;
	int bits=0;&lt;br /&gt;
	int i;&lt;br /&gt;
	unsigned char c;  &lt;br /&gt;
	&lt;br /&gt;
	unsigned char *syxStr=syxTarget;&lt;br /&gt;
&lt;br /&gt;
	// make CRC &lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];&lt;br /&gt;
		crc=crc16(crc, c);&lt;br /&gt;
	}&lt;br /&gt;
	block[len++]= ( crc)&amp;amp;0xff;&lt;br /&gt;
	block[len++]= (crc &amp;gt;&amp;gt;8 ) &amp;amp;0xff;&lt;br /&gt;
	&lt;br /&gt;
	*syxStr++ = (0xF0);	// start sys ex &lt;br /&gt;
	*syxStr++ = (0x70);	// manufacturer id &lt;br /&gt;
	*syxStr++ = (0x7D);	// unit type  &lt;br /&gt;
	*syxStr++ = (0x00); // unit id &lt;br /&gt;
	*syxStr++ = (0x15);	// for normal sysEx it is a adress, here magic byte 1 &lt;br /&gt;
	*syxStr++ = (0x55); // magic byte 2 &lt;br /&gt;
		&lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)	&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];	// data byte 	&lt;br /&gt;
		/*&lt;br /&gt;
			Midi data is 7 bit only, as MSB is command/data bit. &lt;br /&gt;
			So data must be converted to 7 bit. &lt;br /&gt;
			Each source data byte gives 8/7 target bytes, &lt;br /&gt;
			as soon as there is one complete byte more (which happens every 7 input bytes)&lt;br /&gt;
			two target bytes will be generated.&lt;br /&gt;
		*/&lt;br /&gt;
		out&amp;lt;&amp;lt;=8;	&lt;br /&gt;
		out|=c;&lt;br /&gt;
		bits+=8;&lt;br /&gt;
		while(bits&amp;gt;=7)&lt;br /&gt;
		{&lt;br /&gt;
			*syxStr++ = ((out&amp;gt;&amp;gt;(bits-7)) &amp;amp; 0x7f);&lt;br /&gt;
			bits-=7;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if(bits)	// don&#039;t forget about the last bits of the last data byte &lt;br /&gt;
	{&lt;br /&gt;
		out&amp;lt;&amp;lt;= 7-bits;  // this will pad last byte with up to 6 zero bits. &lt;br /&gt;
		*syxStr++ =  (out &amp;amp; 0x7f);&lt;br /&gt;
	}&lt;br /&gt;
	*syxStr++ = 0xF7;	// Sys-Ex end &lt;br /&gt;
&lt;br /&gt;
	return (int) (syxStr - syxTarget); // lenght of generated data &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// argv[1] is filename to be converted &lt;br /&gt;
int main(int argc, _TCHAR* argv[])&lt;br /&gt;
{&lt;br /&gt;
	FILE *fp; &lt;br /&gt;
	int minAdr=0xffff;&lt;br /&gt;
	int maxAdr=0;&lt;br /&gt;
	int chars=0;&lt;br /&gt;
	int error=0;&lt;br /&gt;
	int c; &lt;br /&gt;
	int val;&lt;br /&gt;
	int field=-1;&lt;br /&gt;
	int adress=0;&lt;br /&gt;
	int cnt; &lt;br /&gt;
	int sum=0; &lt;br /&gt;
	int field_len=0; &lt;br /&gt;
	int type=99;&lt;br /&gt;
	int line=0;&lt;br /&gt;
&lt;br /&gt;
	// the controller allways flashes 64 byte blocks, so the last block is padded with arbitrary data &lt;br /&gt;
	int padit[]= {110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
              110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
			  110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43 } ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	printf(&amp;quot;syx876 Version &amp;quot; VERSION &amp;quot;\nHex-File to Sys-Ex Converter for ATMega88 on MCV876\n&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
	// &#039;check&#039; for proper input filename (it must have a &amp;quot;three byte extension&amp;quot; )&lt;br /&gt;
	//  *not* fool proof, try &amp;quot;/../a&amp;quot; and you&#039;ll get interesting results&lt;br /&gt;
	size_t l=strlen(argv[1]);&lt;br /&gt;
&lt;br /&gt;
	if(argc !=2 || l&amp;lt;5 || *(argv[1]+l-4)!=&#039;.&#039; )&lt;br /&gt;
	{&lt;br /&gt;
		printf(&amp;quot;\nUsage: syx876 filename.hex\n&amp;quot;);&lt;br /&gt;
		return -1;&lt;br /&gt;
	}&lt;br /&gt;
	l -= 3; &lt;br /&gt;
	&lt;br /&gt;
	fp=fopen (argv[1],&amp;quot;rb&amp;quot;);	// open input files &lt;br /&gt;
	&lt;br /&gt;
	if(fp)&lt;br /&gt;
	{&lt;br /&gt;
		// make output file name &lt;br /&gt;
		*(argv[1]+l)=0;	&lt;br /&gt;
		strcat (argv[1],&amp;quot;syx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		while( (c=fgetc(fp)) != EOF  ) // read input hex file &lt;br /&gt;
		{&lt;br /&gt;
			chars++;&lt;br /&gt;
			 &lt;br /&gt;
			if(c==&#039;:&#039;)&lt;br /&gt;
			{&lt;br /&gt;
				cnt=0;&lt;br /&gt;
				field=0;&lt;br /&gt;
				sum=0;&lt;br /&gt;
				line++;&lt;br /&gt;
			}&lt;br /&gt;
			else if ( conf( (unsigned char) c ) &amp;gt;=0 )&lt;br /&gt;
			{&lt;br /&gt;
				if(cnt==0)&lt;br /&gt;
					val=conf( (unsigned char) c );&lt;br /&gt;
				else &lt;br /&gt;
				{&lt;br /&gt;
					val&amp;lt;&amp;lt;=4;&lt;br /&gt;
					val += conf( (unsigned char) c );&lt;br /&gt;
				}&lt;br /&gt;
				cnt++;&lt;br /&gt;
				&lt;br /&gt;
				if(cnt==2)	// add up checksum&lt;br /&gt;
					sum+= val;&lt;br /&gt;
				if( cnt==4)&lt;br /&gt;
					sum += val &amp;amp; 0xff;&lt;br /&gt;
				&lt;br /&gt;
				if(field ==0 &amp;amp;&amp;amp; cnt==2)&lt;br /&gt;
				{	// byte count &lt;br /&gt;
					field_len=val; &lt;br /&gt;
					field =1;&lt;br /&gt;
					cnt=0;				&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==1 &amp;amp;&amp;amp; cnt == 4)&lt;br /&gt;
				{	// adress&lt;br /&gt;
					adress=val;&lt;br /&gt;
					field=2;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==2 &amp;amp;&amp;amp; cnt == 2)&lt;br /&gt;
				{	// type &lt;br /&gt;
					type=val;&lt;br /&gt;
					field=4;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len&amp;gt;0)&lt;br /&gt;
				{	// data &lt;br /&gt;
					field_len--;&lt;br /&gt;
					&lt;br /&gt;
					if(adress &amp;lt; minAdr)&lt;br /&gt;
						minAdr=adress;&lt;br /&gt;
					if(adress&amp;gt;maxAdr)&lt;br /&gt;
						maxAdr=adress;&lt;br /&gt;
&lt;br /&gt;
					if(type==0 &amp;amp;&amp;amp; adress &amp;lt; MAX_DATA)&lt;br /&gt;
						data[adress]=val;&lt;br /&gt;
					adress++;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len == 0)&lt;br /&gt;
				{	// checksum &lt;br /&gt;
					cnt=0;&lt;br /&gt;
					sum &amp;amp;=0xff;&lt;br /&gt;
					if(sum)&lt;br /&gt;
					{&lt;br /&gt;
						error++;&lt;br /&gt;
						printf(&amp;quot;Line %d Checksum error (sum=%d)\n&amp;quot;,line, sum); &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else if( c !=10 &amp;amp;&amp;amp; c !=13)&lt;br /&gt;
			{&lt;br /&gt;
				error ++;&lt;br /&gt;
				printf( &amp;quot;Line %d unexpected character (%c)\n&amp;quot;,line, c); // characters I don&#039;t know  &lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		printf( &amp;quot;\nread %d charactes, from adress %d to %d with %d errors\n&amp;quot;, chars, minAdr, maxAdr,error); &lt;br /&gt;
		&lt;br /&gt;
		// generate output data &lt;br /&gt;
		if(error==0 &amp;amp;&amp;amp; minAdr==0) &lt;br /&gt;
		{&lt;br /&gt;
			int blocks; &lt;br /&gt;
			int blockNr; &lt;br /&gt;
			unsigned char block[MAX_SYX_BLOCK];	// buffer to collect/prepare one binary data block &lt;br /&gt;
			unsigned char stp[MAX_SYX_DATA]; 	//  output data buffer, collects the sysEx data&lt;br /&gt;
			unsigned char *str;		// current write pointer &lt;br /&gt;
			int adr; &lt;br /&gt;
			int padded;&lt;br /&gt;
			FILE *wp;&lt;br /&gt;
			int i;&lt;br /&gt;
			&lt;br /&gt;
			str=stp; &lt;br /&gt;
			&lt;br /&gt;
			blocks= (((maxAdr)/64)+1);	// total number of blocks &lt;br /&gt;
						&lt;br /&gt;
			str+= makePrgPrep(str);		// generate sys-ex header (=switch to bootloader command) &lt;br /&gt;
			&lt;br /&gt;
			// first block is a spezial block - it tells controller number of blocks he has to expect. &lt;br /&gt;
			blockNr=0;&lt;br /&gt;
			block[0]=blockNr;&lt;br /&gt;
			block[1]=blocks;&lt;br /&gt;
			block[2]=0;	// reserved &lt;br /&gt;
			block[3]=0;	// reserved &lt;br /&gt;
			str += makeSysEx(block,4,str);	// generate SysEx from block data &lt;br /&gt;
			&lt;br /&gt;
			adr=minAdr;&lt;br /&gt;
			// make flash data blocks &lt;br /&gt;
			// flash data blocks are 65 bytes, 1 Byte Block number, 64 data bytes&lt;br /&gt;
			// the controller allways writes 64 byte blocks to flash, so this format &lt;br /&gt;
			// helps to keep the controller program straight. &lt;br /&gt;
			while (blockNr&amp;lt;blocks)&lt;br /&gt;
			{&lt;br /&gt;
				blockNr++;&lt;br /&gt;
				block[0]=blockNr;	// first data byte in block is block number (so controller sees, when a block is lost) &lt;br /&gt;
				padded=0; &lt;br /&gt;
				for(i=0;i&amp;lt;64;i++)&lt;br /&gt;
				{&lt;br /&gt;
					if(adr&amp;gt;maxAdr)	 // it beyond highest adresses from hex file:  last block is(must be) padded to 64 bytes &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=padit[padded];&lt;br /&gt;
						padded++;&lt;br /&gt;
					}&lt;br /&gt;
					else	// write programm data &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=data[adr++];					&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				str +=makeSysEx(block,65,str);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			// write output file &lt;br /&gt;
			wp=fopen(argv[1],&amp;quot;wb&amp;quot;);&lt;br /&gt;
			if(wp)&lt;br /&gt;
			{&lt;br /&gt;
				fwrite ((void *) stp ,sizeof(unsigned char), str-stp, wp);&lt;br /&gt;
				fclose(wp);&lt;br /&gt;
				&lt;br /&gt;
				printf(&amp;quot;%d sysEx data blocks in %d bytes written\n&amp;quot;,blocks, str-stp);&lt;br /&gt;
			}&lt;br /&gt;
			else &lt;br /&gt;
				printf(&amp;quot;Cant open output file %s\n&amp;quot;,argv[1]);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			if(minAdr)&lt;br /&gt;
				printf(&amp;quot;Lowest adress %d is not zero!\n&amp;quot;,minAdr);&lt;br /&gt;
			return -2;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
		printf (&amp;quot;Input file %s not found\n&amp;quot;,argv[1]);&lt;br /&gt;
&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7325</id>
		<title>MCV876 Controller Update: File Converter</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7325"/>
		<updated>2011-06-27T20:20:05Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Benutzen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Worum geht&#039;s? = &lt;br /&gt;
Der File Konverter ist ein kleines Programm, dass vom Compiler/Linker erzeugte Binär-Datei (im [http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format])) in ein Datenformat wandelt, dass per Midi übertragen werden kann und das der Controller so versteht, dass er sich selber updaten kann. &lt;br /&gt;
&lt;br /&gt;
== Benutzen == &lt;br /&gt;
Der Linker der Entwicklungsumgebung erzeugt ein .hex File. Dieses File zieht man einfach auf das Icon der [http://www.kanowski.de/nordcore/sys876.exe sys876.exe]. Kurz darauf taucht im gleichen Verzeichnis wie die .hex Datei eine .syx Datei sonst gleichen Namens auf. Das war es schon - diese Datei ist das Sys-Ex Update File, was man per Midi an den Controller schickt, worauf er dann sofort mit diesem neuen Programm los rennt.&lt;br /&gt;
&lt;br /&gt;
== Datenformat == &lt;br /&gt;
Die Programm-Daten werden in 64-Byte Nutzdatenhäppchen eingepackt. Diese Pakete sind Brutto 84 Bytes lang. &lt;br /&gt;
Die Daten müssen für Midi umkodiert werden, da Midi nur 7 Bit Nutz-Daten übertragen kann. Dazu werden einfach immer die nächsten 7 bit aus dem 8-Bit breiten Datenstrom entnommen. Das gibt völlig undurchsichtige Werte, geht erst nach 7x8=56 Bytes auf und ist ansonsten recht einfacher Code, der kompakte Daten erzeugt. Um Datenübertragungsfehler zu erkennen, bekommt jedes Paket eine 16 Bit CRC als Prüfsumme. Und damit es als Midi durchgeht, kommen noch ein paar Bytes (&amp;quot;hallo hier kommt ein SysEx&amp;quot;) davor und ein Ende Byte dahinter. &lt;br /&gt;
&lt;br /&gt;
Die 64 Byte sind eine für Midi, Sys-Ex und das Controller-RAM noch verträgliche Datensatzlänge. Und der Controller schreibt seinen Programm-Speicher in genau solchen 64 Byte Blöcken. &lt;br /&gt;
&lt;br /&gt;
Vor die eigentlich Progamm-Daten werden noch zwei Pakete vorangestellt: &lt;br /&gt;
&lt;br /&gt;
Das erste schaltet einen in normalem Midi-Betrieb laufenden Controller auf den Boot-Lader um. Nach dem eigentlichen, kurzen Kommando, das den Normalbetrieb beendet, folgen 70 Nullen, die dem Controller mindestens 22ms Luft verschaffen, um auf den Bootlader zu wechseln. (Ist der Controller bereits im Bootlader wird dieses Paket nebst den Nullen schlichtweg überlesen/ignoriert. ) &lt;br /&gt;
&lt;br /&gt;
Das zweite Paket enthält nur die Info, dass es a)das erste Update-Pakte ist (d.h. es setzt den Empfänger auf &amp;quot;jetzt geht es los&amp;quot; zurück), und b)die Zahl der folgenden Blöcke (damit Vollständigkeit geprüft werden kann). &lt;br /&gt;
&lt;br /&gt;
Dann kommen die eigentlichen Programmdaten.&lt;br /&gt;
&lt;br /&gt;
= Quelltext =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
	Programm zum Erzeugen von Midi-Sys-Ex Software Update Files aus einer HEX-Datei &lt;br /&gt;
	für das MCV-876 Midi Interface mit ATMEL ATMEGA 88 Controller. &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*	&lt;br /&gt;
	Aufruf: &lt;br /&gt;
	sys676 avr-studio-compiler-out.hex &lt;br /&gt;
	&lt;br /&gt;
	das erzeugt dann eine Datei &lt;br /&gt;
	sys676 avr-studio-comipler-out.syx&lt;br /&gt;
	&lt;br /&gt;
	die mit Midi-OX (oder einen andern &amp;quot;ich kann Midi senden&amp;quot; Programm) an das &lt;br /&gt;
	Interface übertragen wird. &lt;br /&gt;
	&lt;br /&gt;
	Unter Windows kann man das übrigens bequem per &amp;quot;Drag and Drop&amp;quot; bedienen, einfach die &lt;br /&gt;
	.hex Datei auf die sys876.exe draufziehen, das Ergebnis taucht dann im gleichen Order wie &lt;br /&gt;
	die .hex Datei auf. (Man sieht halt keine Fehlermeldungen... ) &lt;br /&gt;
&lt;br /&gt;
	Erfolgreich übersetzt mit Visual Studio .net 2003 auf Windows XP &lt;br /&gt;
	Leeres Konsolenprojekt Namens syx876 erzeugen, diesen Quelltext in das .cpp &lt;br /&gt;
	File kopieren und bauen lassen. Ggfls. auf &amp;quot;Release&amp;quot; umstellen, im Release Ordner &lt;br /&gt;
	findet sich dann die fertige Applikation. &lt;br /&gt;
	&lt;br /&gt;
	Sollte sich nach marginalen Änderungen allerdings mit jedem Compiler übersetzen lassen. &lt;br /&gt;
	&lt;br /&gt;
*/&lt;br /&gt;
// für Windows =======================&lt;br /&gt;
#include &amp;quot;stdafx.h&amp;quot;&lt;br /&gt;
#define main _tmain &lt;br /&gt;
&lt;br /&gt;
// für normale Compiler =============&lt;br /&gt;
/*&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
*/&lt;br /&gt;
//===================================&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define VERSION &amp;quot;1.00&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DATA 8192				// netto data size (ATMEGA 88 Flash Rom size) &lt;br /&gt;
#define MAX_SYX_DATA (2*MAX_DATA)	// max. complete syx-ex size&lt;br /&gt;
#define MAX_SYX_BLOCK (2*64)		// one Sys-Ex Block of 64 bytes netto data &lt;br /&gt;
&lt;br /&gt;
unsigned char data[MAX_DATA];	// holds binary program data &lt;br /&gt;
&lt;br /&gt;
// Convert one HEX-ASCII character to its numerical value, or -1 if illegal value &lt;br /&gt;
int conf( char c)&lt;br /&gt;
{&lt;br /&gt;
	switch (c)&lt;br /&gt;
	{&lt;br /&gt;
		case &#039;0&#039;: return 0;&lt;br /&gt;
		case &#039;1&#039;: return 1;&lt;br /&gt;
		case &#039;2&#039;: return 2;&lt;br /&gt;
		case &#039;3&#039;: return 3;&lt;br /&gt;
		case &#039;4&#039;: return 4;&lt;br /&gt;
		case &#039;5&#039;: return 5;&lt;br /&gt;
		case &#039;6&#039;: return 6;&lt;br /&gt;
		case &#039;7&#039;: return 7;&lt;br /&gt;
		case &#039;8&#039;: return 8;&lt;br /&gt;
		case &#039;9&#039;: return 9;&lt;br /&gt;
		case &#039;a&#039;: case &#039;A&#039;: return 10;&lt;br /&gt;
		case &#039;b&#039;: case &#039;B&#039;: return 11;&lt;br /&gt;
		case &#039;c&#039;: case &#039;C&#039;: return 12;&lt;br /&gt;
		case &#039;d&#039;: case &#039;D&#039;: return 13;&lt;br /&gt;
		case &#039;e&#039;: case &#039;E&#039;: return 14;&lt;br /&gt;
		case &#039;f&#039;: case &#039;F&#039;: return 15;&lt;br /&gt;
	}&lt;br /&gt;
	return (-1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate 16 bit crc from previous crc and new data byte, initial (first) crc should be 0xffff, returns new crc &lt;br /&gt;
unsigned int crc16(unsigned int crc, unsigned char byte)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	crc^=byte;&lt;br /&gt;
	for(i=0;i&amp;lt;8;++i)&lt;br /&gt;
	{&lt;br /&gt;
	    if (crc&amp;amp;1)&lt;br /&gt;
			crc=(crc&amp;gt;&amp;gt;1)^0xA001;&lt;br /&gt;
		else&lt;br /&gt;
			crc&amp;gt;&amp;gt;=1;&lt;br /&gt;
	}&lt;br /&gt;
	return crc &amp;amp; 0xffff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// generates first Sys-EX Block, this switches the interface from operation mode to boot-load mode&lt;br /&gt;
int makePrgPrep(unsigned char *str)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	&lt;br /&gt;
	unsigned char a[] = { 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7 }; // the switch command string (see makeSysEx for byte value explanation) &lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;sizeof(a);i++)	&lt;br /&gt;
		*str++=a[i];&lt;br /&gt;
	for(;i&amp;lt;70;i++)	// 70 bytes * 0,32ms for midi data transmission gives the interface the time to start the bootloader &lt;br /&gt;
	{&lt;br /&gt;
		*str++=0;&lt;br /&gt;
	}&lt;br /&gt;
	return i; &lt;br /&gt;
}&lt;br /&gt;
// make a sys-ex data block with proper midi framing and crc from binary data in &amp;quot;block&amp;quot; &lt;br /&gt;
int makeSysEx(unsigned char *block, int len, unsigned char *syxTarget)&lt;br /&gt;
{&lt;br /&gt;
	int crc=0xffff;&lt;br /&gt;
	unsigned int out=0;&lt;br /&gt;
	int bits=0;&lt;br /&gt;
	int i;&lt;br /&gt;
	unsigned char c;  &lt;br /&gt;
	&lt;br /&gt;
	unsigned char *syxStr=syxTarget;&lt;br /&gt;
&lt;br /&gt;
	// make CRC &lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];&lt;br /&gt;
		crc=crc16(crc, c);&lt;br /&gt;
	}&lt;br /&gt;
	block[len++]= ( crc)&amp;amp;0xff;&lt;br /&gt;
	block[len++]= (crc &amp;gt;&amp;gt;8 ) &amp;amp;0xff;&lt;br /&gt;
	&lt;br /&gt;
	*syxStr++ = (0xF0);	// start sys ex &lt;br /&gt;
	*syxStr++ = (0x70);	// manufacturer id &lt;br /&gt;
	*syxStr++ = (0x7D);	// unit type  &lt;br /&gt;
	*syxStr++ = (0x00); // unit id &lt;br /&gt;
	*syxStr++ = (0x15);	// for normal sysEx it is a adress, here magic byte 1 &lt;br /&gt;
	*syxStr++ = (0x55); // magic byte 2 &lt;br /&gt;
		&lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)	&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];	// data byte 	&lt;br /&gt;
		/*&lt;br /&gt;
			Midi data is 7 bit only, as MSB is command/data bit. &lt;br /&gt;
			So data must be converted to 7 bit. &lt;br /&gt;
			Each source data byte gives 8/7 target bytes, &lt;br /&gt;
			as soon as there is one complete byte more (which happens every 7 input bytes)&lt;br /&gt;
			two target bytes will be generated.&lt;br /&gt;
		*/&lt;br /&gt;
		out&amp;lt;&amp;lt;=8;	&lt;br /&gt;
		out|=c;&lt;br /&gt;
		bits+=8;&lt;br /&gt;
		while(bits&amp;gt;=7)&lt;br /&gt;
		{&lt;br /&gt;
			*syxStr++ = ((out&amp;gt;&amp;gt;(bits-7)) &amp;amp; 0x7f);&lt;br /&gt;
			bits-=7;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if(bits)	// don&#039;t forget about the last bits of the last data byte &lt;br /&gt;
	{&lt;br /&gt;
		out&amp;lt;&amp;lt;= 7-bits;  // this will pad last byte with up to 6 zero bits. &lt;br /&gt;
		*syxStr++ =  (out &amp;amp; 0x7f);&lt;br /&gt;
	}&lt;br /&gt;
	*syxStr++ = 0xF7;	// Sys-Ex end &lt;br /&gt;
&lt;br /&gt;
	return (int) (syxStr - syxTarget); // lenght of generated data &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// argv[1] is filename to be converted &lt;br /&gt;
int main(int argc, _TCHAR* argv[])&lt;br /&gt;
{&lt;br /&gt;
	FILE *fp; &lt;br /&gt;
	int minAdr=0xffff;&lt;br /&gt;
	int maxAdr=0;&lt;br /&gt;
	int chars=0;&lt;br /&gt;
	int error=0;&lt;br /&gt;
	int c; &lt;br /&gt;
	int val;&lt;br /&gt;
	int field=-1;&lt;br /&gt;
	int adress=0;&lt;br /&gt;
	int cnt; &lt;br /&gt;
	int sum=0; &lt;br /&gt;
	int field_len=0; &lt;br /&gt;
	int type=99;&lt;br /&gt;
	int line=0;&lt;br /&gt;
&lt;br /&gt;
	// the controller allways flashes 64 byte blocks, so the last block is padded with arbitrary data &lt;br /&gt;
	int padit[]= {110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
              110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
			  110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43 } ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	printf(&amp;quot;syx876 Version &amp;quot; VERSION &amp;quot;\nHex-File to Sys-Ex Converter for ATMega88 on MCV876\n&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
	// &#039;check&#039; for proper input filename (it must have a &amp;quot;three byte extension&amp;quot; )&lt;br /&gt;
	//  *not* fool proof, try &amp;quot;/../a&amp;quot; and you&#039;ll get interesting results&lt;br /&gt;
	size_t l=strlen(argv[1]);&lt;br /&gt;
&lt;br /&gt;
	if(argc !=2 || l&amp;lt;5 || *(argv[1]+l-4)!=&#039;.&#039; )&lt;br /&gt;
	{&lt;br /&gt;
		printf(&amp;quot;\nUsage: syx876 filename.hex\n&amp;quot;);&lt;br /&gt;
		return -1;&lt;br /&gt;
	}&lt;br /&gt;
	l -= 3; &lt;br /&gt;
	&lt;br /&gt;
	fp=fopen (argv[1],&amp;quot;rb&amp;quot;);	// open input files &lt;br /&gt;
	&lt;br /&gt;
	if(fp)&lt;br /&gt;
	{&lt;br /&gt;
		// make output file name &lt;br /&gt;
		*(argv[1]+l)=0;	&lt;br /&gt;
		strcat (argv[1],&amp;quot;syx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		while( (c=fgetc(fp)) != EOF  ) // read input hex file &lt;br /&gt;
		{&lt;br /&gt;
			chars++;&lt;br /&gt;
			 &lt;br /&gt;
			if(c==&#039;:&#039;)&lt;br /&gt;
			{&lt;br /&gt;
				cnt=0;&lt;br /&gt;
				field=0;&lt;br /&gt;
				sum=0;&lt;br /&gt;
				line++;&lt;br /&gt;
			}&lt;br /&gt;
			else if ( conf( (unsigned char) c ) &amp;gt;=0 )&lt;br /&gt;
			{&lt;br /&gt;
				if(cnt==0)&lt;br /&gt;
					val=conf( (unsigned char) c );&lt;br /&gt;
				else &lt;br /&gt;
				{&lt;br /&gt;
					val&amp;lt;&amp;lt;=4;&lt;br /&gt;
					val += conf( (unsigned char) c );&lt;br /&gt;
				}&lt;br /&gt;
				cnt++;&lt;br /&gt;
				&lt;br /&gt;
				if(cnt==2)	// add up checksum&lt;br /&gt;
					sum+= val;&lt;br /&gt;
				if( cnt==4)&lt;br /&gt;
					sum += val &amp;amp; 0xff;&lt;br /&gt;
				&lt;br /&gt;
				if(field ==0 &amp;amp;&amp;amp; cnt==2)&lt;br /&gt;
				{	// byte count &lt;br /&gt;
					field_len=val; &lt;br /&gt;
					field =1;&lt;br /&gt;
					cnt=0;				&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==1 &amp;amp;&amp;amp; cnt == 4)&lt;br /&gt;
				{	// adress&lt;br /&gt;
					adress=val;&lt;br /&gt;
					field=2;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==2 &amp;amp;&amp;amp; cnt == 2)&lt;br /&gt;
				{	// type &lt;br /&gt;
					type=val;&lt;br /&gt;
					field=4;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len&amp;gt;0)&lt;br /&gt;
				{	// data &lt;br /&gt;
					field_len--;&lt;br /&gt;
					&lt;br /&gt;
					if(adress &amp;lt; minAdr)&lt;br /&gt;
						minAdr=adress;&lt;br /&gt;
					if(adress&amp;gt;maxAdr)&lt;br /&gt;
						maxAdr=adress;&lt;br /&gt;
&lt;br /&gt;
					if(type==0 &amp;amp;&amp;amp; adress &amp;lt; MAX_DATA)&lt;br /&gt;
						data[adress]=val;&lt;br /&gt;
					adress++;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len == 0)&lt;br /&gt;
				{	// checksum &lt;br /&gt;
					cnt=0;&lt;br /&gt;
					sum &amp;amp;=0xff;&lt;br /&gt;
					if(sum)&lt;br /&gt;
					{&lt;br /&gt;
						error++;&lt;br /&gt;
						printf(&amp;quot;Line %d Checksum error (sum=%d)\n&amp;quot;,line, sum); &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else if( c !=10 &amp;amp;&amp;amp; c !=13)&lt;br /&gt;
			{&lt;br /&gt;
				error ++;&lt;br /&gt;
				printf( &amp;quot;Line %d unexpected character (%c)\n&amp;quot;,line, c); // characters I don&#039;t know  &lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		printf( &amp;quot;\nread %d charactes, from adress %d to %d with %d errors\n&amp;quot;, chars, minAdr, maxAdr,error); &lt;br /&gt;
		&lt;br /&gt;
		// generate output data &lt;br /&gt;
		if(error==0 &amp;amp;&amp;amp; minAdr==0) &lt;br /&gt;
		{&lt;br /&gt;
			int blocks; &lt;br /&gt;
			int blockNr; &lt;br /&gt;
			unsigned char block[MAX_SYX_BLOCK];	// buffer to collect/prepare one binary data block &lt;br /&gt;
			unsigned char stp[MAX_SYX_DATA]; 	//  output data buffer, collects the sysEx data&lt;br /&gt;
			unsigned char *str;		// current write pointer &lt;br /&gt;
			int adr; &lt;br /&gt;
			int padded;&lt;br /&gt;
			FILE *wp;&lt;br /&gt;
			int i;&lt;br /&gt;
			&lt;br /&gt;
			str=stp; &lt;br /&gt;
			&lt;br /&gt;
			blocks= (((maxAdr)/64)+1);	// total number of blocks &lt;br /&gt;
						&lt;br /&gt;
			str+= makePrgPrep(str);		// generate sys-ex header (=switch to bootloader command) &lt;br /&gt;
			&lt;br /&gt;
			// first block is a spezial block - it tells controller number of blocks he has to expect. &lt;br /&gt;
			blockNr=0;&lt;br /&gt;
			block[0]=blockNr;&lt;br /&gt;
			block[1]=blocks;&lt;br /&gt;
			block[2]=0;	// reserved &lt;br /&gt;
			block[3]=0;	// reserved &lt;br /&gt;
			str += makeSysEx(block,4,str);	// generate SysEx from block data &lt;br /&gt;
			&lt;br /&gt;
			adr=minAdr;&lt;br /&gt;
			// make flash data blocks &lt;br /&gt;
			// flash data blocks are 65 bytes, 1 Byte Block number, 64 data bytes&lt;br /&gt;
			// the controller allways writes 64 byte blocks to flash, so this format &lt;br /&gt;
			// helps to keep the controller program straight. &lt;br /&gt;
			while (blockNr&amp;lt;blocks)&lt;br /&gt;
			{&lt;br /&gt;
				blockNr++;&lt;br /&gt;
				block[0]=blockNr;	// first data byte in block is block number (so controller sees, when a block is lost) &lt;br /&gt;
				padded=0; &lt;br /&gt;
				for(i=0;i&amp;lt;64;i++)&lt;br /&gt;
				{&lt;br /&gt;
					if(adr&amp;gt;maxAdr)	 // it beyond highest adresses from hex file:  last block is(must be) padded to 64 bytes &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=padit[padded];&lt;br /&gt;
						padded++;&lt;br /&gt;
					}&lt;br /&gt;
					else	// write programm data &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=data[adr++];					&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				str +=makeSysEx(block,65,str);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			// write output file &lt;br /&gt;
			wp=fopen(argv[1],&amp;quot;wb&amp;quot;);&lt;br /&gt;
			if(wp)&lt;br /&gt;
			{&lt;br /&gt;
				fwrite ((void *) stp ,sizeof(unsigned char), str-stp, wp);&lt;br /&gt;
				fclose(wp);&lt;br /&gt;
				&lt;br /&gt;
				printf(&amp;quot;%d sysEx data blocks in %d bytes written\n&amp;quot;,blocks, str-stp);&lt;br /&gt;
			}&lt;br /&gt;
			else &lt;br /&gt;
				printf(&amp;quot;Cant open output file %s\n&amp;quot;,argv[1]);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			if(minAdr)&lt;br /&gt;
				printf(&amp;quot;Lowest adress %d is not zero!\n&amp;quot;,minAdr);&lt;br /&gt;
			return -2;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
		printf (&amp;quot;Input file %s not found\n&amp;quot;,argv[1]);&lt;br /&gt;
&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7324</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7324"/>
		<updated>2011-06-27T20:16:07Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Software */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Tonhöhe (Noten) &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;|| Gate 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen- und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V.&lt;br /&gt;
&lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie zweimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
Sollte sie vier mal blinken, hat der Controller die permanent gespeicherte Konfiguration nicht geladen und nutzt statt dessen (wieder) die Standardeinstellung.&lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu einzustellen: Die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Controller für CV3 einstellen ===&lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang(sic!) ge-gated. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig. (Zur Bauanleitung hier lang: [[MCV876_Controller_Update:_Hardware_bauen]].) &lt;br /&gt;
&lt;br /&gt;
Der neue Controller wird ansonsten einfach nur in den Sockel des Alten gesteckt. Alten Controller rausziehen, Atmel mit Adapter reinstecken, fertig. &lt;br /&gt;
&lt;br /&gt;
[[File:AdapterEingebaut.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So sieht der &amp;quot;neue&amp;quot; in einem Semtex aus&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier: &#039;&#039;&#039;[[MCV876_Controller_Update:_Source_Code]]&#039;&#039;&#039;. Da steht dann auch, wie das mit der Entwicklungsumgebung geht. &lt;br /&gt;
Das ist der spannende Teil, nämlich das eigentliche Betriebssystem. Um den Teil zu ändern - sprich: seinen Wünschen anzupassen - und das neue Programm in den Controller zu bekommen, braucht man a)nichts kaufen, b)keine Hardware und kann das c)einfach per Midi an den Controller schicken (das Geräte kann also zu bleiben). (Der doofe Teil ist, dass ich nur beschreiben kann, wie das unter Windows geht. Der Atmel-Compiler sollte sich, da GCC, allerdings auch auf anderen Systemen einfinden, den Konverter kann man problemlos auf jedem System übersetzen ... )&lt;br /&gt;
&lt;br /&gt;
Aus der Entwicklungsumgebung kommt eine Binärdatei, die man mit einem kleinen Programm in ein Midi-Sys-Ex Format konvertieren muss. Diese Sys-Ex Datei überträgt man dann mit einem Tool der Wahl (unter Windows z.B. [http://www.midiox.com/ Midi-OX], aber jedes normale DAW-Programm sollte das können) per Midi auf den Controller. Der verfrachtet das flugs (bei aktueller Programmlänge 1,4 Sekunden) in seinen Flash-Programmspeicher. &lt;br /&gt;
Dieser Datei-Konverter findet sich hier: [[MCV876_Controller_Update:_File_Converter]].&lt;br /&gt;
&lt;br /&gt;
Der Bootlader des Controllers, der sich auf Controller-Seite um das Update kümmert, ist hier: [[MCV876_Controller_Update:_Bootloader]]. Der wird einmalig in den Controller &amp;quot;gebrannt&amp;quot;, und dient dazu, die Software jederzeit updaten zu können, auch wenn man in der eigentlichen Controller-Software gerade mal völligen Murks gemacht hat. Den Code braucht man normalerweise nicht anfassen. (Typischerweise ändert sich der alle paar Jahre mal, es ist aber auch nicht weiter schlimm, wenn man nicht die neueste Version hat... )&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7323</id>
		<title>MCV876 Controller Update: Source Code</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7323"/>
		<updated>2011-06-27T20:03:01Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Übersetzen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Übersetzen= &lt;br /&gt;
Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden.&lt;br /&gt;
Im Studio ein neues, leeres Projekt mit Standard-Einstellungen, einem sinnvollen Namen und an einem Ort, wo man es wiederfindet erstellen.&lt;br /&gt;
In den Projekteinstellungen die passenden CPU wählen und die Taktfrequenz einstellen. Project-&amp;gt;Configuration-Options:&lt;br /&gt;
&lt;br /&gt;
[[File:ProjectSettings.gif]]&lt;br /&gt;
&lt;br /&gt;
Dann Quelltext in das (automatisch erzeugte, leere) C-File kopieren, auf Build-&amp;gt;Build klicken, fertig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das resultierende HEX File kann man mit einem üblichen Programmieradapter in der Chip flashen, für uns zweckmäßiger ist allerdings, das in ein Sys-Ex File zu Konvertieren ( [[MCV876_Controller_Update:_File_Converter]]), und das über Midi an den Controller zu schicken. Dann programmiert der sich selber um. Dazu muss er allerdings einmalig mit dem Bootlader ([[MCV876_Controller_Update:_Bootloader]]) programmiert worden sein. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu den Atmel Controllern findet man eine Menge Info mit Tante Google, [http://www.mikrocontroller.net http://www.mikrocontroller.net] kommt häufig vor, und hat ein ergiebiges Forum und gute Artikel. Auf der Atmel Webseite gibt es auch [http://www.atmel.com/dyn/products/product_card.asp?part_id=3302&amp;amp;category_id=163&amp;amp;family_id=607&amp;amp;subfamily_id=760 einige Info], das [http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf ATMega 88 Datenblatt] ist spätestens dann nötig, wenn es um die Hardware geht.&lt;br /&gt;
&lt;br /&gt;
=Quelltext=&lt;br /&gt;
&amp;lt;pre&amp;gt;/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Midi to 4 Channel Control Voltage and six Gates Interface &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.22 2011-06-26 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
			&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;			// yes, we want to talk to the port pins by name&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;		// ... oh.. and some eeprom access would be nice&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;	// ... and w/o the interupts it wouldn&#039;t do anything... &lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*	===================ZERO LINE=================================&lt;br /&gt;
	WARNING: everthing below the zero line counts from zero! &lt;br /&gt;
	e.g. whats Gate 6 in manual is GATE5 here, CVs are CV0 to 3 &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	where should the LED output be &lt;br /&gt;
*/&lt;br /&gt;
#define LEDLED   6  // LED at Reset-Pin, standard for series, but no debugging possible. &lt;br /&gt;
#define LEDGATE0 0	// Led at Gate 0, no LED (Test I: no LED ) &lt;br /&gt;
#define LEDGATE4 4	// LED at Gate 5, Test II: no Gate 5 Function (Midi Start/Stop) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 #define LEDOUT LEDLED // select your evil... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define ACTIVE_SENSE_TICKS 160 // 2ms Timer Ticks for active sense time out &lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4))) // True if Button pressed &lt;br /&gt;
&lt;br /&gt;
#define NUM_CV 4&lt;br /&gt;
&lt;br /&gt;
#define PARAMETEROFFSET 0x23 // First paramter has adress 0x23&lt;br /&gt;
#define NUM_PARAMTERS 17	 // and there are 17 parameters &lt;br /&gt;
&lt;br /&gt;
#define MODE_MONO 	0x00	// monophon, all assigned to last note on a single channel &lt;br /&gt;
#define MODE_MULTI2	0x40	// two channel, 1/2 for midiChannel, 2/3 for midiChannel+1 &lt;br /&gt;
#define MODE_MULTI4	0x60	// for channel midiChannel to midiChannel+3 &lt;br /&gt;
#define MODE_POLY2	0x80	// not supported &lt;br /&gt;
#define MODE_POLY4	0xA0	// not supported&lt;br /&gt;
&lt;br /&gt;
struct CONFIG {&lt;br /&gt;
	unsigned char midiChannel;&lt;br /&gt;
	unsigned char noteOffset;&lt;br /&gt;
&lt;br /&gt;
	unsigned char cvMidiCC[NUM_CV];&lt;br /&gt;
	unsigned char gateMidiCC[NUM_CV];&lt;br /&gt;
	&lt;br /&gt;
	unsigned char gateTreshhold;&lt;br /&gt;
	unsigned char clockDivider;&lt;br /&gt;
	unsigned char clockTime; &lt;br /&gt;
	unsigned char ifMode0; 	// 	bit 0 ... bit 3 = CV0 .. CV3, 0=Pitch, 1=CC (if pitch, CC assignement is ignored) &lt;br /&gt;
							//	bit 5: voices: 0=2; 1=4 &lt;br /&gt;
							//  bit 7:6= 00 Mono; 01:Multi-Channel; 10: Poly (not supported); &lt;br /&gt;
	&lt;br /&gt;
	unsigned char ifMode1;  // Gate Trigger Mode Bits (0=Re-Trigger, 1=TriggerControl)  &lt;br /&gt;
	unsigned char ifMode2;  // bit0: Velo0=&amp;gt;CV0; bit1:Velo0=&amp;gt;CV1; bit2:Velo-Poly2=&amp;gt;CV2; bit3:Velo1=&amp;gt;CV3&lt;br /&gt;
							// bit4: PB=&amp;gt;CV0; bit5:PB=&amp;gt;CV1; bit6:PB=&amp;gt;CV2; bit7:PB=&amp;gt;CV3&lt;br /&gt;
	unsigned char ifMode3; 	// bit0..bit3 = CV0... CV3; 0=10V, 1=5V Mode &lt;br /&gt;
&lt;br /&gt;
//  internal evaluated configuration &lt;br /&gt;
	unsigned char numMidiChannels;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
} shadow_config, // her goes a copy, when config should be written, because that might take a while &lt;br /&gt;
&lt;br /&gt;
config 	// current configuration, mostly direct evaluated troughout the program&lt;br /&gt;
= {		// default values (Mono-Mode, Midi-Ch 1, CV1=Pitch, CV2=Velocity CV3=ModWheel CV4=Pitch Bend&lt;br /&gt;
			0,  	// Midi Channel &lt;br /&gt;
			24, 	// Note Offset (2 Oktaves) &lt;br /&gt;
		{	255,255,1,255, },  	// CV - assigned Midi Controllers (used if not pitch output) 255=none&lt;br /&gt;
		{   255,18,19,20, }, 	// Gate - asigned Controllers (if not pitch) 255=none &lt;br /&gt;
			63, 	// Treshold&lt;br /&gt;
			6,  	// Divider 6 (1/16 Note) &lt;br /&gt;
			1,  	// clock time (unused) &lt;br /&gt;
			0x0e, 	// mode 0 Low nibble CC-Bits, high: interface mode &lt;br /&gt;
			0, 		// mode 1 re-trigger bits &lt;br /&gt;
			0x82, 	// mode 2 High Nibble: PB-bits, Low: Velo-Bits&lt;br /&gt;
			0,    	// mode 3 (5V-Bits) &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define EE_MAGIC 17&lt;br /&gt;
unsigned char ee_magic EEMEM;	// to check it eeprom was ever written and should be read back &lt;br /&gt;
unsigned char config_eeprom[NUM_PARAMTERS] EEMEM; // Config in EEPROM, saves it during power down. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
volatile unsigned char  eepromWriteFlag;	// set to one, if main-loop should write eeprom &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char button; 	// debounced button status &lt;br /&gt;
volatile unsigned char learn; 	// (channel/base note/cc) &amp;quot;do learn&amp;quot; status &lt;br /&gt;
volatile unsigned char blinker; // some count for blinkenlights &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char gate5off; // timer counter for when clock gate output should go low again. &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char watchTicker;	// midi active sense counter &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setConfig(void); 		&lt;br /&gt;
void setLed(unsigned char on);&lt;br /&gt;
void resetAll(void);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	main() &lt;br /&gt;
	Main does initialization, than runs a loop, which does nearly nothing: &lt;br /&gt;
&lt;br /&gt;
	Programming the EEPROM (because its slow) &lt;br /&gt;
	Blinking &lt;br /&gt;
	Button evaluation &lt;br /&gt;
&lt;br /&gt;
	All the important things are done by the interupts. (Thanks to the &lt;br /&gt;
	20MHZ RISC power everything can be done in less than 320µs=one Midi-Character) &lt;br /&gt;
*/&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char blink=4;&lt;br /&gt;
&lt;br /&gt;
	unsigned char i,x; &lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
	static unsigned char lastButton; &lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
	// Read Configuration &lt;br /&gt;
	_delay_ms(10); // wait for button pin to get high if open. &lt;br /&gt;
	if( eeprom_read_byte(&amp;amp;ee_magic) == EE_MAGIC  &amp;amp;&amp;amp;   !BUTTON) // if eeprom valid AND Button not pressed &lt;br /&gt;
	{&lt;br /&gt;
		cp=(unsigned char* )&amp;amp;config; &lt;br /&gt;
		for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
		{&lt;br /&gt;
			x=eeprom_read_byte(config_eeprom+i);			// read config from eeprom &lt;br /&gt;
			*cp=x;	&lt;br /&gt;
			cp++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
		blink=8; &lt;br /&gt;
&lt;br /&gt;
	setConfig();&lt;br /&gt;
&lt;br /&gt;
	// enable interrupts&lt;br /&gt;
	sei(); &lt;br /&gt;
&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		if(eepromWriteFlag)&lt;br /&gt;
		{	// save configuration &lt;br /&gt;
			cp=(unsigned char* )&amp;amp;shadow_config; &lt;br /&gt;
			for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
			{&lt;br /&gt;
				x=*cp;&lt;br /&gt;
				eeprom_write_byte(config_eeprom+i,x);&lt;br /&gt;
				cp++;&lt;br /&gt;
			}&lt;br /&gt;
			eeprom_write_byte(&amp;amp;ee_magic,EE_MAGIC); &lt;br /&gt;
&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			blink=6;&lt;br /&gt;
			blinker=0;&lt;br /&gt;
			eepromWriteFlag=0;	&lt;br /&gt;
		}&lt;br /&gt;
		if(blink &amp;amp;&amp;amp; blinker&amp;gt;2)  // blink after power up and after storing in nv-memory &lt;br /&gt;
		{&lt;br /&gt;
			blink--;&lt;br /&gt;
			setLed(blink&amp;amp;1);&lt;br /&gt;
			blinker=0; &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if(button != lastButton)&lt;br /&gt;
		{&lt;br /&gt;
			if(button==1 )&lt;br /&gt;
			{&lt;br /&gt;
				resetAll();&lt;br /&gt;
				if(learn==1)&lt;br /&gt;
				{&lt;br /&gt;
					learn=0;&lt;br /&gt;
					setLed(0);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if(button==6) &lt;br /&gt;
			{&lt;br /&gt;
				if(learn ==0)&lt;br /&gt;
				{&lt;br /&gt;
					learn=1;&lt;br /&gt;
					setLed(1);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			lastButton=button; &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return 0; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Hardware Interface&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
/*  Pin assignment &lt;br /&gt;
   Atmel Fkt 	[Orig] &lt;br /&gt;
1  Reset LED 	[MCLR] &lt;br /&gt;
2  RX    Midi-Rx[WR1]&lt;br /&gt;
3  PD1   WR2&lt;br /&gt;
4  PD2   WR3&lt;br /&gt;
5  PD3   WR4&lt;br /&gt;
6  PD4   Button&lt;br /&gt;
7  VCC   +5  	[LED] (Either from Atmel-1(Series) or Atmel-15(w/Debug))&lt;br /&gt;
8  GND   GND&lt;br /&gt;
9  OSC   OSC&lt;br /&gt;
10 OSC   OSC &lt;br /&gt;
11 PD5   G1&lt;br /&gt;
12 PD6   G2&lt;br /&gt;
13 PD7   G3&lt;br /&gt;
14 PB0   G4&lt;br /&gt;
&lt;br /&gt;
15 PB1   G5 (Optional + LED) &lt;br /&gt;
16 PB2   G6 &lt;br /&gt;
17 PB3   WR1    [---] &lt;br /&gt;
18 PB4   D0     [RX]&lt;br /&gt;
19 PB5   D1		[GND]&lt;br /&gt;
20 AVCC  +5&lt;br /&gt;
21 ARef  +5 	[D0]&lt;br /&gt;
22 GND   GND	[D1] &lt;br /&gt;
23 PC0   D2&lt;br /&gt;
24 PC1   D3&lt;br /&gt;
25 PC2   D4&lt;br /&gt;
26 PC3   D5&lt;br /&gt;
27 PC4   D6&lt;br /&gt;
28 PC5   D7&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
// put &amp;quot;val&amp;quot; to the right port bits for the DA-Converter &lt;br /&gt;
// ... better program than solder *that*... &lt;br /&gt;
void adByteOut(unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char b;&lt;br /&gt;
	b=val;&lt;br /&gt;
	b&amp;gt;&amp;gt;=2;&lt;br /&gt;
	PORTC=b;&lt;br /&gt;
	b=val&amp;amp;3;&lt;br /&gt;
	b&amp;lt;&amp;lt;=4;&lt;br /&gt;
	val=PORTB;&lt;br /&gt;
	val &amp;amp;=0xCF;&lt;br /&gt;
	val|=b;&lt;br /&gt;
	PORTB=val;&lt;br /&gt;
}&lt;br /&gt;
// set DA-Output voltage of one CV Channel  &lt;br /&gt;
void set_cv(unsigned char val, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	adByteOut(val); 	// set D0..D7 converter data bits &lt;br /&gt;
	switch (channel)	// pulse with write pin &lt;br /&gt;
	{&lt;br /&gt;
		case 0: //PB3&lt;br /&gt;
			PORTB  &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTB  |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD1&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;1);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;1);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD2&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;2);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;2);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PD3&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// set on of the gate outputs active &lt;br /&gt;
void gate_on(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// clear one of the gate outputs &lt;br /&gt;
void gate_off(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif&lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Data Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
typedef struct {&lt;br /&gt;
	unsigned char channel;  // (relative) Midi channel (0..3) Mode dependant &lt;br /&gt;
	unsigned char cvOut;	// for which CV-Ouput (0..3) fixed &lt;br /&gt;
	unsigned char ccVal;	// current CC Value &lt;br /&gt;
	unsigned char note;		// current Note &lt;br /&gt;
	unsigned char velo;		// current Velocity &lt;br /&gt;
	unsigned char on;		// if Note is on &lt;br /&gt;
	signed char   pb;		// current Pitch Bend Value &lt;br /&gt;
	unsigned char gateDipper;&lt;br /&gt;
} cvoutput;  &lt;br /&gt;
&lt;br /&gt;
cvoutput cv_out[NUM_CV];	// current state for each cv out&lt;br /&gt;
&lt;br /&gt;
unsigned char gates; // which gates are on =&amp;gt; used for LED setting &lt;br /&gt;
&lt;br /&gt;
// set cv output (and assigned gate, if pitch output)&lt;br /&gt;
// called after anything that changes the cv_out structure  &lt;br /&gt;
void updateCv(cvoutput *cvo)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char bm;&lt;br /&gt;
	unsigned char gated=0;&lt;br /&gt;
	int val=0;&lt;br /&gt;
&lt;br /&gt;
	bm=1&amp;lt;&amp;lt;cvo-&amp;gt;cvOut;	// bit mask for configuration flags &lt;br /&gt;
&lt;br /&gt;
	if(config.ifMode0 &amp;amp; bm)	// 1=CC-Mode&lt;br /&gt;
	{&lt;br /&gt;
		if(config.cvMidiCC[cvo-&amp;gt;cvOut]&amp;gt;127) // if no Controller assigned &lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		// if PB is active &lt;br /&gt;
				val=cvo-&amp;gt;pb+0x80;		 		//     pitch sets unipolar value &lt;br /&gt;
			else 						 		// else &lt;br /&gt;
				val=0; 					 		//    value is zero &lt;br /&gt;
		}&lt;br /&gt;
		else 								// else a controller is assigned &lt;br /&gt;
		{&lt;br /&gt;
			val=cvo-&amp;gt;ccVal; 					//  set current controler value ;&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		//  if PB active &lt;br /&gt;
				val+=cvo-&amp;gt;pb;			 		//     add it (bipolar) &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if( (config.ifMode1 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0) // if Trig Flag and note off &lt;br /&gt;
			gated=1; 								// than gate CV output (to zero) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
		if( (config.ifMode3 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0)	// 5V-Flag, abused as Gated-Mode-CV Flag &lt;br /&gt;
			gated=1;&lt;br /&gt;
*/&lt;br /&gt;
	}&lt;br /&gt;
	else // Note&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;note;&lt;br /&gt;
		val -= config.noteOffset;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode3 &amp;amp; bm)	// 5V&lt;br /&gt;
			val &amp;lt;&amp;lt;= 2;&lt;br /&gt;
		else&lt;br /&gt;
			val &amp;lt;&amp;lt;= 1;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) // PB &lt;br /&gt;
			val+=(cvo-&amp;gt;pb&amp;gt;&amp;gt;2);  // scale down pb - value is empirical and works &amp;quot;not too bad&amp;quot; &lt;br /&gt;
								//  (for &amp;quot;good&amp;quot; the converter should have 12bits a least... ) &lt;br /&gt;
	}&lt;br /&gt;
	if(config.ifMode2 &amp;amp; bm) // Velocity &lt;br /&gt;
		val+=cvo-&amp;gt;velo;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if(val&amp;lt;0 || gated)	// clip values to converter range &lt;br /&gt;
		val=0;&lt;br /&gt;
	if(val&amp;gt;255)&lt;br /&gt;
		val=255;&lt;br /&gt;
&lt;br /&gt;
	set_cv((unsigned char)val, cvo-&amp;gt;cvOut);&lt;br /&gt;
	if((config.ifMode0 &amp;amp; bm)==0)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;on)&lt;br /&gt;
		{&lt;br /&gt;
			if(cvo-&amp;gt;gateDipper==0) // no retrigger required&lt;br /&gt;
				gate_on(cvo-&amp;gt;cvOut);&lt;br /&gt;
			else				   // dip gate for a few milliseconds 	&lt;br /&gt;
				gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates |=bm;&lt;br /&gt;
		}&lt;br /&gt;
		else &lt;br /&gt;
		{&lt;br /&gt;
			gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates &amp;amp;=~bm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate/initialize control variables after &lt;br /&gt;
// mode-configuration change &lt;br /&gt;
void setConfig()&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i; &lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0; &lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	switch(config.ifMode0 &amp;amp; 0xf0 )&lt;br /&gt;
	{	&lt;br /&gt;
		default:&lt;br /&gt;
		case MODE_MONO:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=1;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI2:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV/2;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			for(;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=1;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=2;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI4:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=i;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=NUM_CV;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// assign new note state to cv outputs &lt;br /&gt;
void setNote(unsigned char note, unsigned char velo, unsigned char onOff, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;channel==channel)&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;note=note;&lt;br /&gt;
			cvo-&amp;gt;velo=velo;&lt;br /&gt;
			if(onOff==2 &amp;amp;&amp;amp; cvo-&amp;gt;on &amp;amp;&amp;amp; (config.ifMode1 &amp;amp; (1&amp;lt;&amp;lt;i)))&lt;br /&gt;
				cvo-&amp;gt;gateDipper=4;&lt;br /&gt;
			cvo-&amp;gt;on=onOff;&lt;br /&gt;
			updateCv(cvo);&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	setLed(gates);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process new continous controller value &lt;br /&gt;
void setCC(unsigned char cc, unsigned char value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel== cvo-&amp;gt;channel )&lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode0 &amp;amp;(1&amp;lt;&amp;lt;i))&lt;br /&gt;
			{&lt;br /&gt;
				if(config.cvMidiCC[i]==cc)&lt;br /&gt;
				{&lt;br /&gt;
					cvo-&amp;gt;ccVal=value; &lt;br /&gt;
					updateCv(cvo);&lt;br /&gt;
				}&lt;br /&gt;
				if(config.gateMidiCC[i]==cc  )&lt;br /&gt;
				{&lt;br /&gt;
					if(value&amp;gt;config.gateTreshhold)&lt;br /&gt;
						gate_on(i);&lt;br /&gt;
					else &lt;br /&gt;
						gate_off(i);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// process new pitch bend value &lt;br /&gt;
void setPb(signed int value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel==cvo-&amp;gt;channel &amp;amp;&amp;amp; (config.ifMode2 &amp;amp; (16&amp;lt;&amp;lt;i)) )&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;pb	=(signed char) (value&amp;gt;&amp;gt;5);	&lt;br /&gt;
			updateCv(cvo);	&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// prepare for (asynchronous) configuration write to eeprom &lt;br /&gt;
void setEeWrite(void)&lt;br /&gt;
{&lt;br /&gt;
	shadow_config=config; &lt;br /&gt;
	learn=0;&lt;br /&gt;
	eepromWriteFlag=1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*bootloader)(void) = (void*)0x1800;	// Bootloader sits at adress 0x1800 ... &lt;br /&gt;
&lt;br /&gt;
// process sys ex command &lt;br /&gt;
void setSyx(unsigned char adr, unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
&lt;br /&gt;
	cp=(unsigned char *) &amp;amp;config; &lt;br /&gt;
&lt;br /&gt;
	if(adr==0x14 &amp;amp;&amp;amp; val ==0x7b) &lt;br /&gt;
	{	// write eeprom&lt;br /&gt;
		if(eepromWriteFlag==0)&lt;br /&gt;
		{&lt;br /&gt;
			setEeWrite();&lt;br /&gt;
		}&lt;br /&gt;
		return; &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7&lt;br /&gt;
	if(adr==0x16 &amp;amp;&amp;amp; val ==0x42)	// Software update  &lt;br /&gt;
	{&lt;br /&gt;
		cli();			// interupts off &lt;br /&gt;
		resetAll();		// outputs off &lt;br /&gt;
		GPIOR1 = 0xAB;	// tell bootloader it&#039;s an application boot request &lt;br /&gt;
						// (hardware reset sets GPIOR1 to zero, 0xab == magic value for bootloader )&lt;br /&gt;
		bootloader();	// bye bye! &lt;br /&gt;
		return; 	// &amp;lt;-- never! &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	adr-=PARAMETEROFFSET; &lt;br /&gt;
	if(adr &amp;lt; NUM_PARAMTERS)	// if valid parameter &lt;br /&gt;
	{&lt;br /&gt;
		*(cp+adr)=val; 		// set in &lt;br /&gt;
		if(adr==0x30-PARAMETEROFFSET) // if Mode Byte 0, &lt;br /&gt;
			setConfig();			  // .. cv assignement must be recalculated &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Note Puffer Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define MAX_NOTES 0x10  	// one for each finger of a real programmer &lt;br /&gt;
#define MAX_CHANNELS 4&lt;br /&gt;
unsigned char notes[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char velocities[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char playedNote[MAX_CHANNELS]; // number of currently pressed notes &lt;br /&gt;
// update note puffer for a midi note-on message &lt;br /&gt;
void note_on(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char n;&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	if(n&amp;lt;MAX_NOTES)&lt;br /&gt;
	{&lt;br /&gt;
		playedNote[channel]++;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		for (n=MAX_NOTES-1;n&amp;gt;0;--n)&lt;br /&gt;
		{&lt;br /&gt;
			notes[channel][n-1]=notes[channel][n];&lt;br /&gt;
			velocities[channel][n-1]=velocities[channel][n];&lt;br /&gt;
		}&lt;br /&gt;
		n=MAX_NOTES-1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	notes[channel][n]=note; 		&lt;br /&gt;
	velocities[channel][n]=velocity;&lt;br /&gt;
	setNote(note, velocity, 2, channel);&lt;br /&gt;
}&lt;br /&gt;
// update note puffer for a new midi note-off message &lt;br /&gt;
void note_off(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	signed char n; &lt;br /&gt;
&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	--n; &lt;br /&gt;
	for (i=0;i&amp;lt;=n;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(notes[channel][i]==note)&lt;br /&gt;
		{&lt;br /&gt;
			if(i==n) // last pressed (== the on which playes) note released &lt;br /&gt;
			{&lt;br /&gt;
				if(n==0) // it was the only pressed note &lt;br /&gt;
				{&lt;br /&gt;
					setNote(note, velocities[channel][n], 0, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{	&lt;br /&gt;
					--n;&lt;br /&gt;
					setNote(notes[channel][n], velocities[channel][n], 1, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else // non playing note released &lt;br /&gt;
			{	// =&amp;gt;  just remove it from list &lt;br /&gt;
				int j;&lt;br /&gt;
				for(j=i+1;j&amp;lt;=n;++j)&lt;br /&gt;
				{&lt;br /&gt;
					notes[channel][j-1]=notes[channel][j];&lt;br /&gt;
					velocities[channel][j-1]=velocities[channel][j];&lt;br /&gt;
				}&lt;br /&gt;
				playedNote[channel]--;&lt;br /&gt;
			}&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	All Notes off, empty Note Buffer, All Controllers Reset&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void resetAll(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	cli(); // this might be called asynchronous to midi stream &lt;br /&gt;
	for(i=0;i&amp;lt;MAX_CHANNELS;++i)&lt;br /&gt;
		playedNote[i]=0;&lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0;&lt;br /&gt;
		cvo-&amp;gt;on=0;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		updateCv(cvo);&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
	sei();&lt;br /&gt;
&lt;br /&gt;
	setLed(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	static unsigned char buttonCount;&lt;br /&gt;
	static signed char blinkCount; &lt;br /&gt;
&lt;br /&gt;
	// LED blinker &lt;br /&gt;
	if(--blinkCount&amp;lt;=0)&lt;br /&gt;
	{	&lt;br /&gt;
		blinkCount=40;&lt;br /&gt;
		blinker++;&lt;br /&gt;
		if(button)&lt;br /&gt;
		{&lt;br /&gt;
			if(button&amp;lt;10)&lt;br /&gt;
				button++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Button debouncer &lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{	// down &lt;br /&gt;
		if(buttonCount &amp;lt; 10)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount++;&lt;br /&gt;
			if(buttonCount==10)&lt;br /&gt;
			{&lt;br /&gt;
				button=1;&lt;br /&gt;
				blinkCount=40;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{	// up &lt;br /&gt;
		if(buttonCount &amp;gt; 0)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount--;&lt;br /&gt;
			if(buttonCount==0)&lt;br /&gt;
				button=0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Gate 5 timing (clock output) &lt;br /&gt;
	if(gate5off)&lt;br /&gt;
	{&lt;br /&gt;
		--gate5off;&lt;br /&gt;
		if(gate5off==0)&lt;br /&gt;
			gate_off(5);	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// activ sense &lt;br /&gt;
	if(watchTicker)			// if active sense is activated &lt;br /&gt;
	{&lt;br /&gt;
		watchTicker--;		// decrement time &lt;br /&gt;
		if(watchTicker==0)	// if time elapsed&lt;br /&gt;
			resetAll(); 	// switch all notes off 	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// retrigger timing &lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)	// for all cv output channels &lt;br /&gt;
	{&lt;br /&gt;
		if(cv_out[i].gateDipper)	// if gate timer is active &lt;br /&gt;
		{&lt;br /&gt;
			--cv_out[i].gateDipper;		// decrement timer &lt;br /&gt;
			if(cv_out[i].gateDipper==0)	// if timer elapsed&lt;br /&gt;
				gate_on(i);				// activate gate &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Eval Midi Data&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define NOTE_ON         0x90		// Midi note on message &lt;br /&gt;
#define NOTE_OFF        0x80		// Midi note off message &lt;br /&gt;
#define PITCH           0xE0		// pitch bender&lt;br /&gt;
#define AFTERTOUCH      0xD0        // after touch command (unused) &lt;br /&gt;
#define CC		        0xB0        // Continuous Controller&lt;br /&gt;
#define CHAN_PRES	    0xD0        // Channel Pressure (unused)&lt;br /&gt;
#define SONG_POSITION_POINTER 0xF2	// used for clock synchronization (first beat) &lt;br /&gt;
#define SYSEX 			0xF0		// System exclusive beginn&lt;br /&gt;
#define SYSEND			0xF7		// System exclusive end&lt;br /&gt;
&lt;br /&gt;
struct {&lt;br /&gt;
	unsigned char runningStatus;	// Midi runnig status (*)&lt;br /&gt;
	unsigned char msgByteCnt;    	// number of current midi-message byte received &lt;br /&gt;
	unsigned char byte1;			// first data byte &lt;br /&gt;
	unsigned char syxAdr; 			// Sys-Ex Adress Byte&lt;br /&gt;
	unsigned char startStop;		// current  Midi Start/Stop Status&lt;br /&gt;
	unsigned char startFlag;		// Start on next clock&lt;br /&gt;
	unsigned char clockTick;		// counter for clock division&lt;br /&gt;
	unsigned char channel;			// channel of current midi-command (relative, e.g. 0..3 ) 						&lt;br /&gt;
	unsigned int  spp; 				// song position pointer (from midi message), needed to have the clock synced to song position&lt;br /&gt;
}midi; &lt;br /&gt;
// 	(*) running status is also used as &#039;current status&#039; - it remembers allways, what next data byte is for. Or its zero, than data is ignored. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine, &lt;br /&gt;
// complete midi processing is done here, as the CPU is fast enough to &lt;br /&gt;
// do everything before next midi byte can come in. &lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	unsigned char status,byte;&lt;br /&gt;
&lt;br /&gt;
    status = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((status &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(watchTicker)						// if there had been an active sense &lt;br /&gt;
			watchTicker=ACTIVE_SENSE_TICKS;	// every new byte is ok, to &amp;quot;feed the watchdog&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		if (byte &amp;gt; 0x7F)	                // if command byte (MSB set)&lt;br /&gt;
		{&lt;br /&gt;
			if(byte&amp;gt;= 0xF8)  				// if real-time msg &lt;br /&gt;
			{&lt;br /&gt;
				switch (byte)&lt;br /&gt;
				{&lt;br /&gt;
					case 0xF8: 	// Clock &lt;br /&gt;
					{&lt;br /&gt;
						if(midi.startFlag)  // if synchronized start &lt;br /&gt;
						{&lt;br /&gt;
							midi.startStop=1;&lt;br /&gt;
							midi.startFlag=0;&lt;br /&gt;
&lt;br /&gt;
							if(config.clockDivider&amp;gt;1 )// if divided clock &lt;br /&gt;
							{&lt;br /&gt;
								if( midi.spp) // if start not from zero &lt;br /&gt;
								{&lt;br /&gt;
									midi.clockTick=midi.spp%config.clockDivider;			// calculate clock divider count for current song position &lt;br /&gt;
									if(midi.clockTick &amp;gt;= config.clockDivider&amp;gt;&amp;gt;1)	// if calculated divider count is mor than half elapsed &lt;br /&gt;
										gate_off(5); 								// than set clock output off &lt;br /&gt;
									else&lt;br /&gt;
										gate_on(5);									// else set it on &lt;br /&gt;
								}&lt;br /&gt;
								else &lt;br /&gt;
								{&lt;br /&gt;
									gate_on(5);&lt;br /&gt;
									midi.clockTick=0; &lt;br /&gt;
								}&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						else &lt;br /&gt;
						{&lt;br /&gt;
							if(midi.startStop)	// if (already) running &lt;br /&gt;
								++midi.spp;&lt;br /&gt;
							++midi.clockTick; 	// increment counter for clock output &lt;br /&gt;
						}&lt;br /&gt;
&lt;br /&gt;
						if(config.clockDivider&amp;lt;=1)&lt;br /&gt;
						{&lt;br /&gt;
							gate_on(5);								// do clock output &lt;br /&gt;
							gate5off=3;							&lt;br /&gt;
						}&lt;br /&gt;
						else&lt;br /&gt;
						{&lt;br /&gt;
							if(midi.clockTick &amp;gt;= config.clockDivider)	// if divider count elapsed &lt;br /&gt;
							{&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
								gate_on(5);								// do clock output &lt;br /&gt;
							}&lt;br /&gt;
							else if(midi.clockTick == config.clockDivider&amp;gt;&amp;gt;1)	// if divider count half elapsed &lt;br /&gt;
								gate_off(5); 									// set clock output off &lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
					case 0xF9:	// Tick (unused) &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFA: 	// Start &lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.spp=0;&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
							if(config.clockDivider&amp;gt;1)&lt;br /&gt;
							{&lt;br /&gt;
								gate_off(5); 			// set clock output off 								&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFB:	// Continue&lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
						}&lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFC:	// Stop&lt;br /&gt;
						midi.startStop=0;&lt;br /&gt;
						gate_off(4);&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFD:	// unassigned &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFE:	//  Active Sense&lt;br /&gt;
						watchTicker=ACTIVE_SENSE_TICKS;&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFF:	// Reset &lt;br /&gt;
						break; &lt;br /&gt;
				}&lt;br /&gt;
				return ; &lt;br /&gt;
			} &lt;br /&gt;
&lt;br /&gt;
			if(byte &amp;gt;= 0xF0) 					// if System Common Category message&lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)&lt;br /&gt;
				{&lt;br /&gt;
					if(midi.runningStatus==SYSEX &amp;amp;&amp;amp; midi.msgByteCnt==6) // if a complete valid Sys-Ex Frame has been received &lt;br /&gt;
						setSyx(midi.syxAdr, midi.byte1); 				// change configuration &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					midi.runningStatus = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				else if(byte ==SONG_POSITION_POINTER)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;  &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				//							// every other System Common is ignored 	&lt;br /&gt;
				midi.runningStatus = 0;		// just reset running status &lt;br /&gt;
				return ; 					// and done &lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.runningStatus = 0;				// just new status messages left, so running status is lost&lt;br /&gt;
&lt;br /&gt;
			// only channel messages left here ... &lt;br /&gt;
			if(learn)								 // if learn mode &lt;br /&gt;
			{&lt;br /&gt;
				if(config.midiChannel != (byte&amp;amp;0xf)) // if message channel not current channel&lt;br /&gt;
				{&lt;br /&gt;
					config.midiChannel=byte&amp;amp;0xf;	 // assign channel &lt;br /&gt;
					setEeWrite(); 					 // and store it &lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.channel=(byte &amp;amp; 0xf)-config.midiChannel ;&lt;br /&gt;
			if ( midi.channel&amp;lt;config.numMidiChannels)   // if MIDI channel matches,&lt;br /&gt;
			{&lt;br /&gt;
				midi.runningStatus = byte &amp;amp; 0xF0 ;		// set running status (==Midi command w/o channel)&lt;br /&gt;
				midi.msgByteCnt = 0;&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
			return ; &lt;br /&gt;
		}&lt;br /&gt;
		else	// data bytes goes here &lt;br /&gt;
		{&lt;br /&gt;
			switch (midi.runningStatus)		    // last command byte (or zero, if data is not for us) &lt;br /&gt;
			{&lt;br /&gt;
			case SONG_POSITION_POINTER:&lt;br /&gt;
				if(midi.msgByteCnt)	// 2nd Byte&lt;br /&gt;
				{&lt;br /&gt;
					midi.spp= (((int)byte &amp;lt;&amp;lt; 7 ) | midi.byte1) * 6;&lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt=1;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_ON):					// if we&#039;ve had a note on byte&lt;br /&gt;
				if (midi.msgByteCnt)		// if counter = 1&lt;br /&gt;
				{ 			   				// then we&#039;ve just received a velocity byte&lt;br /&gt;
					midi.msgByteCnt = 0;	// msg complete - clear byte count &lt;br /&gt;
					if (byte == 0)			// is it a note off (velo = 0) ?&lt;br /&gt;
						note_off(midi.byte1,byte,midi.channel);  &lt;br /&gt;
					else 					// NOTE ON&lt;br /&gt;
					{&lt;br /&gt;
						if(learn)&lt;br /&gt;
						{&lt;br /&gt;
							config.noteOffset=midi.byte1;&lt;br /&gt;
							setEeWrite();&lt;br /&gt;
						}&lt;br /&gt;
						&lt;br /&gt;
						note_on(midi.byte1, byte,midi.channel);  // ... and do what the note on has told us &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				else						// first data byte &lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;		// save it &lt;br /&gt;
					midi.msgByteCnt = 1;    // set counter to get 2nd data byte&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_OFF): 	        	// command note off&lt;br /&gt;
				if (midi.msgByteCnt)	 	// if complete &lt;br /&gt;
				{ 					 	&lt;br /&gt;
					midi.msgByteCnt = 0;	&lt;br /&gt;
					note_off(midi.byte1,byte,midi.channel); // switch note off&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1; 	&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (PITCH):		        	// PITCH BEND command&lt;br /&gt;
				if (midi.msgByteCnt)   	 	// if second data byte (MSB) &lt;br /&gt;
				{ 						&lt;br /&gt;
					int v;&lt;br /&gt;
					v=byte;&lt;br /&gt;
					v&amp;lt;&amp;lt;=6;&lt;br /&gt;
					v|=midi.byte1;&lt;br /&gt;
					v-=0x1000;&lt;br /&gt;
					setPb(v,midi.channel);&lt;br /&gt;
&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (CC):	             		// Continuous Controller Message &lt;br /&gt;
				if (midi.msgByteCnt)	&lt;br /&gt;
				{&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
&lt;br /&gt;
					if(learn)&lt;br /&gt;
					{&lt;br /&gt;
						config.cvMidiCC[2]=midi.byte1;&lt;br /&gt;
						setEeWrite();&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
					setCC(midi.byte1, byte,midi.channel);&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
				  midi.byte1 = byte;&lt;br /&gt;
				  midi.msgByteCnt = 1;&lt;br /&gt;
				  return; &lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
	//		case (CHAN_PRES):	         	// if channel pressure command&lt;br /&gt;
	//			channel_pressure (byte); 	// set Aftertouch &lt;br /&gt;
			case SYSEX:						// SYS-EX command &lt;br /&gt;
				midi.msgByteCnt++;			// message Byte counter&lt;br /&gt;
				switch(midi.msgByteCnt)		// which byte is on? &lt;br /&gt;
				{&lt;br /&gt;
					case 1:					// Manufactorer &lt;br /&gt;
						if(byte !=0x70)		// if not the right one &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error / wrong byte &lt;br /&gt;
						break; &lt;br /&gt;
					case 2:					// Unit - ID &lt;br /&gt;
						if(byte!=0x7D)&lt;br /&gt;
							midi.msgByteCnt=0;&lt;br /&gt;
						break; &lt;br /&gt;
					case 3:					// Midi-Adress (or Unit ID) (ignored!)&lt;br /&gt;
						if(byte&amp;gt;=0x10)		// if not in legal range &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error &lt;br /&gt;
						break;&lt;br /&gt;
					case 4:					// adress byte&lt;br /&gt;
						midi.syxAdr=byte;	// store it for later&lt;br /&gt;
						break;&lt;br /&gt;
					case 5:					// high data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1=(byte&amp;lt;&amp;lt;4);	// set half the data &lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					case 6: 				// low data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1 |= byte;	// or together the complete data byte &lt;br /&gt;
						else 					// evaluation is  done in the code for SYSEND above! &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					default:				// not our framing &lt;br /&gt;
						midi.msgByteCnt=0;&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
				if(midi.msgByteCnt==0)	// something did not fit &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				break;&lt;br /&gt;
			case 0: // bnwtko (bytes no one wants to know off ) &lt;br /&gt;
				return ;&lt;br /&gt;
			default:&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7322</id>
		<title>MCV876 Controller Update: File Converter</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7322"/>
		<updated>2011-06-27T20:02:29Z</updated>

		<summary type="html">&lt;p&gt;Fetz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Worum geht&#039;s? = &lt;br /&gt;
Der File Konverter ist ein kleines Programm, dass vom Compiler/Linker erzeugte Binär-Datei (im [http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format])) in ein Datenformat wandelt, dass per Midi übertragen werden kann und das der Controller so versteht, dass er sich selber updaten kann. &lt;br /&gt;
&lt;br /&gt;
== Benutzen == &lt;br /&gt;
Der Linker der Entwicklungsumgebung erzeugt ein .hex File. Dieses File zieht man einfach auf das Icon der sys876.exe. Kurz darauf taucht im gleichen Verzeichnis wie die .hex Datei eine .syx Datei sonst gleichen Namens auf. Das war es schon - diese Datei ist das Sys-Ex Update File, was man per Midi an den Controller schickt, worauf er dann sofort mit diesem neuen Programm los rennt. &lt;br /&gt;
&lt;br /&gt;
== Datenformat == &lt;br /&gt;
Die Programm-Daten werden in 64-Byte Nutzdatenhäppchen eingepackt. Diese Pakete sind Brutto 84 Bytes lang. &lt;br /&gt;
Die Daten müssen für Midi umkodiert werden, da Midi nur 7 Bit Nutz-Daten übertragen kann. Dazu werden einfach immer die nächsten 7 bit aus dem 8-Bit breiten Datenstrom entnommen. Das gibt völlig undurchsichtige Werte, geht erst nach 7x8=56 Bytes auf und ist ansonsten recht einfacher Code, der kompakte Daten erzeugt. Um Datenübertragungsfehler zu erkennen, bekommt jedes Paket eine 16 Bit CRC als Prüfsumme. Und damit es als Midi durchgeht, kommen noch ein paar Bytes (&amp;quot;hallo hier kommt ein SysEx&amp;quot;) davor und ein Ende Byte dahinter. &lt;br /&gt;
&lt;br /&gt;
Die 64 Byte sind eine für Midi, Sys-Ex und das Controller-RAM noch verträgliche Datensatzlänge. Und der Controller schreibt seinen Programm-Speicher in genau solchen 64 Byte Blöcken. &lt;br /&gt;
&lt;br /&gt;
Vor die eigentlich Progamm-Daten werden noch zwei Pakete vorangestellt: &lt;br /&gt;
&lt;br /&gt;
Das erste schaltet einen in normalem Midi-Betrieb laufenden Controller auf den Boot-Lader um. Nach dem eigentlichen, kurzen Kommando, das den Normalbetrieb beendet, folgen 70 Nullen, die dem Controller mindestens 22ms Luft verschaffen, um auf den Bootlader zu wechseln. (Ist der Controller bereits im Bootlader wird dieses Paket nebst den Nullen schlichtweg überlesen/ignoriert. ) &lt;br /&gt;
&lt;br /&gt;
Das zweite Paket enthält nur die Info, dass es a)das erste Update-Pakte ist (d.h. es setzt den Empfänger auf &amp;quot;jetzt geht es los&amp;quot; zurück), und b)die Zahl der folgenden Blöcke (damit Vollständigkeit geprüft werden kann). &lt;br /&gt;
&lt;br /&gt;
Dann kommen die eigentlichen Programmdaten.&lt;br /&gt;
&lt;br /&gt;
= Quelltext =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
	Programm zum Erzeugen von Midi-Sys-Ex Software Update Files aus einer HEX-Datei &lt;br /&gt;
	für das MCV-876 Midi Interface mit ATMEL ATMEGA 88 Controller. &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*	&lt;br /&gt;
	Aufruf: &lt;br /&gt;
	sys676 avr-studio-compiler-out.hex &lt;br /&gt;
	&lt;br /&gt;
	das erzeugt dann eine Datei &lt;br /&gt;
	sys676 avr-studio-comipler-out.syx&lt;br /&gt;
	&lt;br /&gt;
	die mit Midi-OX (oder einen andern &amp;quot;ich kann Midi senden&amp;quot; Programm) an das &lt;br /&gt;
	Interface übertragen wird. &lt;br /&gt;
	&lt;br /&gt;
	Unter Windows kann man das übrigens bequem per &amp;quot;Drag and Drop&amp;quot; bedienen, einfach die &lt;br /&gt;
	.hex Datei auf die sys876.exe draufziehen, das Ergebnis taucht dann im gleichen Order wie &lt;br /&gt;
	die .hex Datei auf. (Man sieht halt keine Fehlermeldungen... ) &lt;br /&gt;
&lt;br /&gt;
	Erfolgreich übersetzt mit Visual Studio .net 2003 auf Windows XP &lt;br /&gt;
	Leeres Konsolenprojekt Namens syx876 erzeugen, diesen Quelltext in das .cpp &lt;br /&gt;
	File kopieren und bauen lassen. Ggfls. auf &amp;quot;Release&amp;quot; umstellen, im Release Ordner &lt;br /&gt;
	findet sich dann die fertige Applikation. &lt;br /&gt;
	&lt;br /&gt;
	Sollte sich nach marginalen Änderungen allerdings mit jedem Compiler übersetzen lassen. &lt;br /&gt;
	&lt;br /&gt;
*/&lt;br /&gt;
// für Windows =======================&lt;br /&gt;
#include &amp;quot;stdafx.h&amp;quot;&lt;br /&gt;
#define main _tmain &lt;br /&gt;
&lt;br /&gt;
// für normale Compiler =============&lt;br /&gt;
/*&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
*/&lt;br /&gt;
//===================================&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define VERSION &amp;quot;1.00&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DATA 8192				// netto data size (ATMEGA 88 Flash Rom size) &lt;br /&gt;
#define MAX_SYX_DATA (2*MAX_DATA)	// max. complete syx-ex size&lt;br /&gt;
#define MAX_SYX_BLOCK (2*64)		// one Sys-Ex Block of 64 bytes netto data &lt;br /&gt;
&lt;br /&gt;
unsigned char data[MAX_DATA];	// holds binary program data &lt;br /&gt;
&lt;br /&gt;
// Convert one HEX-ASCII character to its numerical value, or -1 if illegal value &lt;br /&gt;
int conf( char c)&lt;br /&gt;
{&lt;br /&gt;
	switch (c)&lt;br /&gt;
	{&lt;br /&gt;
		case &#039;0&#039;: return 0;&lt;br /&gt;
		case &#039;1&#039;: return 1;&lt;br /&gt;
		case &#039;2&#039;: return 2;&lt;br /&gt;
		case &#039;3&#039;: return 3;&lt;br /&gt;
		case &#039;4&#039;: return 4;&lt;br /&gt;
		case &#039;5&#039;: return 5;&lt;br /&gt;
		case &#039;6&#039;: return 6;&lt;br /&gt;
		case &#039;7&#039;: return 7;&lt;br /&gt;
		case &#039;8&#039;: return 8;&lt;br /&gt;
		case &#039;9&#039;: return 9;&lt;br /&gt;
		case &#039;a&#039;: case &#039;A&#039;: return 10;&lt;br /&gt;
		case &#039;b&#039;: case &#039;B&#039;: return 11;&lt;br /&gt;
		case &#039;c&#039;: case &#039;C&#039;: return 12;&lt;br /&gt;
		case &#039;d&#039;: case &#039;D&#039;: return 13;&lt;br /&gt;
		case &#039;e&#039;: case &#039;E&#039;: return 14;&lt;br /&gt;
		case &#039;f&#039;: case &#039;F&#039;: return 15;&lt;br /&gt;
	}&lt;br /&gt;
	return (-1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate 16 bit crc from previous crc and new data byte, initial (first) crc should be 0xffff, returns new crc &lt;br /&gt;
unsigned int crc16(unsigned int crc, unsigned char byte)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	crc^=byte;&lt;br /&gt;
	for(i=0;i&amp;lt;8;++i)&lt;br /&gt;
	{&lt;br /&gt;
	    if (crc&amp;amp;1)&lt;br /&gt;
			crc=(crc&amp;gt;&amp;gt;1)^0xA001;&lt;br /&gt;
		else&lt;br /&gt;
			crc&amp;gt;&amp;gt;=1;&lt;br /&gt;
	}&lt;br /&gt;
	return crc &amp;amp; 0xffff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// generates first Sys-EX Block, this switches the interface from operation mode to boot-load mode&lt;br /&gt;
int makePrgPrep(unsigned char *str)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	&lt;br /&gt;
	unsigned char a[] = { 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7 }; // the switch command string (see makeSysEx for byte value explanation) &lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;sizeof(a);i++)	&lt;br /&gt;
		*str++=a[i];&lt;br /&gt;
	for(;i&amp;lt;70;i++)	// 70 bytes * 0,32ms for midi data transmission gives the interface the time to start the bootloader &lt;br /&gt;
	{&lt;br /&gt;
		*str++=0;&lt;br /&gt;
	}&lt;br /&gt;
	return i; &lt;br /&gt;
}&lt;br /&gt;
// make a sys-ex data block with proper midi framing and crc from binary data in &amp;quot;block&amp;quot; &lt;br /&gt;
int makeSysEx(unsigned char *block, int len, unsigned char *syxTarget)&lt;br /&gt;
{&lt;br /&gt;
	int crc=0xffff;&lt;br /&gt;
	unsigned int out=0;&lt;br /&gt;
	int bits=0;&lt;br /&gt;
	int i;&lt;br /&gt;
	unsigned char c;  &lt;br /&gt;
	&lt;br /&gt;
	unsigned char *syxStr=syxTarget;&lt;br /&gt;
&lt;br /&gt;
	// make CRC &lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];&lt;br /&gt;
		crc=crc16(crc, c);&lt;br /&gt;
	}&lt;br /&gt;
	block[len++]= ( crc)&amp;amp;0xff;&lt;br /&gt;
	block[len++]= (crc &amp;gt;&amp;gt;8 ) &amp;amp;0xff;&lt;br /&gt;
	&lt;br /&gt;
	*syxStr++ = (0xF0);	// start sys ex &lt;br /&gt;
	*syxStr++ = (0x70);	// manufacturer id &lt;br /&gt;
	*syxStr++ = (0x7D);	// unit type  &lt;br /&gt;
	*syxStr++ = (0x00); // unit id &lt;br /&gt;
	*syxStr++ = (0x15);	// for normal sysEx it is a adress, here magic byte 1 &lt;br /&gt;
	*syxStr++ = (0x55); // magic byte 2 &lt;br /&gt;
		&lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)	&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];	// data byte 	&lt;br /&gt;
		/*&lt;br /&gt;
			Midi data is 7 bit only, as MSB is command/data bit. &lt;br /&gt;
			So data must be converted to 7 bit. &lt;br /&gt;
			Each source data byte gives 8/7 target bytes, &lt;br /&gt;
			as soon as there is one complete byte more (which happens every 7 input bytes)&lt;br /&gt;
			two target bytes will be generated.&lt;br /&gt;
		*/&lt;br /&gt;
		out&amp;lt;&amp;lt;=8;	&lt;br /&gt;
		out|=c;&lt;br /&gt;
		bits+=8;&lt;br /&gt;
		while(bits&amp;gt;=7)&lt;br /&gt;
		{&lt;br /&gt;
			*syxStr++ = ((out&amp;gt;&amp;gt;(bits-7)) &amp;amp; 0x7f);&lt;br /&gt;
			bits-=7;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if(bits)	// don&#039;t forget about the last bits of the last data byte &lt;br /&gt;
	{&lt;br /&gt;
		out&amp;lt;&amp;lt;= 7-bits;  // this will pad last byte with up to 6 zero bits. &lt;br /&gt;
		*syxStr++ =  (out &amp;amp; 0x7f);&lt;br /&gt;
	}&lt;br /&gt;
	*syxStr++ = 0xF7;	// Sys-Ex end &lt;br /&gt;
&lt;br /&gt;
	return (int) (syxStr - syxTarget); // lenght of generated data &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// argv[1] is filename to be converted &lt;br /&gt;
int main(int argc, _TCHAR* argv[])&lt;br /&gt;
{&lt;br /&gt;
	FILE *fp; &lt;br /&gt;
	int minAdr=0xffff;&lt;br /&gt;
	int maxAdr=0;&lt;br /&gt;
	int chars=0;&lt;br /&gt;
	int error=0;&lt;br /&gt;
	int c; &lt;br /&gt;
	int val;&lt;br /&gt;
	int field=-1;&lt;br /&gt;
	int adress=0;&lt;br /&gt;
	int cnt; &lt;br /&gt;
	int sum=0; &lt;br /&gt;
	int field_len=0; &lt;br /&gt;
	int type=99;&lt;br /&gt;
	int line=0;&lt;br /&gt;
&lt;br /&gt;
	// the controller allways flashes 64 byte blocks, so the last block is padded with arbitrary data &lt;br /&gt;
	int padit[]= {110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
              110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
			  110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43 } ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	printf(&amp;quot;syx876 Version &amp;quot; VERSION &amp;quot;\nHex-File to Sys-Ex Converter for ATMega88 on MCV876\n&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
	// &#039;check&#039; for proper input filename (it must have a &amp;quot;three byte extension&amp;quot; )&lt;br /&gt;
	//  *not* fool proof, try &amp;quot;/../a&amp;quot; and you&#039;ll get interesting results&lt;br /&gt;
	size_t l=strlen(argv[1]);&lt;br /&gt;
&lt;br /&gt;
	if(argc !=2 || l&amp;lt;5 || *(argv[1]+l-4)!=&#039;.&#039; )&lt;br /&gt;
	{&lt;br /&gt;
		printf(&amp;quot;\nUsage: syx876 filename.hex\n&amp;quot;);&lt;br /&gt;
		return -1;&lt;br /&gt;
	}&lt;br /&gt;
	l -= 3; &lt;br /&gt;
	&lt;br /&gt;
	fp=fopen (argv[1],&amp;quot;rb&amp;quot;);	// open input files &lt;br /&gt;
	&lt;br /&gt;
	if(fp)&lt;br /&gt;
	{&lt;br /&gt;
		// make output file name &lt;br /&gt;
		*(argv[1]+l)=0;	&lt;br /&gt;
		strcat (argv[1],&amp;quot;syx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		while( (c=fgetc(fp)) != EOF  ) // read input hex file &lt;br /&gt;
		{&lt;br /&gt;
			chars++;&lt;br /&gt;
			 &lt;br /&gt;
			if(c==&#039;:&#039;)&lt;br /&gt;
			{&lt;br /&gt;
				cnt=0;&lt;br /&gt;
				field=0;&lt;br /&gt;
				sum=0;&lt;br /&gt;
				line++;&lt;br /&gt;
			}&lt;br /&gt;
			else if ( conf( (unsigned char) c ) &amp;gt;=0 )&lt;br /&gt;
			{&lt;br /&gt;
				if(cnt==0)&lt;br /&gt;
					val=conf( (unsigned char) c );&lt;br /&gt;
				else &lt;br /&gt;
				{&lt;br /&gt;
					val&amp;lt;&amp;lt;=4;&lt;br /&gt;
					val += conf( (unsigned char) c );&lt;br /&gt;
				}&lt;br /&gt;
				cnt++;&lt;br /&gt;
				&lt;br /&gt;
				if(cnt==2)	// add up checksum&lt;br /&gt;
					sum+= val;&lt;br /&gt;
				if( cnt==4)&lt;br /&gt;
					sum += val &amp;amp; 0xff;&lt;br /&gt;
				&lt;br /&gt;
				if(field ==0 &amp;amp;&amp;amp; cnt==2)&lt;br /&gt;
				{	// byte count &lt;br /&gt;
					field_len=val; &lt;br /&gt;
					field =1;&lt;br /&gt;
					cnt=0;				&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==1 &amp;amp;&amp;amp; cnt == 4)&lt;br /&gt;
				{	// adress&lt;br /&gt;
					adress=val;&lt;br /&gt;
					field=2;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==2 &amp;amp;&amp;amp; cnt == 2)&lt;br /&gt;
				{	// type &lt;br /&gt;
					type=val;&lt;br /&gt;
					field=4;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len&amp;gt;0)&lt;br /&gt;
				{	// data &lt;br /&gt;
					field_len--;&lt;br /&gt;
					&lt;br /&gt;
					if(adress &amp;lt; minAdr)&lt;br /&gt;
						minAdr=adress;&lt;br /&gt;
					if(adress&amp;gt;maxAdr)&lt;br /&gt;
						maxAdr=adress;&lt;br /&gt;
&lt;br /&gt;
					if(type==0 &amp;amp;&amp;amp; adress &amp;lt; MAX_DATA)&lt;br /&gt;
						data[adress]=val;&lt;br /&gt;
					adress++;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len == 0)&lt;br /&gt;
				{	// checksum &lt;br /&gt;
					cnt=0;&lt;br /&gt;
					sum &amp;amp;=0xff;&lt;br /&gt;
					if(sum)&lt;br /&gt;
					{&lt;br /&gt;
						error++;&lt;br /&gt;
						printf(&amp;quot;Line %d Checksum error (sum=%d)\n&amp;quot;,line, sum); &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else if( c !=10 &amp;amp;&amp;amp; c !=13)&lt;br /&gt;
			{&lt;br /&gt;
				error ++;&lt;br /&gt;
				printf( &amp;quot;Line %d unexpected character (%c)\n&amp;quot;,line, c); // characters I don&#039;t know  &lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		printf( &amp;quot;\nread %d charactes, from adress %d to %d with %d errors\n&amp;quot;, chars, minAdr, maxAdr,error); &lt;br /&gt;
		&lt;br /&gt;
		// generate output data &lt;br /&gt;
		if(error==0 &amp;amp;&amp;amp; minAdr==0) &lt;br /&gt;
		{&lt;br /&gt;
			int blocks; &lt;br /&gt;
			int blockNr; &lt;br /&gt;
			unsigned char block[MAX_SYX_BLOCK];	// buffer to collect/prepare one binary data block &lt;br /&gt;
			unsigned char stp[MAX_SYX_DATA]; 	//  output data buffer, collects the sysEx data&lt;br /&gt;
			unsigned char *str;		// current write pointer &lt;br /&gt;
			int adr; &lt;br /&gt;
			int padded;&lt;br /&gt;
			FILE *wp;&lt;br /&gt;
			int i;&lt;br /&gt;
			&lt;br /&gt;
			str=stp; &lt;br /&gt;
			&lt;br /&gt;
			blocks= (((maxAdr)/64)+1);	// total number of blocks &lt;br /&gt;
						&lt;br /&gt;
			str+= makePrgPrep(str);		// generate sys-ex header (=switch to bootloader command) &lt;br /&gt;
			&lt;br /&gt;
			// first block is a spezial block - it tells controller number of blocks he has to expect. &lt;br /&gt;
			blockNr=0;&lt;br /&gt;
			block[0]=blockNr;&lt;br /&gt;
			block[1]=blocks;&lt;br /&gt;
			block[2]=0;	// reserved &lt;br /&gt;
			block[3]=0;	// reserved &lt;br /&gt;
			str += makeSysEx(block,4,str);	// generate SysEx from block data &lt;br /&gt;
			&lt;br /&gt;
			adr=minAdr;&lt;br /&gt;
			// make flash data blocks &lt;br /&gt;
			// flash data blocks are 65 bytes, 1 Byte Block number, 64 data bytes&lt;br /&gt;
			// the controller allways writes 64 byte blocks to flash, so this format &lt;br /&gt;
			// helps to keep the controller program straight. &lt;br /&gt;
			while (blockNr&amp;lt;blocks)&lt;br /&gt;
			{&lt;br /&gt;
				blockNr++;&lt;br /&gt;
				block[0]=blockNr;	// first data byte in block is block number (so controller sees, when a block is lost) &lt;br /&gt;
				padded=0; &lt;br /&gt;
				for(i=0;i&amp;lt;64;i++)&lt;br /&gt;
				{&lt;br /&gt;
					if(adr&amp;gt;maxAdr)	 // it beyond highest adresses from hex file:  last block is(must be) padded to 64 bytes &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=padit[padded];&lt;br /&gt;
						padded++;&lt;br /&gt;
					}&lt;br /&gt;
					else	// write programm data &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=data[adr++];					&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				str +=makeSysEx(block,65,str);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			// write output file &lt;br /&gt;
			wp=fopen(argv[1],&amp;quot;wb&amp;quot;);&lt;br /&gt;
			if(wp)&lt;br /&gt;
			{&lt;br /&gt;
				fwrite ((void *) stp ,sizeof(unsigned char), str-stp, wp);&lt;br /&gt;
				fclose(wp);&lt;br /&gt;
				&lt;br /&gt;
				printf(&amp;quot;%d sysEx data blocks in %d bytes written\n&amp;quot;,blocks, str-stp);&lt;br /&gt;
			}&lt;br /&gt;
			else &lt;br /&gt;
				printf(&amp;quot;Cant open output file %s\n&amp;quot;,argv[1]);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			if(minAdr)&lt;br /&gt;
				printf(&amp;quot;Lowest adress %d is not zero!\n&amp;quot;,minAdr);&lt;br /&gt;
			return -2;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
		printf (&amp;quot;Input file %s not found\n&amp;quot;,argv[1]);&lt;br /&gt;
&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7321</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7321"/>
		<updated>2011-06-27T19:10:26Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Software */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Tonhöhe (Noten) &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;|| Gate 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen- und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V.&lt;br /&gt;
&lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie zweimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
Sollte sie vier mal blinken, hat der Controller die permanent gespeicherte Konfiguration nicht geladen und nutzt statt dessen (wieder) die Standardeinstellung.&lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu einzustellen: Die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Controller für CV3 einstellen ===&lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang(sic!) ge-gated. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig. (Zur Bauanleitung hier lang: [[MCV876_Controller_Update:_Hardware_bauen]].) &lt;br /&gt;
&lt;br /&gt;
Der neue Controller wird ansonsten einfach nur in den Sockel des Alten gesteckt. Alten Controller rausziehen, Atmel mit Adapter reinstecken, fertig. &lt;br /&gt;
&lt;br /&gt;
[[File:AdapterEingebaut.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So sieht der &amp;quot;neue&amp;quot; in einem Semtex aus&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier: &#039;&#039;&#039;[[MCV876_Controller_Update:_Source_Code]]&#039;&#039;&#039;. Da steht dann auch, wie das mit der Entwicklungsumgebung geht. &lt;br /&gt;
Das ist der spannende Teil, nämlich das eigentliche Betriebssystem. Um den Teil zu ändern - sprich: seinen Wünschen anzupassen - und das neue Programm in den Controller zu bekommen, braucht man a)nichts kaufen, b)keine Hardware und kann das c)einfach per Midi an den Controller schicken (das Geräte kann also zu bleiben). &lt;br /&gt;
&lt;br /&gt;
Aus der Entwicklungsumgebung kommt eine Binärdatei, die man mit einem kleinen Programm in ein Midi-Sys-Ex Format konvertieren muss. Diese Sys-Ex Datei überträgt man dann mit einem Tool der Wahl (unter Windows z.B. [http://www.midiox.com/ Midi-OX], aber jedes normale DAW-Programm sollte das können) per Midi auf den Controller. Der verfrachtet das flugs (bei aktueller Programmlänge 1,4 Sekunden) in seinen Flash-Programmspeicher. &lt;br /&gt;
Dieser Datei-Konverter findet sich hier: [[MCV876_Controller_Update:_File_Converter]].&lt;br /&gt;
&lt;br /&gt;
Der Bootlader des Controllers, der sich auf Controller-Seite um das Update kümmert, ist hier: [[MCV876_Controller_Update:_Bootloader]]. Der wird einmalig in den Controller &amp;quot;gebrannt&amp;quot;, und dient dazu, die Software jederzeit updaten zu können, auch wenn man in der eigentlichen Controller-Software gerade mal völligen Murks gemacht hat. Den Code braucht man normalerweise nicht anfassen. (Typischerweise ändert sich der alle paar Jahre mal, es ist aber auch nicht weiter schlimm, wenn man nicht die neueste Version hat... )&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7320</id>
		<title>MCV876 Controller Update: File Converter</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7320"/>
		<updated>2011-06-27T18:59:35Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Worum geht&amp;#039;s? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Worum geht&#039;s? = &lt;br /&gt;
Der File Konverter ist ein kleines Programm, dass vom Compiler/Linker erzeugte Binär-Datei (im [http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format])) in ein Datenformat wandelt, dass per Midi übertragen werden kann und das der Controller so versteht, dass er sich selber updaten kann. &lt;br /&gt;
== Datenformat == &lt;br /&gt;
Die Programm-Daten werden in 64-Byte Nutzdatenhäppchen eingepackt. Diese Pakete sind Brutto 84 Bytes lang. &lt;br /&gt;
Die Daten müssen für Midi umkodiert werden, da Midi nur 7 Bit Nutz-Daten übertragen kann. Dazu werden einfach immer die nächsten 7 bit aus dem 8-Bit breiten Datenstrom entnommen. Das gibt völlig undurchsichtige Werte, geht erst nach 7x8=56 Bytes auf und ist ansonsten recht einfacher Code, der kompakte Daten erzeugt. Um Datenübertragungsfehler zu erkennen, bekommt jedes Paket eine 16 Bit CRC als Prüfsumme. Und damit es als Midi durchgeht, kommen noch ein paar Bytes (&amp;quot;hallo hier kommt ein SysEx&amp;quot;) davor und ein Ende Byte dahinter. &lt;br /&gt;
&lt;br /&gt;
Die 64 Byte sind eine für Midi, Sys-Ex und das Controller-RAM noch verträgliche Datensatzlänge. Und der Controller schreibt seinen Programm-Speicher in genau solchen 64 Byte Blöcken. &lt;br /&gt;
&lt;br /&gt;
Vor die eigentlich Progamm-Daten werden noch zwei Pakete vorangestellt: &lt;br /&gt;
&lt;br /&gt;
Das erste schaltet einen in normalem Midi-Betrieb laufenden Controller auf den Boot-Lader um. Nach dem eigentlichen, kurzen Kommando, das den Normalbetrieb beendet, folgen 70 Nullen, die dem Controller mindestens 22ms Luft verschaffen, um auf den Bootlader zu wechseln. (Ist der Controller bereits im Bootlader wird dieses Paket nebst den Nullen schlichtweg überlesen/ignoriert. ) &lt;br /&gt;
&lt;br /&gt;
Das zweite Paket enthält nur die Info, dass es a)das erste Update-Pakte ist (d.h. es setzt den Empfänger auf &amp;quot;jetzt geht es los&amp;quot; zurück), und b)die Zahl der folgenden Blöcke (damit Vollständigkeit geprüft werden kann). &lt;br /&gt;
&lt;br /&gt;
Dann kommen die eigentlichen Programmdaten.&lt;br /&gt;
&lt;br /&gt;
= Quelltext =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
	Programm zum Erzeugen von Midi-Sys-Ex Software Update Files aus einer HEX-Datei &lt;br /&gt;
	für das MCV-876 Midi Interface mit ATMEL ATMEGA 88 Controller. &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*	&lt;br /&gt;
	Aufruf: &lt;br /&gt;
	sys676 avr-studio-compiler-out.hex &lt;br /&gt;
	&lt;br /&gt;
	das erzeugt dann eine Datei &lt;br /&gt;
	sys676 avr-studio-comipler-out.syx&lt;br /&gt;
	&lt;br /&gt;
	die mit Midi-OX (oder einen andern &amp;quot;ich kann Midi senden&amp;quot; Programm) an das &lt;br /&gt;
	Interface übertragen wird. &lt;br /&gt;
	&lt;br /&gt;
	Unter Windows kann man das übrigens bequem per &amp;quot;Drag and Drop&amp;quot; bedienen, einfach die &lt;br /&gt;
	.hex Datei auf die sys876.exe draufziehen, das Ergebnis taucht dann im gleichen Order wie &lt;br /&gt;
	die .hex Datei auf. (Man sieht halt keine Fehlermeldungen... ) &lt;br /&gt;
&lt;br /&gt;
	Erfolgreich übersetzt mit Visual Studio .net 2003 auf Windows XP &lt;br /&gt;
	Leeres Konsolenprojekt Namens syx876 erzeugen, diesen Quelltext in das .cpp &lt;br /&gt;
	File kopieren und bauen lassen. Ggfls. auf &amp;quot;Release&amp;quot; umstellen, im Release Ordner &lt;br /&gt;
	findet sich dann die fertige Applikation. &lt;br /&gt;
	&lt;br /&gt;
	Sollte sich nach marginalen Änderungen allerdings mit jedem Compiler übersetzen lassen. &lt;br /&gt;
	&lt;br /&gt;
*/&lt;br /&gt;
// für Windows =======================&lt;br /&gt;
#include &amp;quot;stdafx.h&amp;quot;&lt;br /&gt;
#define main _tmain &lt;br /&gt;
&lt;br /&gt;
// für normale Compiler =============&lt;br /&gt;
/*&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
*/&lt;br /&gt;
//===================================&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define VERSION &amp;quot;1.00&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DATA 8192				// netto data size (ATMEGA 88 Flash Rom size) &lt;br /&gt;
#define MAX_SYX_DATA (2*MAX_DATA)	// max. complete syx-ex size&lt;br /&gt;
#define MAX_SYX_BLOCK (2*64)		// one Sys-Ex Block of 64 bytes netto data &lt;br /&gt;
&lt;br /&gt;
unsigned char data[MAX_DATA];	// holds binary program data &lt;br /&gt;
&lt;br /&gt;
// Convert one HEX-ASCII character to its numerical value, or -1 if illegal value &lt;br /&gt;
int conf( char c)&lt;br /&gt;
{&lt;br /&gt;
	switch (c)&lt;br /&gt;
	{&lt;br /&gt;
		case &#039;0&#039;: return 0;&lt;br /&gt;
		case &#039;1&#039;: return 1;&lt;br /&gt;
		case &#039;2&#039;: return 2;&lt;br /&gt;
		case &#039;3&#039;: return 3;&lt;br /&gt;
		case &#039;4&#039;: return 4;&lt;br /&gt;
		case &#039;5&#039;: return 5;&lt;br /&gt;
		case &#039;6&#039;: return 6;&lt;br /&gt;
		case &#039;7&#039;: return 7;&lt;br /&gt;
		case &#039;8&#039;: return 8;&lt;br /&gt;
		case &#039;9&#039;: return 9;&lt;br /&gt;
		case &#039;a&#039;: case &#039;A&#039;: return 10;&lt;br /&gt;
		case &#039;b&#039;: case &#039;B&#039;: return 11;&lt;br /&gt;
		case &#039;c&#039;: case &#039;C&#039;: return 12;&lt;br /&gt;
		case &#039;d&#039;: case &#039;D&#039;: return 13;&lt;br /&gt;
		case &#039;e&#039;: case &#039;E&#039;: return 14;&lt;br /&gt;
		case &#039;f&#039;: case &#039;F&#039;: return 15;&lt;br /&gt;
	}&lt;br /&gt;
	return (-1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate 16 bit crc from previous crc and new data byte, initial (first) crc should be 0xffff, returns new crc &lt;br /&gt;
unsigned int crc16(unsigned int crc, unsigned char byte)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	crc^=byte;&lt;br /&gt;
	for(i=0;i&amp;lt;8;++i)&lt;br /&gt;
	{&lt;br /&gt;
	    if (crc&amp;amp;1)&lt;br /&gt;
			crc=(crc&amp;gt;&amp;gt;1)^0xA001;&lt;br /&gt;
		else&lt;br /&gt;
			crc&amp;gt;&amp;gt;=1;&lt;br /&gt;
	}&lt;br /&gt;
	return crc &amp;amp; 0xffff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// generates first Sys-EX Block, this switches the interface from operation mode to boot-load mode&lt;br /&gt;
int makePrgPrep(unsigned char *str)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	&lt;br /&gt;
	unsigned char a[] = { 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7 }; // the switch command string (see makeSysEx for byte value explanation) &lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;sizeof(a);i++)	&lt;br /&gt;
		*str++=a[i];&lt;br /&gt;
	for(;i&amp;lt;70;i++)	// 70 bytes * 0,32ms for midi data transmission gives the interface the time to start the bootloader &lt;br /&gt;
	{&lt;br /&gt;
		*str++=0;&lt;br /&gt;
	}&lt;br /&gt;
	return i; &lt;br /&gt;
}&lt;br /&gt;
// make a sys-ex data block with proper midi framing and crc from binary data in &amp;quot;block&amp;quot; &lt;br /&gt;
int makeSysEx(unsigned char *block, int len, unsigned char *syxTarget)&lt;br /&gt;
{&lt;br /&gt;
	int crc=0xffff;&lt;br /&gt;
	unsigned int out=0;&lt;br /&gt;
	int bits=0;&lt;br /&gt;
	int i;&lt;br /&gt;
	unsigned char c;  &lt;br /&gt;
	&lt;br /&gt;
	unsigned char *syxStr=syxTarget;&lt;br /&gt;
&lt;br /&gt;
	// make CRC &lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];&lt;br /&gt;
		crc=crc16(crc, c);&lt;br /&gt;
	}&lt;br /&gt;
	block[len++]= ( crc)&amp;amp;0xff;&lt;br /&gt;
	block[len++]= (crc &amp;gt;&amp;gt;8 ) &amp;amp;0xff;&lt;br /&gt;
	&lt;br /&gt;
	*syxStr++ = (0xF0);	// start sys ex &lt;br /&gt;
	*syxStr++ = (0x70);	// manufacturer id &lt;br /&gt;
	*syxStr++ = (0x7D);	// unit type  &lt;br /&gt;
	*syxStr++ = (0x00); // unit id &lt;br /&gt;
	*syxStr++ = (0x15);	// for normal sysEx it is a adress, here magic byte 1 &lt;br /&gt;
	*syxStr++ = (0x55); // magic byte 2 &lt;br /&gt;
		&lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)	&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];	// data byte 	&lt;br /&gt;
		/*&lt;br /&gt;
			Midi data is 7 bit only, as MSB is command/data bit. &lt;br /&gt;
			So data must be converted to 7 bit. &lt;br /&gt;
			Each source data byte gives 8/7 target bytes, &lt;br /&gt;
			as soon as there is one complete byte more (which happens every 7 input bytes)&lt;br /&gt;
			two target bytes will be generated.&lt;br /&gt;
		*/&lt;br /&gt;
		out&amp;lt;&amp;lt;=8;	&lt;br /&gt;
		out|=c;&lt;br /&gt;
		bits+=8;&lt;br /&gt;
		while(bits&amp;gt;=7)&lt;br /&gt;
		{&lt;br /&gt;
			*syxStr++ = ((out&amp;gt;&amp;gt;(bits-7)) &amp;amp; 0x7f);&lt;br /&gt;
			bits-=7;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if(bits)	// don&#039;t forget about the last bits of the last data byte &lt;br /&gt;
	{&lt;br /&gt;
		out&amp;lt;&amp;lt;= 7-bits;  // this will pad last byte with up to 6 zero bits. &lt;br /&gt;
		*syxStr++ =  (out &amp;amp; 0x7f);&lt;br /&gt;
	}&lt;br /&gt;
	*syxStr++ = 0xF7;	// Sys-Ex end &lt;br /&gt;
&lt;br /&gt;
	return (int) (syxStr - syxTarget); // lenght of generated data &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// argv[1] is filename to be converted &lt;br /&gt;
int main(int argc, _TCHAR* argv[])&lt;br /&gt;
{&lt;br /&gt;
	FILE *fp; &lt;br /&gt;
	int minAdr=0xffff;&lt;br /&gt;
	int maxAdr=0;&lt;br /&gt;
	int chars=0;&lt;br /&gt;
	int error=0;&lt;br /&gt;
	int c; &lt;br /&gt;
	int val;&lt;br /&gt;
	int field=-1;&lt;br /&gt;
	int adress=0;&lt;br /&gt;
	int cnt; &lt;br /&gt;
	int sum=0; &lt;br /&gt;
	int field_len=0; &lt;br /&gt;
	int type=99;&lt;br /&gt;
	int line=0;&lt;br /&gt;
&lt;br /&gt;
	// the controller allways flashes 64 byte blocks, so the last block is padded with arbitrary data &lt;br /&gt;
	int padit[]= {110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
              110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
			  110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43 } ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	printf(&amp;quot;syx876 Version &amp;quot; VERSION &amp;quot;\nHex-File to Sys-Ex Converter for ATMega88 on MCV876\n&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
	// &#039;check&#039; for proper input filename (it must have a &amp;quot;three byte extension&amp;quot; )&lt;br /&gt;
	//  *not* fool proof, try &amp;quot;/../a&amp;quot; and you&#039;ll get interesting results&lt;br /&gt;
	size_t l=strlen(argv[1]);&lt;br /&gt;
&lt;br /&gt;
	if(argc !=2 || l&amp;lt;5 || *(argv[1]+l-4)!=&#039;.&#039; )&lt;br /&gt;
	{&lt;br /&gt;
		printf(&amp;quot;\nUsage: syx876 filename.hex\n&amp;quot;);&lt;br /&gt;
		return -1;&lt;br /&gt;
	}&lt;br /&gt;
	l -= 3; &lt;br /&gt;
	&lt;br /&gt;
	fp=fopen (argv[1],&amp;quot;rb&amp;quot;);	// open input files &lt;br /&gt;
	&lt;br /&gt;
	if(fp)&lt;br /&gt;
	{&lt;br /&gt;
		// make output file name &lt;br /&gt;
		*(argv[1]+l)=0;	&lt;br /&gt;
		strcat (argv[1],&amp;quot;syx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		while( (c=fgetc(fp)) != EOF  ) // read input hex file &lt;br /&gt;
		{&lt;br /&gt;
			chars++;&lt;br /&gt;
			 &lt;br /&gt;
			if(c==&#039;:&#039;)&lt;br /&gt;
			{&lt;br /&gt;
				cnt=0;&lt;br /&gt;
				field=0;&lt;br /&gt;
				sum=0;&lt;br /&gt;
				line++;&lt;br /&gt;
			}&lt;br /&gt;
			else if ( conf( (unsigned char) c ) &amp;gt;=0 )&lt;br /&gt;
			{&lt;br /&gt;
				if(cnt==0)&lt;br /&gt;
					val=conf( (unsigned char) c );&lt;br /&gt;
				else &lt;br /&gt;
				{&lt;br /&gt;
					val&amp;lt;&amp;lt;=4;&lt;br /&gt;
					val += conf( (unsigned char) c );&lt;br /&gt;
				}&lt;br /&gt;
				cnt++;&lt;br /&gt;
				&lt;br /&gt;
				if(cnt==2)	// add up checksum&lt;br /&gt;
					sum+= val;&lt;br /&gt;
				if( cnt==4)&lt;br /&gt;
					sum += val &amp;amp; 0xff;&lt;br /&gt;
				&lt;br /&gt;
				if(field ==0 &amp;amp;&amp;amp; cnt==2)&lt;br /&gt;
				{	// byte count &lt;br /&gt;
					field_len=val; &lt;br /&gt;
					field =1;&lt;br /&gt;
					cnt=0;				&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==1 &amp;amp;&amp;amp; cnt == 4)&lt;br /&gt;
				{	// adress&lt;br /&gt;
					adress=val;&lt;br /&gt;
					field=2;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==2 &amp;amp;&amp;amp; cnt == 2)&lt;br /&gt;
				{	// type &lt;br /&gt;
					type=val;&lt;br /&gt;
					field=4;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len&amp;gt;0)&lt;br /&gt;
				{	// data &lt;br /&gt;
					field_len--;&lt;br /&gt;
					&lt;br /&gt;
					if(adress &amp;lt; minAdr)&lt;br /&gt;
						minAdr=adress;&lt;br /&gt;
					if(adress&amp;gt;maxAdr)&lt;br /&gt;
						maxAdr=adress;&lt;br /&gt;
&lt;br /&gt;
					if(type==0 &amp;amp;&amp;amp; adress &amp;lt; MAX_DATA)&lt;br /&gt;
						data[adress]=val;&lt;br /&gt;
					adress++;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len == 0)&lt;br /&gt;
				{	// checksum &lt;br /&gt;
					cnt=0;&lt;br /&gt;
					sum &amp;amp;=0xff;&lt;br /&gt;
					if(sum)&lt;br /&gt;
					{&lt;br /&gt;
						error++;&lt;br /&gt;
						printf(&amp;quot;Line %d Checksum error (sum=%d)\n&amp;quot;,line, sum); &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else if( c !=10 &amp;amp;&amp;amp; c !=13)&lt;br /&gt;
			{&lt;br /&gt;
				error ++;&lt;br /&gt;
				printf( &amp;quot;Line %d unexpected character (%c)\n&amp;quot;,line, c); // characters I don&#039;t know  &lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		printf( &amp;quot;\nread %d charactes, from adress %d to %d with %d errors\n&amp;quot;, chars, minAdr, maxAdr,error); &lt;br /&gt;
		&lt;br /&gt;
		// generate output data &lt;br /&gt;
		if(error==0 &amp;amp;&amp;amp; minAdr==0) &lt;br /&gt;
		{&lt;br /&gt;
			int blocks; &lt;br /&gt;
			int blockNr; &lt;br /&gt;
			unsigned char block[MAX_SYX_BLOCK];	// buffer to collect/prepare one binary data block &lt;br /&gt;
			unsigned char stp[MAX_SYX_DATA]; 	//  output data buffer, collects the sysEx data&lt;br /&gt;
			unsigned char *str;		// current write pointer &lt;br /&gt;
			int adr; &lt;br /&gt;
			int padded;&lt;br /&gt;
			FILE *wp;&lt;br /&gt;
			int i;&lt;br /&gt;
			&lt;br /&gt;
			str=stp; &lt;br /&gt;
			&lt;br /&gt;
			blocks= (((maxAdr)/64)+1);	// total number of blocks &lt;br /&gt;
						&lt;br /&gt;
			str+= makePrgPrep(str);		// generate sys-ex header (=switch to bootloader command) &lt;br /&gt;
			&lt;br /&gt;
			// first block is a spezial block - it tells controller number of blocks he has to expect. &lt;br /&gt;
			blockNr=0;&lt;br /&gt;
			block[0]=blockNr;&lt;br /&gt;
			block[1]=blocks;&lt;br /&gt;
			block[2]=0;	// reserved &lt;br /&gt;
			block[3]=0;	// reserved &lt;br /&gt;
			str += makeSysEx(block,4,str);	// generate SysEx from block data &lt;br /&gt;
			&lt;br /&gt;
			adr=minAdr;&lt;br /&gt;
			// make flash data blocks &lt;br /&gt;
			// flash data blocks are 65 bytes, 1 Byte Block number, 64 data bytes&lt;br /&gt;
			// the controller allways writes 64 byte blocks to flash, so this format &lt;br /&gt;
			// helps to keep the controller program straight. &lt;br /&gt;
			while (blockNr&amp;lt;blocks)&lt;br /&gt;
			{&lt;br /&gt;
				blockNr++;&lt;br /&gt;
				block[0]=blockNr;	// first data byte in block is block number (so controller sees, when a block is lost) &lt;br /&gt;
				padded=0; &lt;br /&gt;
				for(i=0;i&amp;lt;64;i++)&lt;br /&gt;
				{&lt;br /&gt;
					if(adr&amp;gt;maxAdr)	 // it beyond highest adresses from hex file:  last block is(must be) padded to 64 bytes &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=padit[padded];&lt;br /&gt;
						padded++;&lt;br /&gt;
					}&lt;br /&gt;
					else	// write programm data &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=data[adr++];					&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				str +=makeSysEx(block,65,str);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			// write output file &lt;br /&gt;
			wp=fopen(argv[1],&amp;quot;wb&amp;quot;);&lt;br /&gt;
			if(wp)&lt;br /&gt;
			{&lt;br /&gt;
				fwrite ((void *) stp ,sizeof(unsigned char), str-stp, wp);&lt;br /&gt;
				fclose(wp);&lt;br /&gt;
				&lt;br /&gt;
				printf(&amp;quot;%d sysEx data blocks in %d bytes written\n&amp;quot;,blocks, str-stp);&lt;br /&gt;
			}&lt;br /&gt;
			else &lt;br /&gt;
				printf(&amp;quot;Cant open output file %s\n&amp;quot;,argv[1]);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			if(minAdr)&lt;br /&gt;
				printf(&amp;quot;Lowest adress %d is not zero!\n&amp;quot;,minAdr);&lt;br /&gt;
			return -2;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
		printf (&amp;quot;Input file %s not found\n&amp;quot;,argv[1]);&lt;br /&gt;
&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:ProjectSettings.gif&amp;diff=7319</id>
		<title>Datei:ProjectSettings.gif</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:ProjectSettings.gif&amp;diff=7319"/>
		<updated>2011-06-27T18:48:59Z</updated>

		<summary type="html">&lt;p&gt;Fetz: uploaded a new version of &amp;quot;File:ProjectSettings.gif&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Project settings dialog box screen shot&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7318</id>
		<title>MCV876 Controller Update: Source Code</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7318"/>
		<updated>2011-06-27T18:47:10Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Übersetzen-Abschnitt um die Projekteinstellungen erweitert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Übersetzen= &lt;br /&gt;
Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden.&lt;br /&gt;
Im Studio ein neues, leeres Projekt mit Standard-Einstellungen, einem sinnvollen Namen und an einem Ort, wo man es wiederfindet erstellen.&lt;br /&gt;
In den Projekteinstellungen die passenden CPU wählen und die Taktfrequenz einstellen. Project-&amp;gt;Configuration-Options:&lt;br /&gt;
&lt;br /&gt;
[[File:ProjectSettings.gif]]&lt;br /&gt;
&lt;br /&gt;
Dann Quelltext in das (automatisch erzeugte, leere) C-File kopieren, auf Build-&amp;gt;Build klicken, fertig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das resultierende HEX File kann man mit einem üblichen Programmieradapter in der Chip flashen, für uns zweckmäßiger ist allerdings, das in ein Sys-Ex File zu Konvertieren ( [[MCV876_Controller_Update:_File_Converter]]), und das über Midi an den Controller zu schicken. Dann programmiert der sich selber um. Dazu muss er allerdings einmalig mit dem Bootlader ([[MCV876_Controller_Update:_Bootloader]]) programmiert worden sein. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu den Atmel Controllern findet man eine Menge Info mit Tante Google, [http://www.mikrocontroller.net http://www.mikrocontroller.net] kommt häufig vor, und hat ein ergiebiges Forum und gute Artikel. Auf der Atmel Webseite gibt es auch einige Info, das [http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf ATMega 88 Datenblatt] ist spätestens dann nötig, wenn es um die Hardware geht.&lt;br /&gt;
&lt;br /&gt;
=Quelltext=&lt;br /&gt;
&amp;lt;pre&amp;gt;/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Midi to 4 Channel Control Voltage and six Gates Interface &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.22 2011-06-26 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
			&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;			// yes, we want to talk to the port pins by name&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;		// ... oh.. and some eeprom access would be nice&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;	// ... and w/o the interupts it wouldn&#039;t do anything... &lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*	===================ZERO LINE=================================&lt;br /&gt;
	WARNING: everthing below the zero line counts from zero! &lt;br /&gt;
	e.g. whats Gate 6 in manual is GATE5 here, CVs are CV0 to 3 &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	where should the LED output be &lt;br /&gt;
*/&lt;br /&gt;
#define LEDLED   6  // LED at Reset-Pin, standard for series, but no debugging possible. &lt;br /&gt;
#define LEDGATE0 0	// Led at Gate 0, no LED (Test I: no LED ) &lt;br /&gt;
#define LEDGATE4 4	// LED at Gate 5, Test II: no Gate 5 Function (Midi Start/Stop) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 #define LEDOUT LEDLED // select your evil... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define ACTIVE_SENSE_TICKS 160 // 2ms Timer Ticks for active sense time out &lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4))) // True if Button pressed &lt;br /&gt;
&lt;br /&gt;
#define NUM_CV 4&lt;br /&gt;
&lt;br /&gt;
#define PARAMETEROFFSET 0x23 // First paramter has adress 0x23&lt;br /&gt;
#define NUM_PARAMTERS 17	 // and there are 17 parameters &lt;br /&gt;
&lt;br /&gt;
#define MODE_MONO 	0x00	// monophon, all assigned to last note on a single channel &lt;br /&gt;
#define MODE_MULTI2	0x40	// two channel, 1/2 for midiChannel, 2/3 for midiChannel+1 &lt;br /&gt;
#define MODE_MULTI4	0x60	// for channel midiChannel to midiChannel+3 &lt;br /&gt;
#define MODE_POLY2	0x80	// not supported &lt;br /&gt;
#define MODE_POLY4	0xA0	// not supported&lt;br /&gt;
&lt;br /&gt;
struct CONFIG {&lt;br /&gt;
	unsigned char midiChannel;&lt;br /&gt;
	unsigned char noteOffset;&lt;br /&gt;
&lt;br /&gt;
	unsigned char cvMidiCC[NUM_CV];&lt;br /&gt;
	unsigned char gateMidiCC[NUM_CV];&lt;br /&gt;
	&lt;br /&gt;
	unsigned char gateTreshhold;&lt;br /&gt;
	unsigned char clockDivider;&lt;br /&gt;
	unsigned char clockTime; &lt;br /&gt;
	unsigned char ifMode0; 	// 	bit 0 ... bit 3 = CV0 .. CV3, 0=Pitch, 1=CC (if pitch, CC assignement is ignored) &lt;br /&gt;
							//	bit 5: voices: 0=2; 1=4 &lt;br /&gt;
							//  bit 7:6= 00 Mono; 01:Multi-Channel; 10: Poly (not supported); &lt;br /&gt;
	&lt;br /&gt;
	unsigned char ifMode1;  // Gate Trigger Mode Bits (0=Re-Trigger, 1=TriggerControl)  &lt;br /&gt;
	unsigned char ifMode2;  // bit0: Velo0=&amp;gt;CV0; bit1:Velo0=&amp;gt;CV1; bit2:Velo-Poly2=&amp;gt;CV2; bit3:Velo1=&amp;gt;CV3&lt;br /&gt;
							// bit4: PB=&amp;gt;CV0; bit5:PB=&amp;gt;CV1; bit6:PB=&amp;gt;CV2; bit7:PB=&amp;gt;CV3&lt;br /&gt;
	unsigned char ifMode3; 	// bit0..bit3 = CV0... CV3; 0=10V, 1=5V Mode &lt;br /&gt;
&lt;br /&gt;
//  internal evaluated configuration &lt;br /&gt;
	unsigned char numMidiChannels;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
} shadow_config, // her goes a copy, when config should be written, because that might take a while &lt;br /&gt;
&lt;br /&gt;
config 	// current configuration, mostly direct evaluated troughout the program&lt;br /&gt;
= {		// default values (Mono-Mode, Midi-Ch 1, CV1=Pitch, CV2=Velocity CV3=ModWheel CV4=Pitch Bend&lt;br /&gt;
			0,  	// Midi Channel &lt;br /&gt;
			24, 	// Note Offset (2 Oktaves) &lt;br /&gt;
		{	255,255,1,255, },  	// CV - assigned Midi Controllers (used if not pitch output) 255=none&lt;br /&gt;
		{   255,18,19,20, }, 	// Gate - asigned Controllers (if not pitch) 255=none &lt;br /&gt;
			63, 	// Treshold&lt;br /&gt;
			6,  	// Divider 6 (1/16 Note) &lt;br /&gt;
			1,  	// clock time (unused) &lt;br /&gt;
			0x0e, 	// mode 0 Low nibble CC-Bits, high: interface mode &lt;br /&gt;
			0, 		// mode 1 re-trigger bits &lt;br /&gt;
			0x82, 	// mode 2 High Nibble: PB-bits, Low: Velo-Bits&lt;br /&gt;
			0,    	// mode 3 (5V-Bits) &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define EE_MAGIC 17&lt;br /&gt;
unsigned char ee_magic EEMEM;	// to check it eeprom was ever written and should be read back &lt;br /&gt;
unsigned char config_eeprom[NUM_PARAMTERS] EEMEM; // Config in EEPROM, saves it during power down. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
volatile unsigned char  eepromWriteFlag;	// set to one, if main-loop should write eeprom &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char button; 	// debounced button status &lt;br /&gt;
volatile unsigned char learn; 	// (channel/base note/cc) &amp;quot;do learn&amp;quot; status &lt;br /&gt;
volatile unsigned char blinker; // some count for blinkenlights &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char gate5off; // timer counter for when clock gate output should go low again. &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char watchTicker;	// midi active sense counter &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setConfig(void); 		&lt;br /&gt;
void setLed(unsigned char on);&lt;br /&gt;
void resetAll(void);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	main() &lt;br /&gt;
	Main does initialization, than runs a loop, which does nearly nothing: &lt;br /&gt;
&lt;br /&gt;
	Programming the EEPROM (because its slow) &lt;br /&gt;
	Blinking &lt;br /&gt;
	Button evaluation &lt;br /&gt;
&lt;br /&gt;
	All the important things are done by the interupts. (Thanks to the &lt;br /&gt;
	20MHZ RISC power everything can be done in less than 320µs=one Midi-Character) &lt;br /&gt;
*/&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char blink=4;&lt;br /&gt;
&lt;br /&gt;
	unsigned char i,x; &lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
	static unsigned char lastButton; &lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
	// Read Configuration &lt;br /&gt;
	_delay_ms(10); // wait for button pin to get high if open. &lt;br /&gt;
	if( eeprom_read_byte(&amp;amp;ee_magic) == EE_MAGIC  &amp;amp;&amp;amp;   !BUTTON) // if eeprom valid AND Button not pressed &lt;br /&gt;
	{&lt;br /&gt;
		cp=(unsigned char* )&amp;amp;config; &lt;br /&gt;
		for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
		{&lt;br /&gt;
			x=eeprom_read_byte(config_eeprom+i);			// read config from eeprom &lt;br /&gt;
			*cp=x;	&lt;br /&gt;
			cp++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
		blink=8; &lt;br /&gt;
&lt;br /&gt;
	setConfig();&lt;br /&gt;
&lt;br /&gt;
	// enable interrupts&lt;br /&gt;
	sei(); &lt;br /&gt;
&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		if(eepromWriteFlag)&lt;br /&gt;
		{	// save configuration &lt;br /&gt;
			cp=(unsigned char* )&amp;amp;shadow_config; &lt;br /&gt;
			for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
			{&lt;br /&gt;
				x=*cp;&lt;br /&gt;
				eeprom_write_byte(config_eeprom+i,x);&lt;br /&gt;
				cp++;&lt;br /&gt;
			}&lt;br /&gt;
			eeprom_write_byte(&amp;amp;ee_magic,EE_MAGIC); &lt;br /&gt;
&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			blink=6;&lt;br /&gt;
			blinker=0;&lt;br /&gt;
			eepromWriteFlag=0;	&lt;br /&gt;
		}&lt;br /&gt;
		if(blink &amp;amp;&amp;amp; blinker&amp;gt;2)  // blink after power up and after storing in nv-memory &lt;br /&gt;
		{&lt;br /&gt;
			blink--;&lt;br /&gt;
			setLed(blink&amp;amp;1);&lt;br /&gt;
			blinker=0; &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if(button != lastButton)&lt;br /&gt;
		{&lt;br /&gt;
			if(button==1 )&lt;br /&gt;
			{&lt;br /&gt;
				resetAll();&lt;br /&gt;
				if(learn==1)&lt;br /&gt;
				{&lt;br /&gt;
					learn=0;&lt;br /&gt;
					setLed(0);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if(button==6) &lt;br /&gt;
			{&lt;br /&gt;
				if(learn ==0)&lt;br /&gt;
				{&lt;br /&gt;
					learn=1;&lt;br /&gt;
					setLed(1);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			lastButton=button; &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return 0; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Hardware Interface&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
/*  Pin assignment &lt;br /&gt;
   Atmel Fkt 	[Orig] &lt;br /&gt;
1  Reset LED 	[MCLR] &lt;br /&gt;
2  RX    Midi-Rx[WR1]&lt;br /&gt;
3  PD1   WR2&lt;br /&gt;
4  PD2   WR3&lt;br /&gt;
5  PD3   WR4&lt;br /&gt;
6  PD4   Button&lt;br /&gt;
7  VCC   +5  	[LED] (Either from Atmel-1(Series) or Atmel-15(w/Debug))&lt;br /&gt;
8  GND   GND&lt;br /&gt;
9  OSC   OSC&lt;br /&gt;
10 OSC   OSC &lt;br /&gt;
11 PD5   G1&lt;br /&gt;
12 PD6   G2&lt;br /&gt;
13 PD7   G3&lt;br /&gt;
14 PB0   G4&lt;br /&gt;
&lt;br /&gt;
15 PB1   G5 (Optional + LED) &lt;br /&gt;
16 PB2   G6 &lt;br /&gt;
17 PB3   WR1    [---] &lt;br /&gt;
18 PB4   D0     [RX]&lt;br /&gt;
19 PB5   D1		[GND]&lt;br /&gt;
20 AVCC  +5&lt;br /&gt;
21 ARef  +5 	[D0]&lt;br /&gt;
22 GND   GND	[D1] &lt;br /&gt;
23 PC0   D2&lt;br /&gt;
24 PC1   D3&lt;br /&gt;
25 PC2   D4&lt;br /&gt;
26 PC3   D5&lt;br /&gt;
27 PC4   D6&lt;br /&gt;
28 PC5   D7&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
// put &amp;quot;val&amp;quot; to the right port bits for the DA-Converter &lt;br /&gt;
// ... better program than solder *that*... &lt;br /&gt;
void adByteOut(unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char b;&lt;br /&gt;
	b=val;&lt;br /&gt;
	b&amp;gt;&amp;gt;=2;&lt;br /&gt;
	PORTC=b;&lt;br /&gt;
	b=val&amp;amp;3;&lt;br /&gt;
	b&amp;lt;&amp;lt;=4;&lt;br /&gt;
	val=PORTB;&lt;br /&gt;
	val &amp;amp;=0xCF;&lt;br /&gt;
	val|=b;&lt;br /&gt;
	PORTB=val;&lt;br /&gt;
}&lt;br /&gt;
// set DA-Output voltage of one CV Channel  &lt;br /&gt;
void set_cv(unsigned char val, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	adByteOut(val); 	// set D0..D7 converter data bits &lt;br /&gt;
	switch (channel)	// pulse with write pin &lt;br /&gt;
	{&lt;br /&gt;
		case 0: //PB3&lt;br /&gt;
			PORTB  &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTB  |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD1&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;1);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;1);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD2&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;2);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;2);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PD3&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// set on of the gate outputs active &lt;br /&gt;
void gate_on(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// clear one of the gate outputs &lt;br /&gt;
void gate_off(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif&lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Data Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
typedef struct {&lt;br /&gt;
	unsigned char channel;  // (relative) Midi channel (0..3) Mode dependant &lt;br /&gt;
	unsigned char cvOut;	// for which CV-Ouput (0..3) fixed &lt;br /&gt;
	unsigned char ccVal;	// current CC Value &lt;br /&gt;
	unsigned char note;		// current Note &lt;br /&gt;
	unsigned char velo;		// current Velocity &lt;br /&gt;
	unsigned char on;		// if Note is on &lt;br /&gt;
	signed char   pb;		// current Pitch Bend Value &lt;br /&gt;
	unsigned char gateDipper;&lt;br /&gt;
} cvoutput;  &lt;br /&gt;
&lt;br /&gt;
cvoutput cv_out[NUM_CV];	// current state for each cv out&lt;br /&gt;
&lt;br /&gt;
unsigned char gates; // which gates are on =&amp;gt; used for LED setting &lt;br /&gt;
&lt;br /&gt;
// set cv output (and assigned gate, if pitch output)&lt;br /&gt;
// called after anything that changes the cv_out structure  &lt;br /&gt;
void updateCv(cvoutput *cvo)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char bm;&lt;br /&gt;
	unsigned char gated=0;&lt;br /&gt;
	int val=0;&lt;br /&gt;
&lt;br /&gt;
	bm=1&amp;lt;&amp;lt;cvo-&amp;gt;cvOut;	// bit mask for configuration flags &lt;br /&gt;
&lt;br /&gt;
	if(config.ifMode0 &amp;amp; bm)	// 1=CC-Mode&lt;br /&gt;
	{&lt;br /&gt;
		if(config.cvMidiCC[cvo-&amp;gt;cvOut]&amp;gt;127) // if no Controller assigned &lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		// if PB is active &lt;br /&gt;
				val=cvo-&amp;gt;pb+0x80;		 		//     pitch sets unipolar value &lt;br /&gt;
			else 						 		// else &lt;br /&gt;
				val=0; 					 		//    value is zero &lt;br /&gt;
		}&lt;br /&gt;
		else 								// else a controller is assigned &lt;br /&gt;
		{&lt;br /&gt;
			val=cvo-&amp;gt;ccVal; 					//  set current controler value ;&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		//  if PB active &lt;br /&gt;
				val+=cvo-&amp;gt;pb;			 		//     add it (bipolar) &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if( (config.ifMode1 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0) // if Trig Flag and note off &lt;br /&gt;
			gated=1; 								// than gate CV output (to zero) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
		if( (config.ifMode3 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0)	// 5V-Flag, abused as Gated-Mode-CV Flag &lt;br /&gt;
			gated=1;&lt;br /&gt;
*/&lt;br /&gt;
	}&lt;br /&gt;
	else // Note&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;note;&lt;br /&gt;
		val -= config.noteOffset;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode3 &amp;amp; bm)	// 5V&lt;br /&gt;
			val &amp;lt;&amp;lt;= 2;&lt;br /&gt;
		else&lt;br /&gt;
			val &amp;lt;&amp;lt;= 1;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) // PB &lt;br /&gt;
			val+=(cvo-&amp;gt;pb&amp;gt;&amp;gt;2);  // scale down pb - value is empirical and works &amp;quot;not too bad&amp;quot; &lt;br /&gt;
								//  (for &amp;quot;good&amp;quot; the converter should have 12bits a least... ) &lt;br /&gt;
	}&lt;br /&gt;
	if(config.ifMode2 &amp;amp; bm) // Velocity &lt;br /&gt;
		val+=cvo-&amp;gt;velo;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if(val&amp;lt;0 || gated)	// clip values to converter range &lt;br /&gt;
		val=0;&lt;br /&gt;
	if(val&amp;gt;255)&lt;br /&gt;
		val=255;&lt;br /&gt;
&lt;br /&gt;
	set_cv((unsigned char)val, cvo-&amp;gt;cvOut);&lt;br /&gt;
	if((config.ifMode0 &amp;amp; bm)==0)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;on)&lt;br /&gt;
		{&lt;br /&gt;
			if(cvo-&amp;gt;gateDipper==0) // no retrigger required&lt;br /&gt;
				gate_on(cvo-&amp;gt;cvOut);&lt;br /&gt;
			else				   // dip gate for a few milliseconds 	&lt;br /&gt;
				gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates |=bm;&lt;br /&gt;
		}&lt;br /&gt;
		else &lt;br /&gt;
		{&lt;br /&gt;
			gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates &amp;amp;=~bm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate/initialize control variables after &lt;br /&gt;
// mode-configuration change &lt;br /&gt;
void setConfig()&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i; &lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0; &lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	switch(config.ifMode0 &amp;amp; 0xf0 )&lt;br /&gt;
	{	&lt;br /&gt;
		default:&lt;br /&gt;
		case MODE_MONO:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=1;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI2:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV/2;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			for(;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=1;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=2;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI4:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=i;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=NUM_CV;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// assign new note state to cv outputs &lt;br /&gt;
void setNote(unsigned char note, unsigned char velo, unsigned char onOff, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;channel==channel)&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;note=note;&lt;br /&gt;
			cvo-&amp;gt;velo=velo;&lt;br /&gt;
			if(onOff==2 &amp;amp;&amp;amp; cvo-&amp;gt;on &amp;amp;&amp;amp; (config.ifMode1 &amp;amp; (1&amp;lt;&amp;lt;i)))&lt;br /&gt;
				cvo-&amp;gt;gateDipper=4;&lt;br /&gt;
			cvo-&amp;gt;on=onOff;&lt;br /&gt;
			updateCv(cvo);&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	setLed(gates);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process new continous controller value &lt;br /&gt;
void setCC(unsigned char cc, unsigned char value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel== cvo-&amp;gt;channel )&lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode0 &amp;amp;(1&amp;lt;&amp;lt;i))&lt;br /&gt;
			{&lt;br /&gt;
				if(config.cvMidiCC[i]==cc)&lt;br /&gt;
				{&lt;br /&gt;
					cvo-&amp;gt;ccVal=value; &lt;br /&gt;
					updateCv(cvo);&lt;br /&gt;
				}&lt;br /&gt;
				if(config.gateMidiCC[i]==cc  )&lt;br /&gt;
				{&lt;br /&gt;
					if(value&amp;gt;config.gateTreshhold)&lt;br /&gt;
						gate_on(i);&lt;br /&gt;
					else &lt;br /&gt;
						gate_off(i);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// process new pitch bend value &lt;br /&gt;
void setPb(signed int value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel==cvo-&amp;gt;channel &amp;amp;&amp;amp; (config.ifMode2 &amp;amp; (16&amp;lt;&amp;lt;i)) )&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;pb	=(signed char) (value&amp;gt;&amp;gt;5);	&lt;br /&gt;
			updateCv(cvo);	&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// prepare for (asynchronous) configuration write to eeprom &lt;br /&gt;
void setEeWrite(void)&lt;br /&gt;
{&lt;br /&gt;
	shadow_config=config; &lt;br /&gt;
	learn=0;&lt;br /&gt;
	eepromWriteFlag=1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*bootloader)(void) = (void*)0x1800;	// Bootloader sits at adress 0x1800 ... &lt;br /&gt;
&lt;br /&gt;
// process sys ex command &lt;br /&gt;
void setSyx(unsigned char adr, unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
&lt;br /&gt;
	cp=(unsigned char *) &amp;amp;config; &lt;br /&gt;
&lt;br /&gt;
	if(adr==0x14 &amp;amp;&amp;amp; val ==0x7b) &lt;br /&gt;
	{	// write eeprom&lt;br /&gt;
		if(eepromWriteFlag==0)&lt;br /&gt;
		{&lt;br /&gt;
			setEeWrite();&lt;br /&gt;
		}&lt;br /&gt;
		return; &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7&lt;br /&gt;
	if(adr==0x16 &amp;amp;&amp;amp; val ==0x42)	// Software update  &lt;br /&gt;
	{&lt;br /&gt;
		cli();			// interupts off &lt;br /&gt;
		resetAll();		// outputs off &lt;br /&gt;
		GPIOR1 = 0xAB;	// tell bootloader it&#039;s an application boot request &lt;br /&gt;
						// (hardware reset sets GPIOR1 to zero, 0xab == magic value for bootloader )&lt;br /&gt;
		bootloader();	// bye bye! &lt;br /&gt;
		return; 	// &amp;lt;-- never! &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	adr-=PARAMETEROFFSET; &lt;br /&gt;
	if(adr &amp;lt; NUM_PARAMTERS)	// if valid parameter &lt;br /&gt;
	{&lt;br /&gt;
		*(cp+adr)=val; 		// set in &lt;br /&gt;
		if(adr==0x30-PARAMETEROFFSET) // if Mode Byte 0, &lt;br /&gt;
			setConfig();			  // .. cv assignement must be recalculated &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Note Puffer Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define MAX_NOTES 0x10  	// one for each finger of a real programmer &lt;br /&gt;
#define MAX_CHANNELS 4&lt;br /&gt;
unsigned char notes[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char velocities[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char playedNote[MAX_CHANNELS]; // number of currently pressed notes &lt;br /&gt;
// update note puffer for a midi note-on message &lt;br /&gt;
void note_on(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char n;&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	if(n&amp;lt;MAX_NOTES)&lt;br /&gt;
	{&lt;br /&gt;
		playedNote[channel]++;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		for (n=MAX_NOTES-1;n&amp;gt;0;--n)&lt;br /&gt;
		{&lt;br /&gt;
			notes[channel][n-1]=notes[channel][n];&lt;br /&gt;
			velocities[channel][n-1]=velocities[channel][n];&lt;br /&gt;
		}&lt;br /&gt;
		n=MAX_NOTES-1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	notes[channel][n]=note; 		&lt;br /&gt;
	velocities[channel][n]=velocity;&lt;br /&gt;
	setNote(note, velocity, 2, channel);&lt;br /&gt;
}&lt;br /&gt;
// update note puffer for a new midi note-off message &lt;br /&gt;
void note_off(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	signed char n; &lt;br /&gt;
&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	--n; &lt;br /&gt;
	for (i=0;i&amp;lt;=n;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(notes[channel][i]==note)&lt;br /&gt;
		{&lt;br /&gt;
			if(i==n) // last pressed (== the on which playes) note released &lt;br /&gt;
			{&lt;br /&gt;
				if(n==0) // it was the only pressed note &lt;br /&gt;
				{&lt;br /&gt;
					setNote(note, velocities[channel][n], 0, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{	&lt;br /&gt;
					--n;&lt;br /&gt;
					setNote(notes[channel][n], velocities[channel][n], 1, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else // non playing note released &lt;br /&gt;
			{	// =&amp;gt;  just remove it from list &lt;br /&gt;
				int j;&lt;br /&gt;
				for(j=i+1;j&amp;lt;=n;++j)&lt;br /&gt;
				{&lt;br /&gt;
					notes[channel][j-1]=notes[channel][j];&lt;br /&gt;
					velocities[channel][j-1]=velocities[channel][j];&lt;br /&gt;
				}&lt;br /&gt;
				playedNote[channel]--;&lt;br /&gt;
			}&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	All Notes off, empty Note Buffer, All Controllers Reset&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void resetAll(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	cli(); // this might be called asynchronous to midi stream &lt;br /&gt;
	for(i=0;i&amp;lt;MAX_CHANNELS;++i)&lt;br /&gt;
		playedNote[i]=0;&lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0;&lt;br /&gt;
		cvo-&amp;gt;on=0;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		updateCv(cvo);&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
	sei();&lt;br /&gt;
&lt;br /&gt;
	setLed(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	static unsigned char buttonCount;&lt;br /&gt;
	static signed char blinkCount; &lt;br /&gt;
&lt;br /&gt;
	// LED blinker &lt;br /&gt;
	if(--blinkCount&amp;lt;=0)&lt;br /&gt;
	{	&lt;br /&gt;
		blinkCount=40;&lt;br /&gt;
		blinker++;&lt;br /&gt;
		if(button)&lt;br /&gt;
		{&lt;br /&gt;
			if(button&amp;lt;10)&lt;br /&gt;
				button++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Button debouncer &lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{	// down &lt;br /&gt;
		if(buttonCount &amp;lt; 10)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount++;&lt;br /&gt;
			if(buttonCount==10)&lt;br /&gt;
			{&lt;br /&gt;
				button=1;&lt;br /&gt;
				blinkCount=40;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{	// up &lt;br /&gt;
		if(buttonCount &amp;gt; 0)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount--;&lt;br /&gt;
			if(buttonCount==0)&lt;br /&gt;
				button=0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Gate 5 timing (clock output) &lt;br /&gt;
	if(gate5off)&lt;br /&gt;
	{&lt;br /&gt;
		--gate5off;&lt;br /&gt;
		if(gate5off==0)&lt;br /&gt;
			gate_off(5);	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// activ sense &lt;br /&gt;
	if(watchTicker)			// if active sense is activated &lt;br /&gt;
	{&lt;br /&gt;
		watchTicker--;		// decrement time &lt;br /&gt;
		if(watchTicker==0)	// if time elapsed&lt;br /&gt;
			resetAll(); 	// switch all notes off 	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// retrigger timing &lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)	// for all cv output channels &lt;br /&gt;
	{&lt;br /&gt;
		if(cv_out[i].gateDipper)	// if gate timer is active &lt;br /&gt;
		{&lt;br /&gt;
			--cv_out[i].gateDipper;		// decrement timer &lt;br /&gt;
			if(cv_out[i].gateDipper==0)	// if timer elapsed&lt;br /&gt;
				gate_on(i);				// activate gate &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Eval Midi Data&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define NOTE_ON         0x90		// Midi note on message &lt;br /&gt;
#define NOTE_OFF        0x80		// Midi note off message &lt;br /&gt;
#define PITCH           0xE0		// pitch bender&lt;br /&gt;
#define AFTERTOUCH      0xD0        // after touch command (unused) &lt;br /&gt;
#define CC		        0xB0        // Continuous Controller&lt;br /&gt;
#define CHAN_PRES	    0xD0        // Channel Pressure (unused)&lt;br /&gt;
#define SONG_POSITION_POINTER 0xF2	// used for clock synchronization (first beat) &lt;br /&gt;
#define SYSEX 			0xF0		// System exclusive beginn&lt;br /&gt;
#define SYSEND			0xF7		// System exclusive end&lt;br /&gt;
&lt;br /&gt;
struct {&lt;br /&gt;
	unsigned char runningStatus;	// Midi runnig status (*)&lt;br /&gt;
	unsigned char msgByteCnt;    	// number of current midi-message byte received &lt;br /&gt;
	unsigned char byte1;			// first data byte &lt;br /&gt;
	unsigned char syxAdr; 			// Sys-Ex Adress Byte&lt;br /&gt;
	unsigned char startStop;		// current  Midi Start/Stop Status&lt;br /&gt;
	unsigned char startFlag;		// Start on next clock&lt;br /&gt;
	unsigned char clockTick;		// counter for clock division&lt;br /&gt;
	unsigned char channel;			// channel of current midi-command (relative, e.g. 0..3 ) 						&lt;br /&gt;
	unsigned int  spp; 				// song position pointer (from midi message), needed to have the clock synced to song position&lt;br /&gt;
}midi; &lt;br /&gt;
// 	(*) running status is also used as &#039;current status&#039; - it remembers allways, what next data byte is for. Or its zero, than data is ignored. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine, &lt;br /&gt;
// complete midi processing is done here, as the CPU is fast enough to &lt;br /&gt;
// do everything before next midi byte can come in. &lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	unsigned char status,byte;&lt;br /&gt;
&lt;br /&gt;
    status = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((status &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(watchTicker)						// if there had been an active sense &lt;br /&gt;
			watchTicker=ACTIVE_SENSE_TICKS;	// every new byte is ok, to &amp;quot;feed the watchdog&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		if (byte &amp;gt; 0x7F)	                // if command byte (MSB set)&lt;br /&gt;
		{&lt;br /&gt;
			if(byte&amp;gt;= 0xF8)  				// if real-time msg &lt;br /&gt;
			{&lt;br /&gt;
				switch (byte)&lt;br /&gt;
				{&lt;br /&gt;
					case 0xF8: 	// Clock &lt;br /&gt;
					{&lt;br /&gt;
						if(midi.startFlag)  // if synchronized start &lt;br /&gt;
						{&lt;br /&gt;
							midi.startStop=1;&lt;br /&gt;
							midi.startFlag=0;&lt;br /&gt;
&lt;br /&gt;
							if(config.clockDivider&amp;gt;1 )// if divided clock &lt;br /&gt;
							{&lt;br /&gt;
								if( midi.spp) // if start not from zero &lt;br /&gt;
								{&lt;br /&gt;
									midi.clockTick=midi.spp%config.clockDivider;			// calculate clock divider count for current song position &lt;br /&gt;
									if(midi.clockTick &amp;gt;= config.clockDivider&amp;gt;&amp;gt;1)	// if calculated divider count is mor than half elapsed &lt;br /&gt;
										gate_off(5); 								// than set clock output off &lt;br /&gt;
									else&lt;br /&gt;
										gate_on(5);									// else set it on &lt;br /&gt;
								}&lt;br /&gt;
								else &lt;br /&gt;
								{&lt;br /&gt;
									gate_on(5);&lt;br /&gt;
									midi.clockTick=0; &lt;br /&gt;
								}&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						else &lt;br /&gt;
						{&lt;br /&gt;
							if(midi.startStop)	// if (already) running &lt;br /&gt;
								++midi.spp;&lt;br /&gt;
							++midi.clockTick; 	// increment counter for clock output &lt;br /&gt;
						}&lt;br /&gt;
&lt;br /&gt;
						if(config.clockDivider&amp;lt;=1)&lt;br /&gt;
						{&lt;br /&gt;
							gate_on(5);								// do clock output &lt;br /&gt;
							gate5off=3;							&lt;br /&gt;
						}&lt;br /&gt;
						else&lt;br /&gt;
						{&lt;br /&gt;
							if(midi.clockTick &amp;gt;= config.clockDivider)	// if divider count elapsed &lt;br /&gt;
							{&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
								gate_on(5);								// do clock output &lt;br /&gt;
							}&lt;br /&gt;
							else if(midi.clockTick == config.clockDivider&amp;gt;&amp;gt;1)	// if divider count half elapsed &lt;br /&gt;
								gate_off(5); 									// set clock output off &lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
					case 0xF9:	// Tick (unused) &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFA: 	// Start &lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.spp=0;&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
							if(config.clockDivider&amp;gt;1)&lt;br /&gt;
							{&lt;br /&gt;
								gate_off(5); 			// set clock output off 								&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFB:	// Continue&lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
						}&lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFC:	// Stop&lt;br /&gt;
						midi.startStop=0;&lt;br /&gt;
						gate_off(4);&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFD:	// unassigned &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFE:	//  Active Sense&lt;br /&gt;
						watchTicker=ACTIVE_SENSE_TICKS;&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFF:	// Reset &lt;br /&gt;
						break; &lt;br /&gt;
				}&lt;br /&gt;
				return ; &lt;br /&gt;
			} &lt;br /&gt;
&lt;br /&gt;
			if(byte &amp;gt;= 0xF0) 					// if System Common Category message&lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)&lt;br /&gt;
				{&lt;br /&gt;
					if(midi.runningStatus==SYSEX &amp;amp;&amp;amp; midi.msgByteCnt==6) // if a complete valid Sys-Ex Frame has been received &lt;br /&gt;
						setSyx(midi.syxAdr, midi.byte1); 				// change configuration &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					midi.runningStatus = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				else if(byte ==SONG_POSITION_POINTER)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;  &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				//							// every other System Common is ignored 	&lt;br /&gt;
				midi.runningStatus = 0;		// just reset running status &lt;br /&gt;
				return ; 					// and done &lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.runningStatus = 0;				// just new status messages left, so running status is lost&lt;br /&gt;
&lt;br /&gt;
			// only channel messages left here ... &lt;br /&gt;
			if(learn)								 // if learn mode &lt;br /&gt;
			{&lt;br /&gt;
				if(config.midiChannel != (byte&amp;amp;0xf)) // if message channel not current channel&lt;br /&gt;
				{&lt;br /&gt;
					config.midiChannel=byte&amp;amp;0xf;	 // assign channel &lt;br /&gt;
					setEeWrite(); 					 // and store it &lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.channel=(byte &amp;amp; 0xf)-config.midiChannel ;&lt;br /&gt;
			if ( midi.channel&amp;lt;config.numMidiChannels)   // if MIDI channel matches,&lt;br /&gt;
			{&lt;br /&gt;
				midi.runningStatus = byte &amp;amp; 0xF0 ;		// set running status (==Midi command w/o channel)&lt;br /&gt;
				midi.msgByteCnt = 0;&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
			return ; &lt;br /&gt;
		}&lt;br /&gt;
		else	// data bytes goes here &lt;br /&gt;
		{&lt;br /&gt;
			switch (midi.runningStatus)		    // last command byte (or zero, if data is not for us) &lt;br /&gt;
			{&lt;br /&gt;
			case SONG_POSITION_POINTER:&lt;br /&gt;
				if(midi.msgByteCnt)	// 2nd Byte&lt;br /&gt;
				{&lt;br /&gt;
					midi.spp= (((int)byte &amp;lt;&amp;lt; 7 ) | midi.byte1) * 6;&lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt=1;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_ON):					// if we&#039;ve had a note on byte&lt;br /&gt;
				if (midi.msgByteCnt)		// if counter = 1&lt;br /&gt;
				{ 			   				// then we&#039;ve just received a velocity byte&lt;br /&gt;
					midi.msgByteCnt = 0;	// msg complete - clear byte count &lt;br /&gt;
					if (byte == 0)			// is it a note off (velo = 0) ?&lt;br /&gt;
						note_off(midi.byte1,byte,midi.channel);  &lt;br /&gt;
					else 					// NOTE ON&lt;br /&gt;
					{&lt;br /&gt;
						if(learn)&lt;br /&gt;
						{&lt;br /&gt;
							config.noteOffset=midi.byte1;&lt;br /&gt;
							setEeWrite();&lt;br /&gt;
						}&lt;br /&gt;
						&lt;br /&gt;
						note_on(midi.byte1, byte,midi.channel);  // ... and do what the note on has told us &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				else						// first data byte &lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;		// save it &lt;br /&gt;
					midi.msgByteCnt = 1;    // set counter to get 2nd data byte&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_OFF): 	        	// command note off&lt;br /&gt;
				if (midi.msgByteCnt)	 	// if complete &lt;br /&gt;
				{ 					 	&lt;br /&gt;
					midi.msgByteCnt = 0;	&lt;br /&gt;
					note_off(midi.byte1,byte,midi.channel); // switch note off&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1; 	&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (PITCH):		        	// PITCH BEND command&lt;br /&gt;
				if (midi.msgByteCnt)   	 	// if second data byte (MSB) &lt;br /&gt;
				{ 						&lt;br /&gt;
					int v;&lt;br /&gt;
					v=byte;&lt;br /&gt;
					v&amp;lt;&amp;lt;=6;&lt;br /&gt;
					v|=midi.byte1;&lt;br /&gt;
					v-=0x1000;&lt;br /&gt;
					setPb(v,midi.channel);&lt;br /&gt;
&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (CC):	             		// Continuous Controller Message &lt;br /&gt;
				if (midi.msgByteCnt)	&lt;br /&gt;
				{&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
&lt;br /&gt;
					if(learn)&lt;br /&gt;
					{&lt;br /&gt;
						config.cvMidiCC[2]=midi.byte1;&lt;br /&gt;
						setEeWrite();&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
					setCC(midi.byte1, byte,midi.channel);&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
				  midi.byte1 = byte;&lt;br /&gt;
				  midi.msgByteCnt = 1;&lt;br /&gt;
				  return; &lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
	//		case (CHAN_PRES):	         	// if channel pressure command&lt;br /&gt;
	//			channel_pressure (byte); 	// set Aftertouch &lt;br /&gt;
			case SYSEX:						// SYS-EX command &lt;br /&gt;
				midi.msgByteCnt++;			// message Byte counter&lt;br /&gt;
				switch(midi.msgByteCnt)		// which byte is on? &lt;br /&gt;
				{&lt;br /&gt;
					case 1:					// Manufactorer &lt;br /&gt;
						if(byte !=0x70)		// if not the right one &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error / wrong byte &lt;br /&gt;
						break; &lt;br /&gt;
					case 2:					// Unit - ID &lt;br /&gt;
						if(byte!=0x7D)&lt;br /&gt;
							midi.msgByteCnt=0;&lt;br /&gt;
						break; &lt;br /&gt;
					case 3:					// Midi-Adress (or Unit ID) (ignored!)&lt;br /&gt;
						if(byte&amp;gt;=0x10)		// if not in legal range &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error &lt;br /&gt;
						break;&lt;br /&gt;
					case 4:					// adress byte&lt;br /&gt;
						midi.syxAdr=byte;	// store it for later&lt;br /&gt;
						break;&lt;br /&gt;
					case 5:					// high data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1=(byte&amp;lt;&amp;lt;4);	// set half the data &lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					case 6: 				// low data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1 |= byte;	// or together the complete data byte &lt;br /&gt;
						else 					// evaluation is  done in the code for SYSEND above! &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					default:				// not our framing &lt;br /&gt;
						midi.msgByteCnt=0;&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
				if(midi.msgByteCnt==0)	// something did not fit &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				break;&lt;br /&gt;
			case 0: // bnwtko (bytes no one wants to know off ) &lt;br /&gt;
				return ;&lt;br /&gt;
			default:&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:ProjectSettings.gif&amp;diff=7317</id>
		<title>Datei:ProjectSettings.gif</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:ProjectSettings.gif&amp;diff=7317"/>
		<updated>2011-06-27T18:45:59Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Project settings dialog box screen shot&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Project settings dialog box screen shot&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Bootloader&amp;diff=7316</id>
		<title>MCV876 Controller Update: Bootloader</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Bootloader&amp;diff=7316"/>
		<updated>2011-06-26T21:55:57Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Programmieranleitung zugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Worum geht&#039;s? =&lt;br /&gt;
Midi-Bootlader für den ATMega 88.&lt;br /&gt;
&lt;br /&gt;
Empfängt einen als Midi-Sys-Ex Datenstrom eingepacktes Programm-Update und schreibt das in den Programm-Speicher des Controllers. &lt;br /&gt;
 &lt;br /&gt;
= Compilieren =&lt;br /&gt;
Der Code wird mit dem AVR-Studio 4 erzeugt (neues Projekt erzeugen, Quelltext in das Default-File kopieren, fertig) &lt;br /&gt;
&lt;br /&gt;
Einzige Besonderheit ist, dass der Code für die Boolader-Adresse gelinkt werden muss. &lt;br /&gt;
Dazu unter Projekt-&amp;gt; Configuration Options -&amp;gt; Custom Options -&amp;gt; Linker Options eine neue Option &amp;quot;-Ttext=0x1800&amp;quot; zufügen (add) &lt;br /&gt;
&lt;br /&gt;
(Im Prinzip ist das hier nicht viel anders als das, was hier beschrieben wird: http://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung ) &lt;br /&gt;
&lt;br /&gt;
= Controller programmieren = &lt;br /&gt;
Der Bootlader muss mit einem Programmiergerät in den Controller &amp;quot;geflasht&amp;quot; werden. Das sollte mit jedem Programmieradapter gehen, der für den ATmega 88 gedacht ist. &lt;br /&gt;
Für ISP-Adapter braucht man irgendeine Platine mit ISP-Sockel, da der Adapter keine Programmierpins hat. Ich habe einfach eine Platine eines anderen Projektes genommen... &lt;br /&gt;
Damit der Bootlader so arbeitet wie gewünscht, muss man die &amp;quot;Fuses&amp;quot; des Controllers richtig setzen:&lt;br /&gt;
[[File:fuses1.gif]]&lt;br /&gt;
[[File:fuses2.gif]]&lt;br /&gt;
&lt;br /&gt;
Hier als Screen-Shot aus dem AVR-Studio 4, so sieht das mit dem Dragon als Programmieradapter aus. Sollte sich aber problemlos auf andere Programmiergeräte übertragen lassen. &lt;br /&gt;
* Der Bootbereich darf sich nicht selber überschreiben (Look SPM in Boot Section) &lt;br /&gt;
* 2k-Byte Boot-Bereich (kleiner hat keinen Zweck, da die ganzen 2k immer nur unter CPU-Halt beschrieben werden können, was unsere Update-Infrastruktur aber nicht zulässt) &lt;br /&gt;
* Reset springt in den Bootlader &lt;br /&gt;
* Der Reset-Pin ist I/O Pin. (Vorsicht, das sperrt den ISP-Prgrammer final aus - erst ganz zum Schluß machen!) &lt;br /&gt;
* Unterspannungsdetektor bei 4.3V (eher theoretisch...) &lt;br /&gt;
* Oszillator für externen Quarz. &lt;br /&gt;
&lt;br /&gt;
Ein &amp;quot;neuer&amp;quot; Atmega steht übrigens auf intern 8MHz-RC/8, da muss man, zumindest beim Dragon, den Takt erst mal kleiner stellen, sonst spricht der Dragon gar nicht mit dem Controller. &lt;br /&gt;
&lt;br /&gt;
= Quelltext = &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Bootloader &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.98 2011-06-26&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/crc16.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define LEDGATE0 0	// Led an Gate 0, keine LED Ansteuerung&lt;br /&gt;
#define LEDGATE4 4	// LED an Gate 5, &lt;br /&gt;
#define LEDLED   6  // LED an Reset-Pin (Serie, kein Debuging) &lt;br /&gt;
&lt;br /&gt;
#define LEDOUT LEDLED&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*applicationmain)(void) = (void *)0x0000;&lt;br /&gt;
void (*bootloader)(void) = (void *)0x1800;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
volatile unsigned char program_it;&lt;br /&gt;
volatile unsigned char prog_active; &lt;br /&gt;
volatile unsigned char flash_buf[64];&lt;br /&gt;
volatile unsigned char blockAdress;&lt;br /&gt;
&lt;br /&gt;
#define EESTATUS_OK 1		// last flash was ok &lt;br /&gt;
#define EESTATUS_START 2 	// last flash was started (if read on boot-up: ... but never finished) &lt;br /&gt;
&lt;br /&gt;
#define BOOTEEPROMADR ((uint8_t *) 500) // permanent Flag for last update status (&#039;started&#039; or &#039;success&#039;) &lt;br /&gt;
										// more or less obvisious the permanent state musst be success, else it failed to complete &lt;br /&gt;
										// and that can&#039;t be ok... &lt;br /&gt;
&lt;br /&gt;
#define PROGRAM_IDLE 0 &lt;br /&gt;
#define PROGRAM_DO 1&lt;br /&gt;
#define PROGRAM_FINISH 2&lt;br /&gt;
#define PROGRAM_ERROR 3&lt;br /&gt;
#define PROGRAM_START 4&lt;br /&gt;
#define PROGRAM_ABORT 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Main&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i,j; &lt;br /&gt;
	unsigned short int flashAdress;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	volatile uint16_t tmp;&lt;br /&gt;
	volatile uint16_t adr;&lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
	cli();	// just to be sure... its not allways a cold boot which runs here.. &lt;br /&gt;
&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
	if(GPIOR1==0xAB)&lt;br /&gt;
	{&lt;br /&gt;
		// app intended Boot Load&lt;br /&gt;
		GPIOR1 = 0;&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{	// no main application intended Boot-Load&lt;br /&gt;
&lt;br /&gt;
		_delay_ms(10);	// Wait for Button-Pin to get High if not pressed&lt;br /&gt;
		&lt;br /&gt;
		GPIOR1=0;&lt;br /&gt;
		&lt;br /&gt;
		while(BUTTON &amp;amp;&amp;amp; GPIOR1 &amp;lt; 8)&lt;br /&gt;
		{&lt;br /&gt;
			_delay_ms(1800);&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			_delay_ms(200);&lt;br /&gt;
			setLed(0);&lt;br /&gt;
			GPIOR1++;&lt;br /&gt;
		}&lt;br /&gt;
		_delay_ms(10);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
		if( ( !BUTTON &amp;amp;&amp;amp; GPIOR1==0 ) || GPIOR1 &amp;gt;= 8)&lt;br /&gt;
		{&lt;br /&gt;
			unsigned char x; &lt;br /&gt;
		&lt;br /&gt;
			_delay_ms(100);&lt;br /&gt;
		&lt;br /&gt;
			x=eeprom_read_byte(BOOTEEPROMADR); 	&lt;br /&gt;
			if(x != EESTATUS_START &amp;amp;&amp;amp; pgm_read_word(0)!=0xffff ) // if no failed update atemps and not erased &lt;br /&gt;
			{&lt;br /&gt;
				_delay_ms(100);&lt;br /&gt;
				applicationmain();&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/* Enable change of Interrupt Vectors */&lt;br /&gt;
	MCUCR = (1 &amp;lt;&amp;lt; IVCE );&lt;br /&gt;
	/* Move Interrupts to boot flash section */&lt;br /&gt;
	MCUCR = ( 1 &amp;lt;&amp;lt; IVSEL ); &lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
	sei(); // Interupts on &lt;br /&gt;
&lt;br /&gt;
	while(1) // Main Loop &lt;br /&gt;
	{&lt;br /&gt;
		if(program_it== PROGRAM_DO || program_it== PROGRAM_FINISH)&lt;br /&gt;
		{&lt;br /&gt;
			flashAdress=blockAdress  ;  // Block number &lt;br /&gt;
			flashAdress&amp;lt;&amp;lt;=6;			// * block size of 64 bytes &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
			cli();		// some register writes must be done within 4 clock cycles max, Interupts can kill that &lt;br /&gt;
			boot_page_erase(flashAdress);	// issue the block erease command &lt;br /&gt;
			sei();&lt;br /&gt;
&lt;br /&gt;
						&lt;br /&gt;
			do&lt;br /&gt;
			{&lt;br /&gt;
				cli();&lt;br /&gt;
				j=(__SPM_REG &amp;amp; (uint8_t)_BV(__SPM_ENABLE));	// j=it&#039;s done? &lt;br /&gt;
				sei();&lt;br /&gt;
			}while(j);		// wait until j says, that it is done &lt;br /&gt;
			// this takes some millisekonds, during which we&#039;ll receive about a quarter of the next block&lt;br /&gt;
			// so interupts can&#039;t be off the whole time. &lt;br /&gt;
;&lt;br /&gt;
			for (i = 0; i &amp;lt; 64; i+=2) // fill page buffer with 32 words (à two bytes each) &lt;br /&gt;
			{&lt;br /&gt;
				tmp = (flash_buf[i]) | (flash_buf[i + 1]&amp;lt;&amp;lt;8);&lt;br /&gt;
				adr=flashAdress+i;&lt;br /&gt;
				cli();	// again, one of the &amp;quot;max 4 cycle&amp;quot; macros &lt;br /&gt;
				boot_page_fill(adr, tmp);	// write a word &lt;br /&gt;
				sei();&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			cli();&lt;br /&gt;
			boot_page_write(flashAdress);	// write to flash &lt;br /&gt;
			sei();&lt;br /&gt;
			&lt;br /&gt;
			do	// wait until write is done &lt;br /&gt;
			{	&lt;br /&gt;
				cli();										// prepare uninteruptable max. 4 Cylce sequence &lt;br /&gt;
				j=(__SPM_REG &amp;amp; (uint8_t)_BV(__SPM_ENABLE));	// read ready flag &lt;br /&gt;
				sei();										// done &lt;br /&gt;
			}while(j);		// while not ready. Again this takes 3.5 to 4.5ms, during which about another quarter of the &lt;br /&gt;
							// next block comes in. As we don&#039;t want to lose any chars, the interupts cant be turned off the whole time &lt;br /&gt;
&lt;br /&gt;
			cli();&lt;br /&gt;
			boot_rww_enable();	// enable read access to programm flash. &lt;br /&gt;
			sei();&lt;br /&gt;
			&lt;br /&gt;
			if(program_it==PROGRAM_FINISH)	// if last block&lt;br /&gt;
			{&lt;br /&gt;
				eeprom_write_byte(BOOTEEPROMADR,EESTATUS_OK);		// store flag for successfull update &lt;br /&gt;
				prog_active=PROGRAM_IDLE;		// should be senseless, but inhibits permanent eeprom writes if something goes wrong... &lt;br /&gt;
&lt;br /&gt;
				// Update done, go to new application programm &lt;br /&gt;
				cli();										// no more intupts &lt;br /&gt;
				/* Enable change of Interrupt Vectors */	// set interupt vectors to program &lt;br /&gt;
				MCUCR = (1 &amp;lt;&amp;lt; IVCE );&lt;br /&gt;
				/* Move Interrupts to regular flash section */&lt;br /&gt;
				MCUCR = 0; // ( 0 &amp;lt;&amp;lt; IVSEL ); &lt;br /&gt;
&lt;br /&gt;
				applicationmain();							// call application (never comes back, stack is lost...) &lt;br /&gt;
			}&lt;br /&gt;
			if(program_it!=PROGRAM_ERROR)	// if not error 		// which might happen asynchronous - and should be kept&lt;br /&gt;
				program_it=PROGRAM_IDLE;	// programming of current page done. &lt;br /&gt;
		}&lt;br /&gt;
		if(program_it==PROGRAM_START)&lt;br /&gt;
		{&lt;br /&gt;
			prog_active=PROGRAM_START;&lt;br /&gt;
			eeprom_write_byte(BOOTEEPROMADR,EESTATUS_START);&lt;br /&gt;
&lt;br /&gt;
			if(program_it!=PROGRAM_ERROR)	// might happen asynchronous &lt;br /&gt;
				program_it=PROGRAM_IDLE;&lt;br /&gt;
		}&lt;br /&gt;
		if(program_it==PROGRAM_ABORT)&lt;br /&gt;
		{&lt;br /&gt;
			cli();&lt;br /&gt;
			/* Enable change of Interrupt Vectors */&lt;br /&gt;
			MCUCR = (1 &amp;lt;&amp;lt; IVCE );&lt;br /&gt;
			/* Move Interrupts to regular flash section */&lt;br /&gt;
			MCUCR = 0; // ( 0 &amp;lt;&amp;lt; IVSEL ); &lt;br /&gt;
&lt;br /&gt;
			bootloader(); 	// checks, if there is a valid program to start &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
volatile unsigned char dataRx; &lt;br /&gt;
#define DATARX 4&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	static unsigned char bit;&lt;br /&gt;
	static unsigned char blinker;&lt;br /&gt;
	static unsigned char blinks[]={ 40 , 10, 200, 10,   15,15,15,15 }; &lt;br /&gt;
	static unsigned char btn;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if (blinker-- == 0)&lt;br /&gt;
	{&lt;br /&gt;
		bit++;&lt;br /&gt;
		bit&amp;amp;=3;&lt;br /&gt;
		blinker=blinks[bit | dataRx];&lt;br /&gt;
&lt;br /&gt;
		setLed(bit&amp;amp;1);&lt;br /&gt;
&lt;br /&gt;
		dataRx=0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{&lt;br /&gt;
		btn=10;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		if(btn&amp;gt;0)&lt;br /&gt;
		{	&lt;br /&gt;
			btn--;&lt;br /&gt;
			if(btn == 0)&lt;br /&gt;
			{&lt;br /&gt;
				if(prog_active==PROGRAM_IDLE &amp;amp;&amp;amp; program_it == PROGRAM_IDLE)			&lt;br /&gt;
					program_it=PROGRAM_ABORT;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  ***********************************************************************************&lt;br /&gt;
	SERIAL MIDI INTERFACE &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define SYSEX 			0xF0&lt;br /&gt;
#define SYSEND			0xF7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define STAT_IDLE 	  0&lt;br /&gt;
#define STAT_INSYSEX1 1&lt;br /&gt;
#define STAT_INSYSEX2 2&lt;br /&gt;
#define STAT_INSYSEX3 3&lt;br /&gt;
#define STAT_INSYSEX4 4&lt;br /&gt;
#define STAT_INSYSEX5 5&lt;br /&gt;
#define STAT_INSYSEX6 6&lt;br /&gt;
#define STAT_PROGRAM_BYTES 7&lt;br /&gt;
#define STAT_WAITEND 8&lt;br /&gt;
#define STAT_ERROR 9&lt;br /&gt;
&lt;br /&gt;
unsigned char rx_buf[68]; // 1Byte Block-Adress,  64 Data-Bytes, 2Byte CRC &lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine&lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	static unsigned char status, error, current_block,num_blocks; &lt;br /&gt;
//	static unsigned char nibble_cnt,  data;&lt;br /&gt;
	static unsigned short int runningByte; // no joke, this accumulates our 8-bit-bytes from 7bit midi, so it must hold up to 14bit in between... &lt;br /&gt;
	static unsigned char byte_cnt,bits;&lt;br /&gt;
&lt;br /&gt;
	static unsigned short int crc;&lt;br /&gt;
	unsigned char rxStat,byte;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    rxStat = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((rxStat &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(status &amp;gt; STAT_IDLE)&lt;br /&gt;
			dataRx=DATARX; &lt;br /&gt;
			 &lt;br /&gt;
		if (byte &amp;gt; 0x7F )		// Command byte &lt;br /&gt;
		{&lt;br /&gt;
			if( byte &amp;lt; 0xF8 ) // no Real-Time Message &lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX )	// Start&lt;br /&gt;
				{&lt;br /&gt;
					status=STAT_INSYSEX1;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)  // SysEx-End (might not be ours)&lt;br /&gt;
				{&lt;br /&gt;
					if( status==STAT_PROGRAM_BYTES)	// properly framed SysEx received &lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
						if(crc==0 ) &lt;br /&gt;
						{&lt;br /&gt;
							unsigned char i;&lt;br /&gt;
&lt;br /&gt;
							if(rx_buf[0]==0 &amp;amp;&amp;amp; byte_cnt==6 ) // first block = info block&lt;br /&gt;
							{&lt;br /&gt;
								error=0;&lt;br /&gt;
								current_block=1;&lt;br /&gt;
								num_blocks=rx_buf[1];&lt;br /&gt;
								for(i=0;i&amp;lt;2;i++)&lt;br /&gt;
									flash_buf[i]=rx_buf[i+2];&lt;br /&gt;
								program_it=PROGRAM_START;&lt;br /&gt;
		&lt;br /&gt;
							}&lt;br /&gt;
							else if(byte_cnt==67 &amp;amp;&amp;amp; current_block==rx_buf[0] ) &lt;br /&gt;
							{	// Programm Data Block &lt;br /&gt;
&lt;br /&gt;
								for(i=0;i&amp;lt;64;i++)&lt;br /&gt;
									flash_buf[i]=rx_buf[i+1];&lt;br /&gt;
								blockAdress=rx_buf[0]-1;&lt;br /&gt;
&lt;br /&gt;
								if(!error)&lt;br /&gt;
								{&lt;br /&gt;
									if(num_blocks==current_block)&lt;br /&gt;
									{&lt;br /&gt;
										program_it=PROGRAM_FINISH;&lt;br /&gt;
									}&lt;br /&gt;
									else &lt;br /&gt;
									{&lt;br /&gt;
										program_it=PROGRAM_DO;									&lt;br /&gt;
									}&lt;br /&gt;
								}&lt;br /&gt;
								current_block++;&lt;br /&gt;
							}&lt;br /&gt;
							else&lt;br /&gt;
							{&lt;br /&gt;
								status=STAT_ERROR;&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						else &lt;br /&gt;
							status=STAT_ERROR;&lt;br /&gt;
					}&lt;br /&gt;
					else // not my frame format  &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
				}&lt;br /&gt;
				else 	// some other Status byte &lt;br /&gt;
				{&lt;br /&gt;
					if(status!=STAT_IDLE)&lt;br /&gt;
					{&lt;br /&gt;
						if(status&amp;lt;STAT_PROGRAM_BYTES) // might be unrelated to us &lt;br /&gt;
							status=STAT_IDLE;&lt;br /&gt;
						else &lt;br /&gt;
							status=STAT_ERROR; // our Data gets messed up &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			} // no real time message &lt;br /&gt;
		}&lt;br /&gt;
		else // Data Byte&lt;br /&gt;
		{&lt;br /&gt;
			switch(status)&lt;br /&gt;
			{&lt;br /&gt;
				default:	// should never happen&lt;br /&gt;
					status=STAT_ERROR;&lt;br /&gt;
				case STAT_IDLE: // any Data which is not in our SysExFrame &lt;br /&gt;
					break; &lt;br /&gt;
				case STAT_INSYSEX1: // Manufacturer &lt;br /&gt;
					if(byte==0x70)&lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_INSYSEX2;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_INSYSEX2:	// Unit ID&lt;br /&gt;
					if(byte==0x7D)&lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_INSYSEX4;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_INSYSEX4:	// Unit Adress (ignored) &lt;br /&gt;
					if(byte&amp;lt; 0x10)&lt;br /&gt;
					{	status=STAT_INSYSEX5;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_INSYSEX5:	// &amp;quot;Data Adress&amp;quot; (magic byte 1) &lt;br /&gt;
					if(byte == 0x15)&lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_INSYSEX6;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_INSYSEX6:	// magic byte 2 (this would stop the regular SysEx Decoder from taking this as data) &lt;br /&gt;
					if(byte == 0x55)&lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_PROGRAM_BYTES;&lt;br /&gt;
						crc=0xffff;&lt;br /&gt;
						byte_cnt=0;&lt;br /&gt;
						bits=0;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_PROGRAM_BYTES:	// we&#039;ve made it: now the new flash data rolls in 			&lt;br /&gt;
					runningByte &amp;lt;&amp;lt;=7;&lt;br /&gt;
					runningByte|=byte;&lt;br /&gt;
					bits+=7;&lt;br /&gt;
					if(bits&amp;gt;=8) // if enough for one Byte &lt;br /&gt;
					{&lt;br /&gt;
						unsigned char b;&lt;br /&gt;
&lt;br /&gt;
						bits-=8;&lt;br /&gt;
						b=runningByte&amp;gt;&amp;gt;bits;&lt;br /&gt;
						rx_buf[byte_cnt]=b;&lt;br /&gt;
						crc=_crc16_update(crc,b);&lt;br /&gt;
						if(++byte_cnt&amp;gt;67)&lt;br /&gt;
							status=STAT_ERROR;&lt;br /&gt;
					}&lt;br /&gt;
					break;&lt;br /&gt;
			} // switch status&lt;br /&gt;
		}	// data byte&lt;br /&gt;
	}&lt;br /&gt;
	else // any Rx-Erros =&amp;gt; abort &lt;br /&gt;
		status=STAT_ERROR;&lt;br /&gt;
	&lt;br /&gt;
	if(status==STAT_ERROR)&lt;br /&gt;
	{&lt;br /&gt;
		status=STAT_IDLE;&lt;br /&gt;
		error=1;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Fuses2.gif&amp;diff=7315</id>
		<title>Datei:Fuses2.gif</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Fuses2.gif&amp;diff=7315"/>
		<updated>2011-06-26T21:44:06Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Fuse-Einstellungen 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Fuse-Einstellungen 1&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Fuses1.gif&amp;diff=7314</id>
		<title>Datei:Fuses1.gif</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Fuses1.gif&amp;diff=7314"/>
		<updated>2011-06-26T21:43:48Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Fuse-Einstellungen 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Fuse-Einstellungen 1&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Hardware_bauen&amp;diff=7313</id>
		<title>MCV876 Controller Update: Hardware bauen</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Hardware_bauen&amp;diff=7313"/>
		<updated>2011-06-26T20:49:34Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Created page with &amp;#039;= Bauanleitung Adaptersockel =  Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.  Damit man gleich sieht auf was es rausl…&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Bauanleitung Adaptersockel = &lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
Damit man gleich sieht auf was es rausläuft:&lt;br /&gt;
 &lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
Ein fertiger Adaptersockel. &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) Außerdem braucht man einen weiteren 28poligen Sockel als Montagehilfe/Abstandshalter für die beiden Stecksockel, sonst bekommt man das nie gerade und passend hin. &lt;br /&gt;
&lt;br /&gt;
Das hässliche Weiße ist die PIN1 Markierung. Falschrum reinsetzen ist nämlich Mist. &lt;br /&gt;
&lt;br /&gt;
== Der Verdrahtungplan == &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
&lt;br /&gt;
== Schritt für Schritt == &lt;br /&gt;
&lt;br /&gt;
Die Steckteile passen zunächst nicht nebeneinander. Die werden mit mit Dremel oder Feile etwas eingekürzt. An beide Teilen je eine Seite kürzen und dabei aufpassen, dass man die Kontakte nicht ansäbelt) &lt;br /&gt;
&lt;br /&gt;
[[File:build1.jpg]]&lt;br /&gt;
&lt;br /&gt;
[[File:build2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beide Stecksockel in einem extra-Sockel, der (nur) als Montagehalter/hilfe dient, stecken. Das hier wird das Unterteil. &lt;br /&gt;
&lt;br /&gt;
[[File:build3.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;richtige&amp;quot; Sockel (Oberteil, da kommt später der Atmel rein) von unten. Die Pins, die rausgekniffen werden (rotes X im Plan oben) fehlen hier schon, und die 5V Verbindung (zwischen 3 Pins) ist mit einem blanken Draht gelötet. &lt;br /&gt;
&lt;br /&gt;
[[File:build5.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Verbindungsdrähte anlöten:&lt;br /&gt;
&lt;br /&gt;
[[File:build6.jpg]]&lt;br /&gt;
&lt;br /&gt;
(der aufmerksame Betrachter wird den 100nF SMD-Kondensator bemerken, den ich heimlich an die Versorgung gefingert habe. Wenn man da einen bedrahteten nimmt *und* dessen eines Beinchen gleich für die +5V hernimmt, dann lötet sich das vermutlich auch gar nicht so schlimm. )&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zusammenfügen von Ober- und Unterteil.&lt;br /&gt;
Die Pins von den Sockelbeinchen passen genau in die Gabeln von dem Stecksockel.&lt;br /&gt;
Und die Drähte habe ich auch schon mal (etwa) dahin gelegt, wo sie hin sollen.&lt;br /&gt;
&lt;br /&gt;
[[File:build7.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sockel-Pins in den Gabeln festlöten und dann die einzelnen Drähte kürzen, abisolieren und verlöten. Hier ist gerade der Violette an der Reihe:&lt;br /&gt;
&lt;br /&gt;
[[File:build8.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alles verlötet: &lt;br /&gt;
&lt;br /&gt;
[[File:build9.jpg]]&lt;br /&gt;
&lt;br /&gt;
... unteren Stecksockel vorsichtig abhebeln, damit die Beinchen nicht verbiegen. &lt;br /&gt;
&lt;br /&gt;
Atmel reinpröppeln und dann ab in das Interface.&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Build9.jpg&amp;diff=7312</id>
		<title>Datei:Build9.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Build9.jpg&amp;diff=7312"/>
		<updated>2011-06-26T20:45:57Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Fertig&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Fertig&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Build8.jpg&amp;diff=7311</id>
		<title>Datei:Build8.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Build8.jpg&amp;diff=7311"/>
		<updated>2011-06-26T20:45:31Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Drähte verlöten&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Drähte verlöten&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Build7.jpg&amp;diff=7310</id>
		<title>Datei:Build7.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Build7.jpg&amp;diff=7310"/>
		<updated>2011-06-26T20:44:50Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Ober- und Unterteil zusammenfügen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ober- und Unterteil zusammenfügen&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Build6.jpg&amp;diff=7309</id>
		<title>Datei:Build6.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Build6.jpg&amp;diff=7309"/>
		<updated>2011-06-26T20:44:14Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Oberteil mit angelöteten Drähten&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Oberteil mit angelöteten Drähten&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Build5.jpg&amp;diff=7308</id>
		<title>Datei:Build5.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Build5.jpg&amp;diff=7308"/>
		<updated>2011-06-26T20:34:26Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Oberteil-Sockel, mit entfernten Pins und +5V Brücke&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Oberteil-Sockel, mit entfernten Pins und +5V Brücke&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Build3.jpg&amp;diff=7307</id>
		<title>Datei:Build3.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Build3.jpg&amp;diff=7307"/>
		<updated>2011-06-26T20:31:33Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Stecksockel in einem &amp;quot;Hilfssockel&amp;quot; der als Montagehilfe dient&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Stecksockel in einem &amp;quot;Hilfssockel&amp;quot; der als Montagehilfe dient&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Build2.jpg&amp;diff=7306</id>
		<title>Datei:Build2.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Build2.jpg&amp;diff=7306"/>
		<updated>2011-06-26T20:30:06Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Stecksockel-Unterteil gekürzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Stecksockel-Unterteil gekürzt&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Build1.jpg&amp;diff=7305</id>
		<title>Datei:Build1.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Build1.jpg&amp;diff=7305"/>
		<updated>2011-06-26T20:29:24Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Adapter-Unterteile vor der Bearbeitung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Adapter-Unterteile vor der Bearbeitung&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Adapter.gif&amp;diff=7304</id>
		<title>Datei:Adapter.gif</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Adapter.gif&amp;diff=7304"/>
		<updated>2011-06-26T20:16:10Z</updated>

		<summary type="html">&lt;p&gt;Fetz: uploaded a new version of &amp;quot;File:Adapter.gif&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Verdrahtungsplan des Adaptersockels &lt;br /&gt;
(A=Atmel-Pins, P=Platinen-Pins)&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Adapter-sockel.jpg&amp;diff=7303</id>
		<title>Datei:Adapter-sockel.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Adapter-sockel.jpg&amp;diff=7303"/>
		<updated>2011-06-26T20:12:30Z</updated>

		<summary type="html">&lt;p&gt;Fetz: uploaded a new version of &amp;quot;File:Adapter-sockel.jpg&amp;quot;:&amp;amp;#32;Der fertige Adaptersockel&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Adapter-Sockel um den Atmel-Controller in einen PIC-sockel stecken zu können. Die drei Pins an der Seite sind nur für Debug-Zwecke nötig, nicht für den normalen Einsatz.&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7302</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7302"/>
		<updated>2011-06-26T20:03:54Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Hardware */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Tonhöhe (Noten) &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;|| Gate 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen- und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V.&lt;br /&gt;
&lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie zweimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
Sollte sie vier mal blinken, hat der Controller die permanent gespeicherte Konfiguration nicht geladen und nutzt statt dessen (wieder) die Standardeinstellung.&lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu einzustellen: Die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Controller für CV3 einstellen ===&lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang(sic!) ge-gated. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig. (Zur Bauanleitung hier lang: [[MCV876_Controller_Update:_Hardware_bauen]].) &lt;br /&gt;
&lt;br /&gt;
Der neue Controller wird ansonsten einfach nur in den Sockel des Alten gesteckt. Alten Controller rausziehen, Atmel mit Adapter reinstecken, fertig. &lt;br /&gt;
&lt;br /&gt;
[[File:AdapterEingebaut.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So sieht der &amp;quot;neue&amp;quot; in einem Semtex aus&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Der Bootlader ist hier: [[MCV876_Controller_Update:_Bootloader]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:AdapterEingebaut.jpg&amp;diff=7301</id>
		<title>Datei:AdapterEingebaut.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:AdapterEingebaut.jpg&amp;diff=7301"/>
		<updated>2011-06-26T20:02:55Z</updated>

		<summary type="html">&lt;p&gt;Fetz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7300</id>
		<title>MCV876 Controller Update: Source Code</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7300"/>
		<updated>2011-06-26T19:56:43Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Übersetzen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Übersetzen= &lt;br /&gt;
Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden.&lt;br /&gt;
Im Studio ein neues, leeres Projekt mit Standard-Einstellungen, einem sinnvollen Namen und an einem Ort, wo man es wiederfindet erstellen, Quelltext in das (automatisch erzeugte, leere) C-File kopieren, auf Build-&amp;gt;Build klicken, fertig. &lt;br /&gt;
Das resultierende HEX File kann man mit einem üblichen Programmieradapter in der Chip flashen, für uns zweckmäßiger ist allerdings, das in ein Sys-Ex File zu Konvertieren ( [[MCV876_Controller_Update:_File_Converter]]), und das über Midi an den Controller zu schicken. Dann programmiert der sich selber um. Dazu muss er allerdings einmalig mit dem Bootlader ([[MCV876_Controller_Update:_Bootloader]]) programmiert worden sein. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu den Atmel Controllern findet man eine Menge Info mit Tante Google, [http://www.mikrocontroller.net http://www.mikrocontroller.net] kommt häufig vor, und hat ein ergiebiges Forum und gute Artikel. Auf der Atmel Webseite gibt es auch einige Info, das [http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf ATMega 88 Datenblatt] ist spätestens dann nötig, wenn es um die Hardware geht.&lt;br /&gt;
&lt;br /&gt;
=Quelltext=&lt;br /&gt;
&amp;lt;pre&amp;gt;/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Midi to 4 Channel Control Voltage and six Gates Interface &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.22 2011-06-26 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
			&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;			// yes, we want to talk to the port pins by name&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;		// ... oh.. and some eeprom access would be nice&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;	// ... and w/o the interupts it wouldn&#039;t do anything... &lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*	===================ZERO LINE=================================&lt;br /&gt;
	WARNING: everthing below the zero line counts from zero! &lt;br /&gt;
	e.g. whats Gate 6 in manual is GATE5 here, CVs are CV0 to 3 &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	where should the LED output be &lt;br /&gt;
*/&lt;br /&gt;
#define LEDLED   6  // LED at Reset-Pin, standard for series, but no debugging possible. &lt;br /&gt;
#define LEDGATE0 0	// Led at Gate 0, no LED (Test I: no LED ) &lt;br /&gt;
#define LEDGATE4 4	// LED at Gate 5, Test II: no Gate 5 Function (Midi Start/Stop) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 #define LEDOUT LEDLED // select your evil... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define ACTIVE_SENSE_TICKS 160 // 2ms Timer Ticks for active sense time out &lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4))) // True if Button pressed &lt;br /&gt;
&lt;br /&gt;
#define NUM_CV 4&lt;br /&gt;
&lt;br /&gt;
#define PARAMETEROFFSET 0x23 // First paramter has adress 0x23&lt;br /&gt;
#define NUM_PARAMTERS 17	 // and there are 17 parameters &lt;br /&gt;
&lt;br /&gt;
#define MODE_MONO 	0x00	// monophon, all assigned to last note on a single channel &lt;br /&gt;
#define MODE_MULTI2	0x40	// two channel, 1/2 for midiChannel, 2/3 for midiChannel+1 &lt;br /&gt;
#define MODE_MULTI4	0x60	// for channel midiChannel to midiChannel+3 &lt;br /&gt;
#define MODE_POLY2	0x80	// not supported &lt;br /&gt;
#define MODE_POLY4	0xA0	// not supported&lt;br /&gt;
&lt;br /&gt;
struct CONFIG {&lt;br /&gt;
	unsigned char midiChannel;&lt;br /&gt;
	unsigned char noteOffset;&lt;br /&gt;
&lt;br /&gt;
	unsigned char cvMidiCC[NUM_CV];&lt;br /&gt;
	unsigned char gateMidiCC[NUM_CV];&lt;br /&gt;
	&lt;br /&gt;
	unsigned char gateTreshhold;&lt;br /&gt;
	unsigned char clockDivider;&lt;br /&gt;
	unsigned char clockTime; &lt;br /&gt;
	unsigned char ifMode0; 	// 	bit 0 ... bit 3 = CV0 .. CV3, 0=Pitch, 1=CC (if pitch, CC assignement is ignored) &lt;br /&gt;
							//	bit 5: voices: 0=2; 1=4 &lt;br /&gt;
							//  bit 7:6= 00 Mono; 01:Multi-Channel; 10: Poly (not supported); &lt;br /&gt;
	&lt;br /&gt;
	unsigned char ifMode1;  // Gate Trigger Mode Bits (0=Re-Trigger, 1=TriggerControl)  &lt;br /&gt;
	unsigned char ifMode2;  // bit0: Velo0=&amp;gt;CV0; bit1:Velo0=&amp;gt;CV1; bit2:Velo-Poly2=&amp;gt;CV2; bit3:Velo1=&amp;gt;CV3&lt;br /&gt;
							// bit4: PB=&amp;gt;CV0; bit5:PB=&amp;gt;CV1; bit6:PB=&amp;gt;CV2; bit7:PB=&amp;gt;CV3&lt;br /&gt;
	unsigned char ifMode3; 	// bit0..bit3 = CV0... CV3; 0=10V, 1=5V Mode &lt;br /&gt;
&lt;br /&gt;
//  internal evaluated configuration &lt;br /&gt;
	unsigned char numMidiChannels;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
} shadow_config, // her goes a copy, when config should be written, because that might take a while &lt;br /&gt;
&lt;br /&gt;
config 	// current configuration, mostly direct evaluated troughout the program&lt;br /&gt;
= {		// default values (Mono-Mode, Midi-Ch 1, CV1=Pitch, CV2=Velocity CV3=ModWheel CV4=Pitch Bend&lt;br /&gt;
			0,  	// Midi Channel &lt;br /&gt;
			24, 	// Note Offset (2 Oktaves) &lt;br /&gt;
		{	255,255,1,255, },  	// CV - assigned Midi Controllers (used if not pitch output) 255=none&lt;br /&gt;
		{   255,18,19,20, }, 	// Gate - asigned Controllers (if not pitch) 255=none &lt;br /&gt;
			63, 	// Treshold&lt;br /&gt;
			6,  	// Divider 6 (1/16 Note) &lt;br /&gt;
			1,  	// clock time (unused) &lt;br /&gt;
			0x0e, 	// mode 0 Low nibble CC-Bits, high: interface mode &lt;br /&gt;
			0, 		// mode 1 re-trigger bits &lt;br /&gt;
			0x82, 	// mode 2 High Nibble: PB-bits, Low: Velo-Bits&lt;br /&gt;
			0,    	// mode 3 (5V-Bits) &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define EE_MAGIC 17&lt;br /&gt;
unsigned char ee_magic EEMEM;	// to check it eeprom was ever written and should be read back &lt;br /&gt;
unsigned char config_eeprom[NUM_PARAMTERS] EEMEM; // Config in EEPROM, saves it during power down. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
volatile unsigned char  eepromWriteFlag;	// set to one, if main-loop should write eeprom &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char button; 	// debounced button status &lt;br /&gt;
volatile unsigned char learn; 	// (channel/base note/cc) &amp;quot;do learn&amp;quot; status &lt;br /&gt;
volatile unsigned char blinker; // some count for blinkenlights &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char gate5off; // timer counter for when clock gate output should go low again. &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char watchTicker;	// midi active sense counter &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setConfig(void); 		&lt;br /&gt;
void setLed(unsigned char on);&lt;br /&gt;
void resetAll(void);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	main() &lt;br /&gt;
	Main does initialization, than runs a loop, which does nearly nothing: &lt;br /&gt;
&lt;br /&gt;
	Programming the EEPROM (because its slow) &lt;br /&gt;
	Blinking &lt;br /&gt;
	Button evaluation &lt;br /&gt;
&lt;br /&gt;
	All the important things are done by the interupts. (Thanks to the &lt;br /&gt;
	20MHZ RISC power everything can be done in less than 320µs=one Midi-Character) &lt;br /&gt;
*/&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char blink=4;&lt;br /&gt;
&lt;br /&gt;
	unsigned char i,x; &lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
	static unsigned char lastButton; &lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
	// Read Configuration &lt;br /&gt;
	_delay_ms(10); // wait for button pin to get high if open. &lt;br /&gt;
	if( eeprom_read_byte(&amp;amp;ee_magic) == EE_MAGIC  &amp;amp;&amp;amp;   !BUTTON) // if eeprom valid AND Button not pressed &lt;br /&gt;
	{&lt;br /&gt;
		cp=(unsigned char* )&amp;amp;config; &lt;br /&gt;
		for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
		{&lt;br /&gt;
			x=eeprom_read_byte(config_eeprom+i);			// read config from eeprom &lt;br /&gt;
			*cp=x;	&lt;br /&gt;
			cp++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
		blink=8; &lt;br /&gt;
&lt;br /&gt;
	setConfig();&lt;br /&gt;
&lt;br /&gt;
	// enable interrupts&lt;br /&gt;
	sei(); &lt;br /&gt;
&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		if(eepromWriteFlag)&lt;br /&gt;
		{	// save configuration &lt;br /&gt;
			cp=(unsigned char* )&amp;amp;shadow_config; &lt;br /&gt;
			for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
			{&lt;br /&gt;
				x=*cp;&lt;br /&gt;
				eeprom_write_byte(config_eeprom+i,x);&lt;br /&gt;
				cp++;&lt;br /&gt;
			}&lt;br /&gt;
			eeprom_write_byte(&amp;amp;ee_magic,EE_MAGIC); &lt;br /&gt;
&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			blink=6;&lt;br /&gt;
			blinker=0;&lt;br /&gt;
			eepromWriteFlag=0;	&lt;br /&gt;
		}&lt;br /&gt;
		if(blink &amp;amp;&amp;amp; blinker&amp;gt;2)  // blink after power up and after storing in nv-memory &lt;br /&gt;
		{&lt;br /&gt;
			blink--;&lt;br /&gt;
			setLed(blink&amp;amp;1);&lt;br /&gt;
			blinker=0; &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if(button != lastButton)&lt;br /&gt;
		{&lt;br /&gt;
			if(button==1 )&lt;br /&gt;
			{&lt;br /&gt;
				resetAll();&lt;br /&gt;
				if(learn==1)&lt;br /&gt;
				{&lt;br /&gt;
					learn=0;&lt;br /&gt;
					setLed(0);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if(button==6) &lt;br /&gt;
			{&lt;br /&gt;
				if(learn ==0)&lt;br /&gt;
				{&lt;br /&gt;
					learn=1;&lt;br /&gt;
					setLed(1);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			lastButton=button; &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return 0; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Hardware Interface&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
/*  Pin assignment &lt;br /&gt;
   Atmel Fkt 	[Orig] &lt;br /&gt;
1  Reset LED 	[MCLR] &lt;br /&gt;
2  RX    Midi-Rx[WR1]&lt;br /&gt;
3  PD1   WR2&lt;br /&gt;
4  PD2   WR3&lt;br /&gt;
5  PD3   WR4&lt;br /&gt;
6  PD4   Button&lt;br /&gt;
7  VCC   +5  	[LED] (Either from Atmel-1(Series) or Atmel-15(w/Debug))&lt;br /&gt;
8  GND   GND&lt;br /&gt;
9  OSC   OSC&lt;br /&gt;
10 OSC   OSC &lt;br /&gt;
11 PD5   G1&lt;br /&gt;
12 PD6   G2&lt;br /&gt;
13 PD7   G3&lt;br /&gt;
14 PB0   G4&lt;br /&gt;
&lt;br /&gt;
15 PB1   G5 (Optional + LED) &lt;br /&gt;
16 PB2   G6 &lt;br /&gt;
17 PB3   WR1    [---] &lt;br /&gt;
18 PB4   D0     [RX]&lt;br /&gt;
19 PB5   D1		[GND]&lt;br /&gt;
20 AVCC  +5&lt;br /&gt;
21 ARef  +5 	[D0]&lt;br /&gt;
22 GND   GND	[D1] &lt;br /&gt;
23 PC0   D2&lt;br /&gt;
24 PC1   D3&lt;br /&gt;
25 PC2   D4&lt;br /&gt;
26 PC3   D5&lt;br /&gt;
27 PC4   D6&lt;br /&gt;
28 PC5   D7&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
// put &amp;quot;val&amp;quot; to the right port bits for the DA-Converter &lt;br /&gt;
// ... better program than solder *that*... &lt;br /&gt;
void adByteOut(unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char b;&lt;br /&gt;
	b=val;&lt;br /&gt;
	b&amp;gt;&amp;gt;=2;&lt;br /&gt;
	PORTC=b;&lt;br /&gt;
	b=val&amp;amp;3;&lt;br /&gt;
	b&amp;lt;&amp;lt;=4;&lt;br /&gt;
	val=PORTB;&lt;br /&gt;
	val &amp;amp;=0xCF;&lt;br /&gt;
	val|=b;&lt;br /&gt;
	PORTB=val;&lt;br /&gt;
}&lt;br /&gt;
// set DA-Output voltage of one CV Channel  &lt;br /&gt;
void set_cv(unsigned char val, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	adByteOut(val); 	// set D0..D7 converter data bits &lt;br /&gt;
	switch (channel)	// pulse with write pin &lt;br /&gt;
	{&lt;br /&gt;
		case 0: //PB3&lt;br /&gt;
			PORTB  &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTB  |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD1&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;1);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;1);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD2&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;2);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;2);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PD3&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// set on of the gate outputs active &lt;br /&gt;
void gate_on(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// clear one of the gate outputs &lt;br /&gt;
void gate_off(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif&lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Data Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
typedef struct {&lt;br /&gt;
	unsigned char channel;  // (relative) Midi channel (0..3) Mode dependant &lt;br /&gt;
	unsigned char cvOut;	// for which CV-Ouput (0..3) fixed &lt;br /&gt;
	unsigned char ccVal;	// current CC Value &lt;br /&gt;
	unsigned char note;		// current Note &lt;br /&gt;
	unsigned char velo;		// current Velocity &lt;br /&gt;
	unsigned char on;		// if Note is on &lt;br /&gt;
	signed char   pb;		// current Pitch Bend Value &lt;br /&gt;
	unsigned char gateDipper;&lt;br /&gt;
} cvoutput;  &lt;br /&gt;
&lt;br /&gt;
cvoutput cv_out[NUM_CV];	// current state for each cv out&lt;br /&gt;
&lt;br /&gt;
unsigned char gates; // which gates are on =&amp;gt; used for LED setting &lt;br /&gt;
&lt;br /&gt;
// set cv output (and assigned gate, if pitch output)&lt;br /&gt;
// called after anything that changes the cv_out structure  &lt;br /&gt;
void updateCv(cvoutput *cvo)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char bm;&lt;br /&gt;
	unsigned char gated=0;&lt;br /&gt;
	int val=0;&lt;br /&gt;
&lt;br /&gt;
	bm=1&amp;lt;&amp;lt;cvo-&amp;gt;cvOut;	// bit mask for configuration flags &lt;br /&gt;
&lt;br /&gt;
	if(config.ifMode0 &amp;amp; bm)	// 1=CC-Mode&lt;br /&gt;
	{&lt;br /&gt;
		if(config.cvMidiCC[cvo-&amp;gt;cvOut]&amp;gt;127) // if no Controller assigned &lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		// if PB is active &lt;br /&gt;
				val=cvo-&amp;gt;pb+0x80;		 		//     pitch sets unipolar value &lt;br /&gt;
			else 						 		// else &lt;br /&gt;
				val=0; 					 		//    value is zero &lt;br /&gt;
		}&lt;br /&gt;
		else 								// else a controller is assigned &lt;br /&gt;
		{&lt;br /&gt;
			val=cvo-&amp;gt;ccVal; 					//  set current controler value ;&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		//  if PB active &lt;br /&gt;
				val+=cvo-&amp;gt;pb;			 		//     add it (bipolar) &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if( (config.ifMode1 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0) // if Trig Flag and note off &lt;br /&gt;
			gated=1; 								// than gate CV output (to zero) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
		if( (config.ifMode3 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0)	// 5V-Flag, abused as Gated-Mode-CV Flag &lt;br /&gt;
			gated=1;&lt;br /&gt;
*/&lt;br /&gt;
	}&lt;br /&gt;
	else // Note&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;note;&lt;br /&gt;
		val -= config.noteOffset;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode3 &amp;amp; bm)	// 5V&lt;br /&gt;
			val &amp;lt;&amp;lt;= 2;&lt;br /&gt;
		else&lt;br /&gt;
			val &amp;lt;&amp;lt;= 1;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) // PB &lt;br /&gt;
			val+=(cvo-&amp;gt;pb&amp;gt;&amp;gt;2);  // scale down pb - value is empirical and works &amp;quot;not too bad&amp;quot; &lt;br /&gt;
								//  (for &amp;quot;good&amp;quot; the converter should have 12bits a least... ) &lt;br /&gt;
	}&lt;br /&gt;
	if(config.ifMode2 &amp;amp; bm) // Velocity &lt;br /&gt;
		val+=cvo-&amp;gt;velo;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if(val&amp;lt;0 || gated)	// clip values to converter range &lt;br /&gt;
		val=0;&lt;br /&gt;
	if(val&amp;gt;255)&lt;br /&gt;
		val=255;&lt;br /&gt;
&lt;br /&gt;
	set_cv((unsigned char)val, cvo-&amp;gt;cvOut);&lt;br /&gt;
	if((config.ifMode0 &amp;amp; bm)==0)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;on)&lt;br /&gt;
		{&lt;br /&gt;
			if(cvo-&amp;gt;gateDipper==0) // no retrigger required&lt;br /&gt;
				gate_on(cvo-&amp;gt;cvOut);&lt;br /&gt;
			else				   // dip gate for a few milliseconds 	&lt;br /&gt;
				gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates |=bm;&lt;br /&gt;
		}&lt;br /&gt;
		else &lt;br /&gt;
		{&lt;br /&gt;
			gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates &amp;amp;=~bm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate/initialize control variables after &lt;br /&gt;
// mode-configuration change &lt;br /&gt;
void setConfig()&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i; &lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0; &lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	switch(config.ifMode0 &amp;amp; 0xf0 )&lt;br /&gt;
	{	&lt;br /&gt;
		default:&lt;br /&gt;
		case MODE_MONO:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=1;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI2:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV/2;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			for(;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=1;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=2;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI4:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=i;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=NUM_CV;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// assign new note state to cv outputs &lt;br /&gt;
void setNote(unsigned char note, unsigned char velo, unsigned char onOff, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;channel==channel)&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;note=note;&lt;br /&gt;
			cvo-&amp;gt;velo=velo;&lt;br /&gt;
			if(onOff==2 &amp;amp;&amp;amp; cvo-&amp;gt;on &amp;amp;&amp;amp; (config.ifMode1 &amp;amp; (1&amp;lt;&amp;lt;i)))&lt;br /&gt;
				cvo-&amp;gt;gateDipper=4;&lt;br /&gt;
			cvo-&amp;gt;on=onOff;&lt;br /&gt;
			updateCv(cvo);&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	setLed(gates);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process new continous controller value &lt;br /&gt;
void setCC(unsigned char cc, unsigned char value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel== cvo-&amp;gt;channel )&lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode0 &amp;amp;(1&amp;lt;&amp;lt;i))&lt;br /&gt;
			{&lt;br /&gt;
				if(config.cvMidiCC[i]==cc)&lt;br /&gt;
				{&lt;br /&gt;
					cvo-&amp;gt;ccVal=value; &lt;br /&gt;
					updateCv(cvo);&lt;br /&gt;
				}&lt;br /&gt;
				if(config.gateMidiCC[i]==cc  )&lt;br /&gt;
				{&lt;br /&gt;
					if(value&amp;gt;config.gateTreshhold)&lt;br /&gt;
						gate_on(i);&lt;br /&gt;
					else &lt;br /&gt;
						gate_off(i);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// process new pitch bend value &lt;br /&gt;
void setPb(signed int value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel==cvo-&amp;gt;channel &amp;amp;&amp;amp; (config.ifMode2 &amp;amp; (16&amp;lt;&amp;lt;i)) )&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;pb	=(signed char) (value&amp;gt;&amp;gt;5);	&lt;br /&gt;
			updateCv(cvo);	&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// prepare for (asynchronous) configuration write to eeprom &lt;br /&gt;
void setEeWrite(void)&lt;br /&gt;
{&lt;br /&gt;
	shadow_config=config; &lt;br /&gt;
	learn=0;&lt;br /&gt;
	eepromWriteFlag=1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*bootloader)(void) = (void*)0x1800;	// Bootloader sits at adress 0x1800 ... &lt;br /&gt;
&lt;br /&gt;
// process sys ex command &lt;br /&gt;
void setSyx(unsigned char adr, unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
&lt;br /&gt;
	cp=(unsigned char *) &amp;amp;config; &lt;br /&gt;
&lt;br /&gt;
	if(adr==0x14 &amp;amp;&amp;amp; val ==0x7b) &lt;br /&gt;
	{	// write eeprom&lt;br /&gt;
		if(eepromWriteFlag==0)&lt;br /&gt;
		{&lt;br /&gt;
			setEeWrite();&lt;br /&gt;
		}&lt;br /&gt;
		return; &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7&lt;br /&gt;
	if(adr==0x16 &amp;amp;&amp;amp; val ==0x42)	// Software update  &lt;br /&gt;
	{&lt;br /&gt;
		cli();			// interupts off &lt;br /&gt;
		resetAll();		// outputs off &lt;br /&gt;
		GPIOR1 = 0xAB;	// tell bootloader it&#039;s an application boot request &lt;br /&gt;
						// (hardware reset sets GPIOR1 to zero, 0xab == magic value for bootloader )&lt;br /&gt;
		bootloader();	// bye bye! &lt;br /&gt;
		return; 	// &amp;lt;-- never! &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	adr-=PARAMETEROFFSET; &lt;br /&gt;
	if(adr &amp;lt; NUM_PARAMTERS)	// if valid parameter &lt;br /&gt;
	{&lt;br /&gt;
		*(cp+adr)=val; 		// set in &lt;br /&gt;
		if(adr==0x30-PARAMETEROFFSET) // if Mode Byte 0, &lt;br /&gt;
			setConfig();			  // .. cv assignement must be recalculated &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Note Puffer Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define MAX_NOTES 0x10  	// one for each finger of a real programmer &lt;br /&gt;
#define MAX_CHANNELS 4&lt;br /&gt;
unsigned char notes[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char velocities[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char playedNote[MAX_CHANNELS]; // number of currently pressed notes &lt;br /&gt;
// update note puffer for a midi note-on message &lt;br /&gt;
void note_on(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char n;&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	if(n&amp;lt;MAX_NOTES)&lt;br /&gt;
	{&lt;br /&gt;
		playedNote[channel]++;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		for (n=MAX_NOTES-1;n&amp;gt;0;--n)&lt;br /&gt;
		{&lt;br /&gt;
			notes[channel][n-1]=notes[channel][n];&lt;br /&gt;
			velocities[channel][n-1]=velocities[channel][n];&lt;br /&gt;
		}&lt;br /&gt;
		n=MAX_NOTES-1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	notes[channel][n]=note; 		&lt;br /&gt;
	velocities[channel][n]=velocity;&lt;br /&gt;
	setNote(note, velocity, 2, channel);&lt;br /&gt;
}&lt;br /&gt;
// update note puffer for a new midi note-off message &lt;br /&gt;
void note_off(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	signed char n; &lt;br /&gt;
&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	--n; &lt;br /&gt;
	for (i=0;i&amp;lt;=n;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(notes[channel][i]==note)&lt;br /&gt;
		{&lt;br /&gt;
			if(i==n) // last pressed (== the on which playes) note released &lt;br /&gt;
			{&lt;br /&gt;
				if(n==0) // it was the only pressed note &lt;br /&gt;
				{&lt;br /&gt;
					setNote(note, velocities[channel][n], 0, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{	&lt;br /&gt;
					--n;&lt;br /&gt;
					setNote(notes[channel][n], velocities[channel][n], 1, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else // non playing note released &lt;br /&gt;
			{	// =&amp;gt;  just remove it from list &lt;br /&gt;
				int j;&lt;br /&gt;
				for(j=i+1;j&amp;lt;=n;++j)&lt;br /&gt;
				{&lt;br /&gt;
					notes[channel][j-1]=notes[channel][j];&lt;br /&gt;
					velocities[channel][j-1]=velocities[channel][j];&lt;br /&gt;
				}&lt;br /&gt;
				playedNote[channel]--;&lt;br /&gt;
			}&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	All Notes off, empty Note Buffer, All Controllers Reset&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void resetAll(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	cli(); // this might be called asynchronous to midi stream &lt;br /&gt;
	for(i=0;i&amp;lt;MAX_CHANNELS;++i)&lt;br /&gt;
		playedNote[i]=0;&lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0;&lt;br /&gt;
		cvo-&amp;gt;on=0;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		updateCv(cvo);&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
	sei();&lt;br /&gt;
&lt;br /&gt;
	setLed(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	static unsigned char buttonCount;&lt;br /&gt;
	static signed char blinkCount; &lt;br /&gt;
&lt;br /&gt;
	// LED blinker &lt;br /&gt;
	if(--blinkCount&amp;lt;=0)&lt;br /&gt;
	{	&lt;br /&gt;
		blinkCount=40;&lt;br /&gt;
		blinker++;&lt;br /&gt;
		if(button)&lt;br /&gt;
		{&lt;br /&gt;
			if(button&amp;lt;10)&lt;br /&gt;
				button++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Button debouncer &lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{	// down &lt;br /&gt;
		if(buttonCount &amp;lt; 10)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount++;&lt;br /&gt;
			if(buttonCount==10)&lt;br /&gt;
			{&lt;br /&gt;
				button=1;&lt;br /&gt;
				blinkCount=40;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{	// up &lt;br /&gt;
		if(buttonCount &amp;gt; 0)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount--;&lt;br /&gt;
			if(buttonCount==0)&lt;br /&gt;
				button=0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Gate 5 timing (clock output) &lt;br /&gt;
	if(gate5off)&lt;br /&gt;
	{&lt;br /&gt;
		--gate5off;&lt;br /&gt;
		if(gate5off==0)&lt;br /&gt;
			gate_off(5);	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// activ sense &lt;br /&gt;
	if(watchTicker)			// if active sense is activated &lt;br /&gt;
	{&lt;br /&gt;
		watchTicker--;		// decrement time &lt;br /&gt;
		if(watchTicker==0)	// if time elapsed&lt;br /&gt;
			resetAll(); 	// switch all notes off 	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// retrigger timing &lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)	// for all cv output channels &lt;br /&gt;
	{&lt;br /&gt;
		if(cv_out[i].gateDipper)	// if gate timer is active &lt;br /&gt;
		{&lt;br /&gt;
			--cv_out[i].gateDipper;		// decrement timer &lt;br /&gt;
			if(cv_out[i].gateDipper==0)	// if timer elapsed&lt;br /&gt;
				gate_on(i);				// activate gate &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Eval Midi Data&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define NOTE_ON         0x90		// Midi note on message &lt;br /&gt;
#define NOTE_OFF        0x80		// Midi note off message &lt;br /&gt;
#define PITCH           0xE0		// pitch bender&lt;br /&gt;
#define AFTERTOUCH      0xD0        // after touch command (unused) &lt;br /&gt;
#define CC		        0xB0        // Continuous Controller&lt;br /&gt;
#define CHAN_PRES	    0xD0        // Channel Pressure (unused)&lt;br /&gt;
#define SONG_POSITION_POINTER 0xF2	// used for clock synchronization (first beat) &lt;br /&gt;
#define SYSEX 			0xF0		// System exclusive beginn&lt;br /&gt;
#define SYSEND			0xF7		// System exclusive end&lt;br /&gt;
&lt;br /&gt;
struct {&lt;br /&gt;
	unsigned char runningStatus;	// Midi runnig status (*)&lt;br /&gt;
	unsigned char msgByteCnt;    	// number of current midi-message byte received &lt;br /&gt;
	unsigned char byte1;			// first data byte &lt;br /&gt;
	unsigned char syxAdr; 			// Sys-Ex Adress Byte&lt;br /&gt;
	unsigned char startStop;		// current  Midi Start/Stop Status&lt;br /&gt;
	unsigned char startFlag;		// Start on next clock&lt;br /&gt;
	unsigned char clockTick;		// counter for clock division&lt;br /&gt;
	unsigned char channel;			// channel of current midi-command (relative, e.g. 0..3 ) 						&lt;br /&gt;
	unsigned int  spp; 				// song position pointer (from midi message), needed to have the clock synced to song position&lt;br /&gt;
}midi; &lt;br /&gt;
// 	(*) running status is also used as &#039;current status&#039; - it remembers allways, what next data byte is for. Or its zero, than data is ignored. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine, &lt;br /&gt;
// complete midi processing is done here, as the CPU is fast enough to &lt;br /&gt;
// do everything before next midi byte can come in. &lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	unsigned char status,byte;&lt;br /&gt;
&lt;br /&gt;
    status = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((status &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(watchTicker)						// if there had been an active sense &lt;br /&gt;
			watchTicker=ACTIVE_SENSE_TICKS;	// every new byte is ok, to &amp;quot;feed the watchdog&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		if (byte &amp;gt; 0x7F)	                // if command byte (MSB set)&lt;br /&gt;
		{&lt;br /&gt;
			if(byte&amp;gt;= 0xF8)  				// if real-time msg &lt;br /&gt;
			{&lt;br /&gt;
				switch (byte)&lt;br /&gt;
				{&lt;br /&gt;
					case 0xF8: 	// Clock &lt;br /&gt;
					{&lt;br /&gt;
						if(midi.startFlag)  // if synchronized start &lt;br /&gt;
						{&lt;br /&gt;
							midi.startStop=1;&lt;br /&gt;
							midi.startFlag=0;&lt;br /&gt;
&lt;br /&gt;
							if(config.clockDivider&amp;gt;1 )// if divided clock &lt;br /&gt;
							{&lt;br /&gt;
								if( midi.spp) // if start not from zero &lt;br /&gt;
								{&lt;br /&gt;
									midi.clockTick=midi.spp%config.clockDivider;			// calculate clock divider count for current song position &lt;br /&gt;
									if(midi.clockTick &amp;gt;= config.clockDivider&amp;gt;&amp;gt;1)	// if calculated divider count is mor than half elapsed &lt;br /&gt;
										gate_off(5); 								// than set clock output off &lt;br /&gt;
									else&lt;br /&gt;
										gate_on(5);									// else set it on &lt;br /&gt;
								}&lt;br /&gt;
								else &lt;br /&gt;
								{&lt;br /&gt;
									gate_on(5);&lt;br /&gt;
									midi.clockTick=0; &lt;br /&gt;
								}&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						else &lt;br /&gt;
						{&lt;br /&gt;
							if(midi.startStop)	// if (already) running &lt;br /&gt;
								++midi.spp;&lt;br /&gt;
							++midi.clockTick; 	// increment counter for clock output &lt;br /&gt;
						}&lt;br /&gt;
&lt;br /&gt;
						if(config.clockDivider&amp;lt;=1)&lt;br /&gt;
						{&lt;br /&gt;
							gate_on(5);								// do clock output &lt;br /&gt;
							gate5off=3;							&lt;br /&gt;
						}&lt;br /&gt;
						else&lt;br /&gt;
						{&lt;br /&gt;
							if(midi.clockTick &amp;gt;= config.clockDivider)	// if divider count elapsed &lt;br /&gt;
							{&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
								gate_on(5);								// do clock output &lt;br /&gt;
							}&lt;br /&gt;
							else if(midi.clockTick == config.clockDivider&amp;gt;&amp;gt;1)	// if divider count half elapsed &lt;br /&gt;
								gate_off(5); 									// set clock output off &lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
					case 0xF9:	// Tick (unused) &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFA: 	// Start &lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.spp=0;&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
							if(config.clockDivider&amp;gt;1)&lt;br /&gt;
							{&lt;br /&gt;
								gate_off(5); 			// set clock output off 								&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFB:	// Continue&lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
						}&lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFC:	// Stop&lt;br /&gt;
						midi.startStop=0;&lt;br /&gt;
						gate_off(4);&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFD:	// unassigned &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFE:	//  Active Sense&lt;br /&gt;
						watchTicker=ACTIVE_SENSE_TICKS;&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFF:	// Reset &lt;br /&gt;
						break; &lt;br /&gt;
				}&lt;br /&gt;
				return ; &lt;br /&gt;
			} &lt;br /&gt;
&lt;br /&gt;
			if(byte &amp;gt;= 0xF0) 					// if System Common Category message&lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)&lt;br /&gt;
				{&lt;br /&gt;
					if(midi.runningStatus==SYSEX &amp;amp;&amp;amp; midi.msgByteCnt==6) // if a complete valid Sys-Ex Frame has been received &lt;br /&gt;
						setSyx(midi.syxAdr, midi.byte1); 				// change configuration &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					midi.runningStatus = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				else if(byte ==SONG_POSITION_POINTER)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;  &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				//							// every other System Common is ignored 	&lt;br /&gt;
				midi.runningStatus = 0;		// just reset running status &lt;br /&gt;
				return ; 					// and done &lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.runningStatus = 0;				// just new status messages left, so running status is lost&lt;br /&gt;
&lt;br /&gt;
			// only channel messages left here ... &lt;br /&gt;
			if(learn)								 // if learn mode &lt;br /&gt;
			{&lt;br /&gt;
				if(config.midiChannel != (byte&amp;amp;0xf)) // if message channel not current channel&lt;br /&gt;
				{&lt;br /&gt;
					config.midiChannel=byte&amp;amp;0xf;	 // assign channel &lt;br /&gt;
					setEeWrite(); 					 // and store it &lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.channel=(byte &amp;amp; 0xf)-config.midiChannel ;&lt;br /&gt;
			if ( midi.channel&amp;lt;config.numMidiChannels)   // if MIDI channel matches,&lt;br /&gt;
			{&lt;br /&gt;
				midi.runningStatus = byte &amp;amp; 0xF0 ;		// set running status (==Midi command w/o channel)&lt;br /&gt;
				midi.msgByteCnt = 0;&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
			return ; &lt;br /&gt;
		}&lt;br /&gt;
		else	// data bytes goes here &lt;br /&gt;
		{&lt;br /&gt;
			switch (midi.runningStatus)		    // last command byte (or zero, if data is not for us) &lt;br /&gt;
			{&lt;br /&gt;
			case SONG_POSITION_POINTER:&lt;br /&gt;
				if(midi.msgByteCnt)	// 2nd Byte&lt;br /&gt;
				{&lt;br /&gt;
					midi.spp= (((int)byte &amp;lt;&amp;lt; 7 ) | midi.byte1) * 6;&lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt=1;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_ON):					// if we&#039;ve had a note on byte&lt;br /&gt;
				if (midi.msgByteCnt)		// if counter = 1&lt;br /&gt;
				{ 			   				// then we&#039;ve just received a velocity byte&lt;br /&gt;
					midi.msgByteCnt = 0;	// msg complete - clear byte count &lt;br /&gt;
					if (byte == 0)			// is it a note off (velo = 0) ?&lt;br /&gt;
						note_off(midi.byte1,byte,midi.channel);  &lt;br /&gt;
					else 					// NOTE ON&lt;br /&gt;
					{&lt;br /&gt;
						if(learn)&lt;br /&gt;
						{&lt;br /&gt;
							config.noteOffset=midi.byte1;&lt;br /&gt;
							setEeWrite();&lt;br /&gt;
						}&lt;br /&gt;
						&lt;br /&gt;
						note_on(midi.byte1, byte,midi.channel);  // ... and do what the note on has told us &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				else						// first data byte &lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;		// save it &lt;br /&gt;
					midi.msgByteCnt = 1;    // set counter to get 2nd data byte&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_OFF): 	        	// command note off&lt;br /&gt;
				if (midi.msgByteCnt)	 	// if complete &lt;br /&gt;
				{ 					 	&lt;br /&gt;
					midi.msgByteCnt = 0;	&lt;br /&gt;
					note_off(midi.byte1,byte,midi.channel); // switch note off&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1; 	&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (PITCH):		        	// PITCH BEND command&lt;br /&gt;
				if (midi.msgByteCnt)   	 	// if second data byte (MSB) &lt;br /&gt;
				{ 						&lt;br /&gt;
					int v;&lt;br /&gt;
					v=byte;&lt;br /&gt;
					v&amp;lt;&amp;lt;=6;&lt;br /&gt;
					v|=midi.byte1;&lt;br /&gt;
					v-=0x1000;&lt;br /&gt;
					setPb(v,midi.channel);&lt;br /&gt;
&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (CC):	             		// Continuous Controller Message &lt;br /&gt;
				if (midi.msgByteCnt)	&lt;br /&gt;
				{&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
&lt;br /&gt;
					if(learn)&lt;br /&gt;
					{&lt;br /&gt;
						config.cvMidiCC[2]=midi.byte1;&lt;br /&gt;
						setEeWrite();&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
					setCC(midi.byte1, byte,midi.channel);&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
				  midi.byte1 = byte;&lt;br /&gt;
				  midi.msgByteCnt = 1;&lt;br /&gt;
				  return; &lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
	//		case (CHAN_PRES):	         	// if channel pressure command&lt;br /&gt;
	//			channel_pressure (byte); 	// set Aftertouch &lt;br /&gt;
			case SYSEX:						// SYS-EX command &lt;br /&gt;
				midi.msgByteCnt++;			// message Byte counter&lt;br /&gt;
				switch(midi.msgByteCnt)		// which byte is on? &lt;br /&gt;
				{&lt;br /&gt;
					case 1:					// Manufactorer &lt;br /&gt;
						if(byte !=0x70)		// if not the right one &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error / wrong byte &lt;br /&gt;
						break; &lt;br /&gt;
					case 2:					// Unit - ID &lt;br /&gt;
						if(byte!=0x7D)&lt;br /&gt;
							midi.msgByteCnt=0;&lt;br /&gt;
						break; &lt;br /&gt;
					case 3:					// Midi-Adress (or Unit ID) (ignored!)&lt;br /&gt;
						if(byte&amp;gt;=0x10)		// if not in legal range &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error &lt;br /&gt;
						break;&lt;br /&gt;
					case 4:					// adress byte&lt;br /&gt;
						midi.syxAdr=byte;	// store it for later&lt;br /&gt;
						break;&lt;br /&gt;
					case 5:					// high data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1=(byte&amp;lt;&amp;lt;4);	// set half the data &lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					case 6: 				// low data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1 |= byte;	// or together the complete data byte &lt;br /&gt;
						else 					// evaluation is  done in the code for SYSEND above! &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					default:				// not our framing &lt;br /&gt;
						midi.msgByteCnt=0;&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
				if(midi.msgByteCnt==0)	// something did not fit &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				break;&lt;br /&gt;
			case 0: // bnwtko (bytes no one wants to know off ) &lt;br /&gt;
				return ;&lt;br /&gt;
			default:&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7299</id>
		<title>MCV876 Controller Update: Source Code</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7299"/>
		<updated>2011-06-26T19:54:01Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Quelltext */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Übersetzen= &lt;br /&gt;
Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden.&lt;br /&gt;
Im Studio ein neues, leeres Projekt mit Standard-Einstellungen, einem sinnvollen Namen und an einem Ort, wo man es wiederfindet erstellen, Quelltext in das (automatisch erzeugte, leere) C-File kopieren, auf Build-&amp;gt;Build klicken, fertig. &lt;br /&gt;
Das resultierende HEX File kann man mit einem üblichen Programmieradapter in der Chip flashen, für uns zweckmäßiger ist allerdings, das in ein Sys-Ex File zu Konvertieren ( [[MCV876_Controller_Update:_File_Converter]]), und das über Midi an den Controller zu schicken. Dann programmiert der sich selber um. Dazu muss er allerdings einmalig mit dem Bootlader [hier Link auf Bootlader imaginieren.. ] programmiert worden sein. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu den Atmel Controllern findet man eine Menge Info mit Tante Google, [http://www.mikrocontroller.net http://www.mikrocontroller.net] kommt häufig vor, und hat ein ergiebiges Forum und gute Artikel. Auf der Atmel Webseite gibt es auch einige Info, das [http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf ATMega 88 Datenblatt] ist spätestens dann nötig, wenn es um die Hardware geht.&lt;br /&gt;
&lt;br /&gt;
=Quelltext=&lt;br /&gt;
&amp;lt;pre&amp;gt;/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Midi to 4 Channel Control Voltage and six Gates Interface &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.22 2011-06-26 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
			&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;			// yes, we want to talk to the port pins by name&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;		// ... oh.. and some eeprom access would be nice&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;	// ... and w/o the interupts it wouldn&#039;t do anything... &lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*	===================ZERO LINE=================================&lt;br /&gt;
	WARNING: everthing below the zero line counts from zero! &lt;br /&gt;
	e.g. whats Gate 6 in manual is GATE5 here, CVs are CV0 to 3 &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	where should the LED output be &lt;br /&gt;
*/&lt;br /&gt;
#define LEDLED   6  // LED at Reset-Pin, standard for series, but no debugging possible. &lt;br /&gt;
#define LEDGATE0 0	// Led at Gate 0, no LED (Test I: no LED ) &lt;br /&gt;
#define LEDGATE4 4	// LED at Gate 5, Test II: no Gate 5 Function (Midi Start/Stop) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 #define LEDOUT LEDLED // select your evil... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define ACTIVE_SENSE_TICKS 160 // 2ms Timer Ticks for active sense time out &lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4))) // True if Button pressed &lt;br /&gt;
&lt;br /&gt;
#define NUM_CV 4&lt;br /&gt;
&lt;br /&gt;
#define PARAMETEROFFSET 0x23 // First paramter has adress 0x23&lt;br /&gt;
#define NUM_PARAMTERS 17	 // and there are 17 parameters &lt;br /&gt;
&lt;br /&gt;
#define MODE_MONO 	0x00	// monophon, all assigned to last note on a single channel &lt;br /&gt;
#define MODE_MULTI2	0x40	// two channel, 1/2 for midiChannel, 2/3 for midiChannel+1 &lt;br /&gt;
#define MODE_MULTI4	0x60	// for channel midiChannel to midiChannel+3 &lt;br /&gt;
#define MODE_POLY2	0x80	// not supported &lt;br /&gt;
#define MODE_POLY4	0xA0	// not supported&lt;br /&gt;
&lt;br /&gt;
struct CONFIG {&lt;br /&gt;
	unsigned char midiChannel;&lt;br /&gt;
	unsigned char noteOffset;&lt;br /&gt;
&lt;br /&gt;
	unsigned char cvMidiCC[NUM_CV];&lt;br /&gt;
	unsigned char gateMidiCC[NUM_CV];&lt;br /&gt;
	&lt;br /&gt;
	unsigned char gateTreshhold;&lt;br /&gt;
	unsigned char clockDivider;&lt;br /&gt;
	unsigned char clockTime; &lt;br /&gt;
	unsigned char ifMode0; 	// 	bit 0 ... bit 3 = CV0 .. CV3, 0=Pitch, 1=CC (if pitch, CC assignement is ignored) &lt;br /&gt;
							//	bit 5: voices: 0=2; 1=4 &lt;br /&gt;
							//  bit 7:6= 00 Mono; 01:Multi-Channel; 10: Poly (not supported); &lt;br /&gt;
	&lt;br /&gt;
	unsigned char ifMode1;  // Gate Trigger Mode Bits (0=Re-Trigger, 1=TriggerControl)  &lt;br /&gt;
	unsigned char ifMode2;  // bit0: Velo0=&amp;gt;CV0; bit1:Velo0=&amp;gt;CV1; bit2:Velo-Poly2=&amp;gt;CV2; bit3:Velo1=&amp;gt;CV3&lt;br /&gt;
							// bit4: PB=&amp;gt;CV0; bit5:PB=&amp;gt;CV1; bit6:PB=&amp;gt;CV2; bit7:PB=&amp;gt;CV3&lt;br /&gt;
	unsigned char ifMode3; 	// bit0..bit3 = CV0... CV3; 0=10V, 1=5V Mode &lt;br /&gt;
&lt;br /&gt;
//  internal evaluated configuration &lt;br /&gt;
	unsigned char numMidiChannels;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
} shadow_config, // her goes a copy, when config should be written, because that might take a while &lt;br /&gt;
&lt;br /&gt;
config 	// current configuration, mostly direct evaluated troughout the program&lt;br /&gt;
= {		// default values (Mono-Mode, Midi-Ch 1, CV1=Pitch, CV2=Velocity CV3=ModWheel CV4=Pitch Bend&lt;br /&gt;
			0,  	// Midi Channel &lt;br /&gt;
			24, 	// Note Offset (2 Oktaves) &lt;br /&gt;
		{	255,255,1,255, },  	// CV - assigned Midi Controllers (used if not pitch output) 255=none&lt;br /&gt;
		{   255,18,19,20, }, 	// Gate - asigned Controllers (if not pitch) 255=none &lt;br /&gt;
			63, 	// Treshold&lt;br /&gt;
			6,  	// Divider 6 (1/16 Note) &lt;br /&gt;
			1,  	// clock time (unused) &lt;br /&gt;
			0x0e, 	// mode 0 Low nibble CC-Bits, high: interface mode &lt;br /&gt;
			0, 		// mode 1 re-trigger bits &lt;br /&gt;
			0x82, 	// mode 2 High Nibble: PB-bits, Low: Velo-Bits&lt;br /&gt;
			0,    	// mode 3 (5V-Bits) &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define EE_MAGIC 17&lt;br /&gt;
unsigned char ee_magic EEMEM;	// to check it eeprom was ever written and should be read back &lt;br /&gt;
unsigned char config_eeprom[NUM_PARAMTERS] EEMEM; // Config in EEPROM, saves it during power down. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
volatile unsigned char  eepromWriteFlag;	// set to one, if main-loop should write eeprom &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char button; 	// debounced button status &lt;br /&gt;
volatile unsigned char learn; 	// (channel/base note/cc) &amp;quot;do learn&amp;quot; status &lt;br /&gt;
volatile unsigned char blinker; // some count for blinkenlights &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char gate5off; // timer counter for when clock gate output should go low again. &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char watchTicker;	// midi active sense counter &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setConfig(void); 		&lt;br /&gt;
void setLed(unsigned char on);&lt;br /&gt;
void resetAll(void);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	main() &lt;br /&gt;
	Main does initialization, than runs a loop, which does nearly nothing: &lt;br /&gt;
&lt;br /&gt;
	Programming the EEPROM (because its slow) &lt;br /&gt;
	Blinking &lt;br /&gt;
	Button evaluation &lt;br /&gt;
&lt;br /&gt;
	All the important things are done by the interupts. (Thanks to the &lt;br /&gt;
	20MHZ RISC power everything can be done in less than 320µs=one Midi-Character) &lt;br /&gt;
*/&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char blink=4;&lt;br /&gt;
&lt;br /&gt;
	unsigned char i,x; &lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
	static unsigned char lastButton; &lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
	// Read Configuration &lt;br /&gt;
	_delay_ms(10); // wait for button pin to get high if open. &lt;br /&gt;
	if( eeprom_read_byte(&amp;amp;ee_magic) == EE_MAGIC  &amp;amp;&amp;amp;   !BUTTON) // if eeprom valid AND Button not pressed &lt;br /&gt;
	{&lt;br /&gt;
		cp=(unsigned char* )&amp;amp;config; &lt;br /&gt;
		for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
		{&lt;br /&gt;
			x=eeprom_read_byte(config_eeprom+i);			// read config from eeprom &lt;br /&gt;
			*cp=x;	&lt;br /&gt;
			cp++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
		blink=8; &lt;br /&gt;
&lt;br /&gt;
	setConfig();&lt;br /&gt;
&lt;br /&gt;
	// enable interrupts&lt;br /&gt;
	sei(); &lt;br /&gt;
&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		if(eepromWriteFlag)&lt;br /&gt;
		{	// save configuration &lt;br /&gt;
			cp=(unsigned char* )&amp;amp;shadow_config; &lt;br /&gt;
			for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
			{&lt;br /&gt;
				x=*cp;&lt;br /&gt;
				eeprom_write_byte(config_eeprom+i,x);&lt;br /&gt;
				cp++;&lt;br /&gt;
			}&lt;br /&gt;
			eeprom_write_byte(&amp;amp;ee_magic,EE_MAGIC); &lt;br /&gt;
&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			blink=6;&lt;br /&gt;
			blinker=0;&lt;br /&gt;
			eepromWriteFlag=0;	&lt;br /&gt;
		}&lt;br /&gt;
		if(blink &amp;amp;&amp;amp; blinker&amp;gt;2)  // blink after power up and after storing in nv-memory &lt;br /&gt;
		{&lt;br /&gt;
			blink--;&lt;br /&gt;
			setLed(blink&amp;amp;1);&lt;br /&gt;
			blinker=0; &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if(button != lastButton)&lt;br /&gt;
		{&lt;br /&gt;
			if(button==1 )&lt;br /&gt;
			{&lt;br /&gt;
				resetAll();&lt;br /&gt;
				if(learn==1)&lt;br /&gt;
				{&lt;br /&gt;
					learn=0;&lt;br /&gt;
					setLed(0);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if(button==6) &lt;br /&gt;
			{&lt;br /&gt;
				if(learn ==0)&lt;br /&gt;
				{&lt;br /&gt;
					learn=1;&lt;br /&gt;
					setLed(1);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			lastButton=button; &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return 0; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Hardware Interface&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
/*  Pin assignment &lt;br /&gt;
   Atmel Fkt 	[Orig] &lt;br /&gt;
1  Reset LED 	[MCLR] &lt;br /&gt;
2  RX    Midi-Rx[WR1]&lt;br /&gt;
3  PD1   WR2&lt;br /&gt;
4  PD2   WR3&lt;br /&gt;
5  PD3   WR4&lt;br /&gt;
6  PD4   Button&lt;br /&gt;
7  VCC   +5  	[LED] (Either from Atmel-1(Series) or Atmel-15(w/Debug))&lt;br /&gt;
8  GND   GND&lt;br /&gt;
9  OSC   OSC&lt;br /&gt;
10 OSC   OSC &lt;br /&gt;
11 PD5   G1&lt;br /&gt;
12 PD6   G2&lt;br /&gt;
13 PD7   G3&lt;br /&gt;
14 PB0   G4&lt;br /&gt;
&lt;br /&gt;
15 PB1   G5 (Optional + LED) &lt;br /&gt;
16 PB2   G6 &lt;br /&gt;
17 PB3   WR1    [---] &lt;br /&gt;
18 PB4   D0     [RX]&lt;br /&gt;
19 PB5   D1		[GND]&lt;br /&gt;
20 AVCC  +5&lt;br /&gt;
21 ARef  +5 	[D0]&lt;br /&gt;
22 GND   GND	[D1] &lt;br /&gt;
23 PC0   D2&lt;br /&gt;
24 PC1   D3&lt;br /&gt;
25 PC2   D4&lt;br /&gt;
26 PC3   D5&lt;br /&gt;
27 PC4   D6&lt;br /&gt;
28 PC5   D7&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
// put &amp;quot;val&amp;quot; to the right port bits for the DA-Converter &lt;br /&gt;
// ... better program than solder *that*... &lt;br /&gt;
void adByteOut(unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char b;&lt;br /&gt;
	b=val;&lt;br /&gt;
	b&amp;gt;&amp;gt;=2;&lt;br /&gt;
	PORTC=b;&lt;br /&gt;
	b=val&amp;amp;3;&lt;br /&gt;
	b&amp;lt;&amp;lt;=4;&lt;br /&gt;
	val=PORTB;&lt;br /&gt;
	val &amp;amp;=0xCF;&lt;br /&gt;
	val|=b;&lt;br /&gt;
	PORTB=val;&lt;br /&gt;
}&lt;br /&gt;
// set DA-Output voltage of one CV Channel  &lt;br /&gt;
void set_cv(unsigned char val, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	adByteOut(val); 	// set D0..D7 converter data bits &lt;br /&gt;
	switch (channel)	// pulse with write pin &lt;br /&gt;
	{&lt;br /&gt;
		case 0: //PB3&lt;br /&gt;
			PORTB  &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTB  |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD1&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;1);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;1);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD2&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;2);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;2);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PD3&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// set on of the gate outputs active &lt;br /&gt;
void gate_on(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// clear one of the gate outputs &lt;br /&gt;
void gate_off(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif&lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Data Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
typedef struct {&lt;br /&gt;
	unsigned char channel;  // (relative) Midi channel (0..3) Mode dependant &lt;br /&gt;
	unsigned char cvOut;	// for which CV-Ouput (0..3) fixed &lt;br /&gt;
	unsigned char ccVal;	// current CC Value &lt;br /&gt;
	unsigned char note;		// current Note &lt;br /&gt;
	unsigned char velo;		// current Velocity &lt;br /&gt;
	unsigned char on;		// if Note is on &lt;br /&gt;
	signed char   pb;		// current Pitch Bend Value &lt;br /&gt;
	unsigned char gateDipper;&lt;br /&gt;
} cvoutput;  &lt;br /&gt;
&lt;br /&gt;
cvoutput cv_out[NUM_CV];	// current state for each cv out&lt;br /&gt;
&lt;br /&gt;
unsigned char gates; // which gates are on =&amp;gt; used for LED setting &lt;br /&gt;
&lt;br /&gt;
// set cv output (and assigned gate, if pitch output)&lt;br /&gt;
// called after anything that changes the cv_out structure  &lt;br /&gt;
void updateCv(cvoutput *cvo)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char bm;&lt;br /&gt;
	unsigned char gated=0;&lt;br /&gt;
	int val=0;&lt;br /&gt;
&lt;br /&gt;
	bm=1&amp;lt;&amp;lt;cvo-&amp;gt;cvOut;	// bit mask for configuration flags &lt;br /&gt;
&lt;br /&gt;
	if(config.ifMode0 &amp;amp; bm)	// 1=CC-Mode&lt;br /&gt;
	{&lt;br /&gt;
		if(config.cvMidiCC[cvo-&amp;gt;cvOut]&amp;gt;127) // if no Controller assigned &lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		// if PB is active &lt;br /&gt;
				val=cvo-&amp;gt;pb+0x80;		 		//     pitch sets unipolar value &lt;br /&gt;
			else 						 		// else &lt;br /&gt;
				val=0; 					 		//    value is zero &lt;br /&gt;
		}&lt;br /&gt;
		else 								// else a controller is assigned &lt;br /&gt;
		{&lt;br /&gt;
			val=cvo-&amp;gt;ccVal; 					//  set current controler value ;&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		//  if PB active &lt;br /&gt;
				val+=cvo-&amp;gt;pb;			 		//     add it (bipolar) &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if( (config.ifMode1 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0) // if Trig Flag and note off &lt;br /&gt;
			gated=1; 								// than gate CV output (to zero) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
		if( (config.ifMode3 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0)	// 5V-Flag, abused as Gated-Mode-CV Flag &lt;br /&gt;
			gated=1;&lt;br /&gt;
*/&lt;br /&gt;
	}&lt;br /&gt;
	else // Note&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;note;&lt;br /&gt;
		val -= config.noteOffset;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode3 &amp;amp; bm)	// 5V&lt;br /&gt;
			val &amp;lt;&amp;lt;= 2;&lt;br /&gt;
		else&lt;br /&gt;
			val &amp;lt;&amp;lt;= 1;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) // PB &lt;br /&gt;
			val+=(cvo-&amp;gt;pb&amp;gt;&amp;gt;2);  // scale down pb - value is empirical and works &amp;quot;not too bad&amp;quot; &lt;br /&gt;
								//  (for &amp;quot;good&amp;quot; the converter should have 12bits a least... ) &lt;br /&gt;
	}&lt;br /&gt;
	if(config.ifMode2 &amp;amp; bm) // Velocity &lt;br /&gt;
		val+=cvo-&amp;gt;velo;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if(val&amp;lt;0 || gated)	// clip values to converter range &lt;br /&gt;
		val=0;&lt;br /&gt;
	if(val&amp;gt;255)&lt;br /&gt;
		val=255;&lt;br /&gt;
&lt;br /&gt;
	set_cv((unsigned char)val, cvo-&amp;gt;cvOut);&lt;br /&gt;
	if((config.ifMode0 &amp;amp; bm)==0)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;on)&lt;br /&gt;
		{&lt;br /&gt;
			if(cvo-&amp;gt;gateDipper==0) // no retrigger required&lt;br /&gt;
				gate_on(cvo-&amp;gt;cvOut);&lt;br /&gt;
			else				   // dip gate for a few milliseconds 	&lt;br /&gt;
				gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates |=bm;&lt;br /&gt;
		}&lt;br /&gt;
		else &lt;br /&gt;
		{&lt;br /&gt;
			gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates &amp;amp;=~bm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate/initialize control variables after &lt;br /&gt;
// mode-configuration change &lt;br /&gt;
void setConfig()&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i; &lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0; &lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	switch(config.ifMode0 &amp;amp; 0xf0 )&lt;br /&gt;
	{	&lt;br /&gt;
		default:&lt;br /&gt;
		case MODE_MONO:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=1;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI2:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV/2;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			for(;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=1;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=2;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI4:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=i;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=NUM_CV;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// assign new note state to cv outputs &lt;br /&gt;
void setNote(unsigned char note, unsigned char velo, unsigned char onOff, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;channel==channel)&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;note=note;&lt;br /&gt;
			cvo-&amp;gt;velo=velo;&lt;br /&gt;
			if(onOff==2 &amp;amp;&amp;amp; cvo-&amp;gt;on &amp;amp;&amp;amp; (config.ifMode1 &amp;amp; (1&amp;lt;&amp;lt;i)))&lt;br /&gt;
				cvo-&amp;gt;gateDipper=4;&lt;br /&gt;
			cvo-&amp;gt;on=onOff;&lt;br /&gt;
			updateCv(cvo);&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	setLed(gates);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process new continous controller value &lt;br /&gt;
void setCC(unsigned char cc, unsigned char value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel== cvo-&amp;gt;channel )&lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode0 &amp;amp;(1&amp;lt;&amp;lt;i))&lt;br /&gt;
			{&lt;br /&gt;
				if(config.cvMidiCC[i]==cc)&lt;br /&gt;
				{&lt;br /&gt;
					cvo-&amp;gt;ccVal=value; &lt;br /&gt;
					updateCv(cvo);&lt;br /&gt;
				}&lt;br /&gt;
				if(config.gateMidiCC[i]==cc  )&lt;br /&gt;
				{&lt;br /&gt;
					if(value&amp;gt;config.gateTreshhold)&lt;br /&gt;
						gate_on(i);&lt;br /&gt;
					else &lt;br /&gt;
						gate_off(i);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// process new pitch bend value &lt;br /&gt;
void setPb(signed int value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel==cvo-&amp;gt;channel &amp;amp;&amp;amp; (config.ifMode2 &amp;amp; (16&amp;lt;&amp;lt;i)) )&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;pb	=(signed char) (value&amp;gt;&amp;gt;5);	&lt;br /&gt;
			updateCv(cvo);	&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// prepare for (asynchronous) configuration write to eeprom &lt;br /&gt;
void setEeWrite(void)&lt;br /&gt;
{&lt;br /&gt;
	shadow_config=config; &lt;br /&gt;
	learn=0;&lt;br /&gt;
	eepromWriteFlag=1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*bootloader)(void) = (void*)0x1800;	// Bootloader sits at adress 0x1800 ... &lt;br /&gt;
&lt;br /&gt;
// process sys ex command &lt;br /&gt;
void setSyx(unsigned char adr, unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
&lt;br /&gt;
	cp=(unsigned char *) &amp;amp;config; &lt;br /&gt;
&lt;br /&gt;
	if(adr==0x14 &amp;amp;&amp;amp; val ==0x7b) &lt;br /&gt;
	{	// write eeprom&lt;br /&gt;
		if(eepromWriteFlag==0)&lt;br /&gt;
		{&lt;br /&gt;
			setEeWrite();&lt;br /&gt;
		}&lt;br /&gt;
		return; &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7&lt;br /&gt;
	if(adr==0x16 &amp;amp;&amp;amp; val ==0x42)	// Software update  &lt;br /&gt;
	{&lt;br /&gt;
		cli();			// interupts off &lt;br /&gt;
		resetAll();		// outputs off &lt;br /&gt;
		GPIOR1 = 0xAB;	// tell bootloader it&#039;s an application boot request &lt;br /&gt;
						// (hardware reset sets GPIOR1 to zero, 0xab == magic value for bootloader )&lt;br /&gt;
		bootloader();	// bye bye! &lt;br /&gt;
		return; 	// &amp;lt;-- never! &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	adr-=PARAMETEROFFSET; &lt;br /&gt;
	if(adr &amp;lt; NUM_PARAMTERS)	// if valid parameter &lt;br /&gt;
	{&lt;br /&gt;
		*(cp+adr)=val; 		// set in &lt;br /&gt;
		if(adr==0x30-PARAMETEROFFSET) // if Mode Byte 0, &lt;br /&gt;
			setConfig();			  // .. cv assignement must be recalculated &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Note Puffer Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define MAX_NOTES 0x10  	// one for each finger of a real programmer &lt;br /&gt;
#define MAX_CHANNELS 4&lt;br /&gt;
unsigned char notes[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char velocities[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char playedNote[MAX_CHANNELS]; // number of currently pressed notes &lt;br /&gt;
// update note puffer for a midi note-on message &lt;br /&gt;
void note_on(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char n;&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	if(n&amp;lt;MAX_NOTES)&lt;br /&gt;
	{&lt;br /&gt;
		playedNote[channel]++;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		for (n=MAX_NOTES-1;n&amp;gt;0;--n)&lt;br /&gt;
		{&lt;br /&gt;
			notes[channel][n-1]=notes[channel][n];&lt;br /&gt;
			velocities[channel][n-1]=velocities[channel][n];&lt;br /&gt;
		}&lt;br /&gt;
		n=MAX_NOTES-1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	notes[channel][n]=note; 		&lt;br /&gt;
	velocities[channel][n]=velocity;&lt;br /&gt;
	setNote(note, velocity, 2, channel);&lt;br /&gt;
}&lt;br /&gt;
// update note puffer for a new midi note-off message &lt;br /&gt;
void note_off(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	signed char n; &lt;br /&gt;
&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	--n; &lt;br /&gt;
	for (i=0;i&amp;lt;=n;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(notes[channel][i]==note)&lt;br /&gt;
		{&lt;br /&gt;
			if(i==n) // last pressed (== the on which playes) note released &lt;br /&gt;
			{&lt;br /&gt;
				if(n==0) // it was the only pressed note &lt;br /&gt;
				{&lt;br /&gt;
					setNote(note, velocities[channel][n], 0, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{	&lt;br /&gt;
					--n;&lt;br /&gt;
					setNote(notes[channel][n], velocities[channel][n], 1, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else // non playing note released &lt;br /&gt;
			{	// =&amp;gt;  just remove it from list &lt;br /&gt;
				int j;&lt;br /&gt;
				for(j=i+1;j&amp;lt;=n;++j)&lt;br /&gt;
				{&lt;br /&gt;
					notes[channel][j-1]=notes[channel][j];&lt;br /&gt;
					velocities[channel][j-1]=velocities[channel][j];&lt;br /&gt;
				}&lt;br /&gt;
				playedNote[channel]--;&lt;br /&gt;
			}&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	All Notes off, empty Note Buffer, All Controllers Reset&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void resetAll(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	cli(); // this might be called asynchronous to midi stream &lt;br /&gt;
	for(i=0;i&amp;lt;MAX_CHANNELS;++i)&lt;br /&gt;
		playedNote[i]=0;&lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0;&lt;br /&gt;
		cvo-&amp;gt;on=0;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		updateCv(cvo);&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
	sei();&lt;br /&gt;
&lt;br /&gt;
	setLed(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	static unsigned char buttonCount;&lt;br /&gt;
	static signed char blinkCount; &lt;br /&gt;
&lt;br /&gt;
	// LED blinker &lt;br /&gt;
	if(--blinkCount&amp;lt;=0)&lt;br /&gt;
	{	&lt;br /&gt;
		blinkCount=40;&lt;br /&gt;
		blinker++;&lt;br /&gt;
		if(button)&lt;br /&gt;
		{&lt;br /&gt;
			if(button&amp;lt;10)&lt;br /&gt;
				button++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Button debouncer &lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{	// down &lt;br /&gt;
		if(buttonCount &amp;lt; 10)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount++;&lt;br /&gt;
			if(buttonCount==10)&lt;br /&gt;
			{&lt;br /&gt;
				button=1;&lt;br /&gt;
				blinkCount=40;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{	// up &lt;br /&gt;
		if(buttonCount &amp;gt; 0)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount--;&lt;br /&gt;
			if(buttonCount==0)&lt;br /&gt;
				button=0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Gate 5 timing (clock output) &lt;br /&gt;
	if(gate5off)&lt;br /&gt;
	{&lt;br /&gt;
		--gate5off;&lt;br /&gt;
		if(gate5off==0)&lt;br /&gt;
			gate_off(5);	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// activ sense &lt;br /&gt;
	if(watchTicker)			// if active sense is activated &lt;br /&gt;
	{&lt;br /&gt;
		watchTicker--;		// decrement time &lt;br /&gt;
		if(watchTicker==0)	// if time elapsed&lt;br /&gt;
			resetAll(); 	// switch all notes off 	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// retrigger timing &lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)	// for all cv output channels &lt;br /&gt;
	{&lt;br /&gt;
		if(cv_out[i].gateDipper)	// if gate timer is active &lt;br /&gt;
		{&lt;br /&gt;
			--cv_out[i].gateDipper;		// decrement timer &lt;br /&gt;
			if(cv_out[i].gateDipper==0)	// if timer elapsed&lt;br /&gt;
				gate_on(i);				// activate gate &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Eval Midi Data&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define NOTE_ON         0x90		// Midi note on message &lt;br /&gt;
#define NOTE_OFF        0x80		// Midi note off message &lt;br /&gt;
#define PITCH           0xE0		// pitch bender&lt;br /&gt;
#define AFTERTOUCH      0xD0        // after touch command (unused) &lt;br /&gt;
#define CC		        0xB0        // Continuous Controller&lt;br /&gt;
#define CHAN_PRES	    0xD0        // Channel Pressure (unused)&lt;br /&gt;
#define SONG_POSITION_POINTER 0xF2	// used for clock synchronization (first beat) &lt;br /&gt;
#define SYSEX 			0xF0		// System exclusive beginn&lt;br /&gt;
#define SYSEND			0xF7		// System exclusive end&lt;br /&gt;
&lt;br /&gt;
struct {&lt;br /&gt;
	unsigned char runningStatus;	// Midi runnig status (*)&lt;br /&gt;
	unsigned char msgByteCnt;    	// number of current midi-message byte received &lt;br /&gt;
	unsigned char byte1;			// first data byte &lt;br /&gt;
	unsigned char syxAdr; 			// Sys-Ex Adress Byte&lt;br /&gt;
	unsigned char startStop;		// current  Midi Start/Stop Status&lt;br /&gt;
	unsigned char startFlag;		// Start on next clock&lt;br /&gt;
	unsigned char clockTick;		// counter for clock division&lt;br /&gt;
	unsigned char channel;			// channel of current midi-command (relative, e.g. 0..3 ) 						&lt;br /&gt;
	unsigned int  spp; 				// song position pointer (from midi message), needed to have the clock synced to song position&lt;br /&gt;
}midi; &lt;br /&gt;
// 	(*) running status is also used as &#039;current status&#039; - it remembers allways, what next data byte is for. Or its zero, than data is ignored. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine, &lt;br /&gt;
// complete midi processing is done here, as the CPU is fast enough to &lt;br /&gt;
// do everything before next midi byte can come in. &lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	unsigned char status,byte;&lt;br /&gt;
&lt;br /&gt;
    status = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((status &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(watchTicker)						// if there had been an active sense &lt;br /&gt;
			watchTicker=ACTIVE_SENSE_TICKS;	// every new byte is ok, to &amp;quot;feed the watchdog&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		if (byte &amp;gt; 0x7F)	                // if command byte (MSB set)&lt;br /&gt;
		{&lt;br /&gt;
			if(byte&amp;gt;= 0xF8)  				// if real-time msg &lt;br /&gt;
			{&lt;br /&gt;
				switch (byte)&lt;br /&gt;
				{&lt;br /&gt;
					case 0xF8: 	// Clock &lt;br /&gt;
					{&lt;br /&gt;
						if(midi.startFlag)  // if synchronized start &lt;br /&gt;
						{&lt;br /&gt;
							midi.startStop=1;&lt;br /&gt;
							midi.startFlag=0;&lt;br /&gt;
&lt;br /&gt;
							if(config.clockDivider&amp;gt;1 )// if divided clock &lt;br /&gt;
							{&lt;br /&gt;
								if( midi.spp) // if start not from zero &lt;br /&gt;
								{&lt;br /&gt;
									midi.clockTick=midi.spp%config.clockDivider;			// calculate clock divider count for current song position &lt;br /&gt;
									if(midi.clockTick &amp;gt;= config.clockDivider&amp;gt;&amp;gt;1)	// if calculated divider count is mor than half elapsed &lt;br /&gt;
										gate_off(5); 								// than set clock output off &lt;br /&gt;
									else&lt;br /&gt;
										gate_on(5);									// else set it on &lt;br /&gt;
								}&lt;br /&gt;
								else &lt;br /&gt;
								{&lt;br /&gt;
									gate_on(5);&lt;br /&gt;
									midi.clockTick=0; &lt;br /&gt;
								}&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						else &lt;br /&gt;
						{&lt;br /&gt;
							if(midi.startStop)	// if (already) running &lt;br /&gt;
								++midi.spp;&lt;br /&gt;
							++midi.clockTick; 	// increment counter for clock output &lt;br /&gt;
						}&lt;br /&gt;
&lt;br /&gt;
						if(config.clockDivider&amp;lt;=1)&lt;br /&gt;
						{&lt;br /&gt;
							gate_on(5);								// do clock output &lt;br /&gt;
							gate5off=3;							&lt;br /&gt;
						}&lt;br /&gt;
						else&lt;br /&gt;
						{&lt;br /&gt;
							if(midi.clockTick &amp;gt;= config.clockDivider)	// if divider count elapsed &lt;br /&gt;
							{&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
								gate_on(5);								// do clock output &lt;br /&gt;
							}&lt;br /&gt;
							else if(midi.clockTick == config.clockDivider&amp;gt;&amp;gt;1)	// if divider count half elapsed &lt;br /&gt;
								gate_off(5); 									// set clock output off &lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
					case 0xF9:	// Tick (unused) &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFA: 	// Start &lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.spp=0;&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
							if(config.clockDivider&amp;gt;1)&lt;br /&gt;
							{&lt;br /&gt;
								gate_off(5); 			// set clock output off 								&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFB:	// Continue&lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
						}&lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFC:	// Stop&lt;br /&gt;
						midi.startStop=0;&lt;br /&gt;
						gate_off(4);&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFD:	// unassigned &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFE:	//  Active Sense&lt;br /&gt;
						watchTicker=ACTIVE_SENSE_TICKS;&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFF:	// Reset &lt;br /&gt;
						break; &lt;br /&gt;
				}&lt;br /&gt;
				return ; &lt;br /&gt;
			} &lt;br /&gt;
&lt;br /&gt;
			if(byte &amp;gt;= 0xF0) 					// if System Common Category message&lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)&lt;br /&gt;
				{&lt;br /&gt;
					if(midi.runningStatus==SYSEX &amp;amp;&amp;amp; midi.msgByteCnt==6) // if a complete valid Sys-Ex Frame has been received &lt;br /&gt;
						setSyx(midi.syxAdr, midi.byte1); 				// change configuration &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					midi.runningStatus = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				else if(byte ==SONG_POSITION_POINTER)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;  &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				//							// every other System Common is ignored 	&lt;br /&gt;
				midi.runningStatus = 0;		// just reset running status &lt;br /&gt;
				return ; 					// and done &lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.runningStatus = 0;				// just new status messages left, so running status is lost&lt;br /&gt;
&lt;br /&gt;
			// only channel messages left here ... &lt;br /&gt;
			if(learn)								 // if learn mode &lt;br /&gt;
			{&lt;br /&gt;
				if(config.midiChannel != (byte&amp;amp;0xf)) // if message channel not current channel&lt;br /&gt;
				{&lt;br /&gt;
					config.midiChannel=byte&amp;amp;0xf;	 // assign channel &lt;br /&gt;
					setEeWrite(); 					 // and store it &lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.channel=(byte &amp;amp; 0xf)-config.midiChannel ;&lt;br /&gt;
			if ( midi.channel&amp;lt;config.numMidiChannels)   // if MIDI channel matches,&lt;br /&gt;
			{&lt;br /&gt;
				midi.runningStatus = byte &amp;amp; 0xF0 ;		// set running status (==Midi command w/o channel)&lt;br /&gt;
				midi.msgByteCnt = 0;&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
			return ; &lt;br /&gt;
		}&lt;br /&gt;
		else	// data bytes goes here &lt;br /&gt;
		{&lt;br /&gt;
			switch (midi.runningStatus)		    // last command byte (or zero, if data is not for us) &lt;br /&gt;
			{&lt;br /&gt;
			case SONG_POSITION_POINTER:&lt;br /&gt;
				if(midi.msgByteCnt)	// 2nd Byte&lt;br /&gt;
				{&lt;br /&gt;
					midi.spp= (((int)byte &amp;lt;&amp;lt; 7 ) | midi.byte1) * 6;&lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt=1;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_ON):					// if we&#039;ve had a note on byte&lt;br /&gt;
				if (midi.msgByteCnt)		// if counter = 1&lt;br /&gt;
				{ 			   				// then we&#039;ve just received a velocity byte&lt;br /&gt;
					midi.msgByteCnt = 0;	// msg complete - clear byte count &lt;br /&gt;
					if (byte == 0)			// is it a note off (velo = 0) ?&lt;br /&gt;
						note_off(midi.byte1,byte,midi.channel);  &lt;br /&gt;
					else 					// NOTE ON&lt;br /&gt;
					{&lt;br /&gt;
						if(learn)&lt;br /&gt;
						{&lt;br /&gt;
							config.noteOffset=midi.byte1;&lt;br /&gt;
							setEeWrite();&lt;br /&gt;
						}&lt;br /&gt;
						&lt;br /&gt;
						note_on(midi.byte1, byte,midi.channel);  // ... and do what the note on has told us &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				else						// first data byte &lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;		// save it &lt;br /&gt;
					midi.msgByteCnt = 1;    // set counter to get 2nd data byte&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_OFF): 	        	// command note off&lt;br /&gt;
				if (midi.msgByteCnt)	 	// if complete &lt;br /&gt;
				{ 					 	&lt;br /&gt;
					midi.msgByteCnt = 0;	&lt;br /&gt;
					note_off(midi.byte1,byte,midi.channel); // switch note off&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1; 	&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (PITCH):		        	// PITCH BEND command&lt;br /&gt;
				if (midi.msgByteCnt)   	 	// if second data byte (MSB) &lt;br /&gt;
				{ 						&lt;br /&gt;
					int v;&lt;br /&gt;
					v=byte;&lt;br /&gt;
					v&amp;lt;&amp;lt;=6;&lt;br /&gt;
					v|=midi.byte1;&lt;br /&gt;
					v-=0x1000;&lt;br /&gt;
					setPb(v,midi.channel);&lt;br /&gt;
&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (CC):	             		// Continuous Controller Message &lt;br /&gt;
				if (midi.msgByteCnt)	&lt;br /&gt;
				{&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
&lt;br /&gt;
					if(learn)&lt;br /&gt;
					{&lt;br /&gt;
						config.cvMidiCC[2]=midi.byte1;&lt;br /&gt;
						setEeWrite();&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
					setCC(midi.byte1, byte,midi.channel);&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
				  midi.byte1 = byte;&lt;br /&gt;
				  midi.msgByteCnt = 1;&lt;br /&gt;
				  return; &lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
	//		case (CHAN_PRES):	         	// if channel pressure command&lt;br /&gt;
	//			channel_pressure (byte); 	// set Aftertouch &lt;br /&gt;
			case SYSEX:						// SYS-EX command &lt;br /&gt;
				midi.msgByteCnt++;			// message Byte counter&lt;br /&gt;
				switch(midi.msgByteCnt)		// which byte is on? &lt;br /&gt;
				{&lt;br /&gt;
					case 1:					// Manufactorer &lt;br /&gt;
						if(byte !=0x70)		// if not the right one &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error / wrong byte &lt;br /&gt;
						break; &lt;br /&gt;
					case 2:					// Unit - ID &lt;br /&gt;
						if(byte!=0x7D)&lt;br /&gt;
							midi.msgByteCnt=0;&lt;br /&gt;
						break; &lt;br /&gt;
					case 3:					// Midi-Adress (or Unit ID) (ignored!)&lt;br /&gt;
						if(byte&amp;gt;=0x10)		// if not in legal range &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error &lt;br /&gt;
						break;&lt;br /&gt;
					case 4:					// adress byte&lt;br /&gt;
						midi.syxAdr=byte;	// store it for later&lt;br /&gt;
						break;&lt;br /&gt;
					case 5:					// high data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1=(byte&amp;lt;&amp;lt;4);	// set half the data &lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					case 6: 				// low data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1 |= byte;	// or together the complete data byte &lt;br /&gt;
						else 					// evaluation is  done in the code for SYSEND above! &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					default:				// not our framing &lt;br /&gt;
						midi.msgByteCnt=0;&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
				if(midi.msgByteCnt==0)	// something did not fit &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				break;&lt;br /&gt;
			case 0: // bnwtko (bytes no one wants to know off ) &lt;br /&gt;
				return ;&lt;br /&gt;
			default:&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7298</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7298"/>
		<updated>2011-06-26T19:39:14Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Midi-Kanalwahl */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Tonhöhe (Noten) &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;|| Gate 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen- und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V.&lt;br /&gt;
&lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie zweimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
Sollte sie vier mal blinken, hat der Controller die permanent gespeicherte Konfiguration nicht geladen und nutzt statt dessen (wieder) die Standardeinstellung.&lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu einzustellen: Die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Controller für CV3 einstellen ===&lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang(sic!) ge-gated. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Der Bootlader ist hier: [[MCV876_Controller_Update:_Bootloader]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7297</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7297"/>
		<updated>2011-06-26T19:38:15Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Bedienung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Tonhöhe (Noten) &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;|| Gate 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen- und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V.&lt;br /&gt;
&lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie zweimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
Sollte sie vier mal blinken, hat der Controller die permanent gespeicherte Konfiguration nicht geladen und nutzt statt dessen (wieder) die Standardeinstellung.&lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Controller für CV3 einstellen ===&lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang(sic!) ge-gated. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Der Bootlader ist hier: [[MCV876_Controller_Update:_Bootloader]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7296</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7296"/>
		<updated>2011-06-26T19:35:58Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Grund-Note einstellen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Tonhöhe (Noten) &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;|| Gate 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen- und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V.&lt;br /&gt;
&lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie zweimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
Sollte sie vier mal blinken, hat der Controller die permanent gespeicherte Konfiguration nicht geladen und nutzt statt dessen (wieder) die Standardeinstellung.&lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang(sic!) ge-gated. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Der Bootlader ist hier: [[MCV876_Controller_Update:_Bootloader]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7295</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7295"/>
		<updated>2011-06-26T19:35:11Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Die Leuchtdiode */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Tonhöhe (Noten) &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;|| Gate 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen- und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V.&lt;br /&gt;
&lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie zweimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
Sollte sie vier mal blinken, hat der Controller die permanent gespeicherte Konfiguration nicht geladen und nutzt statt dessen (wieder) die Standardeinstellung.&lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen. &lt;br /&gt;
(Wo das jetzt hier zum dritten mal leicht unterschiedlich steht: die finstere Wahrheit ist, das jedes mal die komplette Konfiguration gespeichert wird. ) &lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang(sic!) ge-gated. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Der Bootlader ist hier: [[MCV876_Controller_Update:_Bootloader]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7294</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7294"/>
		<updated>2011-06-26T19:23:46Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Funktionsübersicht – Standardbelegung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Tonhöhe (Noten) &amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;|| Gate 1 &amp;amp;nbsp;&amp;amp;nbsp;|| Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen- und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V.&lt;br /&gt;
&lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie dreimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen. &lt;br /&gt;
(Wo das jetzt hier zum dritten mal leicht unterschiedlich steht: die finstere Wahrheit ist, das jedes mal die komplette Konfiguration gespeichert wird. ) &lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang(sic!) ge-gated. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Der Bootlader ist hier: [[MCV876_Controller_Update:_Bootloader]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7293</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7293"/>
		<updated>2011-06-26T19:20:45Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Betriebsart Controller */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 || Tonhöhe (Noten) || Gate 1 || Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen. und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V. &lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie dreimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen. &lt;br /&gt;
(Wo das jetzt hier zum dritten mal leicht unterschiedlich steht: die finstere Wahrheit ist, das jedes mal die komplette Konfiguration gespeichert wird. ) &lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang(sic!) ge-gated. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Der Bootlader ist hier: [[MCV876_Controller_Update:_Bootloader]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7292</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7292"/>
		<updated>2011-06-26T19:11:42Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Erweiterte Einstellungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 || Tonhöhe (Noten) || Gate 1 || Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen. und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V. &lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie dreimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen. &lt;br /&gt;
(Wo das jetzt hier zum dritten mal leicht unterschiedlich steht: die finstere Wahrheit ist, das jedes mal die komplette Konfiguration gespeichert wird. ) &lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876 (3er Version), er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Interface Betriebsart ==== &lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
&lt;br /&gt;
* Mono (default): 1 Kanal monophon &lt;br /&gt;
&lt;br /&gt;
* Multi2: 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
* Multi4: 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
* Poly2 und Poly4: nicht unterstützt&lt;br /&gt;
&lt;br /&gt;
Weitere Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Kanal-Betriebsart ====&lt;br /&gt;
Bei den vier Ausgangs-Kanälen gehören immer ein Steuerspannungs(=CV)-Ausgang und ein Gate zusammen. Das erste Feld &amp;quot;CC&amp;quot; entscheidet hier (grundlegend), ob der Kanal Tonhöhe und Gate (=Note-An) erzeugt, oder ob er ein Controller Kanal ist. Die &amp;quot;Resteinstellung&amp;quot; ist dann jeweils ziemlich unterschiedlich. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Tonhöhe =====&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. Im Tonhöhen-Modus wird das jeweilige Gate automatisch für Note-An vereinnahmt. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Die Ausgangsspannung ist von der Hardware auf Werte über 0 festgelegt, das wird ggfls. passend begrenzt) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser. &lt;br /&gt;
&lt;br /&gt;
Das &amp;quot;Trig&amp;quot; Feld hinter dem Gate gibt (im Tonhöhen-Modus) an, ob das Gate bei neuen Midi-Noten Retriggert oder nicht. Zum Retrigger wird das Gate kurz ausgeschaltet. Der Retrigger erfolgt nur, wenn man neue Noten drückt, nicht, wenn man Noten loslässt und die Notenpriorisierung auf eine gehaltene Note zurückfällt. &lt;br /&gt;
&lt;br /&gt;
===== Betriebsart Controller =====&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. Wählt man bei Controller &amp;quot;none&amp;quot;, wird 0 als Grundwert genommen. (Sonderregel für Pitch-Bend: ist der Controller &amp;quot;None&amp;quot; und Pitch-Bend gewählt, dann wird 5V als Grundwert genommen, weil der Pitch-Bender nach links sonst keine Funktion hätte. So hat er dann ganz links 0V und ganz rechts 10V.) &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert (bipolar) auf die Ausgangsspannung addiert. (Auch hier geht &amp;quot;unter Null&amp;quot; seitens der Hardware nicht.) &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus hat das 5V Feld keine Funktion. Die Midi-Werte (von 0...127) werden &#039;&#039;&#039;immer&#039;&#039;&#039; auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind, ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Den Gate-Ausgängen kann ein Controller zugewiesen werden. Das Gate ist dann an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Wird das Trig-Feld aktiviert, dann wird der DAC-Ausgang ge-gated werden. Das heißt, der CV-Ausgang wird auf Null geklemmt, wenn keine Note an ist. Damit lässt sich z.B. ein Gate bauen, dass eine analoge, velocity-abhängige Höhe hat. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiele:&#039;&#039;&#039; Drei einfache und häufige Fälle für Steuerspannungen:&lt;br /&gt;
* Modwheel: CC auf Modwheel (Coarse), alles andere aus.&lt;br /&gt;
* PitchBend: CC auf &amp;quot;none&amp;quot;, nur Pitch-Bend ausgewählt. &lt;br /&gt;
* Velocity: CC auf &amp;quot;none&amp;quot;, nur Velocity angewählt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Clock-Einstellung ====&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note (d.h.: Teiler 6=16tel, 12=8tel, 24=1/4). Die Midi Clock wird zum Midi-Start und dem Midi Song Position Pointer synchron gehalten. Wenn man also einen geraden Teiler einstellt, dann hat man den Clock immer synchron zur 1. Bei Midi-Start oder Midi-Continue &#039;springt&#039; der Clockpuls daher ggfls. um wieder synchron zu werden. Der Clock-Ausgang ist immer die halbe Clock-Dauer an. Er lässt sich damit direkt als Midi-synchroner Rechteck-LFO verwenden. &lt;br /&gt;
&lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop. Das wird direkt durchgereicht, 5V wenn es läuft, 0V im Stopp. Wenn der Master das korrekt erzeugt, nämlich etwas vor dem nächsten Clock, der die erste 1 markiert, dann lassen sich damit analoge Sequencer synchron starten. Wenn der Sequencer allerdings sofort auf Start losrattert, dann wird er etwas zu früh sein. (Start &amp;quot;gleichzeitig&amp;quot; mit der Clock ist Mist, da dann unklar ist, ob der erste Clock-Puls erwischt wird oder nicht. )&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Der Bootlader ist hier: [[MCV876_Controller_Update:_Bootloader]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7291</id>
		<title>MCV876 Controller Update: Source Code</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7291"/>
		<updated>2011-06-26T17:25:31Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Quelltext */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Übersetzen= &lt;br /&gt;
Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden.&lt;br /&gt;
Im Studio ein neues, leeres Projekt mit Standard-Einstellungen, einem sinnvollen Namen und an einem Ort, wo man es wiederfindet erstellen, Quelltext in das (automatisch erzeugte, leere) C-File kopieren, auf Build-&amp;gt;Build klicken, fertig. &lt;br /&gt;
Das resultierende HEX File kann man mit einem üblichen Programmieradapter in der Chip flashen, für uns zweckmäßiger ist allerdings, das in ein Sys-Ex File zu Konvertieren ( [[MCV876_Controller_Update:_File_Converter]]), und das über Midi an den Controller zu schicken. Dann programmiert der sich selber um. Dazu muss er allerdings einmalig mit dem Bootlader [hier Link auf Bootlader imaginieren.. ] programmiert worden sein. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu den Atmel Controllern findet man eine Menge Info mit Tante Google, [http://www.mikrocontroller.net http://www.mikrocontroller.net] kommt häufig vor, und hat ein ergiebiges Forum und gute Artikel. Auf der Atmel Webseite gibt es auch einige Info, das [http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf ATMega 88 Datenblatt] ist spätestens dann nötig, wenn es um die Hardware geht.&lt;br /&gt;
&lt;br /&gt;
=Quelltext=&lt;br /&gt;
&amp;lt;pre&amp;gt;/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Midi to 4 Channel Control Voltage and six Gates Interface &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.21 2011-06-26 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
			&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;			// yes, we want to talk to the port pins by name&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;		// ... oh.. and some eeprom access would be nice&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;	// ... and w/o the interupts it wouldn&#039;t do anything... &lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*	===================ZERO LINE=================================&lt;br /&gt;
	WARNING: everthing below the zero line counts from zero! &lt;br /&gt;
	e.g. whats Gate 6 in manual is GATE5 here, CVs are CV0 to 3 &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	where should the LED output be &lt;br /&gt;
*/&lt;br /&gt;
#define LEDLED   6  // LED at Reset-Pin, standard for series, but no debugging possible. &lt;br /&gt;
#define LEDGATE0 0	// Led at Gate 0, no LED (Test I: no LED ) &lt;br /&gt;
#define LEDGATE4 4	// LED at Gate 5, Test II: no Gate 5 Function (Midi Start/Stop) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 #define LEDOUT LEDLED // select your evil... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define ACTIVE_SENSE_TICKS 160 // 2ms Timer Ticks for active sense time out &lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4))) // True if Button pressed &lt;br /&gt;
&lt;br /&gt;
#define NUM_CV 4&lt;br /&gt;
&lt;br /&gt;
#define PARAMETEROFFSET 0x23 // First paramter has adress 0x23&lt;br /&gt;
#define NUM_PARAMTERS 17	 // and there are 17 parameters &lt;br /&gt;
&lt;br /&gt;
#define MODE_MONO 	0x00	// monophon, all assigned to last note on a single channel &lt;br /&gt;
#define MODE_MULTI2	0x40	// two channel, 1/2 for midiChannel, 2/3 for midiChannel+1 &lt;br /&gt;
#define MODE_MULTI4	0x60	// for channel midiChannel to midiChannel+3 &lt;br /&gt;
#define MODE_POLY2	0x80	// not supported &lt;br /&gt;
#define MODE_POLY4	0xA0	// not supported&lt;br /&gt;
&lt;br /&gt;
struct CONFIG {&lt;br /&gt;
	unsigned char midiChannel;&lt;br /&gt;
	unsigned char noteOffset;&lt;br /&gt;
&lt;br /&gt;
	unsigned char cvMidiCC[NUM_CV];&lt;br /&gt;
	unsigned char gateMidiCC[NUM_CV];&lt;br /&gt;
	&lt;br /&gt;
	unsigned char gateTreshhold;&lt;br /&gt;
	unsigned char clockDivider;&lt;br /&gt;
	unsigned char clockTime; &lt;br /&gt;
	unsigned char ifMode0; 	// 	bit 0 ... bit 3 = CV0 .. CV3, 0=Pitch, 1=CC (if pitch, CC assignement is ignored) &lt;br /&gt;
							//	bit 5: voices: 0=2; 1=4 &lt;br /&gt;
							//  bit 7:6= 00 Mono; 01:Multi-Channel; 10: Poly (not supported); &lt;br /&gt;
	&lt;br /&gt;
	unsigned char ifMode1;  // Gate Trigger Mode Bits (0=Re-Trigger, 1=TriggerControl)  &lt;br /&gt;
	unsigned char ifMode2;  // bit0: Velo0=&amp;gt;CV0; bit1:Velo0=&amp;gt;CV1; bit2:Velo-Poly2=&amp;gt;CV2; bit3:Velo1=&amp;gt;CV3&lt;br /&gt;
							// bit4: PB=&amp;gt;CV0; bit5:PB=&amp;gt;CV1; bit6:PB=&amp;gt;CV2; bit7:PB=&amp;gt;CV3&lt;br /&gt;
	unsigned char ifMode3; 	// bit0..bit3 = CV0... CV3; 0=10V, 1=5V Mode &lt;br /&gt;
&lt;br /&gt;
//  internal evaluated configuration &lt;br /&gt;
	unsigned char numMidiChannels;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
} shadow_config, // her goes a copy, when config should be written, because that might take a while &lt;br /&gt;
&lt;br /&gt;
config 	// current configuration, mostly direct evaluated troughout the program&lt;br /&gt;
= {		// default values (Mono-Mode, Midi-Ch 1, CV1=Pitch, CV2=Velocity CV3=ModWheel CV4=Pitch Bend&lt;br /&gt;
			0,  	// Midi Channel &lt;br /&gt;
			24, 	// Note Offset (2 Oktaves) &lt;br /&gt;
		{	255,255,1,255, },  	// CV - assigned Midi Controllers (used if not pitch output) 255=none&lt;br /&gt;
		{   255,18,19,20, }, 	// Gate - asigned Controllers (if not pitch) 255=none &lt;br /&gt;
			63, 	// Treshold&lt;br /&gt;
			6,  	// Divider 6 (1/16 Note) &lt;br /&gt;
			1,  	// clock time (unused) &lt;br /&gt;
			0x0e, 	// mode 0 Low nibble CC-Bits, high: interface mode &lt;br /&gt;
			0, 		// mode 1 re-trigger bits &lt;br /&gt;
			0x82, 	// mode 2 High Nibble: PB-bits, Low: Velo-Bits&lt;br /&gt;
			0,    	// mode 3 (5V-Bits) &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define EE_MAGIC 17&lt;br /&gt;
unsigned char ee_magic EEMEM;	// to check it eeprom was ever written and should be read back &lt;br /&gt;
unsigned char config_eeprom[NUM_PARAMTERS] EEMEM; // Config in EEPROM, saves it during power down. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
volatile unsigned char  eepromWriteFlag;	// set to one, if main-loop should write eeprom &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char button; 	// debounced button status &lt;br /&gt;
volatile unsigned char learn; 	// (channel/base note/cc) &amp;quot;do learn&amp;quot; status &lt;br /&gt;
volatile unsigned char blinker; // some count for blinkenlights &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char gate5off; // timer counter for when clock gate output should go low again. &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char watchTicker;	// midi active sense counter &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setConfig(void); 		&lt;br /&gt;
void setLed(unsigned char on);&lt;br /&gt;
void resetAll(void);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	main() &lt;br /&gt;
	Main does initialization, than runs a loop, which does nearly nothing: &lt;br /&gt;
&lt;br /&gt;
	Programming the EEPROM (because its slow) &lt;br /&gt;
	Blinking &lt;br /&gt;
	Button evaluation &lt;br /&gt;
&lt;br /&gt;
	All the important things are done by the interupts. (Thanks to the &lt;br /&gt;
	20MHZ RISC power everything can be done in less than 320µs=one Midi-Character) &lt;br /&gt;
*/&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char blink=4;&lt;br /&gt;
&lt;br /&gt;
	unsigned char i,x; &lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
	static unsigned char lastButton; &lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
	// Read Configuration &lt;br /&gt;
	_delay_ms(10); // wait for button pin to get high if open. &lt;br /&gt;
	if( eeprom_read_byte(&amp;amp;ee_magic) == EE_MAGIC  &amp;amp;&amp;amp;   !BUTTON) // if eeprom valid AND Button not pressed &lt;br /&gt;
	{&lt;br /&gt;
		cp=(unsigned char* )&amp;amp;config; &lt;br /&gt;
		for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
		{&lt;br /&gt;
			x=eeprom_read_byte(config_eeprom+i);			// read config from eeprom &lt;br /&gt;
			*cp=x;	&lt;br /&gt;
			cp++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
		blink=8; &lt;br /&gt;
&lt;br /&gt;
	setConfig();&lt;br /&gt;
&lt;br /&gt;
	// enable interrupts&lt;br /&gt;
	sei(); &lt;br /&gt;
&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		if(eepromWriteFlag)&lt;br /&gt;
		{	// save configuration &lt;br /&gt;
			cp=(unsigned char* )&amp;amp;shadow_config; &lt;br /&gt;
			for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
			{&lt;br /&gt;
				x=*cp;&lt;br /&gt;
				eeprom_write_byte(config_eeprom+i,x);&lt;br /&gt;
				cp++;&lt;br /&gt;
			}&lt;br /&gt;
			eeprom_write_byte(&amp;amp;ee_magic,EE_MAGIC); &lt;br /&gt;
&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			blink=6;&lt;br /&gt;
			blinker=0;&lt;br /&gt;
			eepromWriteFlag=0;	&lt;br /&gt;
		}&lt;br /&gt;
		if(blink &amp;amp;&amp;amp; blinker&amp;gt;2)  // blink after power up and after storing in nv-memory &lt;br /&gt;
		{&lt;br /&gt;
			blink--;&lt;br /&gt;
			setLed(blink&amp;amp;1);&lt;br /&gt;
			blinker=0; &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if(button != lastButton)&lt;br /&gt;
		{&lt;br /&gt;
			if(button==1 )&lt;br /&gt;
			{&lt;br /&gt;
				resetAll();&lt;br /&gt;
				if(learn==1)&lt;br /&gt;
				{&lt;br /&gt;
					learn=0;&lt;br /&gt;
					setLed(0);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if(button==6) &lt;br /&gt;
			{&lt;br /&gt;
				if(learn ==0)&lt;br /&gt;
				{&lt;br /&gt;
					learn=1;&lt;br /&gt;
					setLed(1);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			lastButton=button; &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return 0; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Hardware Interface&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
/*  Pin assignment &lt;br /&gt;
   Atmel Fkt 	[Orig] &lt;br /&gt;
1  Reset LED 	[MCLR] &lt;br /&gt;
2  RX    Midi-Rx[WR1]&lt;br /&gt;
3  PD1   WR2&lt;br /&gt;
4  PD2   WR3&lt;br /&gt;
5  PD3   WR4&lt;br /&gt;
6  PD4   Button&lt;br /&gt;
7  VCC   +5  	[LED] (Either from Atmel-1(Series) or Atmel-15(w/Debug))&lt;br /&gt;
8  GND   GND&lt;br /&gt;
9  OSC   OSC&lt;br /&gt;
10 OSC   OSC &lt;br /&gt;
11 PD5   G1&lt;br /&gt;
12 PD6   G2&lt;br /&gt;
13 PD7   G3&lt;br /&gt;
14 PB0   G4&lt;br /&gt;
&lt;br /&gt;
15 PB1   G5 (Optional + LED) &lt;br /&gt;
16 PB2   G6 &lt;br /&gt;
17 PB3   WR1    [---] &lt;br /&gt;
18 PB4   D0     [RX]&lt;br /&gt;
19 PB5   D1		[GND]&lt;br /&gt;
20 AVCC  +5&lt;br /&gt;
21 ARef  +5 	[D0]&lt;br /&gt;
22 GND   GND	[D1] &lt;br /&gt;
23 PC0   D2&lt;br /&gt;
24 PC1   D3&lt;br /&gt;
25 PC2   D4&lt;br /&gt;
26 PC3   D5&lt;br /&gt;
27 PC4   D6&lt;br /&gt;
28 PC5   D7&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
// put &amp;quot;val&amp;quot; to the right port bits for the DA-Converter &lt;br /&gt;
// ... better program than solder *that*... &lt;br /&gt;
void adByteOut(unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char b;&lt;br /&gt;
	b=val;&lt;br /&gt;
	b&amp;gt;&amp;gt;=2;&lt;br /&gt;
	PORTC=b;&lt;br /&gt;
	b=val&amp;amp;3;&lt;br /&gt;
	b&amp;lt;&amp;lt;=4;&lt;br /&gt;
	val=PORTB;&lt;br /&gt;
	val &amp;amp;=0xCF;&lt;br /&gt;
	val|=b;&lt;br /&gt;
	PORTB=val;&lt;br /&gt;
}&lt;br /&gt;
// set DA-Output voltage of one CV Channel  &lt;br /&gt;
void set_cv(unsigned char val, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	adByteOut(val); 	// set D0..D7 converter data bits &lt;br /&gt;
	switch (channel)	// pulse with write pin &lt;br /&gt;
	{&lt;br /&gt;
		case 0: //PB3&lt;br /&gt;
			PORTB  &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTB  |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD1&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;1);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;1);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD2&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;2);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;2);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PD3&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// set on of the gate outputs active &lt;br /&gt;
void gate_on(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// clear one of the gate outputs &lt;br /&gt;
void gate_off(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif&lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Data Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
typedef struct {&lt;br /&gt;
	unsigned char channel;  // (relative) Midi channel (0..3) Mode dependant &lt;br /&gt;
	unsigned char cvOut;	// for which CV-Ouput (0..3) fixed &lt;br /&gt;
	unsigned char ccVal;	// current CC Value &lt;br /&gt;
	unsigned char note;		// current Note &lt;br /&gt;
	unsigned char velo;		// current Velocity &lt;br /&gt;
	unsigned char on;		// if Note is on &lt;br /&gt;
	signed char   pb;		// current Pitch Bend Value &lt;br /&gt;
	unsigned char gateDipper;&lt;br /&gt;
} cvoutput;  &lt;br /&gt;
&lt;br /&gt;
cvoutput cv_out[NUM_CV];	// current state for each cv out&lt;br /&gt;
&lt;br /&gt;
unsigned char gates; // which gates are on =&amp;gt; used for LED setting &lt;br /&gt;
&lt;br /&gt;
// set cv output (and assigned gate, if pitch output)&lt;br /&gt;
// called after anything that changes the cv_out structure  &lt;br /&gt;
void updateCv(cvoutput *cvo)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char bm;&lt;br /&gt;
	unsigned char gated=0;&lt;br /&gt;
	int val=0;&lt;br /&gt;
&lt;br /&gt;
	bm=1&amp;lt;&amp;lt;cvo-&amp;gt;cvOut;	// bit mask for configuration flags &lt;br /&gt;
&lt;br /&gt;
	if(config.ifMode0 &amp;amp; bm)	// 1=CC-Mode&lt;br /&gt;
	{&lt;br /&gt;
		if(config.cvMidiCC[cvo-&amp;gt;cvOut]&amp;gt;127) // if no Controller assigned &lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		// if PB is active &lt;br /&gt;
				val=cvo-&amp;gt;pb+0x80;		 		//     pitch sets unipolar value &lt;br /&gt;
			else 						 		// else &lt;br /&gt;
				val=0; 					 		//    value is zero &lt;br /&gt;
		}&lt;br /&gt;
		else 								// else a controller is assigned &lt;br /&gt;
		{&lt;br /&gt;
			val=cvo-&amp;gt;ccVal; 					//  set current controler value ;&lt;br /&gt;
			if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) 		//  if PB active &lt;br /&gt;
				val+=cvo-&amp;gt;pb;			 		//     add it (bipolar) &lt;br /&gt;
		}&lt;br /&gt;
/*&lt;br /&gt;
		if( (config.ifMode3 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0)	// 5V-Flag, abused as Gated-Mode-CV Flag &lt;br /&gt;
			gated=1;&lt;br /&gt;
*/&lt;br /&gt;
	}&lt;br /&gt;
	else // Note&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;note;&lt;br /&gt;
		val -= config.noteOffset;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode3 &amp;amp; bm)	// 5V&lt;br /&gt;
			val &amp;lt;&amp;lt;= 2;&lt;br /&gt;
		else&lt;br /&gt;
			val &amp;lt;&amp;lt;= 1;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) // PB &lt;br /&gt;
			val+=(cvo-&amp;gt;pb&amp;gt;&amp;gt;2);  // scale down pb - value is empirical and works &amp;quot;not too bad&amp;quot; &lt;br /&gt;
								//  (for &amp;quot;good&amp;quot; the converter should have 12bits a least... ) &lt;br /&gt;
	}&lt;br /&gt;
	if(config.ifMode2 &amp;amp; bm) // Velocity &lt;br /&gt;
		val+=cvo-&amp;gt;velo;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if(val&amp;lt;0 || gated)	// clip values to converter range &lt;br /&gt;
		val=0;&lt;br /&gt;
	if(val&amp;gt;255)&lt;br /&gt;
		val=255;&lt;br /&gt;
&lt;br /&gt;
	set_cv((unsigned char)val, cvo-&amp;gt;cvOut);&lt;br /&gt;
	if((config.ifMode0 &amp;amp; bm)==0)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;on)&lt;br /&gt;
		{&lt;br /&gt;
			if(cvo-&amp;gt;gateDipper==0) // no retrigger required&lt;br /&gt;
				gate_on(cvo-&amp;gt;cvOut);&lt;br /&gt;
			else				   // dip gate for a few milliseconds 	&lt;br /&gt;
				gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates |=bm;&lt;br /&gt;
		}&lt;br /&gt;
		else &lt;br /&gt;
		{&lt;br /&gt;
			gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates &amp;amp;=~bm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate/initialize control variables after &lt;br /&gt;
// mode-configuration change &lt;br /&gt;
void setConfig()&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i; &lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0; &lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	switch(config.ifMode0 &amp;amp; 0xf0 )&lt;br /&gt;
	{	&lt;br /&gt;
		default:&lt;br /&gt;
		case MODE_MONO:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=1;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI2:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV/2;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			for(;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=1;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=2;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI4:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=i;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=NUM_CV;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// assign new note state to cv outputs &lt;br /&gt;
void setNote(unsigned char note, unsigned char velo, unsigned char onOff, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;channel==channel)&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;note=note;&lt;br /&gt;
			cvo-&amp;gt;velo=velo;&lt;br /&gt;
			if(onOff==2 &amp;amp;&amp;amp; cvo-&amp;gt;on &amp;amp;&amp;amp; (config.ifMode1 &amp;amp; (1&amp;lt;&amp;lt;i)))&lt;br /&gt;
				cvo-&amp;gt;gateDipper=4;&lt;br /&gt;
			cvo-&amp;gt;on=onOff;&lt;br /&gt;
			updateCv(cvo);&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	setLed(gates);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process new continous controller value &lt;br /&gt;
void setCC(unsigned char cc, unsigned char value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel== cvo-&amp;gt;channel )&lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode0 &amp;amp;(1&amp;lt;&amp;lt;i))&lt;br /&gt;
			{&lt;br /&gt;
				if(config.cvMidiCC[i]==cc)&lt;br /&gt;
				{&lt;br /&gt;
					cvo-&amp;gt;ccVal=value; &lt;br /&gt;
					updateCv(cvo);&lt;br /&gt;
				}&lt;br /&gt;
				if(config.gateMidiCC[i]==cc  )&lt;br /&gt;
				{&lt;br /&gt;
					if(value&amp;gt;config.gateTreshhold)&lt;br /&gt;
						gate_on(i);&lt;br /&gt;
					else &lt;br /&gt;
						gate_off(i);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// process new pitch bend value &lt;br /&gt;
void setPb(signed int value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel==cvo-&amp;gt;channel &amp;amp;&amp;amp; (config.ifMode2 &amp;amp; (16&amp;lt;&amp;lt;i)) )&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;pb	=(signed char) (value&amp;gt;&amp;gt;5);	&lt;br /&gt;
			updateCv(cvo);	&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// prepare for (asynchronous) configuration write to eeprom &lt;br /&gt;
void setEeWrite(void)&lt;br /&gt;
{&lt;br /&gt;
	shadow_config=config; &lt;br /&gt;
	learn=0;&lt;br /&gt;
	eepromWriteFlag=1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*bootloader)(void) = (void*)0x1800;	// Bootloader sits at adress 0x1800 ... &lt;br /&gt;
&lt;br /&gt;
// process sys ex command &lt;br /&gt;
void setSyx(unsigned char adr, unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
&lt;br /&gt;
	cp=(unsigned char *) &amp;amp;config; &lt;br /&gt;
&lt;br /&gt;
	if(adr==0x14 &amp;amp;&amp;amp; val ==0x7b) &lt;br /&gt;
	{	// write eeprom&lt;br /&gt;
		if(eepromWriteFlag==0)&lt;br /&gt;
		{&lt;br /&gt;
			setEeWrite();&lt;br /&gt;
		}&lt;br /&gt;
		return; &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7&lt;br /&gt;
	if(adr==0x16 &amp;amp;&amp;amp; val ==0x42)	// Software update  &lt;br /&gt;
	{&lt;br /&gt;
		cli();			// interupts off &lt;br /&gt;
		resetAll();		// outputs off &lt;br /&gt;
		GPIOR1 = 0xAB;	// tell bootloader it&#039;s an application boot request &lt;br /&gt;
						// (hardware reset sets GPIOR1 to zero, 0xab == magic value for bootloader )&lt;br /&gt;
		bootloader();	// bye bye! &lt;br /&gt;
		return; 	// &amp;lt;-- never! &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	adr-=PARAMETEROFFSET; &lt;br /&gt;
	if(adr &amp;lt; NUM_PARAMTERS)	// if valid parameter &lt;br /&gt;
	{&lt;br /&gt;
		*(cp+adr)=val; 		// set in &lt;br /&gt;
		if(adr==0x30-PARAMETEROFFSET) // if Mode Byte 0, &lt;br /&gt;
			setConfig();			  // .. cv assignement must be recalculated &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Note Puffer Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define MAX_NOTES 0x10  	// one for each finger of a real programmer &lt;br /&gt;
#define MAX_CHANNELS 4&lt;br /&gt;
unsigned char notes[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char velocities[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char playedNote[MAX_CHANNELS]; // number of currently pressed notes &lt;br /&gt;
// update note puffer for a midi note-on message &lt;br /&gt;
void note_on(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char n;&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	if(n&amp;lt;MAX_NOTES)&lt;br /&gt;
	{&lt;br /&gt;
		playedNote[channel]++;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		for (n=MAX_NOTES-1;n&amp;gt;0;--n)&lt;br /&gt;
		{&lt;br /&gt;
			notes[channel][n-1]=notes[channel][n];&lt;br /&gt;
			velocities[channel][n-1]=velocities[channel][n];&lt;br /&gt;
		}&lt;br /&gt;
		n=MAX_NOTES-1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	notes[channel][n]=note; 		&lt;br /&gt;
	velocities[channel][n]=velocity;&lt;br /&gt;
	setNote(note, velocity, 2, channel);&lt;br /&gt;
}&lt;br /&gt;
// update note puffer for a new midi note-off message &lt;br /&gt;
void note_off(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	signed char n; &lt;br /&gt;
&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	--n; &lt;br /&gt;
	for (i=0;i&amp;lt;=n;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(notes[channel][i]==note)&lt;br /&gt;
		{&lt;br /&gt;
			if(i==n) // last pressed (== the on which playes) note released &lt;br /&gt;
			{&lt;br /&gt;
				if(n==0) // it was the only pressed note &lt;br /&gt;
				{&lt;br /&gt;
					setNote(note, velocities[channel][n], 0, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{	&lt;br /&gt;
					--n;&lt;br /&gt;
					setNote(notes[channel][n], velocities[channel][n], 1, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else // non playing note released &lt;br /&gt;
			{	// =&amp;gt;  just remove it from list &lt;br /&gt;
				int j;&lt;br /&gt;
				for(j=i+1;j&amp;lt;=n;++j)&lt;br /&gt;
				{&lt;br /&gt;
					notes[channel][j-1]=notes[channel][j];&lt;br /&gt;
					velocities[channel][j-1]=velocities[channel][j];&lt;br /&gt;
				}&lt;br /&gt;
				playedNote[channel]--;&lt;br /&gt;
			}&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	All Notes off, empty Note Buffer, All Controllers Reset&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void resetAll(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	cli(); // this might be called asynchronous to midi stream &lt;br /&gt;
	for(i=0;i&amp;lt;MAX_CHANNELS;++i)&lt;br /&gt;
		playedNote[i]=0;&lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0;&lt;br /&gt;
		cvo-&amp;gt;on=0;&lt;br /&gt;
		cvo-&amp;gt;pb=0;&lt;br /&gt;
		updateCv(cvo);&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
	sei();&lt;br /&gt;
&lt;br /&gt;
	setLed(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	static unsigned char buttonCount;&lt;br /&gt;
	static signed char blinkCount; &lt;br /&gt;
&lt;br /&gt;
	// LED blinker &lt;br /&gt;
	if(--blinkCount&amp;lt;=0)&lt;br /&gt;
	{	&lt;br /&gt;
		blinkCount=40;&lt;br /&gt;
		blinker++;&lt;br /&gt;
		if(button)&lt;br /&gt;
		{&lt;br /&gt;
			if(button&amp;lt;10)&lt;br /&gt;
				button++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Button debouncer &lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{	// down &lt;br /&gt;
		if(buttonCount &amp;lt; 10)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount++;&lt;br /&gt;
			if(buttonCount==10)&lt;br /&gt;
			{&lt;br /&gt;
				button=1;&lt;br /&gt;
				blinkCount=40;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{	// up &lt;br /&gt;
		if(buttonCount &amp;gt; 0)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount--;&lt;br /&gt;
			if(buttonCount==0)&lt;br /&gt;
				button=0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Gate 5 timing (clock output) &lt;br /&gt;
	if(gate5off)&lt;br /&gt;
	{&lt;br /&gt;
		--gate5off;&lt;br /&gt;
		if(gate5off==0)&lt;br /&gt;
			gate_off(5);	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// activ sense &lt;br /&gt;
	if(watchTicker)			// if active sense is activated &lt;br /&gt;
	{&lt;br /&gt;
		watchTicker--;		// decrement time &lt;br /&gt;
		if(watchTicker==0)	// if time elapsed&lt;br /&gt;
			resetAll(); 	// switch all notes off 	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// retrigger timing &lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)	// for all cv output channels &lt;br /&gt;
	{&lt;br /&gt;
		if(cv_out[i].gateDipper)	// if gate timer is active &lt;br /&gt;
		{&lt;br /&gt;
			--cv_out[i].gateDipper;		// decrement timer &lt;br /&gt;
			if(cv_out[i].gateDipper==0)	// if timer elapsed&lt;br /&gt;
				gate_on(i);				// activate gate &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Eval Midi Data&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define NOTE_ON         0x90		// Midi note on message &lt;br /&gt;
#define NOTE_OFF        0x80		// Midi note off message &lt;br /&gt;
#define PITCH           0xE0		// pitch bender&lt;br /&gt;
#define AFTERTOUCH      0xD0        // after touch command (unused) &lt;br /&gt;
#define CC		        0xB0        // Continuous Controller&lt;br /&gt;
#define CHAN_PRES	    0xD0        // Channel Pressure (unused)&lt;br /&gt;
#define SONG_POSITION_POINTER 0xF2	// used for clock synchronization (first beat) &lt;br /&gt;
#define SYSEX 			0xF0		// System exclusive beginn&lt;br /&gt;
#define SYSEND			0xF7		// System exclusive end&lt;br /&gt;
&lt;br /&gt;
struct {&lt;br /&gt;
	unsigned char runningStatus;	// Midi runnig status (*)&lt;br /&gt;
	unsigned char msgByteCnt;    	// number of current midi-message byte received &lt;br /&gt;
	unsigned char byte1;			// first data byte &lt;br /&gt;
	unsigned char syxAdr; 			// Sys-Ex Adress Byte&lt;br /&gt;
	unsigned char startStop;		// current  Midi Start/Stop Status&lt;br /&gt;
	unsigned char startFlag;		// Start on next clock&lt;br /&gt;
	unsigned char clockTick;		// counter for clock division&lt;br /&gt;
	unsigned char channel;			// channel of current midi-command (relative, e.g. 0..3 ) 						&lt;br /&gt;
	unsigned int  spp; 				// song position pointer (from midi message), needed to have the clock synced to song position&lt;br /&gt;
}midi; &lt;br /&gt;
// 	(*) running status is also used as &#039;current status&#039; - it remembers allways, what next data byte is for. Or its zero, than data is ignored. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine, &lt;br /&gt;
// complete midi processing is done here, as the CPU is fast enough to &lt;br /&gt;
// do everything before next midi byte can come in. &lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	unsigned char status,byte;&lt;br /&gt;
&lt;br /&gt;
    status = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((status &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(watchTicker)						// if there had been an active sense &lt;br /&gt;
			watchTicker=ACTIVE_SENSE_TICKS;	// every new byte is ok, to &amp;quot;feed the watchdog&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		if (byte &amp;gt; 0x7F)	                // if command byte (MSB set)&lt;br /&gt;
		{&lt;br /&gt;
			if(byte&amp;gt;= 0xF8)  				// if real-time msg &lt;br /&gt;
			{&lt;br /&gt;
				switch (byte)&lt;br /&gt;
				{&lt;br /&gt;
					case 0xF8: 	// Clock &lt;br /&gt;
					{&lt;br /&gt;
						if(midi.startFlag)  // if synchronized start &lt;br /&gt;
						{&lt;br /&gt;
							midi.startStop=1;&lt;br /&gt;
							midi.startFlag=0;&lt;br /&gt;
&lt;br /&gt;
							if(config.clockDivider&amp;gt;1 )// if divided clock &lt;br /&gt;
							{&lt;br /&gt;
								if( midi.spp) // if start not from zero &lt;br /&gt;
								{&lt;br /&gt;
									midi.clockTick=midi.spp%config.clockDivider;			// calculate clock divider count for current song position &lt;br /&gt;
									if(midi.clockTick &amp;gt;= config.clockDivider&amp;gt;&amp;gt;1)	// if calculated divider count is mor than half elapsed &lt;br /&gt;
										gate_off(5); 								// than set clock output off &lt;br /&gt;
									else&lt;br /&gt;
										gate_on(5);									// else set it on &lt;br /&gt;
								}&lt;br /&gt;
								else &lt;br /&gt;
								{&lt;br /&gt;
									gate_on(5);&lt;br /&gt;
									midi.clockTick=0; &lt;br /&gt;
								}&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						else &lt;br /&gt;
						{&lt;br /&gt;
							if(midi.startStop)	// if (already) running &lt;br /&gt;
								++midi.spp;&lt;br /&gt;
							++midi.clockTick; 	// increment counter for clock output &lt;br /&gt;
						}&lt;br /&gt;
&lt;br /&gt;
						if(config.clockDivider&amp;lt;=1)&lt;br /&gt;
						{&lt;br /&gt;
							gate_on(5);								// do clock output &lt;br /&gt;
							gate5off=3;							&lt;br /&gt;
						}&lt;br /&gt;
						else&lt;br /&gt;
						{&lt;br /&gt;
							if(midi.clockTick &amp;gt;= config.clockDivider)	// if divider count elapsed &lt;br /&gt;
							{&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
								gate_on(5);								// do clock output &lt;br /&gt;
							}&lt;br /&gt;
							else if(midi.clockTick == config.clockDivider&amp;gt;&amp;gt;1)	// if divider count half elapsed &lt;br /&gt;
								gate_off(5); 									// set clock output off &lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
					case 0xF9:	// Tick (unused) &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFA: 	// Start &lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.spp=0;&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
							if(config.clockDivider&amp;gt;1)&lt;br /&gt;
							{&lt;br /&gt;
								gate_off(5); 			// set clock output off 								&lt;br /&gt;
								midi.clockTick=0;&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFB:	// Continue&lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
						}&lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFC:	// Stop&lt;br /&gt;
						midi.startStop=0;&lt;br /&gt;
						gate_off(4);&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFD:	// unassigned &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFE:	//  Active Sense&lt;br /&gt;
						watchTicker=ACTIVE_SENSE_TICKS;&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFF:	// Reset &lt;br /&gt;
						break; &lt;br /&gt;
				}&lt;br /&gt;
				return ; &lt;br /&gt;
			} &lt;br /&gt;
&lt;br /&gt;
			if(byte &amp;gt;= 0xF0) 					// if System Common Category message&lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)&lt;br /&gt;
				{&lt;br /&gt;
					if(midi.runningStatus==SYSEX &amp;amp;&amp;amp; midi.msgByteCnt==6) // if a complete valid Sys-Ex Frame has been received &lt;br /&gt;
						setSyx(midi.syxAdr, midi.byte1); 				// change configuration &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					midi.runningStatus = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				else if(byte ==SONG_POSITION_POINTER)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;  &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				//							// every other System Common is ignored 	&lt;br /&gt;
				midi.runningStatus = 0;		// just reset running status &lt;br /&gt;
				return ; 					// and done &lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.runningStatus = 0;				// just new status messages left, so running status is lost&lt;br /&gt;
&lt;br /&gt;
			// only channel messages left here ... &lt;br /&gt;
			if(learn)								 // if learn mode &lt;br /&gt;
			{&lt;br /&gt;
				if(config.midiChannel != (byte&amp;amp;0xf)) // if message channel not current channel&lt;br /&gt;
				{&lt;br /&gt;
					config.midiChannel=byte&amp;amp;0xf;	 // assign channel &lt;br /&gt;
					setEeWrite(); 					 // and store it &lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.channel=(byte &amp;amp; 0xf)-config.midiChannel ;&lt;br /&gt;
			if ( midi.channel&amp;lt;config.numMidiChannels)   // if MIDI channel matches,&lt;br /&gt;
			{&lt;br /&gt;
				midi.runningStatus = byte &amp;amp; 0xF0 ;		// set running status (==Midi command w/o channel)&lt;br /&gt;
				midi.msgByteCnt = 0;&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
			return ; &lt;br /&gt;
		}&lt;br /&gt;
		else	// data bytes goes here &lt;br /&gt;
		{&lt;br /&gt;
			switch (midi.runningStatus)		    // last command byte (or zero, if data is not for us) &lt;br /&gt;
			{&lt;br /&gt;
			case SONG_POSITION_POINTER:&lt;br /&gt;
				if(midi.msgByteCnt)	// 2nd Byte&lt;br /&gt;
				{&lt;br /&gt;
					midi.spp= (((int)byte &amp;lt;&amp;lt; 7 ) | midi.byte1) * 6;&lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt=1;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_ON):					// if we&#039;ve had a note on byte&lt;br /&gt;
				if (midi.msgByteCnt)		// if counter = 1&lt;br /&gt;
				{ 			   				// then we&#039;ve just received a velocity byte&lt;br /&gt;
					midi.msgByteCnt = 0;	// msg complete - clear byte count &lt;br /&gt;
					if (byte == 0)			// is it a note off (velo = 0) ?&lt;br /&gt;
						note_off(midi.byte1,byte,midi.channel);  &lt;br /&gt;
					else 					// NOTE ON&lt;br /&gt;
					{&lt;br /&gt;
						if(learn)&lt;br /&gt;
						{&lt;br /&gt;
							config.noteOffset=midi.byte1;&lt;br /&gt;
							setEeWrite();&lt;br /&gt;
						}&lt;br /&gt;
						&lt;br /&gt;
						note_on(midi.byte1, byte,midi.channel);  // ... and do what the note on has told us &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				else						// first data byte &lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;		// save it &lt;br /&gt;
					midi.msgByteCnt = 1;    // set counter to get 2nd data byte&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_OFF): 	        	// command note off&lt;br /&gt;
				if (midi.msgByteCnt)	 	// if complete &lt;br /&gt;
				{ 					 	&lt;br /&gt;
					midi.msgByteCnt = 0;	&lt;br /&gt;
					note_off(midi.byte1,byte,midi.channel); // switch note off&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1; 	&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (PITCH):		        	// PITCH BEND command&lt;br /&gt;
				if (midi.msgByteCnt)   	 	// if second data byte (MSB) &lt;br /&gt;
				{ 						&lt;br /&gt;
					int v;&lt;br /&gt;
					v=byte;&lt;br /&gt;
					v&amp;lt;&amp;lt;=6;&lt;br /&gt;
					v|=midi.byte1;&lt;br /&gt;
					v-=0x1000;&lt;br /&gt;
					setPb(v,midi.channel);&lt;br /&gt;
&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (CC):	             		// Continuous Controller Message &lt;br /&gt;
				if (midi.msgByteCnt)	&lt;br /&gt;
				{&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
&lt;br /&gt;
					if(learn)&lt;br /&gt;
					{&lt;br /&gt;
						config.cvMidiCC[2]=midi.byte1;&lt;br /&gt;
						setEeWrite();&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
					setCC(midi.byte1, byte,midi.channel);&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
				  midi.byte1 = byte;&lt;br /&gt;
				  midi.msgByteCnt = 1;&lt;br /&gt;
				  return; &lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
	//		case (CHAN_PRES):	         	// if channel pressure command&lt;br /&gt;
	//			channel_pressure (byte); 	// set Aftertouch &lt;br /&gt;
			case SYSEX:						// SYS-EX command &lt;br /&gt;
				midi.msgByteCnt++;			// message Byte counter&lt;br /&gt;
				switch(midi.msgByteCnt)		// which byte is on? &lt;br /&gt;
				{&lt;br /&gt;
					case 1:					// Manufactorer &lt;br /&gt;
						if(byte !=0x70)		// if not the right one &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error / wrong byte &lt;br /&gt;
						break; &lt;br /&gt;
					case 2:					// Unit - ID &lt;br /&gt;
						if(byte!=0x7D)&lt;br /&gt;
							midi.msgByteCnt=0;&lt;br /&gt;
						break; &lt;br /&gt;
					case 3:					// Midi-Adress (or Unit ID) (ignored!)&lt;br /&gt;
						if(byte&amp;gt;=0x10)		// if not in legal range &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error &lt;br /&gt;
						break;&lt;br /&gt;
					case 4:					// adress byte&lt;br /&gt;
						midi.syxAdr=byte;	// store it for later&lt;br /&gt;
						break;&lt;br /&gt;
					case 5:					// high data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1=(byte&amp;lt;&amp;lt;4);	// set half the data &lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					case 6: 				// low data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1 |= byte;	// or together the complete data byte &lt;br /&gt;
						else 					// evaluation is  done in the code for SYSEND above! &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					default:				// not our framing &lt;br /&gt;
						midi.msgByteCnt=0;&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
				if(midi.msgByteCnt==0)	// something did not fit &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				break;&lt;br /&gt;
			case 0: // bnwtko (bytes no one wants to know off ) &lt;br /&gt;
				return ;&lt;br /&gt;
			default:&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7290</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7290"/>
		<updated>2011-06-25T22:32:20Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Software */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 || Tonhöhe (Noten) || Gate 1 || Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen. und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V. &lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie dreimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen. &lt;br /&gt;
(Wo das jetzt hier zum dritten mal leicht unterschiedlich steht: die finstere Wahrheit ist, das jedes mal die komplette Konfiguration gespeichert wird. ) &lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876, er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
Mono (default). 1 Kanal Monophon. &lt;br /&gt;
&lt;br /&gt;
Multi 2. 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
Multi 4. 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
Die weiteren Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
Im DAC-Feld kann für jede CV (=jeden Steuerspannungs-Ausgang) gewählt werden, wie er gesteuert wird. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet im Tonhöhen-Modus die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser.  &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus werden die Ccs auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Die Gates 1-4 sind den Tonhöhenkanälen fest zugeordnet, gibt ein DAC eine Tonhöhe aus, ist das zugehörige Gate auch Note an/aus. &lt;br /&gt;
&lt;br /&gt;
Ist der DAC auf CC gestellt, kann der Gate-Ausgang einem Controller zugewiesen werden, das Gate ist an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note. (6=16tel, 12=8tel, 24=1/4) &lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop.&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Der Bootlader ist hier: [[MCV876_Controller_Update:_Bootloader]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Bootloader&amp;diff=7289</id>
		<title>MCV876 Controller Update: Bootloader</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Bootloader&amp;diff=7289"/>
		<updated>2011-06-25T22:31:00Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Created page with &amp;#039;= Worum geht&amp;#039;s? = Midi-Bootlader für den ATMega 88.  Empfängt einen als Midi-Sys-Ex Datenstrom eingepacktes Programm-Update und schreibt das in den Programm-Speicher des Contro…&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Worum geht&#039;s? =&lt;br /&gt;
Midi-Bootlader für den ATMega 88.&lt;br /&gt;
&lt;br /&gt;
Empfängt einen als Midi-Sys-Ex Datenstrom eingepacktes Programm-Update und schreibt das in den Programm-Speicher des Controllers. &lt;br /&gt;
 &lt;br /&gt;
= Compilieren =&lt;br /&gt;
Der Code wird mit dem AVR-Studio 4 erzeugt (neues Projekt erzeugen, Quelltext in das Default-File kopieren, fertig) &lt;br /&gt;
&lt;br /&gt;
Einzige Besonderheit ist, dass der Code für die Boolader-Adresse gelinkt werden muss. &lt;br /&gt;
Dazu unter Projekt-&amp;gt; Configuration Options -&amp;gt; Custom Options -&amp;gt; Linker Options eine neue Option &amp;quot;-Ttext=0x1800&amp;quot; zufügen (add) &lt;br /&gt;
&lt;br /&gt;
(Im Prinzip ist das hier nicht viel anders als das, was hier beschrieben wird: http://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung ) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Bootloader &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.98 2011-06-26&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;avr/boot.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/pgmspace.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/crc16.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define LEDGATE0 0	// Led an Gate 0, keine LED Ansteuerung&lt;br /&gt;
#define LEDGATE4 4	// LED an Gate 5, &lt;br /&gt;
#define LEDLED   6  // LED an Reset-Pin (Serie, kein Debuging) &lt;br /&gt;
&lt;br /&gt;
#define LEDOUT LEDLED&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4)))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*applicationmain)(void) = (void *)0x0000;&lt;br /&gt;
void (*bootloader)(void) = (void *)0x1800;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
volatile unsigned char program_it;&lt;br /&gt;
volatile unsigned char prog_active; &lt;br /&gt;
volatile unsigned char flash_buf[64];&lt;br /&gt;
volatile unsigned char blockAdress;&lt;br /&gt;
&lt;br /&gt;
#define EESTATUS_OK 1		// last flash was ok &lt;br /&gt;
#define EESTATUS_START 2 	// last flash was started (if read on boot-up: ... but never finished) &lt;br /&gt;
&lt;br /&gt;
#define BOOTEEPROMADR ((uint8_t *) 500) // permanent Flag for last update status (&#039;started&#039; or &#039;success&#039;) &lt;br /&gt;
										// more or less obvisious the permanent state musst be success, else it failed to complete &lt;br /&gt;
										// and that can&#039;t be ok... &lt;br /&gt;
&lt;br /&gt;
#define PROGRAM_IDLE 0 &lt;br /&gt;
#define PROGRAM_DO 1&lt;br /&gt;
#define PROGRAM_FINISH 2&lt;br /&gt;
#define PROGRAM_ERROR 3&lt;br /&gt;
#define PROGRAM_START 4&lt;br /&gt;
#define PROGRAM_ABORT 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Main&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i,j; &lt;br /&gt;
	unsigned short int flashAdress;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	volatile uint16_t tmp;&lt;br /&gt;
	volatile uint16_t adr;&lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
	cli();	// just to be sure... its not allways a cold boot which runs here.. &lt;br /&gt;
&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
	if(GPIOR1==0xAB)&lt;br /&gt;
	{&lt;br /&gt;
		// app intended Boot Load&lt;br /&gt;
		GPIOR1 = 0;&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{	// no main application intended Boot-Load&lt;br /&gt;
&lt;br /&gt;
		_delay_ms(10);	// Wait for Button-Pin to get High if not pressed&lt;br /&gt;
		&lt;br /&gt;
		GPIOR1=0;&lt;br /&gt;
		&lt;br /&gt;
		while(BUTTON &amp;amp;&amp;amp; GPIOR1 &amp;lt; 8)&lt;br /&gt;
		{&lt;br /&gt;
			_delay_ms(1800);&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			_delay_ms(200);&lt;br /&gt;
			setLed(0);&lt;br /&gt;
			GPIOR1++;&lt;br /&gt;
		}&lt;br /&gt;
		_delay_ms(10);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
		if( ( !BUTTON &amp;amp;&amp;amp; GPIOR1==0 ) || GPIOR1 &amp;gt;= 8)&lt;br /&gt;
		{&lt;br /&gt;
			unsigned char x; &lt;br /&gt;
		&lt;br /&gt;
			_delay_ms(100);&lt;br /&gt;
		&lt;br /&gt;
			x=eeprom_read_byte(BOOTEEPROMADR); 	&lt;br /&gt;
			if(x != EESTATUS_START &amp;amp;&amp;amp; pgm_read_word(0)!=0xffff ) // if no failed update atemps and not erased &lt;br /&gt;
			{&lt;br /&gt;
				_delay_ms(100);&lt;br /&gt;
				applicationmain();&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	/* Enable change of Interrupt Vectors */&lt;br /&gt;
	MCUCR = (1 &amp;lt;&amp;lt; IVCE );&lt;br /&gt;
	/* Move Interrupts to boot flash section */&lt;br /&gt;
	MCUCR = ( 1 &amp;lt;&amp;lt; IVSEL ); &lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
	sei(); // Interupts on &lt;br /&gt;
&lt;br /&gt;
	while(1) // Main Loop &lt;br /&gt;
	{&lt;br /&gt;
		if(program_it== PROGRAM_DO || program_it== PROGRAM_FINISH)&lt;br /&gt;
		{&lt;br /&gt;
			flashAdress=blockAdress  ;  // Block number &lt;br /&gt;
			flashAdress&amp;lt;&amp;lt;=6;			// * block size of 64 bytes &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
			cli();		// some register writes must be done within 4 clock cycles max, Interupts can kill that &lt;br /&gt;
			boot_page_erase(flashAdress);	// issue the block erease command &lt;br /&gt;
			sei();&lt;br /&gt;
&lt;br /&gt;
						&lt;br /&gt;
			do&lt;br /&gt;
			{&lt;br /&gt;
				cli();&lt;br /&gt;
				j=(__SPM_REG &amp;amp; (uint8_t)_BV(__SPM_ENABLE));	// j=it&#039;s done? &lt;br /&gt;
				sei();&lt;br /&gt;
			}while(j);		// wait until j says, that it is done &lt;br /&gt;
			// this takes some millisekonds, during which we&#039;ll receive about a quarter of the next block&lt;br /&gt;
			// so interupts can&#039;t be off the whole time. &lt;br /&gt;
;&lt;br /&gt;
			for (i = 0; i &amp;lt; 64; i+=2) // fill page buffer with 32 words (à two bytes each) &lt;br /&gt;
			{&lt;br /&gt;
				tmp = (flash_buf[i]) | (flash_buf[i + 1]&amp;lt;&amp;lt;8);&lt;br /&gt;
				adr=flashAdress+i;&lt;br /&gt;
				cli();	// again, one of the &amp;quot;max 4 cycle&amp;quot; macros &lt;br /&gt;
				boot_page_fill(adr, tmp);	// write a word &lt;br /&gt;
				sei();&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			cli();&lt;br /&gt;
			boot_page_write(flashAdress);	// write to flash &lt;br /&gt;
			sei();&lt;br /&gt;
			&lt;br /&gt;
			do	// wait until write is done &lt;br /&gt;
			{	&lt;br /&gt;
				cli();										// prepare uninteruptable max. 4 Cylce sequence &lt;br /&gt;
				j=(__SPM_REG &amp;amp; (uint8_t)_BV(__SPM_ENABLE));	// read ready flag &lt;br /&gt;
				sei();										// done &lt;br /&gt;
			}while(j);		// while not ready. Again this takes 3.5 to 4.5ms, during which about another quarter of the &lt;br /&gt;
							// next block comes in. As we don&#039;t want to lose any chars, the interupts cant be turned off the whole time &lt;br /&gt;
&lt;br /&gt;
			cli();&lt;br /&gt;
			boot_rww_enable();	// enable read access to programm flash. &lt;br /&gt;
			sei();&lt;br /&gt;
			&lt;br /&gt;
			if(program_it==PROGRAM_FINISH)	// if last block&lt;br /&gt;
			{&lt;br /&gt;
				eeprom_write_byte(BOOTEEPROMADR,EESTATUS_OK);		// store flag for successfull update &lt;br /&gt;
				prog_active=PROGRAM_IDLE;		// should be senseless, but inhibits permanent eeprom writes if something goes wrong... &lt;br /&gt;
&lt;br /&gt;
				// Update done, go to new application programm &lt;br /&gt;
				cli();										// no more intupts &lt;br /&gt;
				/* Enable change of Interrupt Vectors */	// set interupt vectors to program &lt;br /&gt;
				MCUCR = (1 &amp;lt;&amp;lt; IVCE );&lt;br /&gt;
				/* Move Interrupts to regular flash section */&lt;br /&gt;
				MCUCR = 0; // ( 0 &amp;lt;&amp;lt; IVSEL ); &lt;br /&gt;
&lt;br /&gt;
				applicationmain();							// call application (never comes back, stack is lost...) &lt;br /&gt;
			}&lt;br /&gt;
			if(program_it!=PROGRAM_ERROR)	// if not error 		// which might happen asynchronous - and should be kept&lt;br /&gt;
				program_it=PROGRAM_IDLE;	// programming of current page done. &lt;br /&gt;
		}&lt;br /&gt;
		if(program_it==PROGRAM_START)&lt;br /&gt;
		{&lt;br /&gt;
			prog_active=PROGRAM_START;&lt;br /&gt;
			eeprom_write_byte(BOOTEEPROMADR,EESTATUS_START);&lt;br /&gt;
&lt;br /&gt;
			if(program_it!=PROGRAM_ERROR)	// might happen asynchronous &lt;br /&gt;
				program_it=PROGRAM_IDLE;&lt;br /&gt;
		}&lt;br /&gt;
		if(program_it==PROGRAM_ABORT)&lt;br /&gt;
		{&lt;br /&gt;
			cli();&lt;br /&gt;
			/* Enable change of Interrupt Vectors */&lt;br /&gt;
			MCUCR = (1 &amp;lt;&amp;lt; IVCE );&lt;br /&gt;
			/* Move Interrupts to regular flash section */&lt;br /&gt;
			MCUCR = 0; // ( 0 &amp;lt;&amp;lt; IVSEL ); &lt;br /&gt;
&lt;br /&gt;
			bootloader(); 	// checks, if there is a valid program to start &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
volatile unsigned char dataRx; &lt;br /&gt;
#define DATARX 4&lt;br /&gt;
&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	static unsigned char bit;&lt;br /&gt;
	static unsigned char blinker;&lt;br /&gt;
	static unsigned char blinks[]={ 40 , 10, 200, 10,   15,15,15,15 }; &lt;br /&gt;
	static unsigned char btn;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if (blinker-- == 0)&lt;br /&gt;
	{&lt;br /&gt;
		bit++;&lt;br /&gt;
		bit&amp;amp;=3;&lt;br /&gt;
		blinker=blinks[bit | dataRx];&lt;br /&gt;
&lt;br /&gt;
		setLed(bit&amp;amp;1);&lt;br /&gt;
&lt;br /&gt;
		dataRx=0;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{&lt;br /&gt;
		btn=10;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		if(btn&amp;gt;0)&lt;br /&gt;
		{	&lt;br /&gt;
			btn--;&lt;br /&gt;
			if(btn == 0)&lt;br /&gt;
			{&lt;br /&gt;
				if(prog_active==PROGRAM_IDLE &amp;amp;&amp;amp; program_it == PROGRAM_IDLE)			&lt;br /&gt;
					program_it=PROGRAM_ABORT;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*  ***********************************************************************************&lt;br /&gt;
	SERIAL MIDI INTERFACE &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define SYSEX 			0xF0&lt;br /&gt;
#define SYSEND			0xF7&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define STAT_IDLE 	  0&lt;br /&gt;
#define STAT_INSYSEX1 1&lt;br /&gt;
#define STAT_INSYSEX2 2&lt;br /&gt;
#define STAT_INSYSEX3 3&lt;br /&gt;
#define STAT_INSYSEX4 4&lt;br /&gt;
#define STAT_INSYSEX5 5&lt;br /&gt;
#define STAT_INSYSEX6 6&lt;br /&gt;
#define STAT_PROGRAM_BYTES 7&lt;br /&gt;
#define STAT_WAITEND 8&lt;br /&gt;
#define STAT_ERROR 9&lt;br /&gt;
&lt;br /&gt;
unsigned char rx_buf[68]; // 1Byte Block-Adress,  64 Data-Bytes, 2Byte CRC &lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine&lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	static unsigned char status, error, current_block,num_blocks; &lt;br /&gt;
//	static unsigned char nibble_cnt,  data;&lt;br /&gt;
	static unsigned short int runningByte; // no joke, this accumulates our 8-bit-bytes from 7bit midi, so it must hold up to 14bit in between... &lt;br /&gt;
	static unsigned char byte_cnt,bits;&lt;br /&gt;
&lt;br /&gt;
	static unsigned short int crc;&lt;br /&gt;
	unsigned char rxStat,byte;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    rxStat = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((rxStat &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(status &amp;gt; STAT_IDLE)&lt;br /&gt;
			dataRx=DATARX; &lt;br /&gt;
			 &lt;br /&gt;
		if (byte &amp;gt; 0x7F )		// Command byte &lt;br /&gt;
		{&lt;br /&gt;
			if( byte &amp;lt; 0xF8 ) // no Real-Time Message &lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX )	// Start&lt;br /&gt;
				{&lt;br /&gt;
					status=STAT_INSYSEX1;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)  // SysEx-End (might not be ours)&lt;br /&gt;
				{&lt;br /&gt;
					if( status==STAT_PROGRAM_BYTES)	// properly framed SysEx received &lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
						if(crc==0 ) &lt;br /&gt;
						{&lt;br /&gt;
							unsigned char i;&lt;br /&gt;
&lt;br /&gt;
							if(rx_buf[0]==0 &amp;amp;&amp;amp; byte_cnt==6 ) // first block = info block&lt;br /&gt;
							{&lt;br /&gt;
								error=0;&lt;br /&gt;
								current_block=1;&lt;br /&gt;
								num_blocks=rx_buf[1];&lt;br /&gt;
								for(i=0;i&amp;lt;2;i++)&lt;br /&gt;
									flash_buf[i]=rx_buf[i+2];&lt;br /&gt;
								program_it=PROGRAM_START;&lt;br /&gt;
		&lt;br /&gt;
							}&lt;br /&gt;
							else if(byte_cnt==67 &amp;amp;&amp;amp; current_block==rx_buf[0] ) &lt;br /&gt;
							{	// Programm Data Block &lt;br /&gt;
&lt;br /&gt;
								for(i=0;i&amp;lt;64;i++)&lt;br /&gt;
									flash_buf[i]=rx_buf[i+1];&lt;br /&gt;
								blockAdress=rx_buf[0]-1;&lt;br /&gt;
&lt;br /&gt;
								if(!error)&lt;br /&gt;
								{&lt;br /&gt;
									if(num_blocks==current_block)&lt;br /&gt;
									{&lt;br /&gt;
										program_it=PROGRAM_FINISH;&lt;br /&gt;
									}&lt;br /&gt;
									else &lt;br /&gt;
									{&lt;br /&gt;
										program_it=PROGRAM_DO;									&lt;br /&gt;
									}&lt;br /&gt;
								}&lt;br /&gt;
								current_block++;&lt;br /&gt;
							}&lt;br /&gt;
							else&lt;br /&gt;
							{&lt;br /&gt;
								status=STAT_ERROR;&lt;br /&gt;
							}&lt;br /&gt;
						}&lt;br /&gt;
						else &lt;br /&gt;
							status=STAT_ERROR;&lt;br /&gt;
					}&lt;br /&gt;
					else // not my frame format  &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
				}&lt;br /&gt;
				else 	// some other Status byte &lt;br /&gt;
				{&lt;br /&gt;
					if(status!=STAT_IDLE)&lt;br /&gt;
					{&lt;br /&gt;
						if(status&amp;lt;STAT_PROGRAM_BYTES) // might be unrelated to us &lt;br /&gt;
							status=STAT_IDLE;&lt;br /&gt;
						else &lt;br /&gt;
							status=STAT_ERROR; // our Data gets messed up &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			} // no real time message &lt;br /&gt;
		}&lt;br /&gt;
		else // Data Byte&lt;br /&gt;
		{&lt;br /&gt;
			switch(status)&lt;br /&gt;
			{&lt;br /&gt;
				default:	// should never happen&lt;br /&gt;
					status=STAT_ERROR;&lt;br /&gt;
				case STAT_IDLE: // any Data which is not in our SysExFrame &lt;br /&gt;
					break; &lt;br /&gt;
				case STAT_INSYSEX1: // Manufacturer &lt;br /&gt;
					if(byte==0x70)&lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_INSYSEX2;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_INSYSEX2:	// Unit ID&lt;br /&gt;
					if(byte==0x7D)&lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_INSYSEX4;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_INSYSEX4:	// Unit Adress (ignored) &lt;br /&gt;
					if(byte&amp;lt; 0x10)&lt;br /&gt;
					{	status=STAT_INSYSEX5;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_INSYSEX5:	// &amp;quot;Data Adress&amp;quot; (magic byte 1) &lt;br /&gt;
					if(byte == 0x15)&lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_INSYSEX6;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_INSYSEX6:	// magic byte 2 (this would stop the regular SysEx Decoder from taking this as data) &lt;br /&gt;
					if(byte == 0x55)&lt;br /&gt;
					{&lt;br /&gt;
						status=STAT_PROGRAM_BYTES;&lt;br /&gt;
						crc=0xffff;&lt;br /&gt;
						byte_cnt=0;&lt;br /&gt;
						bits=0;&lt;br /&gt;
					}&lt;br /&gt;
					else &lt;br /&gt;
						status=STAT_IDLE;&lt;br /&gt;
					break;&lt;br /&gt;
				case STAT_PROGRAM_BYTES:	// we&#039;ve made it: now the new flash data rolls in 			&lt;br /&gt;
					runningByte &amp;lt;&amp;lt;=7;&lt;br /&gt;
					runningByte|=byte;&lt;br /&gt;
					bits+=7;&lt;br /&gt;
					if(bits&amp;gt;=8) // if enough for one Byte &lt;br /&gt;
					{&lt;br /&gt;
						unsigned char b;&lt;br /&gt;
&lt;br /&gt;
						bits-=8;&lt;br /&gt;
						b=runningByte&amp;gt;&amp;gt;bits;&lt;br /&gt;
						rx_buf[byte_cnt]=b;&lt;br /&gt;
						crc=_crc16_update(crc,b);&lt;br /&gt;
						if(++byte_cnt&amp;gt;67)&lt;br /&gt;
							status=STAT_ERROR;&lt;br /&gt;
					}&lt;br /&gt;
					break;&lt;br /&gt;
			} // switch status&lt;br /&gt;
		}	// data byte&lt;br /&gt;
	}&lt;br /&gt;
	else // any Rx-Erros =&amp;gt; abort &lt;br /&gt;
		status=STAT_ERROR;&lt;br /&gt;
	&lt;br /&gt;
	if(status==STAT_ERROR)&lt;br /&gt;
	{&lt;br /&gt;
		status=STAT_IDLE;&lt;br /&gt;
		error=1;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Adapter.gif&amp;diff=7288</id>
		<title>Datei:Adapter.gif</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Adapter.gif&amp;diff=7288"/>
		<updated>2011-06-25T19:33:00Z</updated>

		<summary type="html">&lt;p&gt;Fetz: uploaded a new version of &amp;quot;File:Adapter.gif&amp;quot;:&amp;amp;#32;Korrektur: 
Pin 2 gehört nicht durchverbunden 
irrelevanten Stern an Pin 1/7 entfernt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Verdrahtungsplan des Adaptersockels &lt;br /&gt;
(A=Atmel-Pins, P=Platinen-Pins)&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7284</id>
		<title>MCV876 Controller Update: Source Code</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7284"/>
		<updated>2011-06-23T19:18:36Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Übersetzen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Übersetzen= &lt;br /&gt;
Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden.&lt;br /&gt;
Im Studio ein neues, leeres Projekt mit Standard-Einstellungen, einem sinnvollen Namen und an einem Ort, wo man es wiederfindet erstellen, Quelltext in das (automatisch erzeugte, leere) C-File kopieren, auf Build-&amp;gt;Build klicken, fertig. &lt;br /&gt;
Das resultierende HEX File kann man mit einem üblichen Programmieradapter in der Chip flashen, für uns zweckmäßiger ist allerdings, das in ein Sys-Ex File zu Konvertieren ( [[MCV876_Controller_Update:_File_Converter]]), und das über Midi an den Controller zu schicken. Dann programmiert der sich selber um. Dazu muss er allerdings einmalig mit dem Bootlader [hier Link auf Bootlader imaginieren.. ] programmiert worden sein. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu den Atmel Controllern findet man eine Menge Info mit Tante Google, [http://www.mikrocontroller.net http://www.mikrocontroller.net] kommt häufig vor, und hat ein ergiebiges Forum und gute Artikel. Auf der Atmel Webseite gibt es auch einige Info, das [http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf ATMega 88 Datenblatt] ist spätestens dann nötig, wenn es um die Hardware geht.&lt;br /&gt;
&lt;br /&gt;
=Quelltext=&lt;br /&gt;
&amp;lt;pre&amp;gt;/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Midi to 4 Channel Control Voltage and six Gates Interface &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.20 2011-06-23 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
			&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;			// yes, we want to talk to the port pins by name&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;		// ... oh.. and some eeprom access would be nice&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;	// ... and w/o the interupts it wouldn&#039;t do anything... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*	===================ZERO LINE=================================&lt;br /&gt;
	WARNING: everthing below the zero line counts from zero! &lt;br /&gt;
	e.g. whats Gate 6 in manual is GATE5 here, CVs are CV0 to 3 &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	where should the LED output be &lt;br /&gt;
*/&lt;br /&gt;
#define LEDLED   6  // LED at Reset-Pin, standard for series, but no debugging possible. &lt;br /&gt;
#define LEDGATE0 0	// Led at Gate 0, no LED (Test I: no LED ) &lt;br /&gt;
#define LEDGATE4 4	// LED at Gate 5, Test II: no Gate 5 Function (Midi Start/Stop) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define LEDOUT LEDGATE4 // select your evil... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define ACTIVE_SENSE_TICKS 160 // 2ms Timer Ticks for active sense time out &lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4))) // True if Button pressed &lt;br /&gt;
&lt;br /&gt;
#define NUM_CV 4&lt;br /&gt;
&lt;br /&gt;
#define PARAMETEROFFSET 0x23 // First paramter has adress 0x23&lt;br /&gt;
#define NUM_PARAMTERS 17	 // and there are 17 parameters &lt;br /&gt;
&lt;br /&gt;
#define MODE_MONO 	0x00	// monophon, all assigned to last note on a single channel &lt;br /&gt;
#define MODE_MULTI2	0x40	// two channel, 1/2 for midiChannel, 2/3 for midiChannel+1 &lt;br /&gt;
#define MODE_MULTI4	0x60	// for channel midiChannel to midiChannel+3 &lt;br /&gt;
#define MODE_POLY2	0x80	// not supported &lt;br /&gt;
#define MODE_POLY4	0xA0	// not supported&lt;br /&gt;
&lt;br /&gt;
struct CONFIG {&lt;br /&gt;
	unsigned char midiChannel;&lt;br /&gt;
	unsigned char noteOffset;&lt;br /&gt;
&lt;br /&gt;
	unsigned char cvMidiCC[NUM_CV];&lt;br /&gt;
	unsigned char gateMidiCC[NUM_CV];&lt;br /&gt;
	&lt;br /&gt;
	unsigned char gateTreshhold;&lt;br /&gt;
	unsigned char clockDivider;&lt;br /&gt;
	unsigned char clockTime; &lt;br /&gt;
	unsigned char ifMode0; 	// 	bit 0 ... bit 3 = CV0 .. CV3, 0=Pitch, 1=CC (if pitch, CC assignement is ignored) &lt;br /&gt;
							//	bit 5: voices: 0=2; 1=4 &lt;br /&gt;
							//  bit 7:6= 00 Mono; 01:Multi-Channel; 10: Poly (not supported); &lt;br /&gt;
	&lt;br /&gt;
	unsigned char ifMode1;  // Gate Trigger Mode Bits (0=Re-Trigger, 1=TriggerControl)  &lt;br /&gt;
	unsigned char ifMode2;  // bit0: Velo0=&amp;gt;CV0; bit1:Velo0=&amp;gt;CV1; bit2:Velo-Poly2=&amp;gt;CV2; bit3:Velo1=&amp;gt;CV3&lt;br /&gt;
							// bit4: PB=&amp;gt;CV0; bit5:PB=&amp;gt;CV1; bit6:PB=&amp;gt;CV2; bit7:PB=&amp;gt;CV3&lt;br /&gt;
	unsigned char ifMode3; 	// bit0..bit3 = CV0... CV3; 0=10V, 1=5V Mode &lt;br /&gt;
&lt;br /&gt;
//  internal evaluated configuration &lt;br /&gt;
	unsigned char numMidiChannels;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
} shadow_config, // her goes a copy, when config should be written, because that might take a while &lt;br /&gt;
&lt;br /&gt;
config 	// current configuration, mostly direct evaluated troughout the program&lt;br /&gt;
= {		// default values (Mono-Mode, Midi-Ch 1, CV1=Pitch, CV2=Velocity CV3=ModWheel CV4=Pitch Bend&lt;br /&gt;
			0,  	// Midi Channel &lt;br /&gt;
			24, 	// Note Offset (2 Oktaves) &lt;br /&gt;
		{	255,255,1,255, },  	// CV - assigned Midi Controllers (used if not pitch output) 255=none&lt;br /&gt;
		{   255,18,19,20, }, 	// Gate - asigned Controllers (if not pitch) 255=none &lt;br /&gt;
			63, 	// Treshold&lt;br /&gt;
			6,  	// Divider 6 (1/16 Note) &lt;br /&gt;
			1,  	// clock time (unused) &lt;br /&gt;
			0x0e, 	// mode 1 Low nibble CC-Bits, high: interface mode &lt;br /&gt;
			0x82, 	// mode 2 High Nibble: PB-bits, Low: Velo-Bits&lt;br /&gt;
			0,    	// mode 3 (5V-Bits) &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define EE_MAGIC 17&lt;br /&gt;
unsigned char ee_magic EEMEM;	// to check it eeprom was ever written and should be read back &lt;br /&gt;
unsigned char config_eeprom[NUM_PARAMTERS] EEMEM; // Config in EEPROM, saves it during power down. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// to have the clock synced to &amp;quot;beat 1&amp;quot; of a bar ... &lt;br /&gt;
volatile unsigned int spp; 	// song position pointer (from midi message) &lt;br /&gt;
unsigned char ticksPerBar=96; // How many ticks per bar (is not tx-ed: guess 96 for 4/4 ...  ) &lt;br /&gt;
volatile unsigned char barTick; // where are we inside the bar (counted, because calculation from spp takes some cpu-cycles ) &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char  eepromWriteFlag;	// set to one, if main-loop should write eeprom &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char button; 	// debounced button status &lt;br /&gt;
volatile unsigned char learn; 	// (channel/base note/cc) &amp;quot;do learn&amp;quot; status &lt;br /&gt;
volatile unsigned char blinker; // some count for blinkenlights &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char clocker;	 // timer counter for half a midi-clock (which is needed for divider 1)&lt;br /&gt;
volatile unsigned char gate5off; // timer counter for when clock gate output should go low again. &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char watchTicker;	// midi active sense counter &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setConfig(void); 		&lt;br /&gt;
void setLed(unsigned char on);&lt;br /&gt;
void resetAll(void);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	main() &lt;br /&gt;
	Main does initialization, than runs a loop, which does nearly nothing: &lt;br /&gt;
&lt;br /&gt;
	Programming the EEPROM (because its slow) &lt;br /&gt;
	Blinking &lt;br /&gt;
	Button evaluation &lt;br /&gt;
&lt;br /&gt;
	All the important things are done by the interupts. (Thanks to the &lt;br /&gt;
	20MHZ RISC power everything can be done in less than 320µs=one Midi-Character) &lt;br /&gt;
*/&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char blink=6;&lt;br /&gt;
&lt;br /&gt;
	unsigned char i,x; &lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
	static unsigned char lastButton; &lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
	// Read Configuration &lt;br /&gt;
	if( eeprom_read_byte(ee_magic) == EE_MAGIC  &amp;amp;&amp;amp; !BUTTON) // if eeprom valid AND Button not pressed &lt;br /&gt;
	{&lt;br /&gt;
		cp=(unsigned char* )&amp;amp;config; &lt;br /&gt;
		for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
		{&lt;br /&gt;
			x=eeprom_read_byte(config_eeprom+i);			// read config from eeprom &lt;br /&gt;
			*cp=x;	&lt;br /&gt;
			cp++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	setConfig();&lt;br /&gt;
&lt;br /&gt;
	// enable interrupts&lt;br /&gt;
	sei(); &lt;br /&gt;
&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		if(eepromWriteFlag)&lt;br /&gt;
		{	// save configuration &lt;br /&gt;
			cp=(unsigned char* )&amp;amp;shadow_config; &lt;br /&gt;
			for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
			{&lt;br /&gt;
				x=*cp;&lt;br /&gt;
				eeprom_write_byte(config_eeprom+i,x);&lt;br /&gt;
				cp++;&lt;br /&gt;
			}&lt;br /&gt;
			eeprom_write_byte(ee_magic,EE_MAGIC); &lt;br /&gt;
&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			blink=6;&lt;br /&gt;
			blinker=0;&lt;br /&gt;
			eepromWriteFlag=0;	&lt;br /&gt;
		}&lt;br /&gt;
		if(blink &amp;amp;&amp;amp; blinker&amp;gt;2)  // blink after power up and after storing in nv-memory &lt;br /&gt;
		{&lt;br /&gt;
			blink--;&lt;br /&gt;
			setLed(blink&amp;amp;1);&lt;br /&gt;
			blinker=0; &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if(button != lastButton)&lt;br /&gt;
		{&lt;br /&gt;
			if(button==1 )&lt;br /&gt;
			{&lt;br /&gt;
				resetAll();&lt;br /&gt;
				if(learn==1)&lt;br /&gt;
				{&lt;br /&gt;
					learn=0;&lt;br /&gt;
					setLed(0);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if(button==6) &lt;br /&gt;
			{&lt;br /&gt;
				if(learn ==0)&lt;br /&gt;
				{&lt;br /&gt;
					learn=1;&lt;br /&gt;
					setLed(1);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			lastButton=button; &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return 0; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Hardware Interface&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
/*  Pin assignment &lt;br /&gt;
   Atmel Fkt 	[Orig] &lt;br /&gt;
1  Reset LED 	[MCLR] &lt;br /&gt;
2  RX    Midi-Rx[WR1]&lt;br /&gt;
3  PD1   WR2&lt;br /&gt;
4  PD2   WR3&lt;br /&gt;
5  PD3   WR4&lt;br /&gt;
6  PD4   Button&lt;br /&gt;
7  VCC   +5  	[LED] (Either from Atmel-1(Series) or Atmel-15(w/Debug))&lt;br /&gt;
8  GND   GND&lt;br /&gt;
9  OSC   OSC&lt;br /&gt;
10 OSC   OSC &lt;br /&gt;
11 PD5   G1&lt;br /&gt;
12 PD6   G2&lt;br /&gt;
13 PD7   G3&lt;br /&gt;
14 PB0   G4&lt;br /&gt;
&lt;br /&gt;
15 PB1   G5 (Optional + LED) &lt;br /&gt;
16 PB2   G6 &lt;br /&gt;
17 PB3   WR1    [---] &lt;br /&gt;
18 PB4   D0     [RX]&lt;br /&gt;
19 PB5   D1		[GND]&lt;br /&gt;
20 AVCC  +5&lt;br /&gt;
21 ARef  +5 	[D0]&lt;br /&gt;
22 GND   GND	[D1] &lt;br /&gt;
23 PC0   D2&lt;br /&gt;
24 PC1   D3&lt;br /&gt;
25 PC2   D4&lt;br /&gt;
26 PC3   D5&lt;br /&gt;
27 PC4   D6&lt;br /&gt;
28 PC5   D7&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
// put &amp;quot;val&amp;quot; to the right port bits for the DA-Converter &lt;br /&gt;
// ... better program than solder *that*... &lt;br /&gt;
void adByteOut(unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char b;&lt;br /&gt;
	b=val;&lt;br /&gt;
	b&amp;gt;&amp;gt;=2;&lt;br /&gt;
	PORTC=b;&lt;br /&gt;
	b=val&amp;amp;3;&lt;br /&gt;
	b&amp;lt;&amp;lt;=4;&lt;br /&gt;
	val=PORTB;&lt;br /&gt;
	val &amp;amp;=0xCF;&lt;br /&gt;
	val|=b;&lt;br /&gt;
	PORTB=val;&lt;br /&gt;
}&lt;br /&gt;
// set DA-Output voltage of one CV Channel  &lt;br /&gt;
void set_cv(unsigned char val, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	adByteOut(val); 	// set D0..D7 converter data bits &lt;br /&gt;
	switch (channel)	// pulse with write pin &lt;br /&gt;
	{&lt;br /&gt;
		case 0: //PB3&lt;br /&gt;
			PORTB  &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTB  |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD1&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;1);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;1);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD2&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;2);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;2);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PD3&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// set on of the gate outputs active &lt;br /&gt;
void gate_on(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// clear one of the gate outputs &lt;br /&gt;
void gate_off(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif&lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Data Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
typedef struct {&lt;br /&gt;
	unsigned char channel;  // (relative) Midi channel (0..3) Mode dependant &lt;br /&gt;
	unsigned char cvOut;	// for which CV-Ouput (0..3) fixed &lt;br /&gt;
	unsigned char ccVal;	// current CC Value &lt;br /&gt;
	unsigned char note;		// current Note &lt;br /&gt;
	unsigned char velo;		// current Velocity &lt;br /&gt;
	unsigned char on;		// if Note is on &lt;br /&gt;
	signed char   pb;		// current Pitch Bend Value &lt;br /&gt;
	unsigned char gateDipper;&lt;br /&gt;
} cvoutput;  &lt;br /&gt;
&lt;br /&gt;
cvoutput cv_out[NUM_CV];	// current state for each cv out&lt;br /&gt;
&lt;br /&gt;
unsigned char gates; // which gates are on =&amp;gt; used for LED setting &lt;br /&gt;
&lt;br /&gt;
// set cv output (and assigned gate, if pitch output)&lt;br /&gt;
// called after anything that changes the cv_out structure  &lt;br /&gt;
void updateCv(cvoutput *cvo)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char bm;&lt;br /&gt;
	unsigned char gated=0;&lt;br /&gt;
	int val=0;&lt;br /&gt;
&lt;br /&gt;
	bm=1&amp;lt;&amp;lt;cvo-&amp;gt;cvOut;	// bit mask for configuration flags &lt;br /&gt;
&lt;br /&gt;
	if(config.ifMode0 &amp;amp; bm)	// 1=CC-Mode&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;ccVal; // &amp;lt;&amp;lt;1;&lt;br /&gt;
/*&lt;br /&gt;
		if( (config.ifMode3 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0)	// 5V-Flag, abused as Gated-Mode-CV Flag &lt;br /&gt;
			gated=1;&lt;br /&gt;
*/&lt;br /&gt;
	}&lt;br /&gt;
	else // Note&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;note;&lt;br /&gt;
		val -= config.noteOffset;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode3 &amp;amp; bm)	// 5V&lt;br /&gt;
			val &amp;lt;&amp;lt;= 2;&lt;br /&gt;
		else&lt;br /&gt;
			val &amp;lt;&amp;lt;= 1;&lt;br /&gt;
	}&lt;br /&gt;
	if(config.ifMode2 &amp;amp; bm) // Velocity &lt;br /&gt;
		val+=cvo-&amp;gt;velo;&lt;br /&gt;
	if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) // PB &lt;br /&gt;
		val+=cvo-&amp;gt;pb;&lt;br /&gt;
&lt;br /&gt;
	if(val&amp;lt;0 || gated)	// clip values to converter range &lt;br /&gt;
		val=0;&lt;br /&gt;
	if(val&amp;gt;255)&lt;br /&gt;
		val=255;&lt;br /&gt;
&lt;br /&gt;
	set_cv((unsigned char)val, cvo-&amp;gt;cvOut);&lt;br /&gt;
	if((config.ifMode0 &amp;amp; bm)==0)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;on)&lt;br /&gt;
		{&lt;br /&gt;
			if(cvo-&amp;gt;gateDipper==0) // no retrigger required&lt;br /&gt;
				gate_on(cvo-&amp;gt;cvOut);&lt;br /&gt;
			else				   // dip gate for a few milliseconds 	&lt;br /&gt;
				gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates |=bm;&lt;br /&gt;
		}&lt;br /&gt;
		else &lt;br /&gt;
		{&lt;br /&gt;
			gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates &amp;amp;=~bm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate/initialize control variables after &lt;br /&gt;
// mode-configuration change &lt;br /&gt;
void setConfig()&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i; &lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;pb=0x80;&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	switch(config.ifMode0 &amp;amp; 0xf0 )&lt;br /&gt;
	{	&lt;br /&gt;
		default:&lt;br /&gt;
		case MODE_MONO:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=1;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI2:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV/2;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			for(;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=1;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=2;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI4:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=i;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=NUM_CV;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// assign new note state to cv outputs &lt;br /&gt;
void setNote(unsigned char note, unsigned char velo, unsigned char onOff, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;channel==channel)&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;note=note;&lt;br /&gt;
			cvo-&amp;gt;velo=velo;&lt;br /&gt;
			if(onOff==2 &amp;amp;&amp;amp; cvo-&amp;gt;on &amp;amp;&amp;amp; (config.ifMode1 &amp;amp; (1&amp;lt;&amp;lt;i)))&lt;br /&gt;
				cvo-&amp;gt;gateDipper=4;&lt;br /&gt;
			cvo-&amp;gt;on=onOff;&lt;br /&gt;
			updateCv(cvo);&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	setLed(gates);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process new continous controller value &lt;br /&gt;
void setCC(unsigned char cc, unsigned char value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel== cvo-&amp;gt;channel )&lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode0 &amp;amp;(1&amp;lt;&amp;lt;i))&lt;br /&gt;
			{&lt;br /&gt;
				if(config.cvMidiCC[i]==cc)&lt;br /&gt;
				{&lt;br /&gt;
					cvo-&amp;gt;ccVal=value; &lt;br /&gt;
					updateCv(cvo);&lt;br /&gt;
				}&lt;br /&gt;
				if(config.gateMidiCC[i]==cc  )&lt;br /&gt;
				{&lt;br /&gt;
					if(value&amp;gt;config.gateTreshhold)&lt;br /&gt;
						gate_on(channel);&lt;br /&gt;
					else &lt;br /&gt;
						gate_off(channel);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// process new pitch bend value &lt;br /&gt;
void setPb(signed int value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel==cvo-&amp;gt;channel &amp;amp;&amp;amp; (config.ifMode2 &amp;amp; (16&amp;lt;&amp;lt;i)) )&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;pb	=(signed char) (value&amp;gt;&amp;gt;6);	&lt;br /&gt;
			updateCv(cvo);	&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// prepare for (asynchronous) configuration write to eeprom &lt;br /&gt;
void setEeWrite(void)&lt;br /&gt;
{&lt;br /&gt;
	shadow_config=config; &lt;br /&gt;
	learn=0;&lt;br /&gt;
	eepromWriteFlag=1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*bootloader)(void) = (void*)0x1800;	// Bootloader sits at adress 0x1800 ... &lt;br /&gt;
&lt;br /&gt;
// process sys ex command &lt;br /&gt;
void setSyx(unsigned char adr, unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
&lt;br /&gt;
	cp=(unsigned char *) &amp;amp;config; &lt;br /&gt;
&lt;br /&gt;
	if(adr==0x14 &amp;amp;&amp;amp; val ==0x7b) &lt;br /&gt;
	{	// write eeprom&lt;br /&gt;
		if(eepromWriteFlag==0)&lt;br /&gt;
		{&lt;br /&gt;
			setEeWrite();&lt;br /&gt;
		}&lt;br /&gt;
		return; &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7&lt;br /&gt;
	if(adr==0x16 &amp;amp;&amp;amp; val ==0x42)	// Software update  &lt;br /&gt;
	{&lt;br /&gt;
		cli();			// interupts off &lt;br /&gt;
		resetAll();		// outputs off &lt;br /&gt;
		GPIOR1 = 0xAB;	// tell bootloader it&#039;s an application boot request &lt;br /&gt;
						// (hardware reset sets GPIOR1 to zero, 0xab == magic value for bootloader )&lt;br /&gt;
		bootloader();	// bye bye! &lt;br /&gt;
		return; 	// &amp;lt;-- never! &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	adr-=PARAMETEROFFSET; &lt;br /&gt;
	if(adr &amp;lt; NUM_PARAMTERS)	// if valid parameter &lt;br /&gt;
	{&lt;br /&gt;
		*(cp+adr)=val; 		// set in &lt;br /&gt;
		if(adr==0x30-PARAMETEROFFSET) // if Mode Byte 0, &lt;br /&gt;
			setConfig();			  // .. cv assignement must be recalculated &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Note Puffer Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define MAX_NOTES 0x10  	// one for each finger of a real programmer &lt;br /&gt;
#define MAX_CHANNELS 4&lt;br /&gt;
unsigned char notes[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char velocities[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char playedNote[MAX_CHANNELS]; // number of currently pressed notes &lt;br /&gt;
// update note puffer for a midi note-on message &lt;br /&gt;
void note_on(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char n;&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	if(n&amp;lt;MAX_NOTES)&lt;br /&gt;
	{&lt;br /&gt;
		playedNote[channel]++;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		for (n=MAX_NOTES-1;n&amp;gt;0;--n)&lt;br /&gt;
		{&lt;br /&gt;
			notes[channel][n-1]=notes[channel][n];&lt;br /&gt;
			velocities[channel][n-1]=velocities[channel][n];&lt;br /&gt;
		}&lt;br /&gt;
		n=MAX_NOTES-1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	notes[channel][n]=note; 		&lt;br /&gt;
	velocities[channel][n]=velocity;&lt;br /&gt;
	setNote(note, velocity, 2, channel);&lt;br /&gt;
}&lt;br /&gt;
// update note puffer for a new midi note-off message &lt;br /&gt;
void note_off(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	signed char n; &lt;br /&gt;
&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	--n; &lt;br /&gt;
	for (i=0;i&amp;lt;=n;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(notes[channel][i]==note)&lt;br /&gt;
		{&lt;br /&gt;
			if(i==n) // last pressed (== the on which playes) note released &lt;br /&gt;
			{&lt;br /&gt;
				if(n==0) // it was the only pressed note &lt;br /&gt;
				{&lt;br /&gt;
					setNote(note, velocities[channel][n], 0, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{	&lt;br /&gt;
					--n;&lt;br /&gt;
					setNote(notes[channel][n], velocities[channel][n], 1, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else // non playing note released &lt;br /&gt;
			{	// =&amp;gt;  just remove it from list &lt;br /&gt;
				int j;&lt;br /&gt;
				for(j=i+1;j&amp;lt;=n;++j)&lt;br /&gt;
				{&lt;br /&gt;
					notes[channel][j-1]=notes[channel][j];&lt;br /&gt;
					velocities[channel][j-1]=velocities[channel][j];&lt;br /&gt;
				}&lt;br /&gt;
				playedNote[channel]--;&lt;br /&gt;
			}&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	All Notes off, empty Note Buffer, All Controllers Reset&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void resetAll(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	cli(); // this might be called asynchronous to midi stream &lt;br /&gt;
	for(i=0;i&amp;lt;MAX_CHANNELS;++i)&lt;br /&gt;
		playedNote[i]=0;&lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0;&lt;br /&gt;
		cvo-&amp;gt;on=0;&lt;br /&gt;
		cvo-&amp;gt;pb=0x80;&lt;br /&gt;
		updateCv(cvo);&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
	sei();&lt;br /&gt;
&lt;br /&gt;
	setLed(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	static unsigned char buttonCount;&lt;br /&gt;
	static signed char blinkCount; &lt;br /&gt;
&lt;br /&gt;
	// LED blinker &lt;br /&gt;
	if(--blinkCount&amp;lt;=0)&lt;br /&gt;
	{	&lt;br /&gt;
		blinkCount=40;&lt;br /&gt;
		blinker++;&lt;br /&gt;
		if(button)&lt;br /&gt;
		{&lt;br /&gt;
			if(button&amp;lt;10)&lt;br /&gt;
				button++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Button debouncer &lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{	// down &lt;br /&gt;
		if(buttonCount &amp;lt; 10)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount++;&lt;br /&gt;
			if(buttonCount==10)&lt;br /&gt;
			{&lt;br /&gt;
				button=1;&lt;br /&gt;
				blinkCount=40;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{	// up &lt;br /&gt;
		if(buttonCount &amp;gt; 0)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount--;&lt;br /&gt;
			if(buttonCount==0)&lt;br /&gt;
				button=0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Gate 5 timing (clock output) &lt;br /&gt;
	clocker++;&lt;br /&gt;
	if(gate5off)&lt;br /&gt;
	{&lt;br /&gt;
		--gate5off;&lt;br /&gt;
		if(gate5off==0)&lt;br /&gt;
			gate_off(5);	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// activ sense &lt;br /&gt;
	if(watchTicker)			// if active sense is activated &lt;br /&gt;
	{&lt;br /&gt;
		watchTicker--;		// decrement time &lt;br /&gt;
		if(watchTicker==0)	// if time elapsed&lt;br /&gt;
			resetAll(); 	// switch all notes off 	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// retrigger timing &lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)	// for all cv output channels &lt;br /&gt;
	{&lt;br /&gt;
		if(cv_out[i].gateDipper)	// if gate timer is active &lt;br /&gt;
		{&lt;br /&gt;
			--cv_out[i].gateDipper;		// decrement timer &lt;br /&gt;
			if(cv_out[i].gateDipper==0)	// if timer elapsed&lt;br /&gt;
				gate_on(i);				// activate gate &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Eval Midi Data&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define NOTE_ON         0x90		// Midi note on message &lt;br /&gt;
#define NOTE_OFF        0x80		// Midi note off message &lt;br /&gt;
#define PITCH           0xE0		// pitch bender&lt;br /&gt;
#define AFTERTOUCH      0xD0        // after touch command (unused) &lt;br /&gt;
#define CC		        0xB0        // Continuous Controller&lt;br /&gt;
#define CHAN_PRES	    0xD0        // Channel Pressure (unused)&lt;br /&gt;
#define SONG_POSITION_POINTER 0xF2	// used for clock synchronization (first beat) &lt;br /&gt;
#define SYSEX 			0xF0		// System exclusive beginn&lt;br /&gt;
#define SYSEND			0xF7		// System exclusive end&lt;br /&gt;
&lt;br /&gt;
struct {&lt;br /&gt;
	unsigned char runningStatus;	// Midi runnig status (*)&lt;br /&gt;
	unsigned char msgByteCnt;    	// number of current midi-message byte received &lt;br /&gt;
	unsigned char byte1;			// first data byte &lt;br /&gt;
	unsigned char syxAdr; 			// Sys-Ex Adress Byte&lt;br /&gt;
	unsigned char startStop;		// current  Midi Start/Stop Status&lt;br /&gt;
	unsigned char startFlag;		// Start on next clock&lt;br /&gt;
	unsigned char clockTick;		// counter for clock division&lt;br /&gt;
	unsigned char channel;			// channel of current midi-command (relative, e.g. 0..3 ) &lt;br /&gt;
	unsigned char syncStartFlag;	// start on next 1 &lt;br /&gt;
}midi; &lt;br /&gt;
// 	(*) running status is also used as &#039;current status&#039; - it remembers allways, what next data byte is for. Or its zero, than data is ignored. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine, &lt;br /&gt;
// complete midi processing is done here, as the CPU is fast enough to &lt;br /&gt;
// do everything before next midi byte can come in. &lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	unsigned char status,byte;&lt;br /&gt;
&lt;br /&gt;
    status = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((status &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(watchTicker)						// if there had been an active sense &lt;br /&gt;
			watchTicker=ACTIVE_SENSE_TICKS;	// every new byte is ok, to &amp;quot;feed the watchdog&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		if (byte &amp;gt; 0x7F)	                // if command byte (MSB set)&lt;br /&gt;
		{&lt;br /&gt;
			if(byte&amp;gt;= 0xF8)  				// if real-time msg &lt;br /&gt;
			{&lt;br /&gt;
				switch (byte)&lt;br /&gt;
				{&lt;br /&gt;
					case 0xF8: 	// Clock &lt;br /&gt;
					{&lt;br /&gt;
						if(midi.startFlag)  // if synchronized start &lt;br /&gt;
						{&lt;br /&gt;
							midi.startStop=1;&lt;br /&gt;
							midi.startFlag=0;&lt;br /&gt;
							midi.syncStartFlag=1;&lt;br /&gt;
							midi.clockTick=0;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
						}&lt;br /&gt;
						else if(midi.startStop)	// if (already) running &lt;br /&gt;
						{&lt;br /&gt;
							++spp;&lt;br /&gt;
							++barTick;&lt;br /&gt;
							if(barTick &amp;gt;= ticksPerBar)&lt;br /&gt;
								barTick=0; &lt;br /&gt;
						}&lt;br /&gt;
						++midi.clockTick; 	// increment counter for clock output &lt;br /&gt;
						if(midi.clockTick &amp;gt;= config.clockDivider)	// if divider cout elapsed do clock output &lt;br /&gt;
						{&lt;br /&gt;
							midi.clockTick=0;&lt;br /&gt;
							gate_on(5);&lt;br /&gt;
							gate5off=clocker&amp;gt;&amp;gt;1; // pulse-width counter = half of current midi clock pulse width&lt;br /&gt;
						}&lt;br /&gt;
&lt;br /&gt;
						if(midi.syncStartFlag &amp;amp;&amp;amp; barTick==0 &amp;amp;&amp;amp; midi.startStop ) // synchronize clock output to bars   &lt;br /&gt;
						{	&lt;br /&gt;
							midi.clockTick=0;&lt;br /&gt;
							gate_on(5);&lt;br /&gt;
							gate5off=clocker&amp;gt;&amp;gt;1;&lt;br /&gt;
							midi.syncStartFlag=0;&lt;br /&gt;
						}&lt;br /&gt;
						clocker=0;  // restart midi clock pulse width counter. &lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
					case 0xF9:	// Tick (unused) &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFA: 	// Start &lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							spp=0;&lt;br /&gt;
							barTick=0;&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFB:	// Continue&lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFC:	// Stop&lt;br /&gt;
						midi.startStop=0;&lt;br /&gt;
						gate_off(4);&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFD:	// unassigned &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFE:	//  Active Sense&lt;br /&gt;
						watchTicker=ACTIVE_SENSE_TICKS;&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFF:	// Reset &lt;br /&gt;
						break; &lt;br /&gt;
				}&lt;br /&gt;
				return ; &lt;br /&gt;
			} &lt;br /&gt;
&lt;br /&gt;
			if(byte &amp;gt;= 0xF0) 					// if System Common Category message&lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)&lt;br /&gt;
				{&lt;br /&gt;
					if(midi.runningStatus==SYSEX &amp;amp;&amp;amp; midi.msgByteCnt==6) // if a complete valid Sys-Ex Frame has been received &lt;br /&gt;
						setSyx(midi.syxAdr, midi.byte1); 				// change configuration &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					midi.runningStatus = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				else if(byte ==SONG_POSITION_POINTER)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;  &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				//							// every other System Common is ignored 	&lt;br /&gt;
				midi.runningStatus = 0;		// just reset running status &lt;br /&gt;
				return ; 					// and done &lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.runningStatus = 0;				// just new status messages left, so running status is lost&lt;br /&gt;
&lt;br /&gt;
			// only channel messages left here ... &lt;br /&gt;
			if(learn)								 // if learn mode &lt;br /&gt;
			{&lt;br /&gt;
				if(config.midiChannel != (byte&amp;amp;0xf)) // if message channel not current channel&lt;br /&gt;
				{&lt;br /&gt;
					config.midiChannel=byte&amp;amp;0xf;	 // assign channel &lt;br /&gt;
					setEeWrite(); 					 // and store it &lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.channel=(byte &amp;amp; 0xf)-config.midiChannel ;&lt;br /&gt;
			if ( midi.channel&amp;lt;config.numMidiChannels)   // if MIDI channel matches,&lt;br /&gt;
			{&lt;br /&gt;
				midi.runningStatus = byte &amp;amp; 0xF0 ;		// set running status (==Midi command w/o channel)&lt;br /&gt;
				midi.msgByteCnt = 0;&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
			return ; &lt;br /&gt;
		}&lt;br /&gt;
		else	// data bytes goes here &lt;br /&gt;
		{&lt;br /&gt;
			switch (midi.runningStatus)		    // last command byte (or zero, if data is not for us) &lt;br /&gt;
			{&lt;br /&gt;
			case SONG_POSITION_POINTER:&lt;br /&gt;
				if(midi.msgByteCnt)	// 2nd Byte&lt;br /&gt;
				{&lt;br /&gt;
					spp= (((int)byte &amp;lt;&amp;lt; 7 ) | midi.byte1) * 6;&lt;br /&gt;
					barTick = spp % (unsigned int) ticksPerBar; &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt=1;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_ON):					// if we&#039;ve had a note on byte&lt;br /&gt;
				if (midi.msgByteCnt)		// if counter = 1&lt;br /&gt;
				{ 			   				// then we&#039;ve just received a velocity byte&lt;br /&gt;
					midi.msgByteCnt = 0;	// msg complete - clear byte count &lt;br /&gt;
					if (byte == 0)			// is it a note off (velo = 0) ?&lt;br /&gt;
						note_off(midi.byte1,byte,midi.channel);  &lt;br /&gt;
					else 					// NOTE ON&lt;br /&gt;
					{&lt;br /&gt;
						if(learn)&lt;br /&gt;
						{&lt;br /&gt;
							config.noteOffset=midi.byte1;&lt;br /&gt;
							setEeWrite();&lt;br /&gt;
						}&lt;br /&gt;
						&lt;br /&gt;
						note_on(midi.byte1, byte,midi.channel);  // ... and do what the note on has told us &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				else						// first data byte &lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;		// save it &lt;br /&gt;
					midi.msgByteCnt = 1;    // set counter to get 2nd data byte&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_OFF): 	        	// command note off&lt;br /&gt;
				if (midi.msgByteCnt)	 	// if complete &lt;br /&gt;
				{ 					 	&lt;br /&gt;
					midi.msgByteCnt = 0;	&lt;br /&gt;
					note_off(midi.byte1,byte,midi.channel); // switch note off&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1; 	&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (PITCH):		        	// PITCH BEND command&lt;br /&gt;
				if (midi.msgByteCnt)   	 	// if second data byte (MSB) &lt;br /&gt;
				{ 						&lt;br /&gt;
					int v;&lt;br /&gt;
					v=byte;&lt;br /&gt;
					v&amp;lt;&amp;lt;=6;&lt;br /&gt;
					v|=midi.byte1;&lt;br /&gt;
					v-=0x2000;&lt;br /&gt;
					setPb(v,midi.channel);&lt;br /&gt;
&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (CC):	             		// Continuous Controller Message &lt;br /&gt;
				if (midi.msgByteCnt)	&lt;br /&gt;
				{&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
&lt;br /&gt;
					if(learn)&lt;br /&gt;
					{&lt;br /&gt;
						config.cvMidiCC[3]=midi.byte1;&lt;br /&gt;
						setEeWrite();&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
					setCC(midi.byte1, byte,midi.channel);&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
				  midi.byte1 = byte;&lt;br /&gt;
				  midi.msgByteCnt = 1;&lt;br /&gt;
				  return; &lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
	//		case (CHAN_PRES):	         	// if channel pressure command&lt;br /&gt;
	//			channel_pressure (byte); 	// set Aftertouch &lt;br /&gt;
			case SYSEX:						// SYS-EX command &lt;br /&gt;
				midi.msgByteCnt++;			// message Byte counter&lt;br /&gt;
				switch(midi.msgByteCnt)		// which byte is on? &lt;br /&gt;
				{&lt;br /&gt;
					case 1:					// Manufactorer &lt;br /&gt;
						if(byte !=0x70)		// if not the right one &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error / wrong byte &lt;br /&gt;
						break; &lt;br /&gt;
					case 2:					// Unit - ID &lt;br /&gt;
						if(byte!=0x7D)&lt;br /&gt;
							midi.msgByteCnt=0;&lt;br /&gt;
						break; &lt;br /&gt;
					case 3:					// Midi-Adress (or Unit ID) (ignored!)&lt;br /&gt;
						if(byte&amp;gt;=0x10)		// if not in legal range &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error &lt;br /&gt;
						break;&lt;br /&gt;
					case 4:					// adress byte&lt;br /&gt;
						midi.syxAdr=byte;	// store it for later&lt;br /&gt;
						break;&lt;br /&gt;
					case 5:					// high data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1=(byte&amp;lt;&amp;lt;4);	// set half the data &lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					case 6: 				// low data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1 |= byte;	// or together the complete data byte &lt;br /&gt;
						else 					// evaluation is  done in the code for SYSEND above! &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					default:				// not our framing &lt;br /&gt;
						midi.msgByteCnt=0;&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
				if(midi.msgByteCnt==0)	// something did not fit &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				break;&lt;br /&gt;
			case 0: // bnwtko (bytes no one wants to know off ) &lt;br /&gt;
				return ;&lt;br /&gt;
			default:&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7283</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7283"/>
		<updated>2011-06-23T19:16:25Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Software */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 || Tonhöhe (Noten) || Gate 1 || Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen. und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V. &lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie dreimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen. &lt;br /&gt;
(Wo das jetzt hier zum dritten mal leicht unterschiedlich steht: die finstere Wahrheit ist, das jedes mal die komplette Konfiguration gespeichert wird. ) &lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876, er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
Mono (default). 1 Kanal Monophon. &lt;br /&gt;
&lt;br /&gt;
Multi 2. 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
Multi 4. 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
Die weiteren Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
Im DAC-Feld kann für jede CV (=jeden Steuerspannungs-Ausgang) gewählt werden, wie er gesteuert wird. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet im Tonhöhen-Modus die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser.  &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus werden die Ccs auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Die Gates 1-4 sind den Tonhöhenkanälen fest zugeordnet, gibt ein DAC eine Tonhöhe aus, ist das zugehörige Gate auch Note an/aus. &lt;br /&gt;
&lt;br /&gt;
Ist der DAC auf CC gestellt, kann der Gate-Ausgang einem Controller zugewiesen werden, das Gate ist an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note. (6=16tel, 12=8tel, 24=1/4) &lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop.&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Datei-Konverter, der eine mit der [http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725 Entwicklungsumgebung] erzeugte Binär-Datei ([http://de.wikipedia.org/wiki/Intel_HEX Intel .hex Format]) in ein Sys-Ex File für ein Software-Update über Midi umwandelt, findet sich hier: [[MCV876_Controller_Update:_File_Converter]]&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7282</id>
		<title>MCV876 Controller Update: File Converter</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_File_Converter&amp;diff=7282"/>
		<updated>2011-06-23T19:12:04Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Created page with &amp;#039;= Worum geht&amp;#039;s? =  Der File Konverter ist ein kleines Programm, dass die aus dem Compiler/Linker stammende Programm-Datei (.hex) in ein Datenformat wandelt, dass per Midi übertr…&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Worum geht&#039;s? = &lt;br /&gt;
Der File Konverter ist ein kleines Programm, dass die aus dem Compiler/Linker stammende Programm-Datei (.hex) in ein Datenformat wandelt, dass per Midi übertragen werden kann und das der Controller so versteht, dass er sich selber updaten kann. &lt;br /&gt;
== Datenformat == &lt;br /&gt;
Die Programm-Daten werden in 64-Byte Nutzdatenhäppchen eingepackt. Diese Pakete sind Brutto 84 Bytes lang. &lt;br /&gt;
Die Daten müssen für Midi umkodiert werden, da Midi nur 7 Bit Nutz-Daten übertragen kann. Dazu werden einfach immer die nächsten 7 bit aus dem 8-Bit breiten Datenstrom entnommen. Das gibt völlig undurchsichtige Werte, geht erst nach 7x8=56 Bytes auf und ist ansonsten recht einfacher Code, der kompakte Daten erzeugt. Um Datenübertragungsfehler zu erkennen, bekommt jedes Paket eine 16 Bit CRC als Prüfsumme. Und damit es als Midi durchgeht, kommen noch ein paar Bytes (&amp;quot;hallo hier kommt ein SysEx&amp;quot;) davor und ein Ende Byte dahinter. &lt;br /&gt;
&lt;br /&gt;
Die 64 Byte sind eine für Midi, Sys-Ex und das Controller-RAM noch verträgliche Datensatzlänge. Und der Controller schreibt seinen Programm-Speicher in genau solchen 64 Byte Blöcken. &lt;br /&gt;
&lt;br /&gt;
Vor die eigentlich Progamm-Daten werden noch zwei Pakete vorangestellt: &lt;br /&gt;
&lt;br /&gt;
Das erste schaltet einen in normalem Midi-Betrieb laufenden Controller auf den Boot-Lader um. Nach dem eigentlichen, kurzen Kommando, das den Normalbetrieb beendet, folgen 70 Nullen, die dem Controller mindestens 22ms Luft verschaffen, um auf den Bootlader zu wechseln. (Ist der Controller bereits im Bootlader wird dieses Paket nebst den Nullen schlichtweg überlesen/ignoriert. ) &lt;br /&gt;
&lt;br /&gt;
Das zweite Paket enthält nur die Info, dass es a)das erste Update-Pakte ist (d.h. es setzt den Empfänger auf &amp;quot;jetzt geht es los&amp;quot; zurück), und b)die Zahl der folgenden Blöcke (damit Vollständigkeit geprüft werden kann). &lt;br /&gt;
&lt;br /&gt;
Dann kommen die eigentlichen Programmdaten. &lt;br /&gt;
&lt;br /&gt;
= Quelltext =&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
	Programm zum Erzeugen von Midi-Sys-Ex Software Update Files aus einer HEX-Datei &lt;br /&gt;
	für das MCV-876 Midi Interface mit ATMEL ATMEGA 88 Controller. &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
/*	&lt;br /&gt;
	Aufruf: &lt;br /&gt;
	sys676 avr-studio-compiler-out.hex &lt;br /&gt;
	&lt;br /&gt;
	das erzeugt dann eine Datei &lt;br /&gt;
	sys676 avr-studio-comipler-out.syx&lt;br /&gt;
	&lt;br /&gt;
	die mit Midi-OX (oder einen andern &amp;quot;ich kann Midi senden&amp;quot; Programm) an das &lt;br /&gt;
	Interface übertragen wird. &lt;br /&gt;
	&lt;br /&gt;
	Unter Windows kann man das übrigens bequem per &amp;quot;Drag and Drop&amp;quot; bedienen, einfach die &lt;br /&gt;
	.hex Datei auf die sys876.exe draufziehen, das Ergebnis taucht dann im gleichen Order wie &lt;br /&gt;
	die .hex Datei auf. (Man sieht halt keine Fehlermeldungen... ) &lt;br /&gt;
&lt;br /&gt;
	Erfolgreich übersetzt mit Visual Studio .net 2003 auf Windows XP &lt;br /&gt;
	Leeres Konsolenprojekt Namens syx876 erzeugen, diesen Quelltext in das .cpp &lt;br /&gt;
	File kopieren und bauen lassen. Ggfls. auf &amp;quot;Release&amp;quot; umstellen, im Release Ordner &lt;br /&gt;
	findet sich dann die fertige Applikation. &lt;br /&gt;
	&lt;br /&gt;
	Sollte sich nach marginalen Änderungen allerdings mit jedem Compiler übersetzen lassen. &lt;br /&gt;
	&lt;br /&gt;
*/&lt;br /&gt;
// für Windows =======================&lt;br /&gt;
#include &amp;quot;stdafx.h&amp;quot;&lt;br /&gt;
#define main _tmain &lt;br /&gt;
&lt;br /&gt;
// für normale Compiler =============&lt;br /&gt;
/*&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
*/&lt;br /&gt;
//===================================&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define VERSION &amp;quot;1.00&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define MAX_DATA 8192				// netto data size (ATMEGA 88 Flash Rom size) &lt;br /&gt;
#define MAX_SYX_DATA (2*MAX_DATA)	// max. complete syx-ex size&lt;br /&gt;
#define MAX_SYX_BLOCK (2*64)		// one Sys-Ex Block of 64 bytes netto data &lt;br /&gt;
&lt;br /&gt;
unsigned char data[MAX_DATA];	// holds binary program data &lt;br /&gt;
&lt;br /&gt;
// Convert one HEX-ASCII character to its numerical value, or -1 if illegal value &lt;br /&gt;
int conf( char c)&lt;br /&gt;
{&lt;br /&gt;
	switch (c)&lt;br /&gt;
	{&lt;br /&gt;
		case &#039;0&#039;: return 0;&lt;br /&gt;
		case &#039;1&#039;: return 1;&lt;br /&gt;
		case &#039;2&#039;: return 2;&lt;br /&gt;
		case &#039;3&#039;: return 3;&lt;br /&gt;
		case &#039;4&#039;: return 4;&lt;br /&gt;
		case &#039;5&#039;: return 5;&lt;br /&gt;
		case &#039;6&#039;: return 6;&lt;br /&gt;
		case &#039;7&#039;: return 7;&lt;br /&gt;
		case &#039;8&#039;: return 8;&lt;br /&gt;
		case &#039;9&#039;: return 9;&lt;br /&gt;
		case &#039;a&#039;: case &#039;A&#039;: return 10;&lt;br /&gt;
		case &#039;b&#039;: case &#039;B&#039;: return 11;&lt;br /&gt;
		case &#039;c&#039;: case &#039;C&#039;: return 12;&lt;br /&gt;
		case &#039;d&#039;: case &#039;D&#039;: return 13;&lt;br /&gt;
		case &#039;e&#039;: case &#039;E&#039;: return 14;&lt;br /&gt;
		case &#039;f&#039;: case &#039;F&#039;: return 15;&lt;br /&gt;
	}&lt;br /&gt;
	return (-1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate 16 bit crc from previous crc and new data byte, initial (first) crc should be 0xffff, returns new crc &lt;br /&gt;
unsigned int crc16(unsigned int crc, unsigned char byte)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	crc^=byte;&lt;br /&gt;
	for(i=0;i&amp;lt;8;++i)&lt;br /&gt;
	{&lt;br /&gt;
	    if (crc&amp;amp;1)&lt;br /&gt;
			crc=(crc&amp;gt;&amp;gt;1)^0xA001;&lt;br /&gt;
		else&lt;br /&gt;
			crc&amp;gt;&amp;gt;=1;&lt;br /&gt;
	}&lt;br /&gt;
	return crc &amp;amp; 0xffff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// generates first Sys-EX Block, this switches the interface from operation mode to boot-load mode&lt;br /&gt;
int makePrgPrep(unsigned char *str)&lt;br /&gt;
{&lt;br /&gt;
	int i; &lt;br /&gt;
	&lt;br /&gt;
	unsigned char a[] = { 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7 }; // the switch command string (see makeSysEx for byte value explanation) &lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;sizeof(a);i++)	&lt;br /&gt;
		*str++=a[i];&lt;br /&gt;
	for(;i&amp;lt;70;i++)	// 70 bytes * 0,32ms for midi data transmission gives the interface the time to start the bootloader &lt;br /&gt;
	{&lt;br /&gt;
		*str++=0;&lt;br /&gt;
	}&lt;br /&gt;
	return i; &lt;br /&gt;
}&lt;br /&gt;
// make a sys-ex data block with proper midi framing and crc from binary data in &amp;quot;block&amp;quot; &lt;br /&gt;
int makeSysEx(unsigned char *block, int len, unsigned char *syxTarget)&lt;br /&gt;
{&lt;br /&gt;
	int crc=0xffff;&lt;br /&gt;
	unsigned int out=0;&lt;br /&gt;
	int bits=0;&lt;br /&gt;
	int i;&lt;br /&gt;
	unsigned char c;  &lt;br /&gt;
	&lt;br /&gt;
	unsigned char *syxStr=syxTarget;&lt;br /&gt;
&lt;br /&gt;
	// make CRC &lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];&lt;br /&gt;
		crc=crc16(crc, c);&lt;br /&gt;
	}&lt;br /&gt;
	block[len++]= ( crc)&amp;amp;0xff;&lt;br /&gt;
	block[len++]= (crc &amp;gt;&amp;gt;8 ) &amp;amp;0xff;&lt;br /&gt;
	&lt;br /&gt;
	*syxStr++ = (0xF0);	// start sys ex &lt;br /&gt;
	*syxStr++ = (0x70);	// manufacturer id &lt;br /&gt;
	*syxStr++ = (0x7D);	// unit type  &lt;br /&gt;
	*syxStr++ = (0x00); // unit id &lt;br /&gt;
	*syxStr++ = (0x15);	// for normal sysEx it is a adress, here magic byte 1 &lt;br /&gt;
	*syxStr++ = (0x55); // magic byte 2 &lt;br /&gt;
		&lt;br /&gt;
	for(i=0;i&amp;lt;len;i++)	&lt;br /&gt;
	{&lt;br /&gt;
		c=block[i];	// data byte 	&lt;br /&gt;
		/*&lt;br /&gt;
			Midi data is 7 bit only, as MSB is command/data bit. &lt;br /&gt;
			So data must be converted to 7 bit. &lt;br /&gt;
			Each source data byte gives 8/7 target bytes, &lt;br /&gt;
			as soon as there is one complete byte more (which happens every 7 input bytes)&lt;br /&gt;
			two target bytes will be generated.&lt;br /&gt;
		*/&lt;br /&gt;
		out&amp;lt;&amp;lt;=8;	&lt;br /&gt;
		out|=c;&lt;br /&gt;
		bits+=8;&lt;br /&gt;
		while(bits&amp;gt;=7)&lt;br /&gt;
		{&lt;br /&gt;
			*syxStr++ = ((out&amp;gt;&amp;gt;(bits-7)) &amp;amp; 0x7f);&lt;br /&gt;
			bits-=7;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if(bits)	// don&#039;t forget about the last bits of the last data byte &lt;br /&gt;
	{&lt;br /&gt;
		out&amp;lt;&amp;lt;= 7-bits;  // this will pad last byte with up to 6 zero bits. &lt;br /&gt;
		*syxStr++ =  (out &amp;amp; 0x7f);&lt;br /&gt;
	}&lt;br /&gt;
	*syxStr++ = 0xF7;	// Sys-Ex end &lt;br /&gt;
&lt;br /&gt;
	return (int) (syxStr - syxTarget); // lenght of generated data &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// argv[1] is filename to be converted &lt;br /&gt;
int main(int argc, _TCHAR* argv[])&lt;br /&gt;
{&lt;br /&gt;
	FILE *fp; &lt;br /&gt;
	int minAdr=0xffff;&lt;br /&gt;
	int maxAdr=0;&lt;br /&gt;
	int chars=0;&lt;br /&gt;
	int error=0;&lt;br /&gt;
	int c; &lt;br /&gt;
	int val;&lt;br /&gt;
	int field=-1;&lt;br /&gt;
	int adress=0;&lt;br /&gt;
	int cnt; &lt;br /&gt;
	int sum=0; &lt;br /&gt;
	int field_len=0; &lt;br /&gt;
	int type=99;&lt;br /&gt;
	int line=0;&lt;br /&gt;
&lt;br /&gt;
	// the controller allways flashes 64 byte blocks, so the last block is padded with arbitrary data &lt;br /&gt;
	int padit[]= {110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
              110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43,43,43,32,&lt;br /&gt;
			  110,111,114,100,99,111,114,101,32,119,97,115,32,104,101,114,101,32,43 } ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	printf(&amp;quot;syx876 Version &amp;quot; VERSION &amp;quot;\nHex-File to Sys-Ex Converter for ATMega88 on MCV876\n&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
	// &#039;check&#039; for proper input filename (it must have a &amp;quot;three byte extension&amp;quot; )&lt;br /&gt;
	//  *not* fool proof, try &amp;quot;/../a&amp;quot; and you&#039;ll get interesting results&lt;br /&gt;
	size_t l=strlen(argv[1]);&lt;br /&gt;
&lt;br /&gt;
	if(argc !=2 || l&amp;lt;5 || *(argv[1]+l-4)!=&#039;.&#039; )&lt;br /&gt;
	{&lt;br /&gt;
		printf(&amp;quot;\nUsage: syx876 filename.hex\n&amp;quot;);&lt;br /&gt;
		return -1;&lt;br /&gt;
	}&lt;br /&gt;
	l -= 3; &lt;br /&gt;
	&lt;br /&gt;
	fp=fopen (argv[1],&amp;quot;rb&amp;quot;);	// open input files &lt;br /&gt;
	&lt;br /&gt;
	if(fp)&lt;br /&gt;
	{&lt;br /&gt;
		// make output file name &lt;br /&gt;
		*(argv[1]+l)=0;	&lt;br /&gt;
		strcat (argv[1],&amp;quot;syx&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		while( (c=fgetc(fp)) != EOF  ) // read input hex file &lt;br /&gt;
		{&lt;br /&gt;
			chars++;&lt;br /&gt;
			 &lt;br /&gt;
			if(c==&#039;:&#039;)&lt;br /&gt;
			{&lt;br /&gt;
				cnt=0;&lt;br /&gt;
				field=0;&lt;br /&gt;
				sum=0;&lt;br /&gt;
				line++;&lt;br /&gt;
			}&lt;br /&gt;
			else if ( conf( (unsigned char) c ) &amp;gt;=0 )&lt;br /&gt;
			{&lt;br /&gt;
				if(cnt==0)&lt;br /&gt;
					val=conf( (unsigned char) c );&lt;br /&gt;
				else &lt;br /&gt;
				{&lt;br /&gt;
					val&amp;lt;&amp;lt;=4;&lt;br /&gt;
					val += conf( (unsigned char) c );&lt;br /&gt;
				}&lt;br /&gt;
				cnt++;&lt;br /&gt;
				&lt;br /&gt;
				if(cnt==2)	// add up checksum&lt;br /&gt;
					sum+= val;&lt;br /&gt;
				if( cnt==4)&lt;br /&gt;
					sum += val &amp;amp; 0xff;&lt;br /&gt;
				&lt;br /&gt;
				if(field ==0 &amp;amp;&amp;amp; cnt==2)&lt;br /&gt;
				{	// byte count &lt;br /&gt;
					field_len=val; &lt;br /&gt;
					field =1;&lt;br /&gt;
					cnt=0;				&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==1 &amp;amp;&amp;amp; cnt == 4)&lt;br /&gt;
				{	// adress&lt;br /&gt;
					adress=val;&lt;br /&gt;
					field=2;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if( field ==2 &amp;amp;&amp;amp; cnt == 2)&lt;br /&gt;
				{	// type &lt;br /&gt;
					type=val;&lt;br /&gt;
					field=4;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len&amp;gt;0)&lt;br /&gt;
				{	// data &lt;br /&gt;
					field_len--;&lt;br /&gt;
					&lt;br /&gt;
					if(adress &amp;lt; minAdr)&lt;br /&gt;
						minAdr=adress;&lt;br /&gt;
					if(adress&amp;gt;maxAdr)&lt;br /&gt;
						maxAdr=adress;&lt;br /&gt;
&lt;br /&gt;
					if(type==0 &amp;amp;&amp;amp; adress &amp;lt; MAX_DATA)&lt;br /&gt;
						data[adress]=val;&lt;br /&gt;
					adress++;&lt;br /&gt;
					cnt=0;&lt;br /&gt;
				}&lt;br /&gt;
				else if ( field ==4 &amp;amp;&amp;amp; cnt ==2 &amp;amp;&amp;amp; field_len == 0)&lt;br /&gt;
				{	// checksum &lt;br /&gt;
					cnt=0;&lt;br /&gt;
					sum &amp;amp;=0xff;&lt;br /&gt;
					if(sum)&lt;br /&gt;
					{&lt;br /&gt;
						error++;&lt;br /&gt;
						printf(&amp;quot;Line %d Checksum error (sum=%d)\n&amp;quot;,line, sum); &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else if( c !=10 &amp;amp;&amp;amp; c !=13)&lt;br /&gt;
			{&lt;br /&gt;
				error ++;&lt;br /&gt;
				printf( &amp;quot;Line %d unexpected character (%c)\n&amp;quot;,line, c); // characters I don&#039;t know  &lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		printf( &amp;quot;\nread %d charactes, from adress %d to %d with %d errors\n&amp;quot;, chars, minAdr, maxAdr,error); &lt;br /&gt;
		&lt;br /&gt;
		// generate output data &lt;br /&gt;
		if(error==0 &amp;amp;&amp;amp; minAdr==0) &lt;br /&gt;
		{&lt;br /&gt;
			int blocks; &lt;br /&gt;
			int blockNr; &lt;br /&gt;
			unsigned char block[MAX_SYX_BLOCK];	// buffer to collect/prepare one binary data block &lt;br /&gt;
			unsigned char stp[MAX_SYX_DATA]; 	//  output data buffer, collects the sysEx data&lt;br /&gt;
			unsigned char *str;		// current write pointer &lt;br /&gt;
			int adr; &lt;br /&gt;
			int padded;&lt;br /&gt;
			FILE *wp;&lt;br /&gt;
			int i;&lt;br /&gt;
			&lt;br /&gt;
			str=stp; &lt;br /&gt;
			&lt;br /&gt;
			blocks= (((maxAdr)/64)+1);	// total number of blocks &lt;br /&gt;
						&lt;br /&gt;
			str+= makePrgPrep(str);		// generate sys-ex header (=switch to bootloader command) &lt;br /&gt;
			&lt;br /&gt;
			// first block is a spezial block - it tells controller number of blocks he has to expect. &lt;br /&gt;
			blockNr=0;&lt;br /&gt;
			block[0]=blockNr;&lt;br /&gt;
			block[1]=blocks;&lt;br /&gt;
			block[2]=0;	// reserved &lt;br /&gt;
			block[3]=0;	// reserved &lt;br /&gt;
			str += makeSysEx(block,4,str);	// generate SysEx from block data &lt;br /&gt;
			&lt;br /&gt;
			adr=minAdr;&lt;br /&gt;
			// make flash data blocks &lt;br /&gt;
			// flash data blocks are 65 bytes, 1 Byte Block number, 64 data bytes&lt;br /&gt;
			// the controller allways writes 64 byte blocks to flash, so this format &lt;br /&gt;
			// helps to keep the controller program straight. &lt;br /&gt;
			while (blockNr&amp;lt;blocks)&lt;br /&gt;
			{&lt;br /&gt;
				blockNr++;&lt;br /&gt;
				block[0]=blockNr;	// first data byte in block is block number (so controller sees, when a block is lost) &lt;br /&gt;
				padded=0; &lt;br /&gt;
				for(i=0;i&amp;lt;64;i++)&lt;br /&gt;
				{&lt;br /&gt;
					if(adr&amp;gt;maxAdr)	 // it beyond highest adresses from hex file:  last block is(must be) padded to 64 bytes &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=padit[padded];&lt;br /&gt;
						padded++;&lt;br /&gt;
					}&lt;br /&gt;
					else	// write programm data &lt;br /&gt;
					{&lt;br /&gt;
						block[i+1]=data[adr++];					&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				str +=makeSysEx(block,65,str);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			// write output file &lt;br /&gt;
			wp=fopen(argv[1],&amp;quot;wb&amp;quot;);&lt;br /&gt;
			if(wp)&lt;br /&gt;
			{&lt;br /&gt;
				fwrite ((void *) stp ,sizeof(unsigned char), str-stp, wp);&lt;br /&gt;
				fclose(wp);&lt;br /&gt;
				&lt;br /&gt;
				printf(&amp;quot;%d sysEx data blocks in %d bytes written\n&amp;quot;,blocks, str-stp);&lt;br /&gt;
			}&lt;br /&gt;
			else &lt;br /&gt;
				printf(&amp;quot;Cant open output file %s\n&amp;quot;,argv[1]);&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			if(minAdr)&lt;br /&gt;
				printf(&amp;quot;Lowest adress %d is not zero!\n&amp;quot;,minAdr);&lt;br /&gt;
			return -2;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
		printf (&amp;quot;Input file %s not found\n&amp;quot;,argv[1]);&lt;br /&gt;
&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7280</id>
		<title>MCV876 Controller Update: Source Code</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7280"/>
		<updated>2011-06-23T07:27:24Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Quelltext */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Übersetzen= &lt;br /&gt;
Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden.&lt;br /&gt;
Im Studio ein neues, leeres Projekt mit Standard-Einstellungen, einem sinnvollen Namen und an einem Ort, wo man es wiederfindet erstellen, Quelltext in das (automatisch erzeugte, leere) C-File kopieren, auf Build-&amp;gt;Build klicken, fertig. &lt;br /&gt;
Das resultierende HEX File kann man mit einem üblichen Programmieradapter in der Chip flashen, für uns zweckmäßiger ist allerdings, das in ein Sys-Ex File zu Konvertieren [-&amp;gt;Link auf Konverter folgt...], und das an den Controller zu schicken. Dann programmiert der sich selber um. Dazu muss er allerdings einmalig mit dem Bootlader [hier Link auf Bootlader imaginieren.. ] programmiert worden sein. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu den Atmel Controllern findet man eine Menge Info mit Tante Google, [http://www.mikrocontroller.net http://www.mikrocontroller.net] kommt häufig vor, und hat ein ergiebiges Forum und gute Artikel. Auf der Atmel Webseite gibt es auch einige Info, das [http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf ATMega 88 Datenblatt] ist spätestens dann nötig, wenn es um die Hardware geht. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Quelltext=&lt;br /&gt;
&amp;lt;pre&amp;gt;/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Midi to 4 Channel Control Voltage and six Gates Interface &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.20 2011-06-23 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
			&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;			// yes, we want to talk to the port pins by name&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;		// ... oh.. and some eeprom access would be nice&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;	// ... and w/o the interupts it wouldn&#039;t do anything... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*	===================ZERO LINE=================================&lt;br /&gt;
	WARNING: everthing below the zero line counts from zero! &lt;br /&gt;
	e.g. whats Gate 6 in manual is GATE5 here, CVs are CV0 to 3 &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	where should the LED output be &lt;br /&gt;
*/&lt;br /&gt;
#define LEDLED   6  // LED at Reset-Pin, standard for series, but no debugging possible. &lt;br /&gt;
#define LEDGATE0 0	// Led at Gate 0, no LED (Test I: no LED ) &lt;br /&gt;
#define LEDGATE4 4	// LED at Gate 5, Test II: no Gate 5 Function (Midi Start/Stop) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define LEDOUT LEDGATE4 // select your evil... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define ACTIVE_SENSE_TICKS 160 // 2ms Timer Ticks for active sense time out &lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4))) // True if Button pressed &lt;br /&gt;
&lt;br /&gt;
#define NUM_CV 4&lt;br /&gt;
&lt;br /&gt;
#define PARAMETEROFFSET 0x23 // First paramter has adress 0x23&lt;br /&gt;
#define NUM_PARAMTERS 17	 // and there are 17 parameters &lt;br /&gt;
&lt;br /&gt;
#define MODE_MONO 	0x00	// monophon, all assigned to last note on a single channel &lt;br /&gt;
#define MODE_MULTI2	0x40	// two channel, 1/2 for midiChannel, 2/3 for midiChannel+1 &lt;br /&gt;
#define MODE_MULTI4	0x60	// for channel midiChannel to midiChannel+3 &lt;br /&gt;
#define MODE_POLY2	0x80	// not supported &lt;br /&gt;
#define MODE_POLY4	0xA0	// not supported&lt;br /&gt;
&lt;br /&gt;
struct CONFIG {&lt;br /&gt;
	unsigned char midiChannel;&lt;br /&gt;
	unsigned char noteOffset;&lt;br /&gt;
&lt;br /&gt;
	unsigned char cvMidiCC[NUM_CV];&lt;br /&gt;
	unsigned char gateMidiCC[NUM_CV];&lt;br /&gt;
	&lt;br /&gt;
	unsigned char gateTreshhold;&lt;br /&gt;
	unsigned char clockDivider;&lt;br /&gt;
	unsigned char clockTime; &lt;br /&gt;
	unsigned char ifMode0; 	// 	bit 0 ... bit 3 = CV0 .. CV3, 0=Pitch, 1=CC (if pitch, CC assignement is ignored) &lt;br /&gt;
							//	bit 5: voices: 0=2; 1=4 &lt;br /&gt;
							//  bit 7:6= 00 Mono; 01:Multi-Channel; 10: Poly (not supported); &lt;br /&gt;
	&lt;br /&gt;
	unsigned char ifMode1;  // Gate Trigger Mode Bits (0=Re-Trigger, 1=TriggerControl)  &lt;br /&gt;
	unsigned char ifMode2;  // bit0: Velo0=&amp;gt;CV0; bit1:Velo0=&amp;gt;CV1; bit2:Velo-Poly2=&amp;gt;CV2; bit3:Velo1=&amp;gt;CV3&lt;br /&gt;
							// bit4: PB=&amp;gt;CV0; bit5:PB=&amp;gt;CV1; bit6:PB=&amp;gt;CV2; bit7:PB=&amp;gt;CV3&lt;br /&gt;
	unsigned char ifMode3; 	// bit0..bit3 = CV0... CV3; 0=10V, 1=5V Mode &lt;br /&gt;
&lt;br /&gt;
//  internal evaluated configuration &lt;br /&gt;
	unsigned char numMidiChannels;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
} shadow_config, // her goes a copy, when config should be written, because that might take a while &lt;br /&gt;
&lt;br /&gt;
config 	// current configuration, mostly direct evaluated troughout the program&lt;br /&gt;
= {		// default values (Mono-Mode, Midi-Ch 1, CV1=Pitch, CV2=Velocity CV3=ModWheel CV4=Pitch Bend&lt;br /&gt;
			0,  	// Midi Channel &lt;br /&gt;
			24, 	// Note Offset (2 Oktaves) &lt;br /&gt;
		{	255,255,1,255, },  	// CV - assigned Midi Controllers (used if not pitch output) 255=none&lt;br /&gt;
		{   255,18,19,20, }, 	// Gate - asigned Controllers (if not pitch) 255=none &lt;br /&gt;
			63, 	// Treshold&lt;br /&gt;
			6,  	// Divider 6 (1/16 Note) &lt;br /&gt;
			1,  	// clock time (unused) &lt;br /&gt;
			0x0e, 	// mode 1 Low nibble CC-Bits, high: interface mode &lt;br /&gt;
			0x82, 	// mode 2 High Nibble: PB-bits, Low: Velo-Bits&lt;br /&gt;
			0,    	// mode 3 (5V-Bits) &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define EE_MAGIC 17&lt;br /&gt;
unsigned char ee_magic EEMEM;	// to check it eeprom was ever written and should be read back &lt;br /&gt;
unsigned char config_eeprom[NUM_PARAMTERS] EEMEM; // Config in EEPROM, saves it during power down. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// to have the clock synced to &amp;quot;beat 1&amp;quot; of a bar ... &lt;br /&gt;
volatile unsigned int spp; 	// song position pointer (from midi message) &lt;br /&gt;
unsigned char ticksPerBar=96; // How many ticks per bar (is not tx-ed: guess 96 for 4/4 ...  ) &lt;br /&gt;
volatile unsigned char barTick; // where are we inside the bar (counted, because calculation from spp takes some cpu-cycles ) &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char  eepromWriteFlag;	// set to one, if main-loop should write eeprom &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char button; 	// debounced button status &lt;br /&gt;
volatile unsigned char learn; 	// (channel/base note/cc) &amp;quot;do learn&amp;quot; status &lt;br /&gt;
volatile unsigned char blinker; // some count for blinkenlights &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char clocker;	 // timer counter for half a midi-clock (which is needed for divider 1)&lt;br /&gt;
volatile unsigned char gate5off; // timer counter for when clock gate output should go low again. &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char watchTicker;	// midi active sense counter &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setConfig(void); 		&lt;br /&gt;
void setLed(unsigned char on);&lt;br /&gt;
void resetAll(void);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	main() &lt;br /&gt;
	Main does initialization, than runs a loop, which does nearly nothing: &lt;br /&gt;
&lt;br /&gt;
	Programming the EEPROM (because its slow) &lt;br /&gt;
	Blinking &lt;br /&gt;
	Button evaluation &lt;br /&gt;
&lt;br /&gt;
	All the important things are done by the interupts. (Thanks to the &lt;br /&gt;
	20MHZ RISC power everything can be done in less than 320µs=one Midi-Character) &lt;br /&gt;
*/&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char blink=6;&lt;br /&gt;
&lt;br /&gt;
	unsigned char i,x; &lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
	static unsigned char lastButton; &lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
	// Read Configuration &lt;br /&gt;
	if( eeprom_read_byte(ee_magic) == EE_MAGIC  &amp;amp;&amp;amp; !BUTTON) // if eeprom valid AND Button not pressed &lt;br /&gt;
	{&lt;br /&gt;
		cp=(unsigned char* )&amp;amp;config; &lt;br /&gt;
		for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
		{&lt;br /&gt;
			x=eeprom_read_byte(config_eeprom+i);			// read config from eeprom &lt;br /&gt;
			*cp=x;	&lt;br /&gt;
			cp++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	setConfig();&lt;br /&gt;
&lt;br /&gt;
	// enable interrupts&lt;br /&gt;
	sei(); &lt;br /&gt;
&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		if(eepromWriteFlag)&lt;br /&gt;
		{	// save configuration &lt;br /&gt;
			cp=(unsigned char* )&amp;amp;shadow_config; &lt;br /&gt;
			for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
			{&lt;br /&gt;
				x=*cp;&lt;br /&gt;
				eeprom_write_byte(config_eeprom+i,x);&lt;br /&gt;
				cp++;&lt;br /&gt;
			}&lt;br /&gt;
			eeprom_write_byte(ee_magic,EE_MAGIC); &lt;br /&gt;
&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			blink=6;&lt;br /&gt;
			blinker=0;&lt;br /&gt;
			eepromWriteFlag=0;	&lt;br /&gt;
		}&lt;br /&gt;
		if(blink &amp;amp;&amp;amp; blinker&amp;gt;2)  // blink after power up and after storing in nv-memory &lt;br /&gt;
		{&lt;br /&gt;
			blink--;&lt;br /&gt;
			setLed(blink&amp;amp;1);&lt;br /&gt;
			blinker=0; &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if(button != lastButton)&lt;br /&gt;
		{&lt;br /&gt;
			if(button==1 )&lt;br /&gt;
			{&lt;br /&gt;
				resetAll();&lt;br /&gt;
				if(learn==1)&lt;br /&gt;
				{&lt;br /&gt;
					learn=0;&lt;br /&gt;
					setLed(0);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if(button==6) &lt;br /&gt;
			{&lt;br /&gt;
				if(learn ==0)&lt;br /&gt;
				{&lt;br /&gt;
					learn=1;&lt;br /&gt;
					setLed(1);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			lastButton=button; &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return 0; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Hardware Interface&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
/*  Pin assignment &lt;br /&gt;
   Atmel Fkt 	[Orig] &lt;br /&gt;
1  Reset LED 	[MCLR] &lt;br /&gt;
2  RX    Midi-Rx[WR1]&lt;br /&gt;
3  PD1   WR2&lt;br /&gt;
4  PD2   WR3&lt;br /&gt;
5  PD3   WR4&lt;br /&gt;
6  PD4   Button&lt;br /&gt;
7  VCC   +5  	[LED] (Either from Atmel-1(Series) or Atmel-15(w/Debug))&lt;br /&gt;
8  GND   GND&lt;br /&gt;
9  OSC   OSC&lt;br /&gt;
10 OSC   OSC &lt;br /&gt;
11 PD5   G1&lt;br /&gt;
12 PD6   G2&lt;br /&gt;
13 PD7   G3&lt;br /&gt;
14 PB0   G4&lt;br /&gt;
&lt;br /&gt;
15 PB1   G5 (Optional + LED) &lt;br /&gt;
16 PB2   G6 &lt;br /&gt;
17 PB3   WR1    [---] &lt;br /&gt;
18 PB4   D0     [RX]&lt;br /&gt;
19 PB5   D1		[GND]&lt;br /&gt;
20 AVCC  +5&lt;br /&gt;
21 ARef  +5 	[D0]&lt;br /&gt;
22 GND   GND	[D1] &lt;br /&gt;
23 PC0   D2&lt;br /&gt;
24 PC1   D3&lt;br /&gt;
25 PC2   D4&lt;br /&gt;
26 PC3   D5&lt;br /&gt;
27 PC4   D6&lt;br /&gt;
28 PC5   D7&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
// put &amp;quot;val&amp;quot; to the right port bits for the DA-Converter &lt;br /&gt;
// ... better program than solder *that*... &lt;br /&gt;
void adByteOut(unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char b;&lt;br /&gt;
	b=val;&lt;br /&gt;
	b&amp;gt;&amp;gt;=2;&lt;br /&gt;
	PORTC=b;&lt;br /&gt;
	b=val&amp;amp;3;&lt;br /&gt;
	b&amp;lt;&amp;lt;=4;&lt;br /&gt;
	val=PORTB;&lt;br /&gt;
	val &amp;amp;=0xCF;&lt;br /&gt;
	val|=b;&lt;br /&gt;
	PORTB=val;&lt;br /&gt;
}&lt;br /&gt;
// set DA-Output voltage of one CV Channel  &lt;br /&gt;
void set_cv(unsigned char val, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	adByteOut(val); 	// set D0..D7 converter data bits &lt;br /&gt;
	switch (channel)	// pulse with write pin &lt;br /&gt;
	{&lt;br /&gt;
		case 0: //PB3&lt;br /&gt;
			PORTB  &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTB  |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD1&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;1);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;1);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD2&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;2);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;2);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PD3&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// set on of the gate outputs active &lt;br /&gt;
void gate_on(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// clear one of the gate outputs &lt;br /&gt;
void gate_off(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif&lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Data Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
typedef struct {&lt;br /&gt;
	unsigned char channel;  // (relative) Midi channel (0..3) Mode dependant &lt;br /&gt;
	unsigned char cvOut;	// for which CV-Ouput (0..3) fixed &lt;br /&gt;
	unsigned char ccVal;	// current CC Value &lt;br /&gt;
	unsigned char note;		// current Note &lt;br /&gt;
	unsigned char velo;		// current Velocity &lt;br /&gt;
	unsigned char on;		// if Note is on &lt;br /&gt;
	signed char   pb;		// current Pitch Bend Value &lt;br /&gt;
	unsigned char gateDipper;&lt;br /&gt;
} cvoutput;  &lt;br /&gt;
&lt;br /&gt;
cvoutput cv_out[NUM_CV];	// current state for each cv out&lt;br /&gt;
&lt;br /&gt;
unsigned char gates; // which gates are on =&amp;gt; used for LED setting &lt;br /&gt;
&lt;br /&gt;
// set cv output (and assigned gate, if pitch output)&lt;br /&gt;
// called after anything that changes the cv_out structure  &lt;br /&gt;
void updateCv(cvoutput *cvo)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char bm;&lt;br /&gt;
	unsigned char gated=0;&lt;br /&gt;
	int val=0;&lt;br /&gt;
&lt;br /&gt;
	bm=1&amp;lt;&amp;lt;cvo-&amp;gt;cvOut;	// bit mask for configuration flags &lt;br /&gt;
&lt;br /&gt;
	if(config.ifMode0 &amp;amp; bm)	// 1=CC-Mode&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;ccVal; // &amp;lt;&amp;lt;1;&lt;br /&gt;
/*&lt;br /&gt;
		if( (config.ifMode3 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0)	// 5V-Flag, abused as Gated-Mode-CV Flag &lt;br /&gt;
			gated=1;&lt;br /&gt;
*/&lt;br /&gt;
	}&lt;br /&gt;
	else // Note&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;note;&lt;br /&gt;
		val -= config.noteOffset;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode3 &amp;amp; bm)	// 5V&lt;br /&gt;
			val &amp;lt;&amp;lt;= 2;&lt;br /&gt;
		else&lt;br /&gt;
			val &amp;lt;&amp;lt;= 1;&lt;br /&gt;
	}&lt;br /&gt;
	if(config.ifMode2 &amp;amp; bm) // Velocity &lt;br /&gt;
		val+=cvo-&amp;gt;velo;&lt;br /&gt;
	if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) // PB &lt;br /&gt;
		val+=cvo-&amp;gt;pb;&lt;br /&gt;
&lt;br /&gt;
	if(val&amp;lt;0 || gated)	// clip values to converter range &lt;br /&gt;
		val=0;&lt;br /&gt;
	if(val&amp;gt;255)&lt;br /&gt;
		val=255;&lt;br /&gt;
&lt;br /&gt;
	set_cv((unsigned char)val, cvo-&amp;gt;cvOut);&lt;br /&gt;
	if((config.ifMode0 &amp;amp; bm)==0)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;on)&lt;br /&gt;
		{&lt;br /&gt;
			if(cvo-&amp;gt;gateDipper==0) // no retrigger required&lt;br /&gt;
				gate_on(cvo-&amp;gt;cvOut);&lt;br /&gt;
			else				   // dip gate for a few milliseconds 	&lt;br /&gt;
				gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates |=bm;&lt;br /&gt;
		}&lt;br /&gt;
		else &lt;br /&gt;
		{&lt;br /&gt;
			gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates &amp;amp;=~bm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate/initialize control variables after &lt;br /&gt;
// mode-configuration change &lt;br /&gt;
void setConfig()&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i; &lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;pb=0x80;&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	switch(config.ifMode0 &amp;amp; 0xf0 )&lt;br /&gt;
	{	&lt;br /&gt;
		default:&lt;br /&gt;
		case MODE_MONO:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=1;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI2:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV/2;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			for(;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=1;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=2;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI4:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=i;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=NUM_CV;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// assign new note state to cv outputs &lt;br /&gt;
void setNote(unsigned char note, unsigned char velo, unsigned char onOff, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;channel==channel)&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;note=note;&lt;br /&gt;
			cvo-&amp;gt;velo=velo;&lt;br /&gt;
			if(onOff==2 &amp;amp;&amp;amp; cvo-&amp;gt;on &amp;amp;&amp;amp; (config.ifMode1 &amp;amp; (1&amp;lt;&amp;lt;i)))&lt;br /&gt;
				cvo-&amp;gt;gateDipper=4;&lt;br /&gt;
			cvo-&amp;gt;on=onOff;&lt;br /&gt;
			updateCv(cvo);&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	setLed(gates);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process new continous controller value &lt;br /&gt;
void setCC(unsigned char cc, unsigned char value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel== cvo-&amp;gt;channel )&lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode0 &amp;amp;(1&amp;lt;&amp;lt;i))&lt;br /&gt;
			{&lt;br /&gt;
				if(config.cvMidiCC[i]==cc)&lt;br /&gt;
				{&lt;br /&gt;
					cvo-&amp;gt;ccVal=value; &lt;br /&gt;
					updateCv(cvo);&lt;br /&gt;
				}&lt;br /&gt;
				if(config.gateMidiCC[i]==cc  )&lt;br /&gt;
				{&lt;br /&gt;
					if(value&amp;gt;config.gateTreshhold)&lt;br /&gt;
						gate_on(channel);&lt;br /&gt;
					else &lt;br /&gt;
						gate_off(channel);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// process new pitch bend value &lt;br /&gt;
void setPb(signed int value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel==cvo-&amp;gt;channel &amp;amp;&amp;amp; (config.ifMode2 &amp;amp; (16&amp;lt;&amp;lt;i)) )&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;pb	=(signed char) (value&amp;gt;&amp;gt;6);	&lt;br /&gt;
			updateCv(cvo);	&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// prepare for (asynchronous) configuration write to eeprom &lt;br /&gt;
void setEeWrite(void)&lt;br /&gt;
{&lt;br /&gt;
	shadow_config=config; &lt;br /&gt;
	learn=0;&lt;br /&gt;
	eepromWriteFlag=1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*bootloader)(void) = (void*)0x1800;	// Bootloader sits at adress 0x1800 ... &lt;br /&gt;
&lt;br /&gt;
// process sys ex command &lt;br /&gt;
void setSyx(unsigned char adr, unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
&lt;br /&gt;
	cp=(unsigned char *) &amp;amp;config; &lt;br /&gt;
&lt;br /&gt;
	if(adr==0x14 &amp;amp;&amp;amp; val ==0x7b) &lt;br /&gt;
	{	// write eeprom&lt;br /&gt;
		if(eepromWriteFlag==0)&lt;br /&gt;
		{&lt;br /&gt;
			setEeWrite();&lt;br /&gt;
		}&lt;br /&gt;
		return; &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7&lt;br /&gt;
	if(adr==0x16 &amp;amp;&amp;amp; val ==0x42)	// Software update  &lt;br /&gt;
	{&lt;br /&gt;
		cli();			// interupts off &lt;br /&gt;
		resetAll();		// outputs off &lt;br /&gt;
		GPIOR1 = 0xAB;	// tell bootloader it&#039;s an application boot request &lt;br /&gt;
						// (hardware reset sets GPIOR1 to zero, 0xab == magic value for bootloader )&lt;br /&gt;
		bootloader();	// bye bye! &lt;br /&gt;
		return; 	// &amp;lt;-- never! &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	adr-=PARAMETEROFFSET; &lt;br /&gt;
	if(adr &amp;lt; NUM_PARAMTERS)	// if valid parameter &lt;br /&gt;
	{&lt;br /&gt;
		*(cp+adr)=val; 		// set in &lt;br /&gt;
		if(adr==0x30-PARAMETEROFFSET) // if Mode Byte 0, &lt;br /&gt;
			setConfig();			  // .. cv assignement must be recalculated &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Note Puffer Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define MAX_NOTES 0x10  	// one for each finger of a real programmer &lt;br /&gt;
#define MAX_CHANNELS 4&lt;br /&gt;
unsigned char notes[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char velocities[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char playedNote[MAX_CHANNELS]; // number of currently pressed notes &lt;br /&gt;
// update note puffer for a midi note-on message &lt;br /&gt;
void note_on(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char n;&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	if(n&amp;lt;MAX_NOTES)&lt;br /&gt;
	{&lt;br /&gt;
		playedNote[channel]++;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		for (n=MAX_NOTES-1;n&amp;gt;0;--n)&lt;br /&gt;
		{&lt;br /&gt;
			notes[channel][n-1]=notes[channel][n];&lt;br /&gt;
			velocities[channel][n-1]=velocities[channel][n];&lt;br /&gt;
		}&lt;br /&gt;
		n=MAX_NOTES-1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	notes[channel][n]=note; 		&lt;br /&gt;
	velocities[channel][n]=velocity;&lt;br /&gt;
	setNote(note, velocity, 2, channel);&lt;br /&gt;
}&lt;br /&gt;
// update note puffer for a new midi note-off message &lt;br /&gt;
void note_off(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	signed char n; &lt;br /&gt;
&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	--n; &lt;br /&gt;
	for (i=0;i&amp;lt;=n;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(notes[channel][i]==note)&lt;br /&gt;
		{&lt;br /&gt;
			if(i==n) // last pressed (== the on which playes) note released &lt;br /&gt;
			{&lt;br /&gt;
				if(n==0) // it was the only pressed note &lt;br /&gt;
				{&lt;br /&gt;
					setNote(note, velocities[channel][n], 0, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{	&lt;br /&gt;
					--n;&lt;br /&gt;
					setNote(notes[channel][n], velocities[channel][n], 1, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else // non playing note released &lt;br /&gt;
			{	// =&amp;gt;  just remove it from list &lt;br /&gt;
				int j;&lt;br /&gt;
				for(j=i+1;j&amp;lt;=n;++j)&lt;br /&gt;
				{&lt;br /&gt;
					notes[channel][j-1]=notes[channel][j];&lt;br /&gt;
					velocities[channel][j-1]=velocities[channel][j];&lt;br /&gt;
				}&lt;br /&gt;
				playedNote[channel]--;&lt;br /&gt;
			}&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	All Notes off, empty Note Buffer, All Controllers Reset&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void resetAll(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	cli(); // this might be called asynchronous to midi stream &lt;br /&gt;
	for(i=0;i&amp;lt;MAX_CHANNELS;++i)&lt;br /&gt;
		playedNote[i]=0;&lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0;&lt;br /&gt;
		cvo-&amp;gt;on=0;&lt;br /&gt;
		cvo-&amp;gt;pb=0x80;&lt;br /&gt;
		updateCv(cvo);&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
	sei();&lt;br /&gt;
&lt;br /&gt;
	setLed(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	static unsigned char buttonCount;&lt;br /&gt;
	static signed char blinkCount; &lt;br /&gt;
&lt;br /&gt;
	// LED blinker &lt;br /&gt;
	if(--blinkCount&amp;lt;=0)&lt;br /&gt;
	{	&lt;br /&gt;
		blinkCount=40;&lt;br /&gt;
		blinker++;&lt;br /&gt;
		if(button)&lt;br /&gt;
		{&lt;br /&gt;
			if(button&amp;lt;10)&lt;br /&gt;
				button++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Button debouncer &lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{	// down &lt;br /&gt;
		if(buttonCount &amp;lt; 10)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount++;&lt;br /&gt;
			if(buttonCount==10)&lt;br /&gt;
			{&lt;br /&gt;
				button=1;&lt;br /&gt;
				blinkCount=40;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{	// up &lt;br /&gt;
		if(buttonCount &amp;gt; 0)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount--;&lt;br /&gt;
			if(buttonCount==0)&lt;br /&gt;
				button=0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Gate 5 timing (clock output) &lt;br /&gt;
	clocker++;&lt;br /&gt;
	if(gate5off)&lt;br /&gt;
	{&lt;br /&gt;
		--gate5off;&lt;br /&gt;
		if(gate5off==0)&lt;br /&gt;
			gate_off(5);	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// activ sense &lt;br /&gt;
	if(watchTicker)			// if active sense is activated &lt;br /&gt;
	{&lt;br /&gt;
		watchTicker--;		// decrement time &lt;br /&gt;
		if(watchTicker==0)	// if time elapsed&lt;br /&gt;
			resetAll(); 	// switch all notes off 	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// retrigger timing &lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)	// for all cv output channels &lt;br /&gt;
	{&lt;br /&gt;
		if(cv_out[i].gateDipper)	// if gate timer is active &lt;br /&gt;
		{&lt;br /&gt;
			--cv_out[i].gateDipper;		// decrement timer &lt;br /&gt;
			if(cv_out[i].gateDipper==0)	// if timer elapsed&lt;br /&gt;
				gate_on(i);				// activate gate &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Eval Midi Data&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define NOTE_ON         0x90		// Midi note on message &lt;br /&gt;
#define NOTE_OFF        0x80		// Midi note off message &lt;br /&gt;
#define PITCH           0xE0		// pitch bender&lt;br /&gt;
#define AFTERTOUCH      0xD0        // after touch command (unused) &lt;br /&gt;
#define CC		        0xB0        // Continuous Controller&lt;br /&gt;
#define CHAN_PRES	    0xD0        // Channel Pressure (unused)&lt;br /&gt;
#define SONG_POSITION_POINTER 0xF2	// used for clock synchronization (first beat) &lt;br /&gt;
#define SYSEX 			0xF0		// System exclusive beginn&lt;br /&gt;
#define SYSEND			0xF7		// System exclusive end&lt;br /&gt;
&lt;br /&gt;
struct {&lt;br /&gt;
	unsigned char runningStatus;	// Midi runnig status (*)&lt;br /&gt;
	unsigned char msgByteCnt;    	// number of current midi-message byte received &lt;br /&gt;
	unsigned char byte1;			// first data byte &lt;br /&gt;
	unsigned char syxAdr; 			// Sys-Ex Adress Byte&lt;br /&gt;
	unsigned char startStop;		// current  Midi Start/Stop Status&lt;br /&gt;
	unsigned char startFlag;		// Start on next clock&lt;br /&gt;
	unsigned char clockTick;		// counter for clock division&lt;br /&gt;
	unsigned char channel;			// channel of current midi-command (relative, e.g. 0..3 ) &lt;br /&gt;
	unsigned char syncStartFlag;	// start on next 1 &lt;br /&gt;
}midi; &lt;br /&gt;
// 	(*) running status is also used as &#039;current status&#039; - it remembers allways, what next data byte is for. Or its zero, than data is ignored. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine, &lt;br /&gt;
// complete midi processing is done here, as the CPU is fast enough to &lt;br /&gt;
// do everything before next midi byte can come in. &lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	unsigned char status,byte;&lt;br /&gt;
&lt;br /&gt;
    status = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((status &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(watchTicker)						// if there had been an active sense &lt;br /&gt;
			watchTicker=ACTIVE_SENSE_TICKS;	// every new byte is ok, to &amp;quot;feed the watchdog&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		if (byte &amp;gt; 0x7F)	                // if command byte (MSB set)&lt;br /&gt;
		{&lt;br /&gt;
			if(byte&amp;gt;= 0xF8)  				// if real-time msg &lt;br /&gt;
			{&lt;br /&gt;
				switch (byte)&lt;br /&gt;
				{&lt;br /&gt;
					case 0xF8: 	// Clock &lt;br /&gt;
					{&lt;br /&gt;
						if(midi.startFlag)  // if synchronized start &lt;br /&gt;
						{&lt;br /&gt;
							midi.startStop=1;&lt;br /&gt;
							midi.startFlag=0;&lt;br /&gt;
							midi.syncStartFlag=1;&lt;br /&gt;
							midi.clockTick=0;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
						}&lt;br /&gt;
						else if(midi.startStop)	// if (already) running &lt;br /&gt;
						{&lt;br /&gt;
							++spp;&lt;br /&gt;
							++barTick;&lt;br /&gt;
							if(barTick &amp;gt;= ticksPerBar)&lt;br /&gt;
								barTick=0; &lt;br /&gt;
						}&lt;br /&gt;
						++midi.clockTick; 	// increment counter for clock output &lt;br /&gt;
						if(midi.clockTick &amp;gt;= config.clockDivider)	// if divider cout elapsed do clock output &lt;br /&gt;
						{&lt;br /&gt;
							midi.clockTick=0;&lt;br /&gt;
							gate_on(5);&lt;br /&gt;
							gate5off=clocker&amp;gt;&amp;gt;1; // pulse-width counter = half of current midi clock pulse width&lt;br /&gt;
						}&lt;br /&gt;
&lt;br /&gt;
						if(midi.syncStartFlag &amp;amp;&amp;amp; barTick==0 &amp;amp;&amp;amp; midi.startStop ) // synchronize clock output to bars   &lt;br /&gt;
						{	&lt;br /&gt;
							midi.clockTick=0;&lt;br /&gt;
							gate_on(5);&lt;br /&gt;
							gate5off=clocker&amp;gt;&amp;gt;1;&lt;br /&gt;
							midi.syncStartFlag=0;&lt;br /&gt;
						}&lt;br /&gt;
						clocker=0;  // restart midi clock pulse width counter. &lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
					case 0xF9:	// Tick (unused) &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFA: 	// Start &lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
						{&lt;br /&gt;
							spp=0;&lt;br /&gt;
							barTick=0;&lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
						}&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFB:	// Continue&lt;br /&gt;
						if(! midi.startStop) // if not already running &lt;br /&gt;
							midi.startFlag=1;&lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFC:	// Stop&lt;br /&gt;
						midi.startStop=0;&lt;br /&gt;
						gate_off(4);&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFD:	// unassigned &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFE:	//  Active Sense&lt;br /&gt;
						watchTicker=ACTIVE_SENSE_TICKS;&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFF:	// Reset &lt;br /&gt;
						break; &lt;br /&gt;
				}&lt;br /&gt;
				return ; &lt;br /&gt;
			} &lt;br /&gt;
&lt;br /&gt;
			if(byte &amp;gt;= 0xF0) 					// if System Common Category message&lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)&lt;br /&gt;
				{&lt;br /&gt;
					if(midi.runningStatus==SYSEX &amp;amp;&amp;amp; midi.msgByteCnt==6) // if a complete valid Sys-Ex Frame has been received &lt;br /&gt;
						setSyx(midi.syxAdr, midi.byte1); 				// change configuration &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					midi.runningStatus = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				else if(byte ==SONG_POSITION_POINTER)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;  &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}&lt;br /&gt;
				//							// every other System Common is ignored 	&lt;br /&gt;
				midi.runningStatus = 0;		// just reset running status &lt;br /&gt;
				return ; 					// and done &lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.runningStatus = 0;				// just new status messages left, so running status is lost&lt;br /&gt;
&lt;br /&gt;
			// only channel messages left here ... &lt;br /&gt;
			if(learn)								 // if learn mode &lt;br /&gt;
			{&lt;br /&gt;
				if(config.midiChannel != (byte&amp;amp;0xf)) // if message channel not current channel&lt;br /&gt;
				{&lt;br /&gt;
					config.midiChannel=byte&amp;amp;0xf;	 // assign channel &lt;br /&gt;
					setEeWrite(); 					 // and store it &lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.channel=(byte &amp;amp; 0xf)-config.midiChannel ;&lt;br /&gt;
			if ( midi.channel&amp;lt;config.numMidiChannels)   // if MIDI channel matches,&lt;br /&gt;
			{&lt;br /&gt;
				midi.runningStatus = byte &amp;amp; 0xF0 ;		// set running status (==Midi command w/o channel)&lt;br /&gt;
				midi.msgByteCnt = 0;&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
			return ; &lt;br /&gt;
		}&lt;br /&gt;
		else	// data bytes goes here &lt;br /&gt;
		{&lt;br /&gt;
			switch (midi.runningStatus)		    // last command byte (or zero, if data is not for us) &lt;br /&gt;
			{&lt;br /&gt;
			case SONG_POSITION_POINTER:&lt;br /&gt;
				if(midi.msgByteCnt)	// 2nd Byte&lt;br /&gt;
				{&lt;br /&gt;
					spp= (((int)byte &amp;lt;&amp;lt; 7 ) | midi.byte1) * 6;&lt;br /&gt;
					barTick = spp % (unsigned int) ticksPerBar; &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt=1;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_ON):					// if we&#039;ve had a note on byte&lt;br /&gt;
				if (midi.msgByteCnt)		// if counter = 1&lt;br /&gt;
				{ 			   				// then we&#039;ve just received a velocity byte&lt;br /&gt;
					midi.msgByteCnt = 0;	// msg complete - clear byte count &lt;br /&gt;
					if (byte == 0)			// is it a note off (velo = 0) ?&lt;br /&gt;
						note_off(midi.byte1,byte,midi.channel);  &lt;br /&gt;
					else 					// NOTE ON&lt;br /&gt;
					{&lt;br /&gt;
						if(learn)&lt;br /&gt;
						{&lt;br /&gt;
							config.noteOffset=midi.byte1;&lt;br /&gt;
							setEeWrite();&lt;br /&gt;
						}&lt;br /&gt;
						&lt;br /&gt;
						note_on(midi.byte1, byte,midi.channel);  // ... and do what the note on has told us &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				else						// first data byte &lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;		// save it &lt;br /&gt;
					midi.msgByteCnt = 1;    // set counter to get 2nd data byte&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_OFF): 	        	// command note off&lt;br /&gt;
				if (midi.msgByteCnt)	 	// if complete &lt;br /&gt;
				{ 					 	&lt;br /&gt;
					midi.msgByteCnt = 0;	&lt;br /&gt;
					note_off(midi.byte1,byte,midi.channel); // switch note off&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1; 	&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (PITCH):		        	// PITCH BEND command&lt;br /&gt;
				if (midi.msgByteCnt)   	 	// if second data byte (MSB) &lt;br /&gt;
				{ 						&lt;br /&gt;
					int v;&lt;br /&gt;
					v=byte;&lt;br /&gt;
					v&amp;lt;&amp;lt;=6;&lt;br /&gt;
					v|=midi.byte1;&lt;br /&gt;
					v-=0x2000;&lt;br /&gt;
					setPb(v,midi.channel);&lt;br /&gt;
&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (CC):	             		// Continuous Controller Message &lt;br /&gt;
				if (midi.msgByteCnt)	&lt;br /&gt;
				{&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
&lt;br /&gt;
					if(learn)&lt;br /&gt;
					{&lt;br /&gt;
						config.cvMidiCC[3]=midi.byte1;&lt;br /&gt;
						setEeWrite();&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
					setCC(midi.byte1, byte,midi.channel);&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
				  midi.byte1 = byte;&lt;br /&gt;
				  midi.msgByteCnt = 1;&lt;br /&gt;
				  return; &lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
	//		case (CHAN_PRES):	         	// if channel pressure command&lt;br /&gt;
	//			channel_pressure (byte); 	// set Aftertouch &lt;br /&gt;
			case SYSEX:						// SYS-EX command &lt;br /&gt;
				midi.msgByteCnt++;			// message Byte counter&lt;br /&gt;
				switch(midi.msgByteCnt)		// which byte is on? &lt;br /&gt;
				{&lt;br /&gt;
					case 1:					// Manufactorer &lt;br /&gt;
						if(byte !=0x70)		// if not the right one &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error / wrong byte &lt;br /&gt;
						break; &lt;br /&gt;
					case 2:					// Unit - ID &lt;br /&gt;
						if(byte!=0x7D)&lt;br /&gt;
							midi.msgByteCnt=0;&lt;br /&gt;
						break; &lt;br /&gt;
					case 3:					// Midi-Adress (or Unit ID) (ignored!)&lt;br /&gt;
						if(byte&amp;gt;=0x10)		// if not in legal range &lt;br /&gt;
							midi.msgByteCnt=0;	// frame error &lt;br /&gt;
						break;&lt;br /&gt;
					case 4:					// adress byte&lt;br /&gt;
						midi.syxAdr=byte;	// store it for later&lt;br /&gt;
						break;&lt;br /&gt;
					case 5:					// high data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1=(byte&amp;lt;&amp;lt;4);	// set half the data &lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					case 6: 				// low data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)		// if in range &lt;br /&gt;
							midi.byte1 |= byte;	// or together the complete data byte &lt;br /&gt;
						else 					// evaluation is  done in the code for SYSEND above! &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					default:				// not our framing &lt;br /&gt;
						midi.msgByteCnt=0;&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
				if(midi.msgByteCnt==0)	// something did not fit &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				break;&lt;br /&gt;
			case 0: // bnwtko (bytes no one wants to know off ) &lt;br /&gt;
				return ;&lt;br /&gt;
			default:&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7279</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7279"/>
		<updated>2011-06-22T22:44:43Z</updated>

		<summary type="html">&lt;p&gt;Fetz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 || Tonhöhe (Noten) || Gate 1 || Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen. und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V. &lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie dreimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen. &lt;br /&gt;
(Wo das jetzt hier zum dritten mal leicht unterschiedlich steht: die finstere Wahrheit ist, das jedes mal die komplette Konfiguration gespeichert wird. ) &lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876, er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
Mono (default). 1 Kanal Monophon. &lt;br /&gt;
&lt;br /&gt;
Multi 2. 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
Multi 4. 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
Die weiteren Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
Im DAC-Feld kann für jede CV (=jeden Steuerspannungs-Ausgang) gewählt werden, wie er gesteuert wird. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet im Tonhöhen-Modus die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser.  &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus werden die Ccs auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Die Gates 1-4 sind den Tonhöhenkanälen fest zugeordnet, gibt ein DAC eine Tonhöhe aus, ist das zugehörige Gate auch Note an/aus. &lt;br /&gt;
&lt;br /&gt;
Ist der DAC auf CC gestellt, kann der Gate-Ausgang einem Controller zugewiesen werden, das Gate ist an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note. (6=16tel, 12=8tel, 24=1/4) &lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop.&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;br /&gt;
= Software = &lt;br /&gt;
Der Quelltext für die Controller-Software findet sich hier [[MCV876_Controller_Update:_Source_Code]]. &lt;br /&gt;
&lt;br /&gt;
Fertige Files zum runterladen und selber brennen gibt es, wenn die Software fertig getestet ist, &amp;quot;Beta-Tester&amp;quot; werden über das Forum bzw. per Mail versorgt.&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7278</id>
		<title>MCV876 Controller Update: Source Code</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update:_Source_Code&amp;diff=7278"/>
		<updated>2011-06-22T22:26:25Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Created page with &amp;#039;=Übersetzen=  Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden. Im Studio ein neues, leeres Projekt m…&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Übersetzen= &lt;br /&gt;
Der Code für den ATMega 88 wird mit dem AVR-Studio 4 erstellt. Das kann man auf der Atmel Webseite (kostenlos) runter laden.&lt;br /&gt;
Im Studio ein neues, leeres Projekt mit Standard-Einstellungen, einem sinnvollen Namen und an einem Ort, wo man es wiederfindet erstellen, Quelltext in das (automatisch erzeugte, leere) C-File kopieren, auf Build-&amp;gt;Build klicken, fertig. &lt;br /&gt;
Das resultierende HEX File kann man mit einem üblichen Programmieradapter in der Chip flashen, für uns zweckmäßiger ist allerdings, das in ein Sys-Ex File zu Konvertieren [-&amp;gt;Link auf Konverter folgt...], und das an den Controller zu schicken. Dann programmiert der sich selber um. Dazu muss er allerdings einmalig mit dem Bootlader [hier Link auf Bootlader imaginieren.. ] programmiert worden sein. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu den Atmel Controllern findet man eine Menge Info mit Tante Google, [http://www.mikrocontroller.net http://www.mikrocontroller.net] kommt häufig vor, und hat ein ergiebiges Forum und gute Artikel. Auf der Atmel Webseite gibt es auch einige Info, das [http://www.atmel.com/dyn/resources/prod_documents/doc2545.pdf ATMega 88 Datenblatt] ist spätestens dann nötig, wenn es um die Hardware geht. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Quelltext=&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
	MCV876 with ATMega 88 &lt;br /&gt;
&lt;br /&gt;
	Midi to 4 Channel Control Voltage and six Gates Interface &lt;br /&gt;
&lt;br /&gt;
	Preliminary &lt;br /&gt;
	Version 0.20 2011-06-22 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    This program is free software: you can redistribute it and/or modify&lt;br /&gt;
    it under the terms of the GNU General Public License as published by&lt;br /&gt;
    the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;
    (at your option) any later version.&lt;br /&gt;
&lt;br /&gt;
    This program is distributed in the hope that it will be useful,&lt;br /&gt;
    but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
    GNU General Public License for more details.&lt;br /&gt;
&lt;br /&gt;
    You should have received a copy of the GNU General Public License&lt;br /&gt;
    along with this program.  If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;
			&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;			// yes, we want to talk to the port pins by name&lt;br /&gt;
#include &amp;lt;avr/eeprom.h&amp;gt;		// ... oh.. and some eeprom access would be nice&lt;br /&gt;
#include &amp;lt;avr/interrupt.h&amp;gt;	// ... and w/o the interupts it wouldn&#039;t do anything... &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*	===================ZERO LINE=================================&lt;br /&gt;
	WARNING: everthing below the zero line counts from zero! &lt;br /&gt;
	e.g. whats Gate 6 in manual is GATE5 here, CVs are CV0 to 3 &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	where should the LED output be &lt;br /&gt;
*/&lt;br /&gt;
#define LEDLED   6  // LED at Reset-Pin, standard for series, but no debugging possible. &lt;br /&gt;
#define LEDGATE0 0	// Led at Gate 0, no LED (Test I: no LED ) &lt;br /&gt;
#define LEDGATE4 4	// LED at Gate 5, Test II: no Gate 5 Function (Midi Start/Stop) &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define LEDOUT LEDGATE4 // select your evil... &lt;br /&gt;
&lt;br /&gt;
#define BUTTON (!(PIND &amp;amp; (1&amp;lt;&amp;lt;4))) // True if Button pressed &lt;br /&gt;
&lt;br /&gt;
#define NUM_CV 4&lt;br /&gt;
&lt;br /&gt;
#define PARAMETEROFFSET 0x23 // First paramter has adress 0x23&lt;br /&gt;
#define NUM_PARAMTERS 17	 // and there are 17 parameters &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
struct CONFIG {&lt;br /&gt;
	unsigned char midiChannel;&lt;br /&gt;
	unsigned char noteOffset;&lt;br /&gt;
&lt;br /&gt;
	unsigned char cvMidiCC[NUM_CV];&lt;br /&gt;
	unsigned char gateMidiCC[NUM_CV];&lt;br /&gt;
	&lt;br /&gt;
	unsigned char gateTreshhold;&lt;br /&gt;
	unsigned char clockDivider;&lt;br /&gt;
	unsigned char clockTime; &lt;br /&gt;
	unsigned char ifMode0; 	// 	bit 0 ... bit 3 = CV0 .. CV3, 0=Pitch, 1=CC (if pitch, CC assignement is ignored) &lt;br /&gt;
							//	bit 5: voices: 0=2; 1=4 &lt;br /&gt;
							//  bit 7:6= 00 Mono; 01:Multi-Channel; 10: Poly (not supported); &lt;br /&gt;
	&lt;br /&gt;
	unsigned char ifMode1;  // Gate Trigger Mode Bits (0=Re-Trigger, 1=TriggerControl)  &lt;br /&gt;
	unsigned char ifMode2;  // bit0: Velo0=&amp;gt;CV0; bit1:Velo0=&amp;gt;CV1; bit2:Velo-Poly2=&amp;gt;CV2; bit3:Velo1=&amp;gt;CV3&lt;br /&gt;
							// bit4: PB=&amp;gt;CV0; bit5:PB=&amp;gt;CV1; bit6:PB=&amp;gt;CV2; bit7:PB=&amp;gt;CV3&lt;br /&gt;
	unsigned char ifMode3; 	// bit0..bit3 = CV0... CV3; 0=10V, 1=5V Mode &lt;br /&gt;
&lt;br /&gt;
//  internal evaluated configuration &lt;br /&gt;
	unsigned char numMidiChannels;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
} shadow_config, // her goes a copy, when config should be written, because that might take a while &lt;br /&gt;
&lt;br /&gt;
config 	// current configuration, mostly direct evaluated troughout the program&lt;br /&gt;
= {		// default values (Mono-Mode, Midi-Ch 1, CV1=Pitch, CV2=Velocity CV3=ModWheel CV4=Pitch Bend&lt;br /&gt;
			0,  	// Midi Channel &lt;br /&gt;
			24, 	// Note Offset (2 Oktaves) &lt;br /&gt;
		{	255,255,1,255, },  	// CV - assigned Midi Controllers (used if not pitch output) 255=none&lt;br /&gt;
		{   255,18,19,20, }, 	// Gate - asigned Controllers (if not pitch) 255=none &lt;br /&gt;
			63, 	// Treshold&lt;br /&gt;
			6,  	// Divider 6 (1/16 Note) &lt;br /&gt;
			1,  	// clock time (unused) &lt;br /&gt;
			0x0e, 	// mode 1 Low nibble CC-Bits, high: interface mode &lt;br /&gt;
			0x82, 	// mode 2 High Nibble: PB-bits, Low: Velo-Bits&lt;br /&gt;
			0,    	// mode 3 (5V-Bits) &lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define EE_MAGIC 17&lt;br /&gt;
unsigned char ee_magic EEMEM;&lt;br /&gt;
unsigned char config_eeprom[NUM_PARAMTERS] EEMEM; // Config in EEPROM, saves it during power down. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define MODE_MONO 0x00		// monophon, all assigned to last note on a single channel &lt;br /&gt;
#define MODE_MULTI2 0x40	// two channel, 1/2 for midiChannel, 2/3 for midiChannel+1 &lt;br /&gt;
#define MODE_MULTI4 0x60	// for channel midiChannel to midiChannel+3 &lt;br /&gt;
#define MODE_POLY2  0x80	// not supported &lt;br /&gt;
#define MODE_POLY4  0xA0	// not supported&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// to have the clock synced to &amp;quot;beat 1&amp;quot; of a bar ... &lt;br /&gt;
volatile unsigned int spp; 	// song position pointer (from midi message) &lt;br /&gt;
unsigned char ticksPerBar=96; // How many ticks per bar (is not tx-ed: guess 96 for 4/4 ...  ) &lt;br /&gt;
volatile unsigned char barTick; // where are we inside the bar (counted, because calculation from spp takes some cpu-cycles ) &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char  eepromWriteFlag;	// set to one, if main-loop should write eeprom &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char button; 	// debounced button status &lt;br /&gt;
volatile unsigned char learn; 	// (channel/base note/cc) &amp;quot;do learn&amp;quot; status &lt;br /&gt;
volatile unsigned char blinker; // some count for blinkenlights &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char clocker;	 // timer counter for half a midi-clock (which is needed for divider 1)&lt;br /&gt;
volatile unsigned char gate5off; // timer counter for when clock gate output should go low again. &lt;br /&gt;
&lt;br /&gt;
volatile unsigned char watchTicker;	// midi active sense counter &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void setConfig(void); 		&lt;br /&gt;
void setLed(unsigned char on);&lt;br /&gt;
void resetAll(void);&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	main() &lt;br /&gt;
	Main does initialization, than runs a loop, which does nearly nothing: &lt;br /&gt;
&lt;br /&gt;
	Programming the EEPROM (because its slow) &lt;br /&gt;
	Blinking &lt;br /&gt;
	Button evaluation &lt;br /&gt;
&lt;br /&gt;
	All the important things are done by the interupts. (Thanks to the &lt;br /&gt;
	20MHZ RISC power everything can be done in less than 320µs=one Midi-Character) &lt;br /&gt;
*/&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char blink=6;&lt;br /&gt;
&lt;br /&gt;
	unsigned char i,x; &lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
	static unsigned char lastButton; &lt;br /&gt;
&lt;br /&gt;
/* &lt;br /&gt;
&lt;br /&gt;
		HARDWARE INIT &lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
   // Input/Output Ports initialization&lt;br /&gt;
    // DDRx  = Define I/O direction for each pins&lt;br /&gt;
    // PORTx = If pin is Output then `0&#039; = lo, `1&#039; = hi&lt;br /&gt;
    //         If pin is input then `0&#039; = no pullup, `1&#039; = pullup enabled&lt;br /&gt;
    // `0&#039; = Input `1&#039; = Output&lt;br /&gt;
    // Port B initialization&lt;br /&gt;
    DDRB  = 0x3f;                 // PB0=G4; PB1=G5 (Opt. LED); PB2=G6 ; PB3=WR1; PB4=D0; PB5=D1 (all output)&lt;br /&gt;
    PORTB = 0x04;                 // WR1 High, Rest Low &lt;br /&gt;
&lt;br /&gt;
    // Port C initialization&lt;br /&gt;
    DDRC  = 0x7F;                 // PC0=D2; PC1=D3; PC2=D4; PC3=D5; PC4=D6; PC5=D7; PC6=LED (opt.) (all output)&lt;br /&gt;
	PORTC = 0x00;                 // all Low&lt;br /&gt;
&lt;br /&gt;
    // Port D initialization&lt;br /&gt;
    DDRD  = 0xee;                 // [PD0=RX]; PD1=WR2; PD2=WR3; PD3=WR4; PD4=Button; PD5=G1; PD6=G2; PD7=G3 &lt;br /&gt;
    PORTD = 0x1F;                 // Pullup on RX &amp;amp; Button, all WR High, Gates Low &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // Timer/Counter 1 initialization&lt;br /&gt;
    // Clock source: System Clock&lt;br /&gt;
    // Clock value: 20Mhz /64&lt;br /&gt;
    // Mode: CTC &lt;br /&gt;
    // OC0 output: Disconnected&lt;br /&gt;
	OCR1A= 625; // 2 ms  &lt;br /&gt;
	TCCR1A =0; &lt;br /&gt;
	TCCR1B =8+3; // CTC Mode, TOP from OCR1A + Clock/64 &lt;br /&gt;
	TIMSK1 =2; // OCF1A Interupt &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    // USART initialization&lt;br /&gt;
    // Communication Parameters: 8 Data, 1 Stop, No Parity&lt;br /&gt;
    // USART Receiver: On&lt;br /&gt;
    // USART Transmitter: OFF&lt;br /&gt;
    // USART Mode: Asynchronous&lt;br /&gt;
    UCSR0A = 0x00;&lt;br /&gt;
	/* Set frame format: 8data, 1stop bit */&lt;br /&gt;
	UCSR0C = 6;&lt;br /&gt;
&lt;br /&gt;
    // USART Baud rate: 31250;  20E6/16/31250 - 1&lt;br /&gt;
   	UBRR0H = 0;&lt;br /&gt;
    UBRR0L = 39;&lt;br /&gt;
&lt;br /&gt;
    /*Enable receiver , Enable Rx-int  */&lt;br /&gt;
	UCSR0B = ((1&amp;lt;&amp;lt;RXEN0)|(1&amp;lt;&amp;lt;RXCIE0) ) ;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
	HARDWARE READY &lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
	// Read Configuration &lt;br /&gt;
	if( eeprom_read_byte(ee_magic) == EE_MAGIC  &amp;amp;&amp;amp; !BUTTON) // if eeprom valid AND Button not pressed &lt;br /&gt;
	{&lt;br /&gt;
		cp=(unsigned char* )&amp;amp;config; &lt;br /&gt;
		for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
		{&lt;br /&gt;
			x=eeprom_read_byte(config_eeprom+i);			// read config from eeprom &lt;br /&gt;
			*cp=x;	&lt;br /&gt;
			cp++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	setConfig();&lt;br /&gt;
&lt;br /&gt;
	// enable interrupts&lt;br /&gt;
	sei(); &lt;br /&gt;
&lt;br /&gt;
	while(1)&lt;br /&gt;
	{&lt;br /&gt;
		if(eepromWriteFlag)&lt;br /&gt;
		{	// save configuration &lt;br /&gt;
			cp=(unsigned char* )&amp;amp;shadow_config; &lt;br /&gt;
			for(i=0;i&amp;lt;NUM_PARAMTERS;++i)&lt;br /&gt;
			{&lt;br /&gt;
				x=*cp;&lt;br /&gt;
				eeprom_write_byte(config_eeprom+i,x);&lt;br /&gt;
				cp++;&lt;br /&gt;
			}&lt;br /&gt;
			eeprom_write_byte(ee_magic,EE_MAGIC); &lt;br /&gt;
&lt;br /&gt;
			setLed(1);&lt;br /&gt;
			blink=6;&lt;br /&gt;
			blinker=0;&lt;br /&gt;
			eepromWriteFlag=0;	&lt;br /&gt;
		}&lt;br /&gt;
		if(blink &amp;amp;&amp;amp; blinker&amp;gt;2)  // blink after power up and after storing in nv-memory &lt;br /&gt;
		{&lt;br /&gt;
			blink--;&lt;br /&gt;
			setLed(blink&amp;amp;1);&lt;br /&gt;
			blinker=0; &lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if(button != lastButton)&lt;br /&gt;
		{&lt;br /&gt;
			if(button==1 )&lt;br /&gt;
			{&lt;br /&gt;
				resetAll();&lt;br /&gt;
				if(learn==1)&lt;br /&gt;
				{&lt;br /&gt;
					learn=0;&lt;br /&gt;
					setLed(0);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			if(button==6) &lt;br /&gt;
			{&lt;br /&gt;
				if(learn ==0)&lt;br /&gt;
				{&lt;br /&gt;
					learn=1;&lt;br /&gt;
					setLed(1);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			lastButton=button; &lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return 0; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Hardware Interface&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
/*  Pin assignment &lt;br /&gt;
   Atmel Fkt 	[Orig] &lt;br /&gt;
1  Reset LED 	[MCLR] &lt;br /&gt;
2  RX    Midi-Rx[WR1]&lt;br /&gt;
3  PD1   WR2&lt;br /&gt;
4  PD2   WR3&lt;br /&gt;
5  PD3   WR4&lt;br /&gt;
6  PD4   Button&lt;br /&gt;
7  VCC   +5  	[LED] (Either from Atmel-1(Series) or Atmel-15(w/Debug))&lt;br /&gt;
8  GND   GND&lt;br /&gt;
9  OSC   OSC&lt;br /&gt;
10 OSC   OSC &lt;br /&gt;
11 PD5   G1&lt;br /&gt;
12 PD6   G2&lt;br /&gt;
13 PD7   G3&lt;br /&gt;
14 PB0   G4&lt;br /&gt;
&lt;br /&gt;
15 PB1   G5 (Optional + LED) &lt;br /&gt;
16 PB2   G6 &lt;br /&gt;
17 PB3   WR1    [---] &lt;br /&gt;
18 PB4   D0     [RX]&lt;br /&gt;
19 PB5   D1		[GND]&lt;br /&gt;
20 AVCC  +5&lt;br /&gt;
21 ARef  +5 	[D0]&lt;br /&gt;
22 GND   GND	[D1] &lt;br /&gt;
23 PC0   D2&lt;br /&gt;
24 PC1   D3&lt;br /&gt;
25 PC2   D4&lt;br /&gt;
26 PC3   D5&lt;br /&gt;
27 PC4   D6&lt;br /&gt;
28 PC5   D7&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
// put &amp;quot;val&amp;quot; to the right port bits for the DA-Converter &lt;br /&gt;
// ... better program than solder *that*... &lt;br /&gt;
void adByteOut(unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char b;&lt;br /&gt;
	b=val;&lt;br /&gt;
	b&amp;gt;&amp;gt;=2;&lt;br /&gt;
	PORTC=b;&lt;br /&gt;
	b=val&amp;amp;3;&lt;br /&gt;
	b&amp;lt;&amp;lt;=4;&lt;br /&gt;
	val=PORTB;&lt;br /&gt;
	val &amp;amp;=0xCF;&lt;br /&gt;
	val|=b;&lt;br /&gt;
	PORTB=val;&lt;br /&gt;
}&lt;br /&gt;
// set DA-Output voltage of one CV Channel  &lt;br /&gt;
void set_cv(unsigned char val, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	adByteOut(val); 	// set D0..D7 converter data bits &lt;br /&gt;
	switch (channel)	// pulse with write pin &lt;br /&gt;
	{&lt;br /&gt;
		case 0: //PB3&lt;br /&gt;
			PORTB  &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTB  |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD1&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;1);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;1);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD2&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;2);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;2);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PD3&lt;br /&gt;
			PORTD &amp;amp;= ~(1&amp;lt;&amp;lt;3);&lt;br /&gt;
			PORTD |=  (1&amp;lt;&amp;lt;3);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// set on of the gate outputs active &lt;br /&gt;
void gate_on(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD |= (1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB |= (1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// clear one of the gate outputs &lt;br /&gt;
void gate_off(unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	switch (channel)&lt;br /&gt;
	{&lt;br /&gt;
		case 0:	// PD5&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 5);&lt;br /&gt;
			break;&lt;br /&gt;
		case 1:	// PD6&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
			break;&lt;br /&gt;
		case 2:	// PD7&lt;br /&gt;
			PORTD &amp;amp;=~(1&amp;lt;&amp;lt; 7);&lt;br /&gt;
			break;&lt;br /&gt;
		case 3:	// PB0&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 0);&lt;br /&gt;
			break;&lt;br /&gt;
		case 4:	// PB1&lt;br /&gt;
#if LEDOUT != LEDGATE4&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif&lt;br /&gt;
			break;&lt;br /&gt;
		case 5:	// PB2&lt;br /&gt;
			PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 2);&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	LED&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void setLed(unsigned char on)&lt;br /&gt;
{&lt;br /&gt;
#if LEDOUT == LEDLED&lt;br /&gt;
	if (on)	// PC6&lt;br /&gt;
		PORTC |= (1&amp;lt;&amp;lt; 6);&lt;br /&gt;
	else &lt;br /&gt;
		PORTC &amp;amp;=~(1&amp;lt;&amp;lt; 6);&lt;br /&gt;
#endif &lt;br /&gt;
#if LEDOUT == LEDGATE4&lt;br /&gt;
	if (on)	// PB1&lt;br /&gt;
		PORTB |= (1&amp;lt;&amp;lt; 1);&lt;br /&gt;
	else &lt;br /&gt;
		PORTB &amp;amp;=~(1&amp;lt;&amp;lt; 1);&lt;br /&gt;
#endif &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Data Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
typedef struct {&lt;br /&gt;
	unsigned char channel;  // (relative) Midi channel (0..3) Mode dependant &lt;br /&gt;
	unsigned char cvOut;	// for which CV-Ouput (0..3) fixed &lt;br /&gt;
	unsigned char ccVal;	// current CC Value &lt;br /&gt;
	unsigned char note;		// current Note &lt;br /&gt;
	unsigned char velo;		// current Velocity &lt;br /&gt;
	unsigned char on;		// if Note is on &lt;br /&gt;
	signed char   pb;		// current Pitch Bend Value &lt;br /&gt;
	unsigned char gateDipper;&lt;br /&gt;
} cvoutput;  &lt;br /&gt;
&lt;br /&gt;
cvoutput cv_out[NUM_CV];	// current state for each cv out&lt;br /&gt;
&lt;br /&gt;
unsigned char gates; // which gates are on =&amp;gt; used for LED setting &lt;br /&gt;
&lt;br /&gt;
// set cv output (and assigned gate, if pitch output)&lt;br /&gt;
// called after anything that changes the cv_out structure  &lt;br /&gt;
void updateCv(cvoutput *cvo)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char bm;&lt;br /&gt;
	unsigned char gated=0;&lt;br /&gt;
	int val=0;&lt;br /&gt;
&lt;br /&gt;
	bm=1&amp;lt;&amp;lt;cvo-&amp;gt;cvOut;	// bit mask for configuration flags &lt;br /&gt;
&lt;br /&gt;
	if(config.ifMode0 &amp;amp; bm)	// 1=CC-Mode&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;ccVal; // &amp;lt;&amp;lt;1;&lt;br /&gt;
/*&lt;br /&gt;
		if( (config.ifMode3 &amp;amp; bm) &amp;amp;&amp;amp; cvo-&amp;gt;on==0)	// 5V-Flag, abused as Gated-Mode-CV Flag &lt;br /&gt;
			gated=1;&lt;br /&gt;
*/&lt;br /&gt;
	}&lt;br /&gt;
	else // Note&lt;br /&gt;
	{&lt;br /&gt;
		val=cvo-&amp;gt;note;&lt;br /&gt;
		val -= config.noteOffset;&lt;br /&gt;
&lt;br /&gt;
		if(config.ifMode3 &amp;amp; bm)	// 5V&lt;br /&gt;
			val &amp;lt;&amp;lt;= 2;&lt;br /&gt;
		else&lt;br /&gt;
			val &amp;lt;&amp;lt;= 1;&lt;br /&gt;
	}&lt;br /&gt;
	if(config.ifMode2 &amp;amp; bm) // Velocity &lt;br /&gt;
		val+=cvo-&amp;gt;velo;&lt;br /&gt;
	if(config.ifMode2 &amp;amp; (bm&amp;lt;&amp;lt;4)) // PB &lt;br /&gt;
		val+=cvo-&amp;gt;pb;&lt;br /&gt;
&lt;br /&gt;
	if(val&amp;lt;0 || gated)	// clip values to converter range &lt;br /&gt;
		val=0;&lt;br /&gt;
	if(val&amp;gt;255)&lt;br /&gt;
		val=255;&lt;br /&gt;
&lt;br /&gt;
	set_cv((unsigned char)val, cvo-&amp;gt;cvOut);&lt;br /&gt;
	if((config.ifMode0 &amp;amp; bm)==0)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;on)&lt;br /&gt;
		{&lt;br /&gt;
			if(cvo-&amp;gt;gateDipper==0) // no retrigger required&lt;br /&gt;
				gate_on(cvo-&amp;gt;cvOut);&lt;br /&gt;
			else				   // dip gate for a few milliseconds 	&lt;br /&gt;
				gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates |=bm;&lt;br /&gt;
		}&lt;br /&gt;
		else &lt;br /&gt;
		{&lt;br /&gt;
			gate_off(cvo-&amp;gt;cvOut);&lt;br /&gt;
			gates &amp;amp;=~bm;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// calculate/initialize control variables after &lt;br /&gt;
// mode-configuration change &lt;br /&gt;
void setConfig()&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i; &lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;pb=0x80;&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	switch(config.ifMode0 &amp;amp; 0xf0 )&lt;br /&gt;
	{	&lt;br /&gt;
		default:&lt;br /&gt;
		case MODE_MONO:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=1;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI2:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV/2;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=0;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			for(;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=1;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=2;&lt;br /&gt;
			break;&lt;br /&gt;
		case MODE_MULTI4:&lt;br /&gt;
			for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
			{&lt;br /&gt;
				cvo-&amp;gt;channel=i;&lt;br /&gt;
				cvo++;&lt;br /&gt;
			} &lt;br /&gt;
			config.numMidiChannels=NUM_CV;&lt;br /&gt;
			break;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// assign new note state to cv outputs &lt;br /&gt;
void setNote(unsigned char note, unsigned char velo, unsigned char onOff, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(cvo-&amp;gt;channel==channel)&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;note=note;&lt;br /&gt;
			cvo-&amp;gt;velo=velo;&lt;br /&gt;
			if(onOff==2 &amp;amp;&amp;amp; cvo-&amp;gt;on &amp;amp;&amp;amp; (config.ifMode1 &amp;amp; (1&amp;lt;&amp;lt;i)))&lt;br /&gt;
				cvo-&amp;gt;gateDipper=4;&lt;br /&gt;
			cvo-&amp;gt;on=onOff;&lt;br /&gt;
			updateCv(cvo);&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	setLed(gates);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// process new continous controller value &lt;br /&gt;
void setCC(unsigned char cc, unsigned char value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel== cvo-&amp;gt;channel )&lt;br /&gt;
		{&lt;br /&gt;
			if(config.ifMode0 &amp;amp;(1&amp;lt;&amp;lt;i))&lt;br /&gt;
			{&lt;br /&gt;
				if(config.cvMidiCC[i]==cc)&lt;br /&gt;
				{&lt;br /&gt;
					cvo-&amp;gt;ccVal=value; &lt;br /&gt;
					updateCv(cvo);&lt;br /&gt;
				}&lt;br /&gt;
				if(config.gateMidiCC[i]==cc  )&lt;br /&gt;
				{&lt;br /&gt;
					if(value&amp;gt;config.gateTreshhold)&lt;br /&gt;
						gate_on(channel);&lt;br /&gt;
					else &lt;br /&gt;
						gate_off(channel);&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// process new pitch bend value &lt;br /&gt;
void setPb(signed int value, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		if(channel==cvo-&amp;gt;channel &amp;amp;&amp;amp; (config.ifMode2 &amp;amp; (16&amp;lt;&amp;lt;i)) )&lt;br /&gt;
		{&lt;br /&gt;
			cvo-&amp;gt;pb	=(signed char) (value&amp;gt;&amp;gt;6);	&lt;br /&gt;
			updateCv(cvo);	&lt;br /&gt;
		}&lt;br /&gt;
		cvo++;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// prepare for (asynchronous) configuration write to eeprom &lt;br /&gt;
void setEeWrite(void)&lt;br /&gt;
{&lt;br /&gt;
	shadow_config=config; &lt;br /&gt;
	learn=0;&lt;br /&gt;
	eepromWriteFlag=1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void (*bootloader)(void) = (void*)0x1800;	// Bootloader sits at adress 0x1800 ... &lt;br /&gt;
&lt;br /&gt;
// process sys ex command &lt;br /&gt;
void setSyx(unsigned char adr, unsigned char val)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char *cp;&lt;br /&gt;
&lt;br /&gt;
	cp=(unsigned char *) &amp;amp;config; &lt;br /&gt;
&lt;br /&gt;
	if(adr==0x14 &amp;amp;&amp;amp; val ==0x7b) &lt;br /&gt;
	{	// write eeprom&lt;br /&gt;
		if(eepromWriteFlag==0)&lt;br /&gt;
		{&lt;br /&gt;
			setEeWrite();&lt;br /&gt;
		}&lt;br /&gt;
		return; &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// 0xF0, 0x70, 0x7D,0x00,0x16,0x04, 0x02, 0xF7&lt;br /&gt;
	if(adr==0x16 &amp;amp;&amp;amp; val ==0x42)	// Software update  &lt;br /&gt;
	{&lt;br /&gt;
		cli();			// interupts off &lt;br /&gt;
		resetAll();		// outputs off &lt;br /&gt;
		GPIOR1 = 0xAB;	// tell bootloader it&#039;s an application boot request &lt;br /&gt;
						// (hardware reset sets GPIOR1 to zero, 0xab == magic value for bootloader )&lt;br /&gt;
		bootloader();	// bye bye! &lt;br /&gt;
		return; 	// &amp;lt;-- never! &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	adr-=PARAMETEROFFSET; &lt;br /&gt;
	if(adr &amp;lt; NUM_PARAMTERS)	// if valid parameter &lt;br /&gt;
	{&lt;br /&gt;
		*(cp+adr)=val; 		// set in &lt;br /&gt;
		if(adr==0x30-PARAMETEROFFSET) // if Mode Byte 0, &lt;br /&gt;
			setConfig();			  // .. cv assignement must be recalculated &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Note Puffer Handling&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define MAX_NOTES 0x10  	// one for each finger of a real programmer &lt;br /&gt;
#define MAX_CHANNELS 4&lt;br /&gt;
unsigned char notes[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char velocities[MAX_CHANNELS][MAX_NOTES];&lt;br /&gt;
unsigned char playedNote[MAX_CHANNELS]; // number of currently pressed notes &lt;br /&gt;
// update note puffer for a midi note-on message &lt;br /&gt;
void note_on(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char n;&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	if(n&amp;lt;MAX_NOTES)&lt;br /&gt;
	{&lt;br /&gt;
		playedNote[channel]++;&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{&lt;br /&gt;
		for (n=MAX_NOTES-1;n&amp;gt;0;--n)&lt;br /&gt;
		{&lt;br /&gt;
			notes[channel][n-1]=notes[channel][n];&lt;br /&gt;
			velocities[channel][n-1]=velocities[channel][n];&lt;br /&gt;
		}&lt;br /&gt;
		n=MAX_NOTES-1;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	notes[channel][n]=note; 		&lt;br /&gt;
	velocities[channel][n]=velocity;&lt;br /&gt;
	setNote(note, velocity, 2, channel);&lt;br /&gt;
}&lt;br /&gt;
// update note puffer for a new midi note-off message &lt;br /&gt;
void note_off(unsigned char note ,unsigned char velocity, unsigned char channel)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	signed char n; &lt;br /&gt;
&lt;br /&gt;
	n=playedNote[channel];&lt;br /&gt;
&lt;br /&gt;
	--n; &lt;br /&gt;
	for (i=0;i&amp;lt;=n;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(notes[channel][i]==note)&lt;br /&gt;
		{&lt;br /&gt;
			if(i==n) // last pressed (== the on which playes) note released &lt;br /&gt;
			{&lt;br /&gt;
				if(n==0) // it was the only pressed note &lt;br /&gt;
				{&lt;br /&gt;
					setNote(note, velocities[channel][n], 0, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{	&lt;br /&gt;
					--n;&lt;br /&gt;
					setNote(notes[channel][n], velocities[channel][n], 1, channel);&lt;br /&gt;
					playedNote[channel]--;&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			else // non playing note released &lt;br /&gt;
			{	// =&amp;gt;  just remove it from list &lt;br /&gt;
				int j;&lt;br /&gt;
				for(j=i+1;j&amp;lt;=n;++j)&lt;br /&gt;
				{&lt;br /&gt;
					notes[channel][j-1]=notes[channel][j];&lt;br /&gt;
					velocities[channel][j-1]=velocities[channel][j];&lt;br /&gt;
				}&lt;br /&gt;
				playedNote[channel]--;&lt;br /&gt;
			}&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	All Notes off, empty Note Buffer, All Controllers Reset&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
void resetAll(void)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	cvoutput *cvo;&lt;br /&gt;
	cvo=cv_out;&lt;br /&gt;
&lt;br /&gt;
	cli(); // this might be called asynchronous to midi stream &lt;br /&gt;
	for(i=0;i&amp;lt;MAX_CHANNELS;++i)&lt;br /&gt;
		playedNote[i]=0;&lt;br /&gt;
	&lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;++i)&lt;br /&gt;
	{&lt;br /&gt;
		cvo-&amp;gt;cvOut=i;&lt;br /&gt;
		cvo-&amp;gt;ccVal=0;&lt;br /&gt;
		cvo-&amp;gt;on=0;&lt;br /&gt;
		cvo-&amp;gt;pb=0x80;&lt;br /&gt;
		updateCv(cvo);&lt;br /&gt;
		cvo++;&lt;br /&gt;
	} &lt;br /&gt;
	sei();&lt;br /&gt;
&lt;br /&gt;
	setLed(0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	2ms Timer Interupt&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
ISR(TIMER1_COMPA_vect)&lt;br /&gt;
{&lt;br /&gt;
	unsigned char i;&lt;br /&gt;
	static unsigned char buttonCount;&lt;br /&gt;
	static signed char blinkCount; &lt;br /&gt;
&lt;br /&gt;
	// LED blinker &lt;br /&gt;
	if(--blinkCount&amp;lt;=0)&lt;br /&gt;
	{	&lt;br /&gt;
		blinkCount=40;&lt;br /&gt;
		blinker++;&lt;br /&gt;
		if(button)&lt;br /&gt;
		{&lt;br /&gt;
			if(button&amp;lt;10)&lt;br /&gt;
				button++;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Button debouncer &lt;br /&gt;
	if(BUTTON)&lt;br /&gt;
	{	// down &lt;br /&gt;
		if(buttonCount &amp;lt; 10)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount++;&lt;br /&gt;
			if(buttonCount==10)&lt;br /&gt;
			{&lt;br /&gt;
				button=1;&lt;br /&gt;
				blinkCount=40;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	else &lt;br /&gt;
	{	// up &lt;br /&gt;
		if(buttonCount &amp;gt; 0)&lt;br /&gt;
		{&lt;br /&gt;
			buttonCount--;&lt;br /&gt;
			if(buttonCount==0)&lt;br /&gt;
				button=0;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	// Gate 5 timing (clock output) &lt;br /&gt;
	clocker++;&lt;br /&gt;
	if(gate5off)&lt;br /&gt;
	{&lt;br /&gt;
		--gate5off;&lt;br /&gt;
		if(gate5off==0)&lt;br /&gt;
			gate_off(5);	&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// activ sense &lt;br /&gt;
	if(watchTicker)&lt;br /&gt;
	{&lt;br /&gt;
		watchTicker--;&lt;br /&gt;
		if(watchTicker==0)&lt;br /&gt;
			resetAll(); &lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// retrigger timing &lt;br /&gt;
	for(i=0;i&amp;lt;NUM_CV;i++)&lt;br /&gt;
	{&lt;br /&gt;
		if(cv_out[i].gateDipper)&lt;br /&gt;
		{&lt;br /&gt;
			--cv_out[i].gateDipper;&lt;br /&gt;
			if(cv_out[i].gateDipper==0)&lt;br /&gt;
				gate_on(i);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ***********************************************************************************&lt;br /&gt;
&lt;br /&gt;
	Eval Midi Data&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#define NOTE_ON         0x90		// Midi note on message &lt;br /&gt;
#define NOTE_OFF        0x80		// Midi note off message &lt;br /&gt;
#define PITCH           0xE0		// pitch bender&lt;br /&gt;
#define AFTERTOUCH      0xD0        // after touch command (unused) &lt;br /&gt;
#define CC		        0xB0        // Continuous Controller&lt;br /&gt;
#define CHAN_PRES	    0xD0        // Channel Pressure (unused)&lt;br /&gt;
#define SONG_POSITION_POINTER 0xF2	// used for clock synchronization (first beat) &lt;br /&gt;
#define SYSEX 			0xF0		// System exclusive beginn&lt;br /&gt;
#define SYSEND			0xF7		// System exclusive end&lt;br /&gt;
&lt;br /&gt;
struct {&lt;br /&gt;
	unsigned char runningStatus;	// Midi runnig status (*)&lt;br /&gt;
	unsigned char msgByteCnt;    	// number of current midi-message byte received &lt;br /&gt;
	unsigned char byte1;			// first data byte &lt;br /&gt;
	unsigned char syxAdr; 			// Sys-Ex Adress Byte&lt;br /&gt;
	unsigned char startStop;		// current  Midi Start/Stop Status&lt;br /&gt;
	unsigned char startFlag;		// Start on next clock&lt;br /&gt;
	unsigned char clockTick;		// counter for clock division&lt;br /&gt;
	unsigned char channel;			// channel of current midi-command (relative, e.g. 0..3 ) &lt;br /&gt;
	unsigned char syncStartFlag;	// start on next 1 &lt;br /&gt;
}midi; &lt;br /&gt;
// 	(*) running status is also used as &#039;current status&#039; - it remembers allways, what next data byte is for. Or its zero, than data is ignored. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define FRAMING_ERROR (1&amp;lt;&amp;lt;FE0)&lt;br /&gt;
#define PARITY_ERROR (1&amp;lt;&amp;lt;UPE0)&lt;br /&gt;
#define DATA_OVERRUN (1&amp;lt;&amp;lt;DOR0)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// USART Receiver interrupt service routine, &lt;br /&gt;
// complete midi processing is done here, as the CPU is fast enough to &lt;br /&gt;
// do everything before next midi byte can come in. &lt;br /&gt;
ISR(USART_RX_vect) &lt;br /&gt;
{&lt;br /&gt;
	unsigned char status,byte;&lt;br /&gt;
&lt;br /&gt;
    status = UCSR0A;&lt;br /&gt;
    byte   = UDR0;&lt;br /&gt;
&lt;br /&gt;
    if ((status &amp;amp; (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0 )&lt;br /&gt;
	{&lt;br /&gt;
		if(watchTicker)			// if there had been an active sense &lt;br /&gt;
			watchTicker=160;	// every byte is ok, to see that cable is still plugged in &lt;br /&gt;
&lt;br /&gt;
		if (byte &amp;gt; 0x7F)	                // if command byte (MSB set)&lt;br /&gt;
		{&lt;br /&gt;
			if(byte&amp;gt;= 0xF8)  				// if real-time msg &lt;br /&gt;
			{&lt;br /&gt;
				switch (byte)&lt;br /&gt;
				{&lt;br /&gt;
					case 0xF8: 	// Clock &lt;br /&gt;
					{&lt;br /&gt;
						if(midi.startFlag)  // synchronized start &lt;br /&gt;
						{&lt;br /&gt;
							midi.startStop=1;&lt;br /&gt;
							midi.startFlag=0;&lt;br /&gt;
							midi.syncStartFlag=1;&lt;br /&gt;
							midi.clockTick=0;&lt;br /&gt;
							gate_on(4);&lt;br /&gt;
						}&lt;br /&gt;
						else if(midi.startStop)	// running &lt;br /&gt;
						{&lt;br /&gt;
							++spp;&lt;br /&gt;
							++barTick;&lt;br /&gt;
							if(barTick &amp;gt;= ticksPerBar)&lt;br /&gt;
								barTick=0; &lt;br /&gt;
						}&lt;br /&gt;
						++midi.clockTick; &lt;br /&gt;
						if(midi.clockTick &amp;gt;= config.clockDivider)	// clock output &lt;br /&gt;
						{&lt;br /&gt;
							midi.clockTick=0;&lt;br /&gt;
							gate_on(5);&lt;br /&gt;
							gate5off=clocker&amp;gt;&amp;gt;1; // pulse-width == half of a midi clock &lt;br /&gt;
						}&lt;br /&gt;
&lt;br /&gt;
						if(midi.syncStartFlag &amp;amp;&amp;amp; barTick==0 &amp;amp;&amp;amp; midi.startStop ) // synchronize clock out to bars   &lt;br /&gt;
						{	&lt;br /&gt;
							midi.clockTick=0;&lt;br /&gt;
							gate_on(5);&lt;br /&gt;
							gate5off=clocker&amp;gt;&amp;gt;1;&lt;br /&gt;
							midi.syncStartFlag=0;&lt;br /&gt;
						}&lt;br /&gt;
						clocker=0;  // restart midi clock pulse width counter. &lt;br /&gt;
						break;&lt;br /&gt;
					}&lt;br /&gt;
					case 0xF9:	// Tick&lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFA: 	// Start &lt;br /&gt;
						spp=0;&lt;br /&gt;
						barTick=0;&lt;br /&gt;
						midi.startFlag=1;&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFB:	// Continue&lt;br /&gt;
						midi.startFlag=1;&lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFC:	// Stop&lt;br /&gt;
						midi.startStop=0;&lt;br /&gt;
						gate_off(4);&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFD:	// unassigned &lt;br /&gt;
						break; &lt;br /&gt;
					case 0xFE:	//  Active Sense&lt;br /&gt;
						watchTicker=160;&lt;br /&gt;
						break;&lt;br /&gt;
					case 0xFF:	// Reset&lt;br /&gt;
						break; &lt;br /&gt;
				}&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
			if(byte &amp;gt;= 0xF0) 					// if System Common Category message&lt;br /&gt;
			{&lt;br /&gt;
				if(byte == SYSEX)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;&lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte==SYSEND)&lt;br /&gt;
				{&lt;br /&gt;
					if(midi.runningStatus==SYSEX &amp;amp;&amp;amp; midi.msgByteCnt==6) 	&lt;br /&gt;
						setSyx(midi.syxAdr, midi.byte1); &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
				}&lt;br /&gt;
				else if(byte ==SONG_POSITION_POINTER)&lt;br /&gt;
				{&lt;br /&gt;
					midi.runningStatus=byte;  &lt;br /&gt;
					midi.msgByteCnt = 0;&lt;br /&gt;
					return ; &lt;br /&gt;
				}	&lt;br /&gt;
				midi.runningStatus = 0;&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.runningStatus = 0;				// just new status messages left, so running status is lost&lt;br /&gt;
&lt;br /&gt;
			// only channel messages left here ... &lt;br /&gt;
			if(learn)&lt;br /&gt;
			{&lt;br /&gt;
				if(config.midiChannel != (byte&amp;amp;0xf)) // if message channel not current channel&lt;br /&gt;
				{&lt;br /&gt;
					config.midiChannel=byte&amp;amp;0xf;	// assign channel &lt;br /&gt;
					setEeWrite(); 					// and store it &lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			midi.channel=(byte &amp;amp; 0xf)-config.midiChannel ;&lt;br /&gt;
			if ( midi.channel&amp;lt;config.numMidiChannels)   // if MIDI channel matches,&lt;br /&gt;
			{&lt;br /&gt;
				midi.runningStatus = byte &amp;amp; 0xF0 ;		// set running status (==Midi command w/o channel)&lt;br /&gt;
				midi.msgByteCnt = 0;&lt;br /&gt;
				return ; &lt;br /&gt;
			}&lt;br /&gt;
			return ; &lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			switch (midi.runningStatus)		    // last command byte (or zero, if data is not for us) &lt;br /&gt;
			{&lt;br /&gt;
			case SONG_POSITION_POINTER:&lt;br /&gt;
				if(midi.msgByteCnt)	// 2nd Byte&lt;br /&gt;
				{&lt;br /&gt;
					spp= (((int)byte &amp;lt;&amp;lt; 7 ) | midi.byte1) * 6;&lt;br /&gt;
					barTick = spp % (unsigned int) ticksPerBar; &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt=1;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_ON):					// if we&#039;ve had a note on byte&lt;br /&gt;
				if (midi.msgByteCnt)		// if counter = 1&lt;br /&gt;
				{ 			   				// then we&#039;ve just received a velocity byte&lt;br /&gt;
					midi.msgByteCnt = 0;	// msg complete - clear byte count &lt;br /&gt;
					if (byte == 0)			// is it a note off (velo = 0) ?&lt;br /&gt;
						note_off(midi.byte1,byte,midi.channel);  &lt;br /&gt;
					else 					// NOTE ON&lt;br /&gt;
					{&lt;br /&gt;
						if(learn)&lt;br /&gt;
						{&lt;br /&gt;
							config.noteOffset=midi.byte1;&lt;br /&gt;
							setEeWrite();&lt;br /&gt;
						}&lt;br /&gt;
						&lt;br /&gt;
						note_on(midi.byte1, byte,midi.channel);  // ... and do what the note on has told us &lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
				else						// first data byte &lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;		// save it &lt;br /&gt;
					midi.msgByteCnt = 1;    // set counter to get 2nd data byte&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (NOTE_OFF): 	        	// if we&#039;ve had a note off byte&lt;br /&gt;
				if (midi.msgByteCnt)	 	// is second data byte ? &lt;br /&gt;
				{ 					 	&lt;br /&gt;
					midi.msgByteCnt = 0;	// clear byte count&lt;br /&gt;
					note_off(midi.byte1,byte,midi.channel); // switch note off&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1; 	// set counter to get 2nd byte&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (PITCH):		        	// PITCH BEND command&lt;br /&gt;
				if (midi.msgByteCnt)   	 	// second data byte (MSB) &lt;br /&gt;
				{ 						&lt;br /&gt;
					int v;&lt;br /&gt;
					v=byte;&lt;br /&gt;
					v&amp;lt;&amp;lt;=6;&lt;br /&gt;
					v|=midi.byte1;&lt;br /&gt;
					v-=0x2000;&lt;br /&gt;
					setPb(v,midi.channel);&lt;br /&gt;
&lt;br /&gt;
					midi.msgByteCnt = 0;	 // clear byte count&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
					midi.byte1 = byte;&lt;br /&gt;
					midi.msgByteCnt = 1;  	// set counter to get next byte&lt;br /&gt;
					return;&lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
			case (CC):	             		// Continuous Controller Message &lt;br /&gt;
				if (midi.msgByteCnt)		// if we&#039;ve had first byte&lt;br /&gt;
				{&lt;br /&gt;
					midi.msgByteCnt = 0;	// clear byte count&lt;br /&gt;
&lt;br /&gt;
					if(learn)&lt;br /&gt;
					{&lt;br /&gt;
						config.cvMidiCC[3]=midi.byte1;&lt;br /&gt;
						setEeWrite();&lt;br /&gt;
					}&lt;br /&gt;
&lt;br /&gt;
					setCC(midi.byte1, byte,midi.channel);&lt;br /&gt;
				}&lt;br /&gt;
				else&lt;br /&gt;
				{&lt;br /&gt;
				  midi.byte1 = byte;&lt;br /&gt;
				  midi.msgByteCnt = 1; 	 // set counter ready to get 3rd byte&lt;br /&gt;
				  return; &lt;br /&gt;
				}&lt;br /&gt;
				break;&lt;br /&gt;
	//		case (CHAN_PRES):	         // if channel pressure command&lt;br /&gt;
	//			channel_pressure (byte); // set Aftertouch &lt;br /&gt;
			case SYSEX:&lt;br /&gt;
				midi.msgByteCnt++;&lt;br /&gt;
				switch(midi.msgByteCnt)&lt;br /&gt;
				{&lt;br /&gt;
					case 1:	// Manufactorer &lt;br /&gt;
						if(byte==0x70)&lt;br /&gt;
							;&lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;&lt;br /&gt;
						break; &lt;br /&gt;
					case 2:	// Unit - ID &lt;br /&gt;
						if(byte==0x7D)&lt;br /&gt;
							;&lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;&lt;br /&gt;
						break; &lt;br /&gt;
					case 3:	// Midi-Adress (ignored!)&lt;br /&gt;
						if(byte&amp;lt;0x10)&lt;br /&gt;
							;&lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					case 4:	// adress byte&lt;br /&gt;
						midi.syxAdr=byte;&lt;br /&gt;
						break;&lt;br /&gt;
					case 5:	// high data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)&lt;br /&gt;
						{&lt;br /&gt;
							midi.byte1=(byte&amp;lt;&amp;lt;4);&lt;br /&gt;
						}&lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					case 6: // low data nibble &lt;br /&gt;
						if(byte&amp;lt;0x10)&lt;br /&gt;
						{&lt;br /&gt;
							midi.byte1 |= byte;&lt;br /&gt;
						}&lt;br /&gt;
						else &lt;br /&gt;
							midi.msgByteCnt=0;				&lt;br /&gt;
						break;&lt;br /&gt;
					default:	// not our framing &lt;br /&gt;
						midi.msgByteCnt=0;&lt;br /&gt;
						break;&lt;br /&gt;
				}&lt;br /&gt;
				if(midi.msgByteCnt==0)	// something did not fit &lt;br /&gt;
					midi.runningStatus=0;&lt;br /&gt;
				break;&lt;br /&gt;
			case 0: // bnwtko (bytes no one wants to know off ) &lt;br /&gt;
				return ;&lt;br /&gt;
			default:&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7276</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7276"/>
		<updated>2011-06-22T06:10:37Z</updated>

		<summary type="html">&lt;p&gt;Fetz: /* Bedienung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 || Tonhöhe (Noten) || Gate 1 || Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen. und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V. &lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
&lt;br /&gt;
=== Die Leuchtdiode === &lt;br /&gt;
Leuchtet, solange eine Note (=ein Gate) eingeschaltet ist. &lt;br /&gt;
Nach dem Einschalten blinkt sie dreimal, um anzuzeigen, dass der Controller lebt. &lt;br /&gt;
&lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
&lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen. &lt;br /&gt;
(Wo das jetzt hier zum dritten mal leicht unterschiedlich steht: die finstere Wahrheit ist, das jedes mal die komplette Konfiguration gespeichert wird. ) &lt;br /&gt;
&lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
&lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht.&lt;br /&gt;
&lt;br /&gt;
=== Service Funktionen === &lt;br /&gt;
Wird die Taste beim Einschalten festgehalten, kann man zwei Service-Funktionen wählen: &lt;br /&gt;
==== Bootlader ====&lt;br /&gt;
Wird die Taste beim Einschalten festgehalten und gleich (ähm ... Reaktionszeit eines Dinosauriers ist auch ok... )  losgelassen, wenn die Leuchtdiode zu blinken anfängt, dann kommt man direkt in den &amp;quot;Bootlader&amp;quot;, das ist ein spezielles Programm, mit dem man ein Software-Update über Midi einspielen kann. Das braucht man nur, wenn etwas schief gegangen ist (mehr dazu im Abschnitt über Software bauen und eintwickeln). Normalerweise kann man ein Update einfach aufspielen. &lt;br /&gt;
Im Bootlader blinkt die Leuchtdiode zwei mal kurz, dann etwas Pause. Das kann durch erneuten Tastendruck, oder Aus- und Einschalten abgebrochen werden. &lt;br /&gt;
==== Einstellungen zurücksetzen ====&lt;br /&gt;
Wird die Taste beim Einschalten sehr lange festgehalten (knapp 10 Sekunden, bis das Geblinke aufhört), dann werden die Einstellungen des Interfaces gelöscht und auf den Standard zurückgestellt. (Monophon, Midi-Kanal 1)&lt;br /&gt;
&lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876, er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
Mono (default). 1 Kanal Monophon. &lt;br /&gt;
&lt;br /&gt;
Multi 2. 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
Multi 4. 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
Die weiteren Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
Im DAC-Feld kann für jede CV (=jeden Steuerspannungs-Ausgang) gewählt werden, wie er gesteuert wird. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet im Tonhöhen-Modus die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser.  &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus werden die Ccs auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Die Gates 1-4 sind den Tonhöhenkanälen fest zugeordnet, gibt ein DAC eine Tonhöhe aus, ist das zugehörige Gate auch Note an/aus. &lt;br /&gt;
&lt;br /&gt;
Ist der DAC auf CC gestellt, kann der Gate-Ausgang einem Controller zugewiesen werden, das Gate ist an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note. (6=16tel, 12=8tel, 24=1/4) &lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop.&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7273</id>
		<title>MCV876 Controller Update</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=MCV876_Controller_Update&amp;diff=7273"/>
		<updated>2011-06-21T21:09:07Z</updated>

		<summary type="html">&lt;p&gt;Fetz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ATMEGA88 CPU für MCV876 Midi Interface =&lt;br /&gt;
&lt;br /&gt;
WARNUNG: Dieser Artikel ist in Arbeit und kann sich jederzeit deutlich ändern. &lt;br /&gt;
&lt;br /&gt;
Das Projekt ist zwar schon recht weit fortgeschritten (sprich: die Software tut, was sie soll), die Dokumentation ist aber derzeit Baustelle und unvollständig. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Worum geht’s? ==&lt;br /&gt;
Das Midi-Interface MCV876 von Marc Bareille hat im Original einen PIC-Controller. Der muss aus Rechenleistungs- und Speichergründen in Assembler programmiert werden, was Wartung und Pflege erschwert. &lt;br /&gt;
&lt;br /&gt;
Hier wird ein alternativer Controller für die gleiche Hardware beschrieben. Mit dem Atmel ATMEGA 88 steht ein halbwegs Pinkompatibler Controller zur Verfügung, der komfortabel in C programmiert werden kann. Um diesen Controller auf der für den PIC gedachten Platine verwenden zu können, ist ein Adapter-Sockel nötig. &lt;br /&gt;
Die Software für den ATMEGA ist einiger Hinsicht kompatibel zum Original-Controller. Allerdings ist es kein 100% Clone, einige Funktionen der PIC-Software sind mir auch verborgen geblieben. &lt;br /&gt;
&lt;br /&gt;
Deshalb kann es sehr gut sein, dass die Funktionen abweichen. Diese Beschreibung bezieht sich ausschließlich auf die Software für den ATMEL Controller. &lt;br /&gt;
Der Controller wurde mit Blick auf dem Einsatz im Semtex XL (und vergleichbare Mono-Synths) entwickelt, für andere Einsatzfälle mögen Systementscheidungen daher nicht optimal sein. &lt;br /&gt;
Die Software ist unter der GPL veröffentlicht. Als Entwicklungsumgebung kommt das (kostenlos verfügbare) ATMEL AVR Studio 4 zum Einsatz. Ist der Controller einmal mit einem Bootlader versehen worden, ist ein Software-Update über Midi im Gerät möglich. Damit kann man mit komplett kostenlos verfügbarer ToolChain und ohne (Programmier-) Hardware eigene Codeänderungen einpflegen. &lt;br /&gt;
&lt;br /&gt;
== Ausstattungsmerkmale ==&lt;br /&gt;
Die Beschreibung ist für das komplette Interface, d.h. MCV876 Platine (bzw. der entsprechende Teil auf der SEMTEX-XL Platine) mit dem ATMEGA88 Controller. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hardware&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
4 Spannungsausgänge (CV) von 0-10V &lt;br /&gt;
&lt;br /&gt;
6 Gate Ausgänge 0 auf 5V schaltend. &lt;br /&gt;
&lt;br /&gt;
1 LED „Universalanzeige“ &lt;br /&gt;
&lt;br /&gt;
1 Taster. &lt;br /&gt;
&lt;br /&gt;
Midi-Eingang &lt;br /&gt;
&lt;br /&gt;
Midi-Thru Ausgang &lt;br /&gt;
&lt;br /&gt;
(Es gibt keinen Midi-Ausgang, das Interface kann also nicht ausgelesen werden. ) &lt;br /&gt;
== Funktionsübersicht – Standardbelegung ==&lt;br /&gt;
Das Interface kann zwar (über Midi und Sys-Ex Befehle) durchaus unterschiedlich konfiguriert werden. Zunächst aber die Standardbelegung. Die ist einkanalig und monophon. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;table&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
| CV 1 || Tonhöhe (Noten) || Gate 1 || Note-ON&lt;br /&gt;
|-&lt;br /&gt;
| CV 2 || Velocity || Gate 2 || CC 18&lt;br /&gt;
|-&lt;br /&gt;
| CV 3 || Mod Wheel || Gate 3 || CC 19 &lt;br /&gt;
|-&lt;br /&gt;
| CV 4 || Pitch Bend || Gate 4 || CC 20 &lt;br /&gt;
|-&lt;br /&gt;
|  || || Gate 5 || Midi Start/Stop&lt;br /&gt;
|-&lt;br /&gt;
|  ||  || Gate 6 || Midi Clock /6 (entsprechend 16-tel) &lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
CV 1 und Gate 1 sind die Standardausgänge für den Tonhöhen. und Gate-Eingang des Synths. Die CV ist für 1V/Oktave ausgelegt. &lt;br /&gt;
CV2 ist die Anschlagstärke und geht von 0-5V. &lt;br /&gt;
CV3 ist der Pitchbender, in Mittelstellung liegen hier 5V an, es geht von 0...10V. &lt;br /&gt;
CV4 ist das Modulationsrad, von 0 (Ruhestellung) bis 5V. &lt;br /&gt;
= Bedienung =&lt;br /&gt;
Im „Normalbetrieb“ gibt es gar nichts zu Bedienen, das Interface setzt eben Midi auf CV &amp;amp; Gate um. &lt;br /&gt;
=== Midi-Kanalwahl ===&lt;br /&gt;
Um den Midi Kanal neu Einzustellen die Taste vom Interface drücken, bis die LED leuchtet. &lt;br /&gt;
Dann eine Note mit dem gewünschten Kanal am Keyboard spielen. Das Interface stellt sich auf diesen Kanal ein. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Kanals anzuzeigen. &lt;br /&gt;
=== Grund-Note einstellen ===&lt;br /&gt;
Die „tiefste Note“, also die Note, bei der das Interface 0 V ausgibt, wird ganz ähnlich wie der Kanal eingestellt. Taste drücken, bis die LED angeht, dann gewünschte tiefste Note spielen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern der Note anzuzeigen. &lt;br /&gt;
Controller für CV3 einstellen &lt;br /&gt;
Taste drücken, bis die LED angeht, dann einen Controller bewegen. Dieser Controller wird dem dritten Steuerspannungsausgang zugewiesen. &lt;br /&gt;
Die LED blinkt drei mal, um das permanente Abspeichern des Controllers anzuzeigen. &lt;br /&gt;
(Wo das jetzt hier zum dritten mal leicht unterschiedlich steht: die finstere Wahrheit ist, das jedes mal die komplette Konfiguration gespeichert wird. ) &lt;br /&gt;
=== Alle Noten aus ===&lt;br /&gt;
Taste kurz drücken. Dann werden alle Noten auf „aus“ gesetzt und die Controller auf 0 zurückgestellt. &lt;br /&gt;
Da Midi immer nur Datenänderungen überträgt, kann es schon mal zu hängenden Noten und schief stehenden Controllern kommen. Dann hilft ein kurzer Druck auf die Taste. &lt;br /&gt;
Das Interface selber sollte das bei korrektem Midi-Datenstrom allerdings niemals alleine fertigbringen. Nur ist nicht jeder Midi-Datenstrom immer korrekt. &lt;br /&gt;
=== Active Sense ===&lt;br /&gt;
Das Interface wertet Active-Sense Nachrichten aus. Erzeugt der Controller (bzw. das sendende Midi-Interface) Active-Sense und zieht man dann das Midi-Kabel ab, werden (nach 330ms) alle Noten ausgeschaltet. &lt;br /&gt;
Roland und Edirol senden das eher mal, die  andern eher nicht. &lt;br /&gt;
= Erweiterte Einstellungen =&lt;br /&gt;
Der Controller ist Sys-Ex-Kompatibel zum PIC-Controller des MCV876, er lässt sich daher mit dem gleichen PC-Programm einstellen. &lt;br /&gt;
&lt;br /&gt;
Bei der Programmierung wird der Midi-Kanal ignoriert, man kann also nur ein Interface diesen Typs zur Zeit (oder mehrere genau gleich) konfigurieren. Damit die Konfiguration permanent bleibt, muss sie gespeichert werden (Chip-Symbol drücken, LED am Interface blinkt 3 mal. ) &lt;br /&gt;
&lt;br /&gt;
Es werden 3 grundsätzliche Betriebsarten (Mode) unterschieden: &lt;br /&gt;
Mono (default). 1 Kanal Monophon. &lt;br /&gt;
&lt;br /&gt;
Multi 2. 2 Kanal Monophon &lt;br /&gt;
&lt;br /&gt;
Multi 4. 4 Kanal Monophon&lt;br /&gt;
&lt;br /&gt;
Die weiteren Kanäle werden über den nächsten oder die drei nächsten Midi-Kanäle angesteuert. (Sollte man dabei Kanäle „über 16“ bekommen, kann man diese nicht ansteuern. ) &lt;br /&gt;
&lt;br /&gt;
Im DAC-Feld kann für jede CV (=jeden Steuerspannungs-Ausgang) gewählt werden, wie er gesteuert wird. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aus (nicht grün), dann wird eine Tonhöhenspannung erzeugt. Im „Mono“ Mode ist das ggfls. 4 mal die gleiche, in Multi2 gehören DAC 1 und 2 zum ersten und DAC 3 und 4 zum 2. Midi-Kanal. In Multi 4 gehört jeder DAC zu einem Midi-Kanal. &lt;br /&gt;
&lt;br /&gt;
Ist das PB-Feld aktiv, wird der Pitch-Bend Wert auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das VL-Feld aktiv, wird die Anschlagstärke (Velocity) auf die Ausgangsspannung addiert. &lt;br /&gt;
&lt;br /&gt;
Ist das CC-Feld aktiv, wird der eingestellte Controller als Grundwert genommen, und nicht die Tonhöhe. &lt;br /&gt;
&lt;br /&gt;
Das 5V-Feld schaltet im Tonhöhen-Modus die Ausgabe so um, das ein auf 5V Full-Scale skalierter Wandler 1V/Oktave macht. Das sind dann nur 5 Oktaven, aber doppelt so viele Bits, d.h. die Töne treffen besser.  &lt;br /&gt;
&lt;br /&gt;
Im CC-Modus werden die Ccs auf 5V skaliert, nicht auf 10V. Da sie nur 7bittig sind ist das ausreichend, oft sinnvoller skaliert, und dadurch das es einige „Additionen“ gibt generell eher sinnvoll. &lt;br /&gt;
&lt;br /&gt;
Die Gates 1-4 sind den Tonhöhenkanälen fest zugeordnet, gibt ein DAC eine Tonhöhe aus, ist das zugehörige Gate auch Note an/aus. &lt;br /&gt;
&lt;br /&gt;
Ist der DAC auf CC gestellt, kann der Gate-Ausgang einem Controller zugewiesen werden, das Gate ist an, wenn der Controller über den Wert gestellt wird, der bei Tresh.(hold) eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Duration und Gate 5 im „Triggers“-Feld haben keine Funktion. (Wenn mir dazu was sinnvolles einfällt, dann ändert sich das noch..) &lt;br /&gt;
&lt;br /&gt;
Unter Midi-Clock wird eingestellt, was Gate 6 erzeugt. Midi-Clock sind 24 Ticks pro Viertel-Note. (6=16tel, 12=8tel, 24=1/4) &lt;br /&gt;
Gate 5 ist immer Midi-Start/Stop.&lt;br /&gt;
= Hardware =&lt;br /&gt;
Verwendet wird ein ATMEGA 88. &lt;br /&gt;
Der ist gut erhältlich, nicht all zu teuer (um 3.60€) und für die Anwendung ausreichend. Er hat einen getrennten und schütztbaren Bootlader-Bereich und kann (Bootlader-)Code ausführen, während er den Anwenndungs-Programmspeicher ändert. Das ist sehr hilfreich, um beim Update über Midi eine unproblematische Handhabung zu erzielen. &lt;br /&gt;
&lt;br /&gt;
Der Atmel Controller ist nicht völlig pinkompatibel programmierbar, daher ist ein Adaptersockel nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:Adapter-sockel.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;So kann der Sockel aussehen&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Oben ist ein normaler 28-poliger DIL-Sockel, unten sind zwei 14 polige Stecksockel von Fischer. ( Conrad 187607 ) &lt;br /&gt;
(Die drei extra-Pins zur Seite braucht man nicht, die sind nur Anschluss für meinen Debugger.) &lt;br /&gt;
&lt;br /&gt;
Der Verdrahtungplan: &lt;br /&gt;
&lt;br /&gt;
[[File:Adapter.gif]]&lt;br /&gt;
&lt;br /&gt;
Die Pins mit den grünen = werden 1:1 aneinander gelötet, bei den roten X werden die Pins am Sockel weg gekniffen und die Verbindung wird mit einem Draht hergestellt.&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Adapter.gif&amp;diff=7272</id>
		<title>Datei:Adapter.gif</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Adapter.gif&amp;diff=7272"/>
		<updated>2011-06-21T21:04:27Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Verdrahtungsplan des Adaptersockels 
(A=Atmel-Pins, P=Platinen-Pins)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Verdrahtungsplan des Adaptersockels &lt;br /&gt;
(A=Atmel-Pins, P=Platinen-Pins)&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
	<entry>
		<id>https://www.sequencer.de/synth/index.php?title=Datei:Adapter-sockel.jpg&amp;diff=7271</id>
		<title>Datei:Adapter-sockel.jpg</title>
		<link rel="alternate" type="text/html" href="https://www.sequencer.de/synth/index.php?title=Datei:Adapter-sockel.jpg&amp;diff=7271"/>
		<updated>2011-06-21T20:52:44Z</updated>

		<summary type="html">&lt;p&gt;Fetz: Adapter-Sockel um den Atmel-Controller in einen PIC-sockel stecken zu können. Die drei Pins an der Seite sind nur für Debug-Zwecke nötig, nicht für den normalen Einsatz.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Adapter-Sockel um den Atmel-Controller in einen PIC-sockel stecken zu können. Die drei Pins an der Seite sind nur für Debug-Zwecke nötig, nicht für den normalen Einsatz.&lt;/div&gt;</summary>
		<author><name>Fetz</name></author>
	</entry>
</feed>