NFLic

STM8uLoader

Класс Dumps. Модули чтения памяти (Статья 7)

В предыдущей статье научились создавать дампы и вставлять дампы в исходный код. Попытаемся составить примерный перечень дампов с учетом того, что энергонезависимая память в STM8 пишется/стирается байтами, словами (4 байта), блоками (64 байта) и у областей EEPROM, OPTION, FLASH различные сигнатуры/регистры разрешения записи/стирания и с учетом выбранной скорости 128000 UART:

- Read_128000v14 - этот дамп должен уметь читать все адресное пространство STM8S103F3 от $0000 до $9FFF (раздвинем границу до $EFFF);

- Write_RAM_128000v14 - этот дамп должен уметь писать в адресном пространстве от $0000 до $03FF;

- Copy_RAM_128000v14 - этот дамп должен уметь копировать в адресное пространство от $0000 до $03FF;

- WriteByte_EEPROM_128000v14 - этот дамп должен уметь писать в адресном пространстве от $4000 до $427F;

- WriteWord_EEPROM_128000v14 - этот дамп должен уметь писать в адресном пространстве от $4000 до $427F;

- WriteBlock_EEPROM_128000v14 - этот дамп должен уметь писать в адресном пространстве от $4000 до $427F;

- CopyByte_EEPROM_128000v14 - этот дамп должен уметь копировать в адресное пространство от $4000 до $427F;

- CopyWord_EEPROM_128000v14 - этот дамп должен уметь копировать в адресное пространство от $4000 до $427F;

- CopyBlock_EEPROM_128000v14 - этот дамп должен уметь копировать в адресное пространство от $4000 до $427F;

- WriteByte_OPTION_128000v14 - этот дамп должен уметь писать в адресном пространстве от $4800 до $480A;

- WriteByte_FLASH_128000v14 - этот дамп должен уметь писать в адресном пространстве от $8000 до $9FFF;

- WriteWord_FLASH_128000v14 - этот дамп должен уметь писать в адресном пространстве от $8000 до $9FFF;

- WriteBlock_FLASH_128000v14 - этот дамп должен уметь писать в адресном пространстве от $8000 до $9FFF;

- EraseByte_FLASH_128000v14 - этот дамп должен уметь стирать в адресном пространстве от $8000 до $9FFF;

- EraseWord_FLASH_128000v14 - этот дамп должен уметь стирать в адресном пространстве от $8000 до $9FFF;

- EraseBlock_FLASH_128000v14 - этот дамп должен уметь стирать в адресном пространстве от $8000 до $9FFF;

- CopyByte_FLASH_128000v14 - этот дамп должен уметь копировать в адресное пространство от $8000 до $9FFF;

- CopyWord_FLASH_128000v14 - этот дамп должен уметь копировать в адресное пространство от $8000 до $9FFF;

- CopyBlock_FLASH_128000v14 - этот дамп должен уметь копировать в адресное пространство от $8000 до $9FFF;

 

Требуется проверка необходимости выравнивания адресов по $xxx0, $xxx4, $xxx8, $xxxC при записи/стирании словами и по $xx00, $xx40, $xx80, $xxC0 при записи/стирании блоками. В адресах $4800 до $480A расположены пары взаимоинверсных OPTION байтов, запись в эту область может изменить скорость работы UART и еще кое-какие препятствия для функционирования начального копировщика boot_FLASH и загрузчика boot_OPTION. Следует не забывать, что в блоке памяти OPTION Bytes (адреса $480B...$483F) находится образ начального загрузчика boot_OPTION, а в адресах $8000...$8003 и $9FF0...$9FFD находится код начального копировщика boot_FLASH. Следует корректно писать в регистровую пару $9FFE:$9FFF с учетом фактического расположения кода/образа прикладной программы во FLASH/EEPROM памяти.

