Почему я получаю эту ошибку в 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), но я не знаю, как его использовать

сообщение


Ответы (0 шт):