Ниже приведен пример простого загрузчика, использующего USART для связи через COM-порт компьютера (схема такого подключения находится в разделе “Отладка”). Загрузчик занимает менее 128 слов памяти программ и, поэтому, целиком помещается в Boot Loader Section наименьшего размера.
Существует два варианта запуска boot-loader. В первом из них обращение к коду загрузчика происходит из тела основной программы, расположенного в Application Section. В ином случае, сбросом FUSE-бита BOOTRST, положение вектора сброса можно перенести непосредственно в Boot Loader Section и запускать загрузчик каждый раз при старте устройства. В примере на рис.3 реализован второй способ управления. Рассмотрим его подробнее.
После сброса микроконтроллер начинает выполнять программу с адреса SMALLBOOTSTART = 0xF80 (BOOTSZ1:BOOTSZ0 = 11). После инициализации стека и настроек USART, управляющая программа переходит к проверке определенного условия, указывающего на необходимость модификации FLASH-памяти. В данном случае таким условием, выбранным произвольным образом, является логический уровень на выводе PB0. Если линия закорочена на землю (присутствует лог.0), то происходит вызов подпрограммы загрузчика. В противном случае происходит передача управления на нулевой адрес, т.е. в самое начало прикладной программы.
.nolist .include "m8def.inc" .list .equ XTAL = 3686 ;частота генератора в кГц .equ BAUD = 115200 ;необходимая скорость обмена в бит/с .equ NB = ((10000*XTAL)/(16*BAUD)-5)/10 .def temp = R16 ;вспомогательный регистр .cseg .org SMALLBOOTSTART ldi temp,low(RAMEND) ;инициализация стека out SPL,temp ldi temp,high(RAMEND) out SPH,temp clr temp ;задаем параметры USART: 115200, 8-N-1 out UBRRH,temp ldi temp,NB out UBRRL,temp ldi temp,(1<< RXEN)|(1<< TXEN) out UCSRB,temp ldi temp,(1<< URSEL)|(1<< UCSZ0)|(1<< UCSZ1) out UCSRC,temp sbi PORTB,PB0 ;подключаем к линии 0 порта B nop ;внутренней pull-up резистор sbis PINB,PB0 ;если на выводе PB0 лог.0, то rcall boot_loader ;запускаем загрузчик rjmp 0 ;если лог.1, то переходим к основной программе ; Подпрограмма загрузчик ; R16 - регистр для приема/передачи данных по USART ; R17 - регистр для промежуточных операций ; R18 - используется как счетчик слов ; ZH:ZL - регистр с адресом страницы и указатель на строку ; R1,R0 - используются с инструкцией spm boot_loader: rcall getc ;принимаем через порт команду cpi R16,'D' ;если "D", то передаем строку brne bl1 rjmp device_id ;подтверждения присутствия на линии bl1: cpi R16,'F' ;если "F", то переходим к brne bl2 rjmp read_fuse ;чтению FUSE-битов и ячеек защиты bl2: cpi R16,'P' ;если "P", то переходим к brne bl3 rjmp program_flash ;программированию страницы FLASH-памяти bl3: cpi R16,'R' ;если "R", то переходим к brne bl4 rjmp read_flash ;чтению страницы FLASH-памяти bl4: cpi R16,'E' ;если "E", то переходим к brne bl5 rjmp erase_flash;стиранию страницы FLASH-памяти bl5: cpi R16,'O' ;если "O", то завершение подпрограммы brne bl6 ret ;если принят иной символ, то возвращаем bl6: rcall put_err ;символ ошибки "?" rjmp boot_loader device_id: ldi ZH,high(2*id_string);заносим в указатель Z адрес ldi ZL,low(2*id_string) ;начала строки подтверждения di1: lpm R16,Z+ ;передаем символы до тех пор, пока tst R16 ;не встретится 0 (конец строки) breq boot_loader rcall putc rjmp di1 read_fuse: ldi R17,(1<< BLBSET)|(1<< SPMEN) clr ZH clr ZL out SPMCR,R17 lpm R16,Z ;считываем младший байт конфигурации rcall putc ldi ZL,1 out SPMCR,R17 lpm R16,Z ;считываем содержимое ячеек защиты rcall putc ldi ZL,3 out SPMCR,R17 lpm R16,Z ;считываем старший байт конфигурации rcall putc rjmp boot_loader program_flash: rcall get_adr ;принимаем адрес страницы ldi R16,(1<< PGERS)|(1<< SPMEN) rcall do_spm ;стираем страницу ldi R18,PAGESIZE ;инициализируем счетчик слов pf1: rcall getc ;принимаем младший байт кода mov R0,R16 rcall getc ;принимаем старший байт кода mov R1,R16 ldi R16,1<< SPMEN rcall do_spm ;заносим слово в буфер записи adiw ZH:ZL,2 dec R18 ;повторяем 32 раза brne pf1 subi ZL,PAGESIZE ;восстанавливаем адрес страницы sbci ZH,0 ldi R16,(1<< PGWRT)|(1<< SPMEN) rcall do_spm ;записываем страницу rcall put_ret ;отсылаем символ подтверждения "!" rjmp boot_loader read_flash: rcall get_adr ;принимаем адрес страницы ldi R16,(1<< RWWSRE)|(1<< SPMEN) rcall do_spm ;разрешаем доступ к области RWW ldi R18,PAGESIZE ;инициализируем счетчик слов rf1: lpm R16,Z+ rcall putc ;передаем младший байт кода lpm R16,Z+ rcall putc ;передаем старший байт кода dec R18 ;повторяем 32 раза brne rf1 rcall put_ret ;отсылаем символ подтверждения "!" rjmp boot_loader erase_flash: rcall get_adr ldi R16,(1<< PGERS)|(1<< SPMEN) rcall do_spm ;стираем страницу rcall put_ret ;отсылаем символ подтверждения "!" rjmp boot_loader ; Подпрограмма запуска команды spm ; R16 - байт для передачи кода в регистр SPMCR do_spm: in R17,SPMCR ;ожидаем пока не закончится предыдущая sbrc R17,SPMEN ;операция rjmp do_spm ds1: sbic EECR,EEWE ;ожидаем пока не закончится запись rjmp ds1 ;в EEPROM-память out SPMCR,R16 ;модифицируем регистр SPMCR spm ;в течении 4-х циклов выполняем команду spm ret ; Подпрограмма передачи байта ; R16 - байт для передачи на входе в подпрограмму putc: sbis UCSRA,UDRE ;ожидаем пока освободится регистр rjmp putc ;данных передатчика USART out UDR,R16 ;передаем байт данных ret ; Подпрограмма приема байта ; R16 - принятый байт на выходе из подпрограммы getc: sbis UCSRA,RXC ;ожидаем пока не будет принят байт rjmp getc in R16,UDR ;переносим байт в рабочий регистр ret ; Подпрограмма приема адреса страницы ; ZH:ZL - принятый адрес на выходе из подпрограммы get_adr: rcall getc ;принимаем младший байт адреса страницы mov ZL,R16 rcall getc ;принимаем старший байт адреса страницы mov ZH,R16 ret ; Подпрограмма передачи символа подтверждения "!" put_ret: ldi R16,'!' rcall putc ret ; Подпрограмма передачи символа ошибки "?" put_err: ldi R16,'?' rcall putc ret id_string: ;строка подтверждения .db "ATmega8",0
Скачать исходник asm и прошивку hex
Перейти к следующей части: Управляющая программа высокого уровня
Комментарии (1) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация