我需要为我的ATmega 8微控制器编写汇编代码,该代码将生成频率范围为10 Hz至1 kHz的12位三角电压。此外,该代码应允许使用电位器控制频率。
我在Proteus中设计了一个示意图:我将一个电位器连接到ATmega 8微控制器的PC 0/ADC 0端口,并将电源和地连接到它。然后,我将电源连接到微控制器的AVCC和AREF端口。为了将MCP 4921与ATmega 8微控制器连接,我使用MCP 4921的PB 5/SCK端口用于SCK,PB 3/MOSI/OC 2端口用于SDI,从MCP 4921上接PB 2/SS/OC 1B端口,然后通过LDAC端口将MCP 4921接地,通过VREFA端口为MCP 4921供电,并将电压表连接到VOUTA端口,接地,沿着示波器。
我的代码:
; Assembly code for ATmega8, which generates a 12-bit triangular voltage ranging from 10 Hz to 1 kHz
; and controls the frequency using a potentiometer connected to the PC0/ADC0 port.
; Connection scheme of MCP4921 to ATmega8:
; SCK - PB5
; SDI - PB3
; CS - PB2
; LDAC - GND
; VREFA - VCC
; VOUTA - voltmeter and oscilloscope
.include "m8def.inc" ; Definitions for ATmega8
.equ F_CPU = 8000000 ; Clock frequency
.equ SPI_DDR = DDRB ; Port B data direction register
.equ SPI_PORT = PORTB ; Port B data register
.equ SPI_SCK = PB5 ; SCK pin
.equ SPI_MOSI = PB3 ; MOSI pin
.equ SPI_CS = PB2 ; CS pin
.def temp = r16 ; Temporary register
.def value = r17 ; Register to store potentiometer value
.def step = r18 ; Register to store voltage change step
.def dac = r19 ; Register to store value for MCP4921
.def flag = r20 ; Register to store direction flag for voltage change
.org 0x0000 ; Program start
rjmp start ; Jump to start label
.org 0x0012 ; Timer 2 comparison interrupt vector
rjmp timer2_isr ; Jump to interrupt handler
start:
; SPI initialization
ldi temp, (1\<\<SPI_SCK)|(1\<\<SPI_MOSI)|(1\<\<SPI_CS) ; Set SCK, MOSI, and CS pins as outputs
out SPI_DDR, temp ; Write to Port B data direction register
ldi temp, (1\<\<SPE)|(1\<\<MSTR)|(1\<\<SPR0) ; Enable SPI, set master mode, set clock rate divider to 16
out SPCR, temp ; Write to SPI control register
; ADC initialization
ldi temp, (1\<\<REFS0) ; Select internal reference voltage source
out ADMUX, temp ; Write to ADC channel selection register
ldi temp, (1\<\<ADEN)|(1\<\<ADPS2)|(1\<\<ADPS1)|(1\<\<ADPS0) ; Enable ADC, set clock rate divider to 128
out ADCSRA, temp ; Write to ADC control and status register A
; Timer 2 initialization
ldi temp, (1\<\<WGM21) ; Set CTC mode (clear timer on compare match)
out TCCR2, temp ; Write to Timer 2 control register
ldi temp, 249 ; Set comparison value for 1 kHz interrupt frequency
out OCR2, temp ; Write to Timer 2 compare register
ldi temp, (1\<\<OCIE2) ; Enable Timer 2 comparison interrupt
out TIMSK, temp ; Write to Timer interrupt mask register
ldi temp, (1\<\<CS21)|(1\<\<CS20) ; Start Timer 2 with a frequency divider of 32
out TCCR2, temp ; Write to Timer 2 control register
sei ; Enable global interrupts
ldi step, 1 ; Initialize voltage change step
ldi flag, 0 ; Initialize voltage change direction flag
main:
rcall read_adc ; Read potentiometer value
rcall write_dac ; Write value to MCP4921
rjmp main ; Infinite loop
read_adc:
sbi ADCSRA, ADSC ; Start ADC conversion
adc_wait: ; Wait for conversion to complete
in temp, ADCSRA ; Read ADC control and status register
sbrs temp, ADSC ; Check ADSC bit
rjmp adc_wait ; Continue waiting if bit is not cleared
in value, ADCH ; Read high byte of ADC result
ret ; Return from subroutine
write_dac:
ldi temp, 0b00110000 ; Set configuration bits for MCP4921
or temp, dac ; Add high 4 bits of value for MCP4921
cbi SPI_PORT, SPI_CS ; Set CS pin low
rcall spi_send ; Send byte over SPI
swap dac ; Swap high and low nibbles of value for MCP4921
andi dac, 0b00001111 ; Clear high 4 bits of value for MCP4921
rcall spi_send ; Send byte over SPI
sbi SPI_PORT, SPI_CS ; Set CS pin high
ret ; Return from subroutine
spi_send:
out SPDR, temp ; Write byte to SPI data register
spi_wait: ; Wait for transmission to complete
in temp, SPSR ; Read SPI status register
sbrs temp, SPIF ; Check SPIF bit
rjmp spi_wait ; Continue waiting if bit is not set
ret ; Return from subroutine
timer2_isr:
; Timer 2 comparison interrupt handler
; Changes the value for MCP4921 based on potentiometer value and direction flag
push temp ; Save temp register to stack
push value ; Save value register to stack
push step ; Save step register to stack
push dac ; Save dac register to stack
push flag ; Save flag register to stack
rcall read_adc ; Read potentiometer value
lsr value ; Right shift value by 1 bit
lsr value ; Right shift value by another bit
mov step, value ; Copy value to step register
tst step ; Check if value is zero
breq timer2_exit ; Exit handler if value is zero
tst flag ; Check direction flag
breq timer2_up ; Jump to timer2_up if flag is zero
timer2_down: ; Label for decreasing voltage
sub dac, step ; Subtract step from value for MCP4921
brcc timer2_exit ; Exit handler if no carry
ldi dac, 0xFF ; Set value for MCP4921 to maximum if carry
timer2_up: ; Label for increasing voltage
add dac, step ; Add step to value for MCP4921
brcc timer2_exit ; Exit handler if no carry
ldi dac, 0x00 ; Set value for MCP4921 to minimum if carry
ldi flag, 1 ; Change direction flag to 1
rjmp timer2_exit ; Jump to exit label
timer2_exit: ; Exit label for handler
pop flag ; Restore flag register from stack
pop dac ; Restore dac register from stack
pop step ; Restore step register from stack
pop value ; Restore value register from stack
pop temp ; Restore temp register from stack
reti ; Return from interrupt
字符串
my scheme
留言: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
我找到了这个(Why am I getting this error in proteus: " Invalid opcode 0xFFFF at PC=0x002A")但我不知道如何使用它
1条答案
按热度按时间yks3o0rb1#
编译器告诉你在
TIMER2
的中断向量表中没有有效的指令。它告诉你这是因为这两行:字符串
地址
0x0012
不是TIMER2
比较匹配中断的正确地址,它属于USART RXC
(本手册第46页列出了中断向量的地址)。为了修复编译错误,您需要将其更改为正确的地址:0x0003
。