Шаблон программы для микроконтроллера ATmega328P

Шаблон имеет зависимость m328Pdef.inc , в среде AtmelStudio подключается автоматически при выборе соответствующего чипа.

Замечание! Не обязательно определять все сегменты кода, даже программный сегмент, его компилятор принимает по умолчанию.

Замечание! Не обязательно описывать все точки входа прерываний, но их очередность в адресном пространстве программы строго определена.

Замечание! Каждая точка входа каждого прерывания занимает 2 машинных слова (4 байта) в отличии от ATtiny2313 - 1 машинное слово (2 байта)

Замечание! Нужно учитывать, что помимо конфигурирования программы нужно соответствующим образом запрграммировать fuse-биты

Приведенный ниже код является сложным для восприятия, но достаточно универсален с точки зрения шаблона. Во-первых, я постарался показать все возможные варианты начальной структуры программы устройства, собрав все воедино. Во-вторых, хотел продемонстрировать некоторые возможности препроцессора и директив ассемблера.

/*
 *	AppATmega328P.asm
 *  Template app for ATmega328P
 *  Created: 30.06.2016 16:36:42
 *  Author: Дмитрий Самбулов
 *	E-mail: Этот адрес электронной почты защищён от спам-ботов. У вас должен быть включен JavaScript для просмотра.
 *	WEB: www.gosmart.su
 */

.include "m328Pdef.inc"

; Конфигурация программы, раскомментировать если необходимо:
;#define BOOTRST_FUSE_PROGRAMMED	; Загрузка с boot-сектора
;#define IVSEL_BIT_SET			; Использовать прерывания в загрузчике, 
					; перенести векторы прерываний в boot-сектор

;#define BOOTSTART FIRSTBOOTSTART	; boot-сектор 256 байт
;#define BOOTSTART SECONDBOOTSTART	; boot-сектор 512 байт
;#define BOOTSTART THIRDBOOTSTART	; boot-сектор 1024 байта
#define BOOTSTART FOURTHBOOTSTART	; boot-сектор 2048 байт

#define ENTRY_POINT INT_VECTORS_SIZE	; Адрес точки входа программы устройства
;#define ENTRY_POINT 0x0000
;#define ENTRY_POINT ______		; TODO: указать произвольную точку входа 

.eseg	; Сегмент EEPROM


.dseg	; Сегмент данных
.org SRAM_START	; Адрес начала SRAM
data:


.cseg	; Программный сегмент 
; Точки входа прерываний
; Определяем расположение точки входа вектора Reset
#ifdef BOOTRST_FUSE_PROGRAMMED	; Если загрузка с boot-сектора
.WARNING "Start from bootloader"
.org BOOTSTART			; Установить адрес начала boot-сектора
#else				; Иначе
.WARNING "Start from program"
.org 0x0000			; Установить адрес начала программной памяти
#endif
.overlap	; Переход на обработчик Reset может быть перекрыт другим кодом в 
		; случае переноса остальных векторов прерываний
jmp RESET	; Переход на обработчик Reset
.nooverlap
; Определяем расположение точек входа остальных векторов
#ifdef IVSEL_BIT_SET	; Если бит переноса векторов установлен
.WARNING "Other interrupt vectors moved to boot section"
.org BOOTSTART + 0x0002	; Установить адрес в boot-секторе
#else			; Иначе
.WARNING "Other interrupt vectors start from 0x0002"
.org 0x0002		; Установить адрес следующий за Reset
#endif
jmp INT0_I	; Переход на обработчик внешнего прерывания INT0
jmp INT1_I	; Переход на обработчик внешнего прерывания INT1
jmp PCINT0_I	; Переход на обработчик внешнего прерывания PCINT
jmp PCINT1_I	; Переход на обработчик внешнего прерывания PCINT
jmp PCINT2_I	; Переход на обработчик внешнего прерывания PCINT
jmp WDT_OVERFLOW ; Переход на обработчик прерывания переполнения Watchdog
jmp TIM2_COMPA	; Переход на обработчик прерывания сравнения Timer1 канал A
jmp TIM2_COMPB	; Переход на обработчик прерывания сравнения Timer1 канал A
jmp TIM2_OVF	; Переход на обработчик прерывания переполнения таймера 1
jmp TIM1_CAPT	; Переход на обработчик прерывания захват по событию Timer1
jmp TIM1_COMPA	; Переход на обработчик прерывания сравнения Timer1 канал A
jmp TIM1_COMPB	; Переход на обработчик прерывания сравнения Timer1 канал A
jmp TIM1_OVF	; Переход на обработчик прерывания переполнения таймера 0
jmp TIMER0_COMPA ; Переход на обработчик прерывания сравнения Timer0 канал A
jmp TIMER0_COMPB ; Переход на обработчик прерывания сравнения Timer0 канал B
jmp TIM0_OVF	; Переход на обработчик прерывания переполнения таймера 0
jmp SPI_STC	; SPI передача завершена
jmp USART_RXC	; Переход на обработчик прерывания USART0 прием завершен
jmp USART_DRE	; Переход на обработчик прерывания USART0, UDR пуст
jmp USART_TXC	; Переход на обработчик прерывания USART0 передача завершена
jmp ADC_CC	; Переход на обработчик прерывания АЦП преобразование завершено
jmp EE_READY	; Переход на обработчик прерывания EEPROM готова
jmp ANA_COMP	; Переход на обработчик прерывания аналогового компаратора
jmp TWI		; Переход на обработчик прерывания интерфейса TWI
jmp SPM_RDY	; Переход на обработчик прерывания записи в программную память