Ввиду ограниченного адресного простраства младших моделей STM8 диапазоном $0000...$9FFF (для STM8S103F3 в частности) старший байт адреса в каждом дампе можно нагрузить дополнительным функционалом при значениях более $9F (раздвинем границу для чтения до $FEFF. Здесь могут быть команды передачи управления прикладной программе, начальному копировщику boot_FLASH, начальному загрузчику boot_OPTION, команды сброса и пр.

Попытаемся сформулировать общий функционал для всех дампов (модулей). Каждый дамп должен уметь:

1 - принимать по UART старший байт адреса и перейти к п.2, если байт является командой перейти на п.4;

2 - отправить по UART эхо старшего байта адреса;

2 - принимать по UART младший байт адреса;

3 - выполнить свой основной функционал и вернуться на п.1;

4 - если команда передачи управления (примем пока $F5), отправить эхо команды, принять старший и младший байты адреса в индексный регист Y, принять старший и младший байты адреса перехода и сохранить их в ячейках $03FE и $03FF, принять размер следующего модуля и пометить его в регистр A, загрузить в индексный регистр X и указатель вершины стека SP величину $03FF, передать управление по адресу в регистровой паре $03FE:$03FF;

5 - если неизвестная команда отправить по UART код ошибки (примем пока $F1) и перейти к п1.

 

Программа boot_PC при формировании адреса передачи управления должна учитывать :

- для версии $x4 начального загрузчика boot_OPTION (нужен для смены дампа) управление передается на метку boot_OPTION_rx_wait_block: по адресу $0017 (для сохранения скорости 128000 UART) при этом регистр A уже должен содержать размер следующего блока;

- для версии $1x начального копировщика boot_FLASH (нужен для переноса образа из EEPROM в RAM и, возможно, для переноса образа из резервных областей HW регистров в области RAM занятые текущим дампом либо для полного заполнения RAM из резервгых областей HW регистров) управление передается на метку boot_FLASH_copy по адресу $9FF4;

- передача управления по адресу $8000 (вектор сброса) не имеет смысла так как обнуляется указатель вершины стека и начальный копировщик boot_FLAH не сможет перенести код начального загрузчика boot_OPTION в RAM память, для этого мы имеем кнопку сброса;

- прикладная программа в RAM памяти может находиться в диапазоне адресов $0035...$03FF(минус размер критического кода дампа) для сохранения от повреждения кода текущего дампа и начального загрузчика boot_OPTION или во всем диапазоне адресов RAM если удастся воспользоваться временным хранилищем в резервной области HW регистров;

- прикладная программа во FLAH памяти может находиться в диапазоне адресов $8004...$9FEF.

 

В этой статье создадим класс [Dumps] и наполним его методами и модулями для чтения памяти STM8.

 

Классы [FileOpenMemorySorting], [IntelHEXfile], [MotorolaS19file].

 

Файл Dumps.cs :

 

// Dumps.cs
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Collections.Generic;

public class Dumps
{
        public Dumps()
        {
	
			//Load_Dump(ReadByte_128000v14);
	
			// переходим на общение с отправленным дампом, меняем скорость COM-порта
			// sPort.BaudRate = 128000;
        	
        }// Dumps()
        
	    public static void Load_First_Dump(byte[] dmp){
	    	// метод вызывается при загрузке первого блока в верхние адреса RAM после нажатия кнопки сброса
	    	byte[] dmpToLoad = dmp;
        	
			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);		
					byte[] txBytes = {(byte)dmpToLoad.Length};
					sPort.Write(txBytes, 0, 1); // отправляем размер дампа
					unready = false;
				} else Console.WriteLine("Принят байт 0x{0:X2}. Нажми кнопку сброса на плате.", rx_byte);
	
			}//while(unready)
			sPort.Write(dmpToLoad, 0, dmpToLoad.Length);				// отправляем сам дамп
			while(sPort.BytesToWrite > 0){}
			Thread.Sleep(500); // подождем пока очистится буфер передачи
			sPort.BaudRate = 128000;
			Thread.Sleep(10);
			Console.WriteLine();
        }// Load_first_Dump()

        
	    public static void Load_Dump(byte[] dmp){
	    	// метод вызывается при замене блока в верхних адресах RAM памяти
	    	// необходимо отправить команду загруженному блоку в формате  { GO cmd, YH, YL, go_adrH, go_adrL, cntr }
	    	err_Msg = "OK";
	    	byte[] dmpToLoad = dmp;
	    	go_bytes[0] = 0xF5;	//  GO cmd команда перехода
	    	// go_bytes[1] = 0x00;	//  YH не для этого метода
	    	// go_bytes[2] = 0x00;	//  YL не для этого метода
	    	go_bytes[3] = 0x00;	//  go_adrH 	старший байт требуемого адреса boot_OPTION
	    	go_bytes[4] = 0x17;	//  go_adrL 	младший байт требуемого адреса boot_OPTION
	    	go_bytes[5] = (byte)dmpToLoad.Length;	//  cntr 	размер следующего к загрузке блока
        	
        	queueBytes.Clear(); // очистим очередь приема

        	sPort.Write(go_bytes, 0, 1); // передаем команду $F5
        	
				while(queueBytes.Count == 0){}  rx_byte = queueBytes.Dequeue(); 
				        	if ( rx_byte != go_bytes[0] ){ 
        						err_Msg = "Эхо не соответствует команде 0xF5"; 
        						return; }
        	sPort.Write(go_bytes, 1, go_bytes.Length-1); // передаем адреса и размер модуля
        	sPort.Write(dmpToLoad, 0, dmpToLoad.Length); // передаем сам модуль
			while(sPort.BytesToWrite > 0){} Console.WriteLine("Модуль отправлен в boot_OPTION\n");
			Thread.Sleep(10); 
	    }// Load_Dump()
        
        public static SortedDictionary Read_128000(int adr, int cntr){
        	err_Msg = "OK";
        	int addrSpc = adr;	//  начальный адрес оласти памяти для выгрузки (адрес первого блока)
        	int cntrSpc = cntr;	//  размер оласти памяти для выгрузки
			int sizeBlck = 64;  // размер блока 
			SortedDictionary AddressByte = new SortedDictionary();
        	AddressByte.Clear();
        	
        	if ( addrSpc < 0x0000 | addrSpc >= 0xF000){ 
        		err_Msg = "Адрес должен быть в диапазоне 0x0000...0xEFFF"; 
        		return AddressByte; 
        	}

			int j = sizeBlck;  // размер блока 64 байта
			queueBytes.Clear(); // очистим очередь приема
			for (; cntrSpc > 0; ){
				if(cntrSpc > 64) j = sizeBlck; else j = cntrSpc;
				//Console.WriteLine(" ${0:X4}", addrSpc);
				//queueBytes.Clear(); // очистим очередь приема
			    
				tx2Bytes[0] = (byte)(addrSpc>>8);
				tx2Bytes[1] = (byte)addrSpc;
	
				sPort.Write(tx2Bytes, 0, 1); // отправляем старший байт адрес блока / команду
	
					while(queueBytes.Count == 0){}  rx_byte = queueBytes.Dequeue(); 
					//Console.WriteLine("Принят байт 0x{0:X2}.", rx_byte);
					        	if ( rx_byte != tx2Bytes[0] ){ 
	        						err_Msg = "Эхо не соответствует старшему байту адреса"; 
	        						return AddressByte;}
        	
					sPort.Write(tx2Bytes, 1, 1); // отправляем младший байт адрес блока
					tx2Bytes[0] = (byte)j;
					sPort.Write(tx2Bytes, 0, 1); // отправляем размер блока
					while(sPort.BytesToWrite > 0){}
	
				for (; j > 0x0000; addrSpc++){ // принимаем 64 байта
					while(queueBytes.Count == 0){}  rx_byte = queueBytes.Dequeue();
					//Console.WriteLine(" ${0:X4} ${1:X2} {2}", addrSpc, rx_byte, cntrSpc);
					AddressByte.Add(addrSpc, rx_byte);
					cntrSpc--; j--;
				}
				Thread.Sleep(100); 
			}
			//Console.WriteLine();
			return AddressByte;
		}// ReadBlock_128000()


       
        public static void COM_port_Open(){
			// инициализация 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, Parity.None, 8, StopBits.One);
			 try
             {
                  sPort.Open();		//открываем COM порт                       
             }
             catch (ThreadAbortException) {Console.WriteLine("sPort.Open() ThreadAbortException"); } 
             catch (IOException)  {Console.WriteLine("sPort.Open() IOException"); } 
             catch (TimeoutException)  {Console.WriteLine("sPort.Open() TimeoutException"); } 
             catch (Exception ex) { Console.WriteLine("sPort.Open() Exception" + ex.Message + "\n: read()"); }
             
             queueBytes = new Queue();
             queueBytes.Clear();
	}
 	
	public static void COM_port_Close(){
			//readThread.Join();	// останавливаем поток чтения COM порта
			sPort.Close();		// закрываем COM порт
	}
       
	    public static void Read()
		{
			while (inCycle)
			{
				if (sPort.IsOpen)
				{
					try
					{
						if(sPort.BytesToRead > 0){
						queueBytes.Enqueue((byte)sPort.ReadByte());
						//Console.WriteLine((byte)sPort.ReadByte());
						}
					} catch (TimeoutException)   {Console.WriteLine("Read. TimeoutException"); }
				}
				//}
			}
		}// Read()

	    public static void Dump_To_Console(SortedDictionary rxAB){
	    	SortedDictionary AddressByte = new SortedDictionary(rxAB);
	    	if(Dumps.err_Msg == "OK"){	Console.Write(Dumps.err_Msg);
	    		int i = 0; int predAdr = 0;
	    		foreach( KeyValuePair kvp in AddressByte){
	    			if (i == 0) {	Console.Write("\n ${0:X4} ", kvp.Key);
	    							predAdr = kvp.Key;}
	    			else { if( (kvp.Key % 16) == 0 | (kvp.Key - predAdr) > 1) Console.Write("\n ${0:X4} ", kvp.Key); }
					Console.Write("${0:X2} ", kvp.Value);
					i++;
					predAdr = kvp.Key;
				}// foreach
			}else Console.WriteLine("\n" + Dumps.err_Msg);
	    	Console.WriteLine("\n");
	    }
	    	
	    
   public readonly static byte[] Read_128000v14 = { 0x00, 
0x00, 0xFE, 0x03, 0xCC, 0x72, 0x94, 0xFF, 0x03, 0xAE, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 
0x72, 0xFF, 0x03, 0x31, 0x52, 0x55, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xFE, 0x03, 0x31, 0x52, 0x55, 
0xFB, 0x30, 0x52, 0x0B, 0x72, 0x95, 0x90, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x95, 
0x90, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 
0xC7, 0xA5, 0x20, 0xF7, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xF1, 0x35, 0x0B, 0x27, 0xF5, 0xA1, 
0xB4, 0x20, 0x08, 0x50, 0x1B, 0x72, 0x07, 0x50, 0x1B, 0x72, 0xF2, 0x26, 0x5A, 0x90, 0x5C, 0xFB, 
0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xC7, 0xF6, 0x08, 0x50, 0x1A, 0x72, 0x07, 0x50, 0x1A, 0x72, 
0x97, 0x90, 0x31, 0x52, 0xC6, 0x95, 0x90, 0x00, 0xA6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x97, 0x31, 
0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0xFB, 0x30, 0x52, 0x0F, 0x72, 0x31, 0x52, 0xC7, 0x95, 
0x40, 0x22, 0xEF, 0xA1, 0x31, 0x52, 0xC6, 0xFB, 0x30, 0x52, 0x0B, 0x72, 0x35, 0x52, 0x0C, 0x35, 
0x32, 0x52, 0x01, 0x35, 0x33, 0x52, 0x00, 0x35 };

	public static bool inCycle = true;
    public static Queue queueBytes;
    public static Thread readThread;
    public static SerialPort sPort;
	public static string portName;
	
	
	public static SortedDictionary rxAddressByte;
	//public static ArrayList byteArrayList;
	public static bool unready = true;
	public static byte rx_byte;
	public static string err_Msg = "OK";
	public static byte[] rx1Byte = new byte[1];
	public static byte[] tx2Bytes = new byte[2];
	public static byte[] rx4Bytes = new byte[4];
	public static byte[] rx64Bytes = new byte[64];
	public static byte[] tx64Bytes = new byte[64];
	public static byte[] go_bytes = { 0xF5, 0x00, 0x00, 0x00, 0x17, 0x00}; // { GO cmd, YH, YL, go_adrH, go_adrL, cntr }
}// class Dumps
// Dumps.cs

 

