Почему я получаю эту ошибку в proteus: "Invalid opcode 0xFFFF at PC=0x0008"
Мне нужно написать ассемблерный код для моего микроконтроллера ATmega8, который будет генерировать 12-разрядное треугольное напряжение с частотой от 10 Гц до 1 кГц. Кроме того, код должен позволять управлять частотой с помощью потенциометра.
Я разработал схему в Proteus: я подключил потенциометр к порту PC0/ADC0 микроконтроллера ATmega8 и подключил к нему источник питания и заземление. Затем я подключил питание к портам AVCC и AREF микроконтроллера. Для сопряжения MCP4921 с микроконтроллером ATmega8 я использовал порт PB5/SCK для SCK, порт PB3/MOSI/OC2 для SDI от MCP4921 и порт PB2/SS/OC1B для CS от MCP4921. После этого я подключил MCP4921 через порт LDAC к земле. Я также подал питание на MCP4921 через порт VREFA и подключил вольтметр к порту VITA, подключенный к земле, вместе с осциллографом.
; Код на ассемблере для ATmega8, который формирует 12-разрядное треугольное напряжение частотой от 10 Гц до 1 кГц
; и управляет частотой с помощью потенциометра, подключенного к порту PC0/ADC0
; Схема подключения MCP4921 к ATmega8:
; SCK - PB5
; SDI - PB3
; CS - PB2
; LDAC - GND
; VREFA - VCC
; VOUTA - вольтметр и осциллоскоп
.include "m8def.inc" ; определения для ATmega8
.equ F_CPU = 8000000 ; частота тактового генератора
.equ SPI_DDR = DDRB ; регистр направления порта B
.equ SPI_PORT = PORTB ; регистр данных порта B
.equ SPI_SCK = PB5 ; пин SCK
.equ SPI_MOSI = PB3 ; пин MOSI
.equ SPI_CS = PB2 ; пин CS
.def temp = r16 ; временный регистр
.def value = r17 ; регистр для хранения значения потенциометра
.def step = r18 ; регистр для хранения шага изменения напряжения
.def dac = r19 ; регистр для хранения значения для MCP4921
.def flag = r20 ; регистр для хранения флага направления изменения напряжения
.org 0x0000 ; начало программы
rjmp start ; переход к метке start
.org 0x0012 ; вектор прерывания по совпадению счетчика 2
rjmp timer2_isr ; переход к обработчику прерывания
start:
; инициализация SPI
ldi temp, (1<<SPI_SCK)|(1<<SPI_MOSI)|(1<<SPI_CS) ; устанавливаем пины SCK, MOSI и CS как выходы
out SPI_DDR, temp ; записываем в регистр направления порта B
ldi temp, (1<<SPE)|(1<<MSTR)|(1<<SPR0) ; включаем SPI, устанавливаем режим мастера и делитель частоты 16
out SPCR, temp ; записываем в регистр управления SPI
; инициализация АЦП
ldi temp, (1<<REFS0) ; выбираем внутренний источник опорного напряжения
out ADMUX, temp ; записываем в регистр выбора канала АЦП
ldi temp, (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; включаем АЦП, устанавливаем делитель частоты 128
out ADCSRA, temp ; записываем в регистр управления АЦП
; инициализация таймера 2
ldi temp, (1<<WGM21) ; устанавливаем режим CTC (сброс по совпадению)
out TCCR2, temp ; записываем в регистр управления таймером 2
ldi temp, 249 ; устанавливаем значение сравнения для получения частоты прерываний 1 кГц
out OCR2, temp ; записываем в регистр сравнения таймера 2
ldi temp, (1<<OCIE2) ; разрешаем прерывание по совпадению счетчика 2
out TIMSK, temp ; записываем в регистр маски прерываний таймера
ldi temp, (1<<CS21)|(1<<CS20) ; запускаем таймер 2 с делителем частоты 32
out TCCR2, temp ; записываем в регистр управления таймером 2
sei ; разрешаем глобальные прерывания
ldi step, 1 ; инициализируем шаг изменения напряжения
ldi flag, 0 ; инициализируем флаг направления изменения напряжения
main:
rcall read_adc ; читаем значение потенциометра
rcall write_dac ; записываем значение в MCP4921
rjmp main ; бесконечный цикл
read_adc:
sbi ADCSRA, ADSC ; запускаем преобразование АЦП
adc_wait: ; метка ожидания окончания преобразования
in temp, ADCSRA ; читаем регистр управления АЦП
sbrs temp, ADSC ; проверяем бит ADSC
rjmp adc_wait ; если бит не сброшен, то ждем дальше
in value, ADCH ; читаем старший байт результата АЦП
ret ; возвращаемся из подпрограммы
write_dac:
ldi temp, 0b00110000 ; устанавливаем биты конфигурации для MCP4921
or temp, dac ; добавляем старшие 4 бита значения для MCP4921
cbi SPI_PORT, SPI_CS ; устанавливаем пин CS в низкий уровень
rcall spi_send ; отправляем байт по SPI
swap dac ; меняем местами старший и младший нибблы значения для MCP4921
andi dac, 0b00001111 ; обнуляем старшие 4 бита значения для MCP4921
rcall spi_send ; отправляем байт по SPI
sbi SPI_PORT, SPI_CS ; устанавливаем пин CS в высокий уровень
ret ; возвращаемся из подпрограммы
spi_send:
out SPDR, temp ; записываем байт в регистр данных SPI
spi_wait: ; метка ожидания окончания передачи
in temp, SPSR ; читаем регистр состояния SPI
sbrs temp, SPIF ; проверяем бит SPIF
rjmp spi_wait ; если бит не установлен, то ждем дальше
ret ; возвращаемся из подпрограммы
timer2_isr:
; обработчик прерывания по совпадению счетчика 2
; изменяет значение для MCP4921 в зависимости от значения потенциометра и флага направления
push temp ; сохраняем регистр temp на стеке
push value ; сохраняем регистр value на стеке
push step ; сохраняем регистр step на стеке
push dac ; сохраняем регистр dac на стеке
push flag ; сохраняем регистр flag на стеке
rcall read_adc ; читаем значение потенциометра
lsr value ; сдвигаем значение вправо на 1 бит
lsr value ; сдвигаем значение вправо еще на 1 бит
mov step, value ; копируем значение в регистр step
tst step ; проверяем, не равно ли значение нулю
breq timer2_exit ; если равно, то выходим из обработчика
tst flag ; проверяем флаг направления
breq timer2_up ; если равен нулю, то переходим к метке timer2_up
timer2_down: ; метка уменьшения напряжения
sub dac, step ; вычитаем шаг из значения для MCP4921
brcc timer2_exit ; если нет переноса, то выходим из обработчика
ldi dac, 0xFF ; если есть перенос, то устанавливаем значение для MCP4921 в максим
timer2_up: ; метка увеличения напряжения
add dac, step ; прибавляем шаг к значению для MCP4921
brcc timer2_exit ; если нет переноса, то выходим из обработчика
ldi dac, 0x00 ; если есть перенос, то устанавливаем значение для MCP4921 в минимум
ldi flag, 1 ; меняем флаг направления на 1
rjmp timer2_exit ; переходим к метке выхода
timer2_exit: ; метка выхода из обработчика
pop flag ; восстанавливаем регистр flag со стека
pop dac ; восстанавливаем регистр dac со стека
pop step ; восстанавливаем регистр step со стека
pop value ; восстанавливаем регистр value со стека
pop temp ; восстанавливаем регистр temp со стека
reti ; возвращаемся из прерывания
Сообщение: PROSPICE 8.13.00 (Build 32709) (C) Labcenter Electronics 1993-2023. Loaded netlist 'C:\Users\maxim\AppData\Local\Temp\LISA7525.SDF' for design 'Kursach.pdsprj' AVR Release 8.3SP0 build 33337 for ATMEGA8. [U1] QPainter::begin: Paint device returned engine == 0, type: 1 Invalid opcode 0xFFFF at PC=0x0008 @0.016216000s
Я нашел это (https://stackoverflow.com/questions/74407214/why-am-i-getting-this-error-in-proteus-invalid-opcode-0xffff-at-pc-0x002a), но я не знаю, как его использовать

