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