Файл STM8uLoader.cs :

 

// STM8uLoader.cs
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Collections.Generic;

public class STM8uLoader
{
	
	public static void Main()   { 

		//Dumps	myDumps = new Dumps();

		Dumps.COM_port_Open();
		readThread = new Thread(Dumps.Read);
		readThread.IsBackground = true; // не позволит остаться потоку в памяти после закрытия программы ?
			try {
				readThread.Start();		// запускаем поток чтения COM порта
			} catch (Exception ex) {Console.WriteLine("readThread.Start(). Exception" + ex.Message); } 
					
		Dumps.Load_First_Dump(Dumps.Read_128000v14);

		blckAdr = 0x9FFE;	blckSize = 2;
		Console.WriteLine("Адрес прикладной программы");
		Dumps.Damp_To_Console(Dumps.Read_128000(blckAdr, blckSize));

		blckAdr = 0x8000;	blckSize = 4;
		Console.WriteLine("Копировщик boot_FLASH");
		Dumps.Damp_To_Console(Dumps.Read_128000(blckAdr, blckSize));

		blckAdr = 0x0000;	blckSize = 53;
		Console.WriteLine("Код загрузчика boot_OPTION");
		Dumps.Damp_To_Console(Dumps.Read_128000(blckAdr, blckSize));
		
		Dumps.Load_Dump(Dumps.Read_128000v14);
		
		blckAdr = 0x480B;	blckSize = 53;
		Console.WriteLine("Образ кода загрузчика boot_OPTION");
		Dumps.Damp_To_Console(Dumps.Read_128000(blckAdr, blckSize));
		
		blckAdr = 0x9FE0;	blckSize =32;
		Console.WriteLine("Код копировщика boot_FLASH");
		Dumps.Damp_To_Console(Dumps.Read_128000(blckAdr, blckSize));
		
		blckAdr = 0xA000;	blckSize =64;
		Console.WriteLine("Заглянем за \"горизонт\"");
		Dumps.Damp_To_Console(Dumps.Read_128000(blckAdr, blckSize));
		
		readThread.Join();	// останавливаем поток чтения COM порта
		Dumps.COM_port_Close();
		Console.ReadKey(); return;
    } // Main();
    public static Thread readThread;

	public static string stringMemoryMap = "";
	public  static byte rx_byte = 0x00;
	public static byte[] btBytes = new byte[1];
	public static byte[] wrd4Bytes = new byte[4];
	public static byte[] blck64Bytes = new byte[64];
	public static int blckAdr;
	public static int blckSize;
	public  static bool unready = true;
	public  static string fileName;	
	public static byte[] txBytes = {0, 0};

}// class STM8uLoader
// STM8uLoader.cs

 

BAT файл здесь не требуется. Запускаем файл [STM8uLoader.exe], нажимаем кнопку сброса на плате STM8S103F3.

 

 

Исходники:[boot_PC.zip] , [Read_128000v14.asm.zip] .

 

[Создатель дампов. (Статья 6)] [Оглавление.] [Класс Dumps. Модули записи и копирования в память RAM (Статья 8)]