В предыдущей статье первый блок для загрузки мы подгружали извне с помощью командной строки. Ввиду того, что энергонезависимая память в STM8 пишется/стирается байтами, словами (4 байта), блоками (64 байта) и у областей EEPROM, OPTION, FLASH различные сигнатуры/регистры разрешения записи/стирания нам потребуется некоторое количество различных дампов для поочередной передачи в загрузчик. Логично было бы хранить все необходимые дампы в исходном коде в виде, напр. (не забываем об обратом порядке байтов):
public readonly byte[] ReadBlock_128000 = {0x00, ..., 0x01};
В этой статье пишем создатель дампов который
- принимает в командной строке файл прошивки;
- извлекает из области RAM массив байтов;
- изменяет порядок байтов в массиве на обратный;
- создает текстовый файл с именем как у файла прошивки;
- создает переменну типа byte[] с имененм как у файла прошивки;
- инициализирует переменную массивом байтов;
- сохраняет в текстовый файл;
- выходит.
Классы [FileOpenMemorySorting], [IntelHEXfile], [MotorolaS19file]. Файл dump_creator.cs :
// dump_creator.cs using System; using System.IO; using System.Collections.Generic; public partial class Dump_creator { public static void Main(string[] arg) { if (arg.Length == 0) {Console.WriteLine("Отсутствуют аргументы командной строки"); Console.ReadKey(); return;} else fileName = arg[0]; Console.WriteLine("Обнаружены {0} аргумент(ов) командной строки", arg.Length); foreach(string strArg in arg){ Console.WriteLine(strArg); }//foreach Console.WriteLine("Загружен файл {0}", fileName); fileToWrite = fileName.Replace(".s19", ""); fileToWrite = fileToWrite.Replace(".S19", ""); fileToWrite = fileToWrite.Replace(".hex", ""); fileToWrite = fileToWrite.Replace(".HEX", ""); stringDump = stringDump + fileToWrite + " = { "; FileOpenMemorySorting fileSorted = new FileOpenMemorySorting(fileName); Listlist_Bytes = new List (); list_Bytes.Clear(); Console.Write("\nRAM memory:"); foreach( KeyValuePair kvp in fileSorted.GetRAMaddressByteSorted() ){ //if(kvp.Key % 16 == 0) Console.Write("\n${0:X4} ", kvp.Key); if(kvp.Key % 16 == 0) Console.Write("\n${0:X4} ", kvp.Key); Console.Write("${0:X2} ", kvp.Value); }// foreach Console.WriteLine("\n"); // извлечем дамп для RAM памяти foreach( KeyValuePair kvp in fileSorted.GetAllMemoryaddressByteSorted() ){ list_Bytes.Add(kvp.Value); }// foreach list_Bytes.Reverse(); // дамп должен быть передан в обратном порядке byte[] bytesDump = list_Bytes.ToArray(); for(int i = 0; i < bytesDump.Length; i++){ stringDump = stringDump + String.Format("0x{0:X2}", bytesDump[i]); if(i < bytesDump.Length - 1) stringDump = stringDump + ", "; if(i % 16 == 0) stringDump = stringDump + "\n"; } stringDump = stringDump + " };\n"; Console.WriteLine(stringDump); StreamWriter fstr_out; try { fstr_out = new StreamWriter(fileToWrite + ".txt"); } catch(IOException exc) { Console.WriteLine(exc.Message); Console.ReadLine(); return; } try { fstr_out.WriteLine(stringDump); } catch(IOException exc) { Console.WriteLine(exc.Message); Console.ReadLine(); return; } fstr_out.Close(); Console.ReadKey(); return; } // Main(); public static string stringDump = " public readonly byte[] "; public readonly byte[] ReadBlock_128000 = {0x00, 0x01}; public static string fileName; public static string fileToWrite; } // dump_creator.cs
Файл first_block.s19 (из предыдущей статьи) :
S00600004844521B S11300003500523335015232350C5235720B5230B1 S1130010FBC6523195720B5230FBC6523197721A9D S11300205007721A50083520523690AE0040F6C779 S11300305231720F5230FB5C905A26F2721B5007F9 S1090040721B500820C6EB S9030000FC
Файл run.bat :
dump_creator first_block.s19 pause
Программа, прошивка, bat-ник одним архивом[dump_creator.zip].
Запускаем bat файл:.
Созданный программой файл first_block.txt :
public readonly static byte[] first_block = { 0xC6, 0x20, 0x08, 0x50, 0x1B, 0x72, 0x07, 0x50, 0x1B, 0x72, 0xF2, 0x26, 0x5A, 0x90, 0x5C, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xC7, 0xF6, 0x40, 0x00, 0xAE, 0x90, 0x36, 0x52, 0x20, 0x35, 0x08, 0x50, 0x1A, 0x72, 0x07, 0x50, 0x1A, 0x72, 0x97, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x95, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x35, 0x52, 0x0C, 0x35, 0x32, 0x52, 0x01, 0x35, 0x33, 0x52, 0x00, 0x35 };
Исходники:[dump_creator_src.zip] и [first_block_asm.zip] .
Теперь самое время пересобрать проект C# из предыдущей статьи путем вставки полученного дампа в исходный код. Добавим также запись содержимого памяти STMS103F3 в файл stringMemoryMap.txt
Файл STM8S103F3 uLoader.cs :
// STM8uLoader.cs using System; using System.IO; using System.IO.Ports; using System.Threading; using System.Collections.Generic; public partial class STM8S103F3 uLoader { public static void Main() { // инициализация COM порта if(SerialPort.GetPortNames().Length == 0) { Console.WriteLine("COM порты не найдены"); Console.ReadKey(); return;} Console.WriteLine("Доступны COM порты:"); foreach (string s in SerialPort.GetPortNames()) { Console.WriteLine(" {0}", s); portName = s; } // создаем экземпляр COM-порта с настройками для общения с начальным загрузчиком sPort = new SerialPort(portName, 9600); sPort.ReadBufferSize = 20000; sPort.Open(); //открываем COM порт //queueBytes = new Queue(); //readThread = new Thread(Read); //readThread.Start(); // запускаем поток чтения COM порта Console.WriteLine("Ждем байт 0x14 версии загрузчика. Нажми кнопку сброса на плате."); while(unready) { //while(queueBytes.Count == 0){} rx_byte = queueBytes.Dequeue(); while(sPort.BytesToRead == 0){} rx_byte = (byte)sPort.ReadByte(); // ждем версию загрузчика if(rx_byte == 0x14) { Console.WriteLine("Принят байт 0x{0:X2} от boot_OPTION", rx_byte); Console.WriteLine("Отправляем размер 0x{0:X2} байт блока", first_block.Length); byte[] txBytes = {(byte)first_block.Length}; sPort.Write(txBytes, 0, 1); // отправляем размер дампа unready = false; } }//while(unready) sPort.Write(first_block, 0, first_block.Length); // отправляем сам дамп Thread.Sleep(500); // подождем пока очистится буфер передачи // переходим на общение с отправленным дампом, меняем скорость COM-порта sPort.BaudRate = 128000; // где-то здесь не плохо бы очистить приемный буфер COM-порта int i = 0x0000; // адрес первого блока //queueBytes.Clear(); // очистим очередь приема for (; i <= 0x9FFF; ){ Console.WriteLine(); //Console.WriteLine("${0:X4} ", i); int j = 0x003F; // размер блока 64 байта byte[] txBytes = {(byte)(i>>8), (byte)i}; sPort.Write(txBytes, 0, txBytes.Length); // отправляем адрес текущего блока Thread.Sleep(20000*j/sPort.BaudRate); // 2994 для sPort.BaudRate == 64000 и j == 0x1FFF for (; j >= 0x0000; i++, j-- ){ // принимаем 64 байта //while(queueBytes.Count == 0){} rx_byte = queueBytes.Dequeue(); while(sPort.BytesToRead == 0){} rx_byte = (byte)sPort.ReadByte(); //if((j+1) % 32 == 0) Console.Write("\n${0:X4} ", i); if((j+1) % 16 == 0) Console.Write("\n${0:X4} ", i); Console.Write("${0:X2} ", rx_byte); //if (j==0x0000 | j==0x0010 | j==0x0020 | j==0x0030) Console.WriteLine(); // 16 байт в строке //if (j==0x0000 | j==0x0020 ) Console.WriteLine(); // 32 байта в строке } }//for (; i <= 0x9FFF; ) Console.WriteLine(); Console.Write("\nПодождите идет повторное чтение памяти с записью в файл.\n"); // повторим в файл i = 0x0000; // адрес первого блока //queueBytes.Clear(); // очистим очередь приема for (; i <= 0x9FFF; ){ //Console.WriteLine(); stringMemoryMap = stringMemoryMap + "\n"; //Console.WriteLine("${0:X4} ", i); int j = 0x003F; // размер блока 64 байта byte[] txBytes = {(byte)(i>>8), (byte)i}; sPort.Write(txBytes, 0, txBytes.Length); // отправляем адрес текущего блока Thread.Sleep(20000*j/sPort.BaudRate); // 2994 для sPort.BaudRate == 64000 и j == 0x1FFF for (; j >= 0x0000; i++, j-- ){ // принимаем 64 байта //while(queueBytes.Count == 0){} rx_byte = queueBytes.Dequeue(); while(sPort.BytesToRead == 0){} rx_byte = (byte)sPort.ReadByte(); //if((j+1) % 32 == 0) Console.Write("\n${0:X4} ", i); if((j+1) % 16 == 0) stringMemoryMap = stringMemoryMap + String.Format("\n${0:X4} ", i); //if((j+1) % 16 == 0) Console.Write("\n${0:X4} ", i); stringMemoryMap = stringMemoryMap + String.Format("${0:X2} ", rx_byte); //Console.Write("${0:X2} ", rx_byte); //if (j==0x0000 | j==0x0010 | j==0x0020 | j==0x0030) Console.WriteLine(); // 16 байт в строке //if (j==0x0000 | j==0x0020 ) Console.WriteLine(); // 32 байта в строке } }//for (; i <= 0x9FFF; ) stringMemoryMap = stringMemoryMap + "\n"; StreamWriter fstr_out; try { fstr_out = new StreamWriter("stringMemoryMap.txt"); } catch(IOException exc) { Console.WriteLine(exc.Message); Console.ReadLine(); return; } try { fstr_out.WriteLine(stringMemoryMap); } catch(IOException exc) { Console.WriteLine(exc.Message); Console.ReadLine(); return; } fstr_out.Close(); Console.Write("\nГотово.\n"); //readThread.Join(); sPort.Close(); Console.ReadKey(); return; } // Main(); /* public static void Read() { while (inCycle) { try { if(sPort.BytesToRead > 0){ queueBytes.Enqueue((byte)sPort.ReadByte()); } } catch (TimeoutException) { } } }*/ //public static Thread readThread; //public static Queue queueBytes; public static string stringMemoryMap = ""; public static bool inCycle = true; public static byte rx_byte = 0x00; public static bool unready = true; public static string fileName; public static SerialPort sPort; public static string portName; public readonly static byte[] first_block = { 0xC6, 0x20, 0x08, 0x50, 0x1B, 0x72, 0x07, 0x50, 0x1B, 0x72, 0xF2, 0x26, 0x5A, 0x90, 0x5C, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xC7, 0xF6, 0x40, 0x00, 0xAE, 0x90, 0x36, 0x52, 0x20, 0x35, 0x08, 0x50, 0x1A, 0x72, 0x07, 0x50, 0x1A, 0x72, 0x97, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x95, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x35, 0x52, 0x0C, 0x35, 0x32, 0x52, 0x01, 0x35, 0x33, 0x52, 0x00, 0x35 }; } // STM8uLoader.cs
BAT файл теперь не требуется. Просто запускаем файл [STM8uLoader.exe], нажимаем кнопку сброса на плате STM8 и ждем появление файла [stringMemoryMap.txt] .
Исходники:[boot_PC.zip] и [first_block_asm.zip] .
Если first_block.asm назвать прикладной программой, то мы отправили дамп прикладной программы и передали ей управление. Если ограничиться размером 255 байт, забыть про прерывания и подпрограммы (дамп располагается в области стека RAM памяти) и пытаться сохранить от перезаписи области FLASH и EEPROM, то уже можно назваться "прграмматором-загрузчиком". В следующей статье пора подумать, что нам нужно для полноценного освоения областей RAM, EEPROM, FLASH.