.MACRO BOOT_INIT		; Макрос описывающий начальную инициализацию контроллера
	ldi r16, high(RAMEND)	; Установка указателя стека на конец оперативной памяти
	out SPH, r16
	ldi r16, LOW(RAMEND)
	out SPL, r16
#ifdef IVSEL_BIT_SET		; Если бит переноса векторов установлен 
	ldi r16, (1<<IVCE)	; Разрешить изменение расположения векторов прерываний
	out MCUCR, r16
	ldi r16, (1<<IVSEL)	; Перенести векторы прерываний в boot-сектор
	out MCUCR, r16
#endif
.ENDMACRO


.org ENTRY_POINT
#ifndef BOOTRST_FUSE_PROGRAMMED
RESET:
	BOOT_INIT
#endif
	MAIN:

	; TODO: Инициаализировать контроллер

	sei	; Разрешить прерывания
LOOP:	; Основный программый цикл

	; TODO: Описать логику устройства

	jmp LOOP

ENDPROG:

#ifdef BOOTRST_FUSE_PROGRAMMED	; Если загрузка с boot-сектора
	#ifdef IVSEL_BIT_SET	; Если бит переноса векторов установлен
.org BOOTSTART + INT_VECTORS_SIZE ; Установить адрес загрузчика после векторов прерываний
	#else			; Иначе
.org BOOTSTART			; Установить адрес загрузчика в начале boot-сектора
	#endif
RESET:
	BOOT_INIT

	; TODO: Описать логику программы загрузчика

	jmp ENTRY_POINT
#endif

#ifdef BOOTRST_FUSE_PROGRAMMED	; Если загрузка с boot-сектора
	#ifndef IVSEL_BIT_SET	; Если бит переноса векторов не установлен
.org ENDPROG			; Установить адрес обработчиков прерываний в конец программы
	#endif
#endif

INT0_I:		; TODO: Обработчик внешнего прерывания INT0
INT1_I:		; TODO: Обработчик внешнего прерывания INT1
PCINT0_I:	; TODO: Обработчик внешнего прерывания PCINT
PCINT1_I:	; TODO: Обработчик внешнего прерывания PCINT
PCINT2_I:	; TODO: Обработчик внешнего прерывания PCINT
WDT_OVERFLOW:	; TODO: Обработчик прерывания переполнения Watchdog
TIM2_COMPA:	; TODO: Обработчик прерывания сравнения Timer2 канал A
TIM2_COMPB:	; TODO: Обработчик прерывания сравнения Timer2 канал A
TIM2_OVF:	; TODO: Обработчик прерывания переполнения таймера 2
TIM1_CAPT:	; TODO: Обработчик прерывания захват по событию Timer1
TIM1_COMPA:	; TODO: Обработчик прерывания сравнения Timer1 канал A
TIM1_COMPB:	; TODO: Обработчик прерывания сравнения Timer1 канал A
TIM1_OVF:	; TODO: Обработчик прерывания переполнения таймера 1
TIMER0_COMPA:	; TODO: Обработчик прерывания сравнения Timer0 канал A
TIMER0_COMPB:	; TODO: Обработчик прерывания сравнения Timer0 канал B
TIM0_OVF:	; TODO: Обработчик прерывания переполнения таймера 0
SPI_STC:	; TODO: Обработчик прерывания SPI передача завершена
USART_RXC:	; TODO: Обработчик прерывания USART прием завершен
USART_DRE:	; TODO: Обработчик прерывания USART, UDR пуст
USART_TXC:	; TODO: Обработчик прерывания USART передача завершена
ADC_CC:		; TODO: Обработчик прерывания АЦП преобразование завершено
EE_READY:	; TODO: Обработчик прерывания EEPROM готова
ANA_COMP:	; TODO: Обработчик прерывания аналогового компаратора
TWI:		; TODO: Обработчик прерывания интерфейса TWI
SPM_RDY:	; TODO: Обработчик прерывания записи в программную память
reti