В процессе поиска загрузчика для микроконтроллера STM8S103F3 было обнаружено, что имеющиеся загрузчики в основном написаны на ”C”, «крадут» значительный объем у FLASH памяти, переносят таблицу векторов прерываний.
Загрузчик был необходим для некоего устройства, к которому невозможно подключить программатор.
Было решено попробовать самостоятельно написать загрузчик со следующими требованиями:
— загрузчик должен был называться STM8uLoader;
— код должен быть написан на ассемблере (благо ассемблер законодательно пока не запрещен);
— загрузчик должен занимать минимально возможный объем во FLASH памяти, объем занимаемый в компьютере будем считать не ограниченным;
— загрузчик не должен перемещать таблицу векторов прерываний;
— загрузчик должен иметь минимальный функционал, весь основной функционал должен взять на себя компьютер;
— загрузчик должен передавать управление прикладной программе за разумное время после сброса/включения при отсутствии соединения с компьютером.
Первое условие было моментально выполнено, а вот над последующими требованиями пришлось потрудиться.
Для сохранения таблицы векторов на своем месте принято решение разместить код в конце FLASH памяти и переходить на него сразу с вектора сброса $8000.
При загрузке передается управление коду загрузчика по адресу $9FC2. Загрузчик настраивает UART 9600 8N1, ждет по UART два байта, и не дождавшись передает управление прикладной программе по адресу хранящемуся в паре $9FFE:$9FFF.
Если загрузчик принимает старший и младший байты размера ожидаемого дампа от хост-программы, принимает следом сам дамп, помещает дамп в память RAM и передает ему управление.
Далее вся забота ложится на программу в компьютере и посылаемый ею дамп. Она должна посылать именно те дампы, которые нужны для выполнения текущей задачи (чтение/стирание/запись/копирование ячеек памяти STM8). Дампы должны уметь заменять друг друга в памяти RAM и передавать управление прикладной программе.
Адрес перехода к прикладной программе здесь $9FFE:$9FFF.
Отобрать 65 байт у памяти FLASH (в STM8S103F3 ее всего то 8192 байт) это не человечно. Ведь рядом валяется никому не нужная память EEPROM со своими 640 байт. Давайте разделим код загрузчика на две части boot_FLASH и boot_EEPROM.
При загрузке передается управление коду boot_FLASH по адресу $9FEF. boot_FLASH копирует из памяти EEPROM в память RAM образ кода boot_EEPROM и передает ему управление.
Теперь уже boot_EEPROM настраивает UART 9600 8N1, ждет по UART байт, и не дождавшись передает управление прикладной программе (адрес оставим там же $9FFE:$9FFF).
Если boot_EEPROM принимает байт с размером ожидаемого дампа для памяти RAM, принимает следом дамп, помещает дамп в другую область памяти RAM и передает ему управление.
Далее все как в первом этапе.
Запускаем файл runSTM8uLoader.bat, нажимаем кнопку сброса на плате, загрузчик посылает байт 0x01. В память RAM STM8 по UART отправляется дамп с кодом из файла main_RAM.hex. Плата начинает моргать светодиодом и посылать байты 0x20 и 0x02. Повторно нажимаем кнопку сброса. Запускается прикладная программа из FLASH памяти, светодиод начинает моргать быстрее и посылать байты 0x80 и 0x08.
C памятью EEPROM мы конечно же поспешили. Где теперь хранить синусы и прочие таблицы? Да и с FLASH памятью не все однозначно. Кто решил хранить адрес передачи управления прикладной программе именно в памяти FLASH? А один и тот же байт версии загрузчика вообще хранится в двух местах сразу. Куда же втиснуть 52 байта предназначенные для EEPROM?
Тут нам в помощь литография. Память EEPROM состоит из 10 блоков по 64 байта каждый. Добавлять к этим блокам еще один блок, но с другим размером экономически не целесообразно. Фирма STMicroelectronics так и поступила, добавила еще один блок размером 64 байта, назвала эту область OPTION Bytes и хранит там важные энергонезависимые настройки микроконтроллера (у STM8S103F3 это целых 11 байт). И конечно же фирма STM забыла упомянуть, что в этой области осталось еще 53 работоспособные ячейки. Видимо моделей STM8 много, надо оставить место для будущих важных настроек.
Наш загрузчик претендует только на модели STM8 без встроенных загрузчиков. По этому забираем резервные ячейки блока OPTION Bytes пока никто не видит. Правда тут есть одно маленькое, но решаемое, неудобство. Обычный программатор не позволит вам писАть информацию в эти ячейки.
При загрузке передается управление коду начального копировщика boot_FLASH по адресу $9FF2. boot_FLASH переносит из области OPTION Bytes в память RAM образ начального загрузчика boot_OPTION.
boot_OPTION настраивает UART 9600 8N1, отправляет в UART байт со своей версией, ждет по UART байт от хост-программы, и не дождавшись в течении 0,2 сек передает управление прикладной программе по адресу расположенному в паре $4831:$4832.
Если boot_OPTION после отправки байта со своей версией принимает байт размера ожидаемого дампа, то принимает следом сам дамп, помещает дамп в память RAM и передает ему управление.
Далее вся забота ложится на программу в компьютере и посылаемый ею дамп. Она должна посылать именно те дампы, которые нужны для выполнения текущей задачи (чтение/стирание/запись/копирование ячеек памяти STM8). Дампы должны уметь заменять друг друга в памяти RAM и передавать управление прикладной программе.
Адрес перехода к прикладной программе здесь $4831:$4832.
Запускаем файл runSTM8uLoader.bat, нажимаем кнопку сброса на плате, загрузчик посылает байт 0x25. В память RAM STM8 по UART отправляется дамп с кодом из файла main_RAM.hex. Плата начинает моргать светодиодом и посылать байты 0x20 и 0x02. Повторно нажимаем кнопку сброса. Запускается прикладная программа из FLASH памяти, светодиод начинает моргать быстрее и посылать байты 0x80 и 0x08.
На последнем этапе, чтобы записать образ загрузчика в область OPTION Bytes, необходимо воспользоваться способом. Суть способа в том, что сначала надо программатором записать во FLAH память STM8 файл прошивки boot_OPTION_rev25.hex, перезагрузить микроконтроллер, произойдет заполнение области OPTION Bytes необходимой информацией и включится светодиод. Потом опять программатором записать во FLASH файл прошивки из этой статьи boot_FLASH_OPTION.hex.
Добавил «чистый» код загрузчика версии 0x14 без кода прикладной программы. Развернул образ boot_OPTION в исходный код. Подправил комментарии. В отличие от версии $25 адрес передачи управления прикладной программе находится в ячейках $9FFE:$9FFFF памяти FLASH. Размер в памяти FLASH соответственно 20 байт.
Добавил «чистый» код загрузчика версии 0x25 без кода прикладной программы. Развернул образ boot_OPTION в исходный код. Подправил комментарии. В отличие от версии $14 адрес передачи управления прикладной программе находится в ячейках $4831:$4832 области OPTION Bytes. Занимаемый размер в памяти FLASH соответственно уменьшился до 18 байт. Занимаемый размер в области OPTION Bytes не изменился (52 байта + 1 резервный).
Адрес передачи управления прикладной программе во FLASH памяти можно выбрать из диапазона $8004...$9FF1. Для образа кода прикладной программы из EEPROM памяти передача управления возможна только по адресу $0000 в памяти RAM.
Хост программе вторым аргументом командной строки можно передать любой адрес передачи управления.
Исходный код хост программы можно поискать здесь . Там же и контакты для более развернутого общения.
Прошу от читателей целевой критики и предложений по дальнейшему уменьшению кода.
Предлагаю ознакомиться также со статьей ” Как сжать загрузчик для STM8 до размера 8 байт в памяти FLASH” .
Загрузчик был необходим для некоего устройства, к которому невозможно подключить программатор.
Было решено попробовать самостоятельно написать загрузчик со следующими требованиями:
— загрузчик должен был называться STM8uLoader;
— код должен быть написан на ассемблере (благо ассемблер законодательно пока не запрещен);
— загрузчик должен занимать минимально возможный объем во FLASH памяти, объем занимаемый в компьютере будем считать не ограниченным;
— загрузчик не должен перемещать таблицу векторов прерываний;
— загрузчик должен иметь минимальный функционал, весь основной функционал должен взять на себя компьютер;
— загрузчик должен передавать управление прикладной программе за разумное время после сброса/включения при отсутствии соединения с компьютером.
Первое условие было моментально выполнено, а вот над последующими требованиями пришлось потрудиться.
Первый этап. Код размером 65 байт во FLASH памяти
Для сохранения таблицы векторов на своем месте принято решение разместить код в конце FLASH памяти и переходить на него сразу с вектора сброса $8000.
При загрузке передается управление коду загрузчика по адресу $9FC2. Загрузчик настраивает UART 9600 8N1, ждет по UART два байта, и не дождавшись передает управление прикладной программе по адресу хранящемуся в паре $9FFE:$9FFF.
Если загрузчик принимает старший и младший байты размера ожидаемого дампа от хост-программы, принимает следом сам дамп, помещает дамп в память RAM и передает ему управление.
Далее вся забота ложится на программу в компьютере и посылаемый ею дамп. Она должна посылать именно те дампы, которые нужны для выполнения текущей задачи (чтение/стирание/запись/копирование ячеек памяти STM8). Дампы должны уметь заменять друг друга в памяти RAM и передавать управление прикладной программе.
Адрес перехода к прикладной программе здесь $9FFE:$9FFF.
Файл boot_FLASH.asm:
stm8/ TITLE "boot_FLASH.asm"
.NOLIST
#include "STM8S103F3P.inc"
.LIST
MOTOROLA
WORDS
segment byte at 8000 'boot_start'
boot_start:
jp boot_FLASH_start
dc.b $00 ;версия boot_FLASH
; ********************************************************
; адреса 0x8004...0x9FC1 свободны для прошивки прикладной программы
WORDS ;
segment byte at 8004 'main_FLASH'
main_FLASH_start:
ldw X, #$03FF
ldw SP, X
mov UART1_BRR1, #13
mov UART1_CR2, #%00001100
main_FLASH_cycle:
callr main_delay
; выключаем светодиод
bset PB_DDR,#5
bset PB_CR1,#5
; отправляем байт
byte1_tx:
mov UART1_DR, #$80
byte1_wait_tx
btjf UART1_SR, #7, byte1_wait_tx
callr main_delay
boot_RAM_exit1:
; выключаем светодиод
bres PB_DDR,#5 ;
bres PB_CR1,#5 ;
; отправляем байт
byte2_tx:
mov UART1_DR, #$08
byte2_wait_tx
btjf UART1_SR, #7, byte2_wait_tx
jra main_FLASH_cycle
main_delay:
decw X
jrne main_delay
ret
segment byte at 9FC2 'boot_FLASH'
boot_FLASH_start:
mov UART1_BRR1, #13; Fmaster=16/8=2МГц/9600/16
mov UART1_CR2, #%00001100; разрешаем передачу/прием
; отправляем по UART1 содержимое региста RST_SR
boot_FLASH_RST_SR_tx:
mov UART1_DR, RST_SR
; это сигнал хост программе, что можно отправлять дамп памяти
; ждем первый байт блока данных
; и перебираем регистр X для отсчета таймаута (примерно 200 миллисекунд)
ldw X,#0
boot_FLASH_wait_byte1:
decw X
jreq boot_FLASH_exit; по истечению таймаута выходим из бутлоадера
btjf UART1_SR, #5, boot_FLASH_wait_byte1
; первый байт принят, прекращаем отсчитывать таймаут,
; регистр X используем для косвенной адресации
ld A, UART1_DR
ld XH, A
; ждем второй байт блока данных
boot_FLASH_wait_byte2:
btjf UART1_SR, #5, boot_FLASH_wait_byte2
; второй байт принят
ld A, UART1_DR
ld XL, A ; указатель X -количество байт в блоке
; в регистре X количество оставшихся байт в блоке данных
ldw Y, #$0400 ; указатель Y на адрес 0x0400 (RAM_END + 1)
; ждем очередной байт
boot_FLASH_rx_block_wait:
btjf UART1_SR, #5, boot_FLASH_rx_block_wait
boot_EEPROM_rx_block_entry:
decw Y ; после каждой итерации в регистре Y адрес последнего загруженного байта
ld A, UART1_DR
ld (Y), A
decw X ; после каждой итерации в регистре X количество оставшихся для загрузки байтов
jrne boot_FLASH_rx_block_wait
; передаем управление принятому блоку данных
jp (Y)
; передаем управление основной (прикладной) программе
boot_FLASH_exit:
dc.b $CC
boot_FLASH_exit_addr:
dc.w main_FLASH_start
end
;
Второй этап. Код размером 21 байт во FLASH и 52 байта в EEPROM памяти
Отобрать 65 байт у памяти FLASH (в STM8S103F3 ее всего то 8192 байт) это не человечно. Ведь рядом валяется никому не нужная память EEPROM со своими 640 байт. Давайте разделим код загрузчика на две части boot_FLASH и boot_EEPROM.
При загрузке передается управление коду boot_FLASH по адресу $9FEF. boot_FLASH копирует из памяти EEPROM в память RAM образ кода boot_EEPROM и передает ему управление.
Теперь уже boot_EEPROM настраивает UART 9600 8N1, ждет по UART байт, и не дождавшись передает управление прикладной программе (адрес оставим там же $9FFE:$9FFF).
Если boot_EEPROM принимает байт с размером ожидаемого дампа для памяти RAM, принимает следом дамп, помещает дамп в другую область памяти RAM и передает ему управление.
Далее все как в первом этапе.
Файл boot_FLASH_EEPROM.asm:
stm8/ TITLE "boot_FLASH_EEPROM.asm"
.NOLIST
#include "STM8S103F3P.inc"
.LIST
MOTOROLA
WORDS
segment byte at 4000 'eeprom'
; образ boot_EEPROM
dc.b $35, $0D, $52, $32, $35, $0C, $52, $35
dc.b $35, $01, $52, $31, $5A, $27, $16, $72
dc.b $0B, $52, $30, $F8, $C6, $52, $31, $72
dc.b $0B, $52, $30, $FB, $3B, $52, $31, $4A
dc.b $26, $F5, $96, $5C, $FC, $CE, $9F, $FE
dc.b $2B, $FA, $90, $AE, $42, $7F, $AE, $02
dc.b $7F, $CC, $9F, $F4
segment byte at 8000 'boot_start'
boot_start:
jp boot_FLASH_start
dc.b $01 ;версия boot_FLASH_EEPROM
; ********************************************************
; адреса 0x8004...0x9FEE свободны для прошивки прикладной программы
segment byte at 8004 'main_FLASH'
; прикладная программа
main_FLASH_start:
ldw X, #$03FF
ldw SP, X
mov UART1_BRR1, #13
mov UART1_CR2, #%00001100
main_FLASH_cycle:
callr main_delay
; выключаем светодиод
bset PB_DDR,#5
bset PB_CR1,#5
; отправляем байт
byte1_tx:
mov UART1_DR, #$80
byte1_wait_tx
btjf UART1_SR, #7, byte1_wait_tx
callr main_delay
boot_RAM_exit1:
; выключаем светодиод
bres PB_DDR,#5 ;
bres PB_CR1,#5 ;
; отправляем байт
byte2_tx:
mov UART1_DR, #$08
byte2_wait_tx
btjf UART1_SR, #7, byte2_wait_tx
jra main_FLASH_cycle
main_delay:
decw X
jrne main_delay
ret
; начальный копировщик EEPROM -> RAM
segment byte at 9FEF 'boot_FLASH'
boot_FLASH_start:
ldw X, SP
; Y <- { EEPROM_START + RAM_END}
; Y <- { $4000 + $03FF = $43FF }
ldw Y, #$43FF
boot_FLASH_copy:
ld A, (Y)
ld (X), A
decw Y
decw X
jrpl boot_FLASH_copy
incw X
jp (X)
boot_FLASH_exit_address:
dc.w main_FLASH_start
end
;
Запускаем файл runSTM8uLoader.bat, нажимаем кнопку сброса на плате, загрузчик посылает байт 0x01. В память RAM STM8 по UART отправляется дамп с кодом из файла main_RAM.hex. Плата начинает моргать светодиодом и посылать байты 0x20 и 0x02. Повторно нажимаем кнопку сброса. Запускается прикладная программа из FLASH памяти, светодиод начинает моргать быстрее и посылать байты 0x80 и 0x08.
Третий этап. Код размером 18 байт во FLASH памяти и 52 байта в OPTION Bytes
C памятью EEPROM мы конечно же поспешили. Где теперь хранить синусы и прочие таблицы? Да и с FLASH памятью не все однозначно. Кто решил хранить адрес передачи управления прикладной программе именно в памяти FLASH? А один и тот же байт версии загрузчика вообще хранится в двух местах сразу. Куда же втиснуть 52 байта предназначенные для EEPROM?
Тут нам в помощь литография. Память EEPROM состоит из 10 блоков по 64 байта каждый. Добавлять к этим блокам еще один блок, но с другим размером экономически не целесообразно. Фирма STMicroelectronics так и поступила, добавила еще один блок размером 64 байта, назвала эту область OPTION Bytes и хранит там важные энергонезависимые настройки микроконтроллера (у STM8S103F3 это целых 11 байт). И конечно же фирма STM забыла упомянуть, что в этой области осталось еще 53 работоспособные ячейки. Видимо моделей STM8 много, надо оставить место для будущих важных настроек.
Наш загрузчик претендует только на модели STM8 без встроенных загрузчиков. По этому забираем резервные ячейки блока OPTION Bytes пока никто не видит. Правда тут есть одно маленькое, но решаемое, неудобство. Обычный программатор не позволит вам писАть информацию в эти ячейки.
При загрузке передается управление коду начального копировщика boot_FLASH по адресу $9FF2. boot_FLASH переносит из области OPTION Bytes в память RAM образ начального загрузчика boot_OPTION.
boot_OPTION настраивает UART 9600 8N1, отправляет в UART байт со своей версией, ждет по UART байт от хост-программы, и не дождавшись в течении 0,2 сек передает управление прикладной программе по адресу расположенному в паре $4831:$4832.
Если boot_OPTION после отправки байта со своей версией принимает байт размера ожидаемого дампа, то принимает следом сам дамп, помещает дамп в память RAM и передает ему управление.
Далее вся забота ложится на программу в компьютере и посылаемый ею дамп. Она должна посылать именно те дампы, которые нужны для выполнения текущей задачи (чтение/стирание/запись/копирование ячеек памяти STM8). Дампы должны уметь заменять друг друга в памяти RAM и передавать управление прикладной программе.
Адрес перехода к прикладной программе здесь $4831:$4832.
Код загрузчика и прикладной программы для выполнения в памяти FLASH:
stm8/ TITLE "boot_FLASH_OPTION.asm"
.NOLIST
#include "STM8S103F3P.inc"
.LIST
MOTOROLA
WORDS
segment byte at 4800 'boot_OPTION'
; образ начального загрузчика boot_OPTION
dc.b $00, $00, $FF, $00, $FF, $00, $FF, $00
dc.b $FF, $00, $FF, $35, $0D, $52, $32, $35
dc.b $0C, $52, $35, $35, $25, $52, $31, $5A
dc.b $27, $16, $72, $0B, $52, $30, $F8, $C6
dc.b $52, $31, $72, $0B, $52, $30, $FB, $3B
dc.b $52, $31, $4A, $26, $F5, $96, $5C, $FC
dc.b $AE, $80, $04, $2B, $FA, $90, $AE, $42
dc.b $7F, $AE, $02, $7F, $CC, $9F, $F6, $00
segment byte at 8000 'boot_start'
boot_start:
ldw X, SP
jp boot_FLASH_start
; ********************************************************
; адреса 0x8004...0x9FF1 свободны для прошивки прикладной программы
segment byte at 8004 'main_FLASH'
; прикладная программа
main_FLASH_start:
ldw X, #$03FF
ldw SP, X
mov UART1_BRR1, #13
mov UART1_CR2, #%00001100
main_FLASH_cycle:
callr main_delay
; выключаем светодиод
bset PB_DDR,#5
bset PB_CR1,#5
; отправляем байт
byte1_tx:
mov UART1_DR, #$80
byte1_wait_tx
btjf UART1_SR, #7, byte1_wait_tx
callr main_delay
boot_RAM_exit1:
; выключаем светодиод
bres PB_DDR,#5 ;
bres PB_CR1,#5 ;
; отправляем байт
byte2_tx:
mov UART1_DR, #$08
byte2_wait_tx
btjf UART1_SR, #7, byte2_wait_tx
jra main_FLASH_cycle
main_delay:
decw X
jrne main_delay
ret
; начальный копировщик OPTION -> RAM
segment byte at 9FF2 'boot_FLASH'
boot_FLASH_start:
; Y <- { OPTION_START + RAM_END}
; Y <- { $4800 + $03FF = $43FF }
ldw Y, #$43FF
boot_FLASH_copy:
ld A, (Y)
ld (X), A
decw Y
decw X
jrpl boot_FLASH_copy
incw X
jp (X)
boot_FLASH_exit_address:
dc.w main_FLASH_start
end
;
Код прикладной программы для исполнения в памяти RAM:
stm8/
TITLE “boot_RAM.asm”
MOTOROLA
#include "STM8S103F3P.inc"
BYTES
segment byte at 0000 'boot_RAM_data'
boot_RAM_start:
; Включаем pull-up на портах (если подтяжка не предусмотрена внешней схемой) или не включаем, все равно работает, экономим 14 байт
; ld A, #%01001100 ; [A6 4C]
; cpl A ; [43]
; ld PA_CR1, A ; [C7 50 03]
; ld PB_CR1, A ; [C7 50 08]
; ld PC_CR1, A ; [C7 50 0D]
; ld PD_CR1, A ; [C7 50 12] подтяжка на PD6(UART1_RX), PD2, PD1
; настраиваем UART1 на прием/передачу на скорости 9600, остальные настройки по умолчанию (8 бит, нет бита четности, 1 стоповый бит)
; mov UART1_BRR2, #0 ; [35 00 52 33] для Fmaster=16/8=2МГц и 9600
mov UART1_BRR1, #13 ; [35 0D 52 32] для Fmaster=16/8=2МГц и 9600
mov UART1_CR2, #%00001100 ; [35 0C 52 35] UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1 разрешаем передачу/прием
; отправляем байт по UART1
boot_RAM_byte1_tx:
mov UART1_DR, #$02
boot_RAM_byte1_wait_tx
btjf UART1_SR, #7, boot_RAM_byte1_wait_tx
ldw X,#0 ; [AE 00 00] boot_FLASH обнуляет содержимое индексного регистра X
boot_RAM_wait1:
decw X ; [5A]
jreq boot_RAM_exit1 ;
jra boot_RAM_wait1
boot_RAM_exit1:
; выключаем светодиод
bres PB_DDR,#5 ;
bres PB_CR1,#5 ;
; отправляем байт по UART1
boot_RAM_byte2_tx:
mov UART1_DR, #$20 ; [35 11 52 31]
boot_RAM_byte2_wait_tx
btjf UART1_SR, #7, boot_RAM_byte2_wait_tx
ldw X,#0 ; [AE 00 00] boot_FLASH обнуляет содержимое индексного регистра X
boot_RAM_wait2:
decw X ; [5A]
jreq boot_RAM_exit2 ;
jra boot_RAM_wait2
boot_RAM_exit2:
; выключаем светодиод
bset PB_DDR,#5 ;
bset PB_CR1,#5 ;
jra boot_RAM_byte1_tx
end
Запускаем файл runSTM8uLoader.bat, нажимаем кнопку сброса на плате, загрузчик посылает байт 0x25. В память RAM STM8 по UART отправляется дамп с кодом из файла main_RAM.hex. Плата начинает моргать светодиодом и посылать байты 0x20 и 0x02. Повторно нажимаем кнопку сброса. Запускается прикладная программа из FLASH памяти, светодиод начинает моргать быстрее и посылать байты 0x80 и 0x08.
На последнем этапе, чтобы записать образ загрузчика в область OPTION Bytes, необходимо воспользоваться способом. Суть способа в том, что сначала надо программатором записать во FLAH память STM8 файл прошивки boot_OPTION_rev25.hex, перезагрузить микроконтроллер, произойдет заполнение области OPTION Bytes необходимой информацией и включится светодиод. Потом опять программатором записать во FLASH файл прошивки из этой статьи boot_FLASH_OPTION.hex.
Добавил «чистый» код загрузчика версии 0x14 без кода прикладной программы. Развернул образ boot_OPTION в исходный код. Подправил комментарии. В отличие от версии $25 адрес передачи управления прикладной программе находится в ячейках $9FFE:$9FFFF памяти FLASH. Размер в памяти FLASH соответственно 20 байт.
boot_uC_rev14.asm:
stm8/ TITLE "boot_uC_rev14.asm" ; boot_uC = boot_OPTION + boot_FLASH
MOTOROLA
.NOLIST
#include "STM8S103F3P.inc"
.LIST
WORDS
; ********************************************************
segment byte at 4800 'boot_O_IMG'
;0000FF00FF00FF00FF00FF350D523235
;0C5235351452315A2716720B5230F8C6
;5231720B5230FB3B52314A26F5965CFC
;CE9FFE2BFA90AE427FAE027FCC9FF400
; содержимое конфигурационных регистров
; $4800 не копируется в RAM
dc.b $00, $00, $FF, $00, $FF, $00, $FF, $00, $FF, $00, $FF
; OPTION (RAM)
; $480B ($0000) образ начального загрузчика boot_O
boot_O_start:
; настраиваем UART 96008N1 Fmaster=16/8=2МГц/9600/16
; mov UART1_BRR2, #0 ; [35 00 52 33] исключаем для экономии места
mov UART1_BRR1, #13 ; [35 0D 52 32]
; UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1 разрешаем передачу/прием
mov UART1_CR2, #%00001100 ; [35 0C 52 35]
; $4813 ($0008)
boot_E_byte1_tx:
; отправляем версию $14 загрузчика
mov UART1_DR, #$14 ; [35 14 52 31]
; это сигнал хост программе, что можно отправлять данные
; ждем байт с размером дампа
; регистр X отсчитывает таймаут (примерно 200 миллисекунд)
; clrw X ; [5F] регистр X уже обнулил boot_F
; $4817 ($000C)
boot_O_rx_wait_byte:
decw X ; [5A]
jreq boot_O_exit ; [27 16] по истечению таймаута выходим из бутлоадера
btjf UART1_SR, #5, boot_O_rx_wait_byte ; [72 OB 52 30 F8]
; первый байт принят, прекращаем отсчитывать таймаут, регистр A используем как счетчик
; $481F ($0014)
ld A, UART1_DR ; [C6 52 31]
; $4822 ($0017) ждем очередной байт
boot_O_rx_wait_block:
btjf UART1_SR, #5, boot_O_rx_wait_block ; [72 OB 52 30 FB]
push UART1_DR ; [3B 52 31]
dec A ; [4A]
; после каждой итерации в регистре A количество оставшихся для загрузки байтов
jrne boot_O_rx_wait_block ; [26 F5]
; $482D ($0022) передаем управление принятому блоку данных
ldw X, SP ; [96]
incw X ; [5C]
boot_O_exit_to_FLASH:
jp (X) ; [FC]
; $4830 ($0025) передаем управление прикладной программе
boot_O_exit:
ldw X, boot_F_exit_address ; [CE 9F FE]
jrmi boot_O_exit_to_FLASH ; [2B FA]
; if X < $8000 адрес пердачи управления равен $0000
; а код находится в EEPROM
boot_O_exit_to_EEPROM:
; Y <- { EEPROM_END}
ldw Y, #$427F ; [90 AE 42 7F]
; X <- { EEPROM_END - EEPROM_START }
; грузим копию EEPROM в RAM
ldw X, #$027F ; [AE 02 7F]
jp boot_F_copy ; [CC 9F F4]
; $483F ($0034)
dc.b $00 ; резервная ячейка
boot_O_end:
; ********************************************************
segment byte at 8000 'RESET_vector'
;96CC9FF0
ldw X, SP ; [96] X <- RAM_END
jp boot_F_start ; [CC 9F F0]
; ********************************************************
; адреса 0x8004...0x9FEF свободны для прошивки прикладной программы
segment byte at 8004 'main_FLASH'
;20FE
jra * ; [20 FE]
; ********************************************************
; начальный копировщик boot_FLASH
segment byte at 9FF0 'boot_F'
;90AE4C0A90F6F7905A5A2AF85CFC8004
boot_F_start:
; Y <- { boot_O_START + RAM_END} { $480B + $03FF = $4C0A }
ldw Y, #$4C0A ; [90 AE 4C 0A]
; этот участок кода используют
; boot_FLASH, boot_OPTION и дампы из хост программы
boot_F_copy:
ld A, (Y) ; [90 F6]
ld (X), A ; [F7]
decw Y ; [90 5A]
decw X ; [5A]
jrpl boot_F_copy ; [2A F8] если X(Y) >= RAM_START(boot_O_START)
incw X ; [5C]
jp (X) ; [FC]
boot_F_exit_address:
dc.w $8004 ; [80 04]
; dc.w $0000 ; [00 00]
end
;
Добавил «чистый» код загрузчика версии 0x25 без кода прикладной программы. Развернул образ boot_OPTION в исходный код. Подправил комментарии. В отличие от версии $14 адрес передачи управления прикладной программе находится в ячейках $4831:$4832 области OPTION Bytes. Занимаемый размер в памяти FLASH соответственно уменьшился до 18 байт. Занимаемый размер в области OPTION Bytes не изменился (52 байта + 1 резервный).
boot_uC_rev14.asm:
stm8/ TITLE "boot_uC_rev25.asm" ; boot_uC = boot_OPTION + boot_FLASH
MOTOROLA
.NOLIST
#include "STM8S103F3P.inc"
.LIST
BYTES
; ********************************************************
; отсюда стартуют либо образ прикладной прораммы из EEPROM памяти
; boot_O_exit_address должен быть равен $0000 ( проверяется на <$8000)
; либо прикладная программа из файла прошивки
; адрес передается хост программе вторым аргументом в командной строке
segment byte at 0000 'boot_O_IMG'
main_ram:
;20FE
jra * ; [20 FE]
WORDS
; ********************************************************
segment byte at 4800 'boot_O_IMG'
;0000FF00FF00FF00FF00FF350D523235
;0C5235351452315A2716720B5230F8C6
;5231720B5230FB3B52314A26F5965CFC
;AE80042BFA90AE427FAE027FCC9FF600
; содержимое конфигурационных регистров
; $4800 не копируется в RAM
dc.b $00, $00, $FF, $00, $FF, $00, $FF, $00, $FF, $00, $FF
; OPTION (RAM)
; $480B ($0000) образ начального загрузчика boot_OPTION
boot_O_start:
; настраиваем UART 96008N1 Fmaster=16/8=2МГц/9600/16
; mov UART1_BRR2, #0 ; [35 00 52 33] исключаем для экономии места
mov UART1_BRR1, #13 ; [35 0D 52 32]
; UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1 разрешаем передачу/прием
mov UART1_CR2, #%00001100 ; [35 0C 52 35]
; $4813 ($0008)
boot_E_byte1_tx:
; отправляем версию $14 загрузчика
mov UART1_DR, #$14 ; [35 14 52 31]
; это сигнал хост программе, что можно отправлять данные
; ждем байт с размером дампа
; регистр X отсчитывает таймаут (примерно 200 миллисекунд)
; clrw X ; [5F] регистр X уже обнулил boot_F
; $4817 ($000C)
boot_O_rx_wait_byte:
decw X ; [5A]
jreq boot_O_exit ; [27 16] по истечению таймаута выходим из бутлоадера
btjf UART1_SR, #5, boot_O_rx_wait_byte ; [72 OB 52 30 F8]
; первый байт принят, прекращаем отсчитывать таймаут, регистр A используем как счетчик
; $481F ($0014)
ld A, UART1_DR ; [C6 52 31]
; $4822 ($0017) ждем очередной байт
boot_O_rx_wait_block:
btjf UART1_SR, #5, boot_O_rx_wait_block ; [72 OB 52 30 FB]
push UART1_DR ; [3B 52 31]
dec A ; [4A]
; после каждой итерации в регистре A количество оставшихся для загрузки байтов
jrne boot_O_rx_wait_block ; [26 F5]
; $482D ($0022) передаем управление принятому блоку данных
ldw X, SP ; [96]
incw X ; [5C]
boot_O_exit_to_FLASH:
jp (X) ; [FC]
; $4830 ($0025) передаем управление прикладной программе
boot_O_exit:
dc.b $AE ; ldw X, #boot_O_exit_address ; [AE 80 04]
; $4831 ($0026)
; адрес передачи управления прикладной программе
boot_O_exit_address:
dc.w main_flash ; [80 04]
; dc.w main_ram ; [00 00]
jrmi boot_O_exit_to_FLASH ; [2B FA]
; if X < $8000 адрес пердачи управления равен $0000
; а код находится в EEPROM
boot_O_exit_to_EEPROM:
; Y <- { EEPROM_END}
ldw Y, #$427F ; [90 AE 42 7F]
; X <- { EEPROM_END - EEPROM_START }
; грузим копию EEPROM в RAM
ldw X, #$027F ; [AE 02 7F]
jp boot_F_copy ; [CC 9F F4]
; $483F ($0034)
dc.b $00 ; резервная ячейка
boot_O_end:
; ********************************************************
segment byte at 8000-8003 'RESET_vector'
;96CC9FF2
ldw X, SP ; [96] X <- RAM_END
jp boot_F_start ; [CC 9F F2]
; ********************************************************
; адреса 0x8004...0x9FF1 свободны для прошивки прикладной программы
segment byte at 8004 'main_FLASH'
main_flash:
;20FE
jra * ; [20 FE]
; ********************************************************
; начальный копировщик boot_FLASH
segment byte at 9FF2-9FFF 'boot_F'
;90AE4C0A90F6F7905A5A2AF85CFC
boot_F_start:
; Y <- { boot_O_START + RAM_END} { $480B + $03FF = $4C0A }
ldw Y, #$4C0A ; [90 AE 4C 0A]
; этот участок кода используют
; boot_FLASH, boot_OPTION и дампы из хост программы
boot_F_copy:
ld A, (Y) ; [90 F6]
ld (X), A ; [F7]
decw Y ; [90 5A]
decw X ; [5A]
jrpl boot_F_copy ; [2A F8] если X(Y) >= RAM_START(boot_O_START)
incw X ; [5C]
jp (X) ; [FC]
end
;
Адрес передачи управления прикладной программе во FLASH памяти можно выбрать из диапазона $8004...$9FF1. Для образа кода прикладной программы из EEPROM памяти передача управления возможна только по адресу $0000 в памяти RAM.
Хост программе вторым аргументом командной строки можно передать любой адрес передачи управления.
Исходный код хост программы можно поискать здесь . Там же и контакты для более развернутого общения.
Прошу от читателей целевой критики и предложений по дальнейшему уменьшению кода.
Предлагаю ознакомиться также со статьей ” Как сжать загрузчик для STM8 до размера 8 байт в памяти FLASH” .