В предыдущих статьях в класс Dumps.sc были добавлены модули записи в EEPROM память данных и FLASH памяти программ микоконтроллера STM8S103F3. Выяснилось, что для полноценного написания модулей/дампов, методов записи, стирания, копирования в EEPROM и FLASH память необходимо соответствующим образом отсортировать содержимое файла прошивки. Рассмотрим три варианта сортировки содержимого файла прошивки :
1 - Дополняем содержимое всей памяти где есть разрывы адресов например нулями $00 (а для FLASH памяти еще и кодом копировщика boot_FLASH) и записываем все блоки. При этом будут записываться ячейки всего адресного пространства памяти EEPROM и FLASH, а не только содержимое файла прошивки. В зависимости от размера файла прошивки может увеличится время записи, уменьшается ресурс циклов записи ячеек не предназначенных для записи. Необходимо перезаписывать ячейки с кодом начального копировщика boot_FLASH и при возможном сбое придется обращаться к программатору. Алгоритм заполнения недостающих ячеек менее сложный, чем сортировка по блокам/словам/байтам. Виду малого объема (630 байт) и большего ресурса записи ячеек, этот вариант можно было бы применить для EEPROM памяти на первом этапе.
2 - Дополняем содержимое блоков где есть разрывы адресов например нулями $00 (а для FLASH памяти еще и кодом копировщика boot_FLASH) и записываем только блоки полностью или частично присутствующие в файле прошивки. При этом не будут записываться ячейки блоков отсутствующих в файле прошивки, а не только содержимое файла прошивки. Время записи будет адекватно размеру файла прошивки, уменьшается ресурс циклов записи ячеек не предназначенных для записи только блоков, присутствующих в файле прошивке. Необходимо перезаписывать ячейки с кодом начального копировщика boot_FLASH и при возможном сбое придется обращаться к программатору. Алгоритм заполнения недостающих ячеек менее сложный, чем сортировка по блокам/словам/байтам. Если нет времени на реализацию сложного алгоритма и плата доступна для возможного подключения к программатору то этот вариант для FLASH памяти на первод этапе предпочтителен. Виду малого объема (630 байт) и большего ресурса записи ячеек, этот вариант можно было бы применить для EEPROM памяти как основной.
3 - сортируем содержимое по блокам/словам/байтам, разрывы адресов не дополняем, записывем только содержимое файла прошивки. При этом не будут записываться ячейки, отсутствующие в файле прошивки. Время записи может быть ниже чем в предыдущем пункте, не уменьшается ресурс циклов записи ячеек не предназначенных для записи. Нет необходимости в перезаписи ячеек с кодом начального копировщика boot_FLASH. Алгоритм сортировки по блокам/словам/байтам боле сложный чем алгоритм дополнения блоков недостающими байтами. Если плата не имеет доступа к подключению программатора (залита компаундом, заняты соответствующие пины, ...) то этот вариант предпочтителен. Однако, поскольку конвеер у семейства STM8 загружается из памяти FLASH 4-х байтовыми словами с начальным адресом выровненным по маске $xxx0, $xxx4, $xxx8, $xxxC, есть подозрение, что при побайтовой записи все равно переписывается все слово. По-этому, возможно, имеет смысл только сортировка по блокам/словам с дополнением недостающих адресов в словах напр. нулями, что еще более усложняет алгоритм сортировки. Также при определенном количестве последовательно расположенных слов в блоке имеет смысл запонить блок в незанятых адресах нулевыми ($00000000) словами, что может значительно сократить время записи. Конечно необходимо также учитывать временные затраты на прием микроконтроллером по UART информации с учетом выбранной скорости 128000, которая не может быть реализована на время записи, так как выполнение команд останавливается и в STM8S103F3 отсутсвует модуль прямого доступа к памяти. Этот вариант обязателен при прошивке в два и более этапа, когда в микроконтроллере не должны быть перезаписаны некоторые ранее записанные области.
В этой статье примем вариант по п.2 для записи памяти EEPROM и FLASH блоками. В одной из будущих статей можно будет задуматься о реализации сортировки по п.3 при записи в память FLASH. Начнем с EEPROM. Наполним файл EEPROM.asm мусором:
stm8/ TITLE “EEPROM.asm” MOTOROLA WORDS segment byte at 4000 - 403F 'EEPROM_1' ; dc.w $1010, $1011, ... $101D, $101E ; insert 15 words val CEQU $1010 REPEAT DC.W val val CEQU {val+1} UNTIL {val eq $101F} segment byte at 4243 - 427F 'EEPROM_3' ; dc.b $00, $02, $04, ... $72, $74 ; insert 59 bytes val CEQU 0 REPEAT DC.B {2 mult val} val CEQU {val+1} UNTIL {val eq 59} segment byte at 4210 - 423F 'EEPROM_4' SKIP 13, $17 ; insert 13 bytes $17 segment byte at 4105 - 41FF 'EEPROM_4' SKIP 215, $F1 ; insert 215 bytes $F1 segment byte at 4040 - 40FF 'EEPROM_2' ; dc.w $0000, $0000, ... $0000, $0000 ; insert 17 words $0000 val CEQU 0 REPEAT DC.W $0000 ;val CEQU {val+1} ; UNTIL {val eq 17} val CEQU {val+16} UNTIL {val eq 272} end ; после end должна быть хотя бы одна строка
Получим файл прошивки EEPROM.hex:
:020000040000FA :104000001010101110121013101410151016101794 :0E40100010181019101A101B101C101D101E75 :1042430000020406080A0C0E10121416181A1C1E7B :1042530020222426282A2C2E30323436383A3C3E6B :1042630040424446484A4C4E50525456585A5C5E5B :0B42730060626466686A6C6E707274B2 :0D4210001717171717171717171717171776 :10410500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F19A :10411500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F18A :10412500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F17A :10413500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F16A :10414500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F15A :10415500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F14A :10416500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F13A :10417500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F12A :10418500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F11A :10419500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F10A :1041A500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1FA :1041B500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1EA :1041C500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1DA :0741D500F1F1F1F1F1F1F14C :104040000000000000000000000000000000000070 :104050000000000000000000000000000000000060 :0240600000005E :00000001FF
Метод заполнения блоков EEPROM :
static SortedDictionaryEEPROM_AdrBlcsSorting(SortedDictionary sort) { SortedDictionary sortAdrBt = new SortedDictionary (sort); // для счетчика foreach SortedDictionary fullAdrBt = new SortedDictionary (sortAdrBt); // для заполнения SortedDictionary EEPROM_AdrBlcks = new SortedDictionary (); // для возврата методом //EEPROM_AdrBlcks.Clear(); int crntADR = 0; // текущий адрес int baseADR = 0; // базовый адрес блока или смежныхблоков Queue queue4000 = new Queue (); foreach(KeyValuePair kvp in sortAdrBt){ // sortAdrBt содержит сортированные пары (адрес, байт) из файла прошивки, // адреса расположены в порядке возрастания, но не гарантируется непрерывность последовательности адреса // последовательность адресов может быть не выровнена по начальному и/или конечному адресу блока if ( (kvp.Key % 64) != 0 ){ // if(fullAdrBt.ContainsKey(kvp.Key - 1) == false){ // если предыдущий адрес отсутствует crntADR = kvp.Key - (kvp.Key % 64); for(;crntADR < kvp.Key; crntADR++){ if(fullAdrBt.ContainsKey(crntADR) == false) fullAdrBt.Add(crntADR, 0x00); // //else Console.WriteLine(" 1 Повторная запись в адрес 0x{0:X4}", crntADR); } } } if ( (kvp.Key % 64) != 63){ // if(fullAdrBt.ContainsKey(kvp.Key + 1) == false){ // если следующий адрес отсутствует crntADR = kvp.Key; for(; (crntADR % 64) <= 62; ){ crntADR++; if(fullAdrBt.ContainsKey(crntADR) == false) fullAdrBt.Add(crntADR, 0x00); // //else Console.WriteLine(" 2 Повторная запись в адрес 0x{0:X4}", crntADR); } } } } // fullAdrBt содержит сортированные пары (адрес, байт) из файла прошивки, // дополненные нулями до полного размера каждого используемого блока // могут использоваться все или не все блоки // используемые блоки могут граничить друг с другом // или разделены неиспользуемыми блоками // в следующем цикле в EEPROM_AdrBlcks должны быть сформированы пары <адрес, массив> // где массив может содержать один или более смежных блоков crntADR = 0; baseADR = 0; queue4000.Clear(); foreach(KeyValuePair kvp in fullAdrBt){ if(crntADR == 0) baseADR = crntADR = kvp.Key; if(fullAdrBt.ContainsKey(kvp.Key + 1)) { queue4000.Enqueue(kvp.Value); crntADR = kvp.Key + 1; } if(fullAdrBt.ContainsKey(kvp.Key + 1) == false){ queue4000.Enqueue(kvp.Value); EEPROM_AdrBlcks.Add(baseADR, queue4000.ToArray()); queue4000.Clear(); crntADR = 0; } } return EEPROM_AdrBlcks; }//SortedDictionary EEPROM_AdrBlcsSorting(SortedDictionary sort)
До сортировки блоков EEPROM :
Результат сортировки блоков EEPROM :
.
Метод заполнения блоков FLASH аналогичен предыдущему при следующих дополнениях :
- необходимо проверить отсутствие в файле прошивки адресов $8000...$8003, $9FF0...$9FFD ;
- необходимо заполнить адреса $8000...$8003, $9FF0...$9FFD кодом начального копировщика boot_FLASH .
Наполним файл FLASH.asm разрозненным содержимым:
stm8/ TITLE “FLASH.asm” MOTOROLA WORDS segment byte at 8004 - 803F 'start' ; dc.w $1010, $1011, ... $101D, $101E ; insert 15 words val CEQU $1010 REPEAT DC.W val val CEQU {val+1} UNTIL {val eq $101F} segment byte at 8243 - 827F 'FLASH_3' ; dc.b $02, $04, $06, ... $7C, $7E ; insert 64 bytes val CEQU 1 REPEAT DC.B {2 mult val} val CEQU {val+1} UNTIL {val eq 59} segment byte at 8210 - 822F 'FLASH_5' SKIP 13, $17 ; insert $17 bytes segment byte at 9241 - 9242 'FLASH_6' dc.b $77 segment byte at 8105 - 81FF 'FLASH_4' SKIP 215, $F1 ; insert $F1 bytes segment byte at 8040 - 80FF 'FLASH_2' ; dc.w $0101, $0101, ... $0101, $0101 ; insert 17 words val CEQU 0 REPEAT DC.W $0101 ;val CEQU {val+1} ; UNTIL {val eq 17} val CEQU {val+16} UNTIL {val eq 272} segment byte at 9FFE 'address' dc.w $8004 end ; после end должна быть хотя бы одна строка
Соответствующий файл прошивки FLASH.hex:
:020000040000FA :108004001010101110121013101410151016101750 :0E80140010181019101A101B101C101D101E31 :10824300020406080A0C0E10121416181A1C1E201B :10825300222426282A2C2E30323436383A3C3E400B :10826300424446484A4C4E50525456585A5C5E60FB :0A827300626466686A6C6E707274D3 :0D8210001717171717171717171717171736 :0192410077B5 :10810500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F15A :10811500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F14A :10812500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F13A :10813500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F12A :10814500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F11A :10815500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F10A :10816500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1FA :10817500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1EA :10818500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1DA :10819500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1CA :1081A500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1BA :1081B500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1AA :1081C500F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F19A :0781D500F1F1F1F1F1F1F10C :108040000101010101010101010101010101010120 :108050000101010101010101010101010101010110 :0280600001011C :029FFE008004DD :00000001FF
Метод заполнения блоков FLASH :
static SortedDictionaryFLASH_AdrBlcsSorting(SortedDictionary sort) { SortedDictionary sortAdrBt = new SortedDictionary (sort); // для счетчика foreach SortedDictionary FLASH_AdrBlcks = new SortedDictionary (); // для возврата методом int crntADR = 0; // текущий адрес int baseADR = 0; // базовый адрес блока или смежных блоков Queue queue8000 = new Queue (); err_Msg = " OK"; if( sortAdrBt.ContainsKey(0x8000) | sortAdrBt.ContainsKey(0x8001) | sortAdrBt.ContainsKey(0x8001) | sortAdrBt.ContainsKey(0x8003) | sortAdrBt.ContainsKey(0x9FF0) | sortAdrBt.ContainsKey(0x9FF0) | sortAdrBt.ContainsKey(0x9FF1) | sortAdrBt.ContainsKey(0x9FF2) | sortAdrBt.ContainsKey(0x9FF3) | sortAdrBt.ContainsKey(0x9FF4) | sortAdrBt.ContainsKey(0x9FF5) | sortAdrBt.ContainsKey(0x9FF6) | sortAdrBt.ContainsKey(0x9FF7) | sortAdrBt.ContainsKey(0x9FF8) | sortAdrBt.ContainsKey(0x9FF9) | sortAdrBt.ContainsKey(0x9FFA) | sortAdrBt.ContainsKey(0x9FFB) | sortAdrBt.ContainsKey(0x9FFC) | sortAdrBt.ContainsKey(0x9FFD) ) {err_Msg = " Имеются адреса кода копировщика"; return FLASH_AdrBlcks;} crntADR = 0x8000;// дополняем кодом начального копировщика boot_FLASH for(int i=0; i < boot_FLASH_8000.Length; i++, crntADR++){ sortAdrBt.Add(crntADR, boot_FLASH_8000[i]); } crntADR = 0x9FF0;// дополняем кодом начального копировщика boot_FLASH for(int i=0; i < boot_FLASH_9FF0.Length; i++, crntADR++){ sortAdrBt.Add(crntADR, boot_FLASH_9FF0[i]); } SortedDictionary fullAdrBt = new SortedDictionary (sortAdrBt); // для заполнения foreach(KeyValuePair kvp in sortAdrBt){ // sortAdrBt содержит сортированные пары (адрес, байт) из файла прошивки, // адреса расположены в порядке возрастания, но не гарантируется непрерывность последовательности адресов // последовательность адресов может быть не выровнена по начальному и/или конечному адресу блока if ( (kvp.Key % 64) != 0 ){ // if(fullAdrBt.ContainsKey(kvp.Key - 1) == false){ // если предыдущий адрес отсутствует crntADR = kvp.Key - (kvp.Key % 64); for(;crntADR < kvp.Key; crntADR++){ if(fullAdrBt.ContainsKey(crntADR) == false) fullAdrBt.Add(crntADR, 0x00); // //else Console.WriteLine(" 1 Повторная запись в адрес 0x{0:X4}", crntADR); } } } if ( (kvp.Key % 64) != 63){ // if(fullAdrBt.ContainsKey(kvp.Key + 1) == false){ // если следующий адрес отсутствует crntADR = kvp.Key; for(; (crntADR % 64) <= 62; ){ crntADR++; if(fullAdrBt.ContainsKey(crntADR) == false) fullAdrBt.Add(crntADR, 0x00); // //else Console.WriteLine(" 2 Повторная запись в адрес 0x{0:X4}", crntADR); } } } } // fullAdrBt содержит сортированные пары (адрес, байт) из файла прошивки, // дополненные нулями до полного размера каждого используемого блока // могут использоваться все или не все блоки // используемые блоки могут граничить друг с другом // или разделены неиспользуемыми блоками // в следующем цикле во FLASH_AdrBlcks должны быть сформированы пары <адрес, массив> // где массив может содержать один или более смежных блоков crntADR = 0; baseADR = 0; queue8000.Clear(); foreach(KeyValuePair kvp in fullAdrBt){ if(crntADR == 0) baseADR = crntADR = kvp.Key; if(fullAdrBt.ContainsKey(kvp.Key + 1)) { queue8000.Enqueue(kvp.Value); crntADR = kvp.Key + 1; } if(fullAdrBt.ContainsKey(kvp.Key + 1) == false){ queue8000.Enqueue(kvp.Value); FLASH_AdrBlcks.Add(baseADR, queue8000.ToArray()); queue8000.Clear(); crntADR = 0; } } return FLASH_AdrBlcks; }//SortedDictionary FLASH_AdrBlcsSorting(SortedDictionary sort)
До сортировки блоков FLASH :
Результат сортировки блоков FLASH :
Исходники к статье:[boot_PC.zip] , [EEPROM.asm] , [FLASH.asm] .
В следующей статье создаем реальный файл прошивки с областями RAM, EEPROM, FLASH. Прошиваем в микроконтроллер и передаем управление прикладной программе по адресу в командной строке.