在没有 PWM 的 AVR 组件中生成方波
Generating square wave in AVR Assembly without PWM
我正在使用 ATmega328。问题是我想生成给定频率和给定幅度的方波。不能使用PWM,因为给我的板子已经焊好了,所以波形必须放在连接到处理器B端口的R2R resistor ladder的输出端。所以,基本上,我的想法是我必须周期性地将端口 B 的引脚置于 0 和 VOLUME(VOLUME 是一个从 1 到 255 的数字),具有给定的频率和 50% 的占空比。请记住:无 PWM。频率应该能够每 100 毫秒改变一次,但我无法完成这项工作,所以我只是尝试生成一个恒定频率,看看一开始会发生什么。
我是 运行 1MHz 的时钟。我写了下面的代码:
.DSEG
.ORG 0x100
.CSEG
.ORG 0x100
;Initializing stack pointer
LDI R16,HIGH(RAMEND)
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
MAIN:
CALL GENERATE ;Calling the generating routine
RJMP MAIN ;Repeat this forever
;I will generate a 440Hz frequency. It has an approximate period of 2273 microseconds
;This means that half a period stands for approximately 1136 clocks
GENERATE:
LDI R17, 0x70
LDI R18, 0x04 ;Half the period in hexadecimal is 0x0470
LDI R19, 243 ;Volume = 243 (arbitrary, it could be any number)
LDI R21, 88 ;The amount of half-periods in 100 ms (arbitrary election, too)
LDI R25, 0xFF
OUT DDRB, R25 ;Port B is an output port
LDI R24, 0xFF ;R25R24 = 0xFFFF
CLC ;Clean the carry
SBC R24, R17
SBC R25, R18 ;R25R24 = 0xFFFF - Halfperiod
ADIW R25:R24, 1 ;R25R24 = 0xFFFF - Halfperiod +1
OUT PORTB, R18 ;The wave starts at 0
BEGIN:
CALL LOOP_1
EOR R19, R19 ;It varies between 0 and volume
OUT PORTB, R19 ;It puts the output to the actual value of R19 (0 or volume)
CLZ ;Clean Z flag
DEC R21
BREQ END ;When 100ms have passed, generation is over
JMP BEGIN ;If not, generation continues
LOOP_1: STS TCNT1H, R25
STS TCNT1L, R24 ;Loading the amount of clocks the timer has to count
LDI R16, 0x00
STS TCCR1A, R16
LDI R16, 0x01
STS TCCR1B, R16 ;Timer operating in normal mode, no prescaler
LOOP_2: IN R16, TIFR1
SBRS R16, TOV1 ;If timer's over, skip the next jump
JMP LOOP_2
LDI R16, 0x00
STS TCCR1B, R16 ;Stopping the timer
LDI R16, 0x04
OUT TIFR1, R16 ;Clean TOV1
RET ;Back to BEGIN
END:
RET ;Back to MAIN
这是我最初接触汇编的方法之一,因此读起来可能很难看。该代码目前无法正常工作。有什么想法吗?
编辑:
感谢 Spektre 向我指出了这一点,我更正了上面的一段代码。除了
,代码是一样的
GENERATE:
.
.
.
LDI R21, 44 ;The amount of PERIODS (not half-periods as before) in 100 ms
.
.
.
BEGIN:
OUT PORTB, R18 ;This was before the BEGIN tag, now it is after it
CALL LOOP_1 ;It counts a halfperiod with output=0
OUT PORTB, R19 ;Now output=volume
CALL LOOP_1 ;It counts a halfperiod with output=volume
CLZ ;Clean Z Flag
DEC R21
BREQ END ;When 100ms have passed, generation is over
JMP BEGIN ;If not, generation continues
我为这样的架构做了很多年......我正在使用 UC3(后继者)并且在 MCU 66 MHz
时钟我可以达到2-5 MHz
轮询频率。但恐怕在您的 Target 平台上这会低很多。因此,首先测量轮询频率以查看 HW.
的限制
1 MHz
是输出时钟还是MCU时钟?您需要输出的最大频率是多少?
有更多方法可以实现这一点,显而易见的是:
Timer/Counter
你可以设置Timer,比如1us,在那里做定时和切换。或者您可以使用它来设置新频率。但要注意任何中断都会导致输出信号出现时序问题,如抖动......如果有足够的时间,这些问题是可以避免的,但它通常会大大限制最大输出频率。
主线程轮询
看起来这就是你在做的事情。但是您忘记了所有说明都有自己的时间安排。条件语句可以有不同的时间,所以你应该补偿。
利用接口(如果存在)
查看数据表,看看是否有任何 HW 连接到该端口。 MCUs 通常在引脚上复用更多功能,有时允许利用它们。比如我曾经利用串口、DMA+ABI/SD卡接口做了一个VGA图像生成器。每种方法都正确地产生了 16 色 VGA 信号,但在同一芯片和引脚上具有完全不同的代码和硬件架构......要与 ATMega168 上具有 20 MHz
时钟的平台进行比较(它与 ATMega328) 的核心相同,我能够为 LCD[= 生成 1bit LCD 640x480
信号149=](比彩色 VGA 信号低很多倍)。有时你甚至可以在运行时改变引脚的含义......例如为了实现 100 000 rpm
BLDC 控制器我改变了几次内部电路和电机的电气周期只是为了能够产生如此高的速度(否则无法通过直接方式实现。
您需要试验哪种方法 fast/accurate/comfortable 对您来说足够了。从第一次看你的代码我偶然发现了这个:
EOR R19, R19 ;It varies between 0 and volume
OUT PORTB, R19 ;It puts the output to the actual value of R19 (0 or volume)
如果我没看错的话,你正在 XORing R19
和 R19
... 在第一次通过后销毁 R19
的内容.所以你一直在输出 0
到 B
端口:
XOR Volume,Volume -> Volume = 0
XOR 0,0 -> 0
相反,我会:
- 出端口量
- 等半期
- 输出端口零
- 等半期
- 如果达到100ms改变频率
- 转到#1
您应该尽可能少地使用端口访问。不确定 ATMega 是否也是这种情况,但在较新的体系结构上,端口由 HW 接口/[=211 访问=] 如果使用不当的话,高速切换真的很慢。
PS.
希望你的 R2R 梯形图在输入电阻之前有二极管,或者至少 PORTB 有集电极开路输出,否则它将无法工作,因为相邻引脚之间的电流,如果都设置为不同的值。
[Edit1] 一些说明
更清楚R2R电流问题
二极管应该有相同的PN势垒电压!!!
1MHz时钟
你写了I'm running the clock at 1MHz
但是没有说明是MCU/CPU时钟,Timer时钟还是输出方波信号频率。现在很明显,您的输出信号是 <130,523> Hz
,这在您的平台上应该很容易实现。我之前不确定......我在 MCU 上生成了甚至大约 30 MHz 的信号,所以 1 MHz 是可能的。
时间
大多数平台上的条件指令,如 BREQ END
在条件为真和为假时有不同的时间,因此为了保持同步,你应该考虑到这一点......有时 nop
可以处理问题...如果是这样的话,你应该检查文档中的指令时序。
你说你正在使用 counter/timer 我在你的代码中没有看到它。为了正确使用你应该
- 为您想要的模式和频率配置硬件
- 注册中断子程序
- 启用中断和/或触发定时器计数器事件...
内部中断事件处理程序
如果需要,重置计数器值并清除中断标志,以便发生另一个事件。如果您接近芯片功能的边缘,有时甚至需要更正计数器值以获得正确的时序。
我看到的是 timer/counter 的一些配置,不确定是否正确,因为我已经很长时间没有使用该平台了。我在任何地方都看不到中断处理程序子例程......可能您正在轮询计数器寄存器但再次不确定它是否是正确的方法(并非所有硬件寄存器都可以读取)您应该面对文档。但无论如何它正在轮询,所以指令的时间很重要......
如果有帮助,我会为 ATMega168 挖掘我的一个古老的发电机(在 PortC 上生成方波)asm 代码:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; generator ver: 0.00 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
CPU: ATMega168 8MHz internal RC
OUT: PC
*/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ATMega168 startup: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; fuses:BOOTRST=1 disabled
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.equ stack =0x04FF
.equ cpuclk =8000000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.device ATmega168 ; device selection
.equ PINB =0x03 ; IO reg adresses definition in/out
.equ DDRB =0x04
.equ PORTB =0x05
.equ PINC =0x06
.equ DDRC =0x07
.equ PORTC =0x08
.equ PIND =0x09
.equ DDRD =0x0A
.equ PORTD =0x0B
.equ TIFR0 =0x15
.equ TIFR1 =0x16
.equ TIFR2 =0x17
.equ PCIFR =0x1B
.equ EIFR =0x1C
.equ EIMSK =0x1D
.equ GPIOR0=0x1E
.equ EECR =0x1F
.equ EEDR =0x20
.equ EEARL =0x21
.equ EEARH =0x22
.equ GTCCR =0x23
.equ TCCROA=0x24
.equ TCCROB=0x25
.equ TCNT0 =0x26
.equ OCROA =0x27
.equ OCROB =0x28
.equ GPIOR1=0x2A
.equ GPIOR2=0x2B
.equ SPCR =0x2C
.equ SPSR =0x2D
.equ SPDR =0x2E
.equ ACSR =0x30
.equ SMCR =0x33
.equ MCUSR =0x34
.equ MCUCR =0x35
.equ SPMCSR=0x37
.equ SPL =0x3D
.equ SPH =0x3E
.equ SREG =0x3F
.equ WDTCSR=0x60 ; sts,lds
.equ CLKPR =0x61
.equ PRR =0x64
.equ OSCCAL=0x66
.equ PCICR =0x68
.equ EICRA =0x69
.equ PCMSK0=0x6B
.equ PCMSK1=0x6C
.equ PCMSK2=0x6D
.equ TIMSK0=0x6E
.equ TIMSK1=0x6F
.equ TIMSK2=0x70
.equ ADCL =0x78
.equ ADCH =0x79
.equ ADCSRA=0x7A
.equ ADCSRB=0x7B
.equ ADMUX =0x7C
.equ DIDR0 =0x7E
.equ DIDR1 =0x7F
.equ TCCR1A=0x80
.equ TCCR1B=0x81
.equ TCCR1C=0x82
.equ TCNT1L=0x84
.equ TCNT1H=0x85
.equ ICR1L =0x86
.equ ICR1H =0x87
.equ OCR1AL=0x88
.equ OCR1AH=0x89
.equ OCR1BL=0x8A
.equ OCR1BH=0x8B
.equ TCCR2A=0xB0
.equ TCCR2B=0xB1
.equ TCNT2 =0xB2
.equ OCR2A =0xB3
.equ ASSR =0xB6
.equ TWBR =0xB8
.equ TWSR =0xB9
.equ TWAR =0xBA
.equ TWDR =0xBB
.equ TWCR =0xBC
.equ TWAMR =0xBD
.equ UCSR0A=0xC0
.equ UCSR0B=0xC1
.equ UCSR0C=0xC2
.equ UBRR0L=0xC4
.equ UBRR0H=0xC5
.equ UDR0 =0xC6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.cseg ; Start of code segment
.org 0x0000 ; interrupts, highest priority first
jmp RESET ; 3 Reset Handler
jmp EXT_INT0 ; 3 IRQ0 Handler
jmp EXT_INT1 ; 3 IRQ1 Handler
jmp PCINT0 ; 3 PCINT0 Handler
jmp PCINT1 ; 3 PCINT1 Handler
jmp PCINT2 ; 3 PCINT2 Handler
jmp WDT ; 3 Watchdog Timer Handler
jmp TIM2_COMPA ; 3 Timer2 Compare A Handler
jmp TIM2_COMPB ; 3 Timer2 Compare B Handler
jmp TIM2_OVF ; 3 Timer2 Overflow Handler
jmp TIM1_CAPT ; 3 Timer1 Capture Handler
jmp TIM1_COMPA ; 3 Timer1 Compare A Handler
jmp TIM1_COMPB ; 3 Timer1 Compare B Handler
jmp TIM1_OVF ; 3 Timer1 Overflow Handler
jmp TIM0_COMPA ; 3 Timer0 Compare A Handler
jmp TIM0_COMPB ; 3 Timer0 Compare B Handler
jmp TIM0_OVF ; 3 Timer0 Overflow Handler
jmp SPI_STC ; 3 SPI Transfer Complete Handler
jmp USART_RXC ; 3 USART, RX Complete Handler
jmp USART_UDRE ; 3 USART, UDR Empty Handler
jmp USART_TXC ; 3 USART, TX Complete Handler
jmp ADC_DONE ; 3 ADC Conversion Complete Handler
jmp EE_RDY ; 3 EEPROM Ready Handler
jmp ANA_COMP ; 3 Analog Comparator Handler
jmp TWI ; 3 2-wire Serial Interface Handler
jmp SPM_RDY ; 3 Store Program Memory Ready Handler
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RESET: cli ; Reset Handler
ldi r16,high(stack)
out SPH,r16
ldi r16,low(stack)
out SPL,r16
ldi r16, 0xFF ; DDRB 1 - output, 0 - input direction
out DDRB, r16
ldi r16, 0xFF ; DDRC 1 - output, 0 - input direction
out DDRC, r16
ldi r16, 0xFF ; DDRD 1 - output, 0 - input direction
out DDRD, r16
ldi r16, 0xFF ; all outputs high,and all inputs Pull Up to Ucc
out PORTB,r16
out PORTC,r16
out PORTD,r16
jmp main
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EXT_INT0: ; IRQ0 Handler
reti
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EXT_INT1: ; IRQ1 Handler
reti
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PCINT0: ; PCINT0 Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PCINT1: ; PCINT1 Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PCINT2: ; PCINT2 Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
WDT: ; Watchdog Timer Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM2_COMPA: ; Timer2 Compare A Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM2_COMPB: ; Timer2 Compare B Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM2_OVF: ; Timer2 Overflow Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM1_CAPT: ; Timer1 Capture Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM1_COMPA: ; Timer1 Compare A Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM1_COMPB: ; Timer1 Compare B Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM1_OVF: ; Timer1 Overflow Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM0_COMPA: ; Timer0 Compare A Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM0_COMPB: ; Timer0 Compare B Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM0_OVF: ; Timer0 Overflow Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SPI_STC: ; SPI Transfer Complete Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
USART_RXC: ; USART, RX Complete Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
USART_UDRE: ; USART, UDR Empty Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
USART_TXC: ; USART, TX Complete Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ADC_DONE: ; ADC Conversion Complete Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EE_RDY: ; EEPROM Ready Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ANA_COMP: ; Analog Comparator Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TWI: ; 2-wire Serial Interface Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SPM_RDY: ; Store Program Memory Ready Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
main: ldi r16,0
main0: out PORTC,r16 ; 1 8T / 8MHz = 1MHz PC0, 0.5MHz PC1, ...
inc r16 ; 1
nop ; 1
nop ; 1
nop ; 1
nop ; 1
rjmp main0 ; 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
waitms: push r31 ; 2 wait cca r31 [ms] +(15+r31*4)T
push r30 ; 2
push r29 ; 2
waitms2:ldi r30,cpuclk/200000;1 1ms
waitms1:ldi r29,49 ; 1 200T
waitms0:nop ; 1
dec r29 ; 1
brne waitms0 ; 2/1
dec r30 ; 1
brne waitms1 ; 2/1
dec r31 ; 1
brne waitms2 ; 2/1
pop r29 ; 2
pop r30 ; 2
pop r31 ; 2
ret ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ID: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; end. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
这是我当时用于这些设备的标准模板...看看 waitms
子例程,它尽可能接近 r31 [ms]
等待。存在指令时序,是的,条件指令在该芯片上具有不同的时序。由于 ATMega328 具有相同的内核,因此您的芯片也相同。
呵呵,我的带有 C++ 示波器的 ATMega 模拟器仍然可以在 Win7 上运行...(写在 w9x 或 w2k 上,现在不确定)(与从我发布的源代码中编译的十六进制一起使用)。
我正在使用 ATmega328。问题是我想生成给定频率和给定幅度的方波。不能使用PWM,因为给我的板子已经焊好了,所以波形必须放在连接到处理器B端口的R2R resistor ladder的输出端。所以,基本上,我的想法是我必须周期性地将端口 B 的引脚置于 0 和 VOLUME(VOLUME 是一个从 1 到 255 的数字),具有给定的频率和 50% 的占空比。请记住:无 PWM。频率应该能够每 100 毫秒改变一次,但我无法完成这项工作,所以我只是尝试生成一个恒定频率,看看一开始会发生什么。
我是 运行 1MHz 的时钟。我写了下面的代码:
.DSEG
.ORG 0x100
.CSEG
.ORG 0x100
;Initializing stack pointer
LDI R16,HIGH(RAMEND)
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16
MAIN:
CALL GENERATE ;Calling the generating routine
RJMP MAIN ;Repeat this forever
;I will generate a 440Hz frequency. It has an approximate period of 2273 microseconds
;This means that half a period stands for approximately 1136 clocks
GENERATE:
LDI R17, 0x70
LDI R18, 0x04 ;Half the period in hexadecimal is 0x0470
LDI R19, 243 ;Volume = 243 (arbitrary, it could be any number)
LDI R21, 88 ;The amount of half-periods in 100 ms (arbitrary election, too)
LDI R25, 0xFF
OUT DDRB, R25 ;Port B is an output port
LDI R24, 0xFF ;R25R24 = 0xFFFF
CLC ;Clean the carry
SBC R24, R17
SBC R25, R18 ;R25R24 = 0xFFFF - Halfperiod
ADIW R25:R24, 1 ;R25R24 = 0xFFFF - Halfperiod +1
OUT PORTB, R18 ;The wave starts at 0
BEGIN:
CALL LOOP_1
EOR R19, R19 ;It varies between 0 and volume
OUT PORTB, R19 ;It puts the output to the actual value of R19 (0 or volume)
CLZ ;Clean Z flag
DEC R21
BREQ END ;When 100ms have passed, generation is over
JMP BEGIN ;If not, generation continues
LOOP_1: STS TCNT1H, R25
STS TCNT1L, R24 ;Loading the amount of clocks the timer has to count
LDI R16, 0x00
STS TCCR1A, R16
LDI R16, 0x01
STS TCCR1B, R16 ;Timer operating in normal mode, no prescaler
LOOP_2: IN R16, TIFR1
SBRS R16, TOV1 ;If timer's over, skip the next jump
JMP LOOP_2
LDI R16, 0x00
STS TCCR1B, R16 ;Stopping the timer
LDI R16, 0x04
OUT TIFR1, R16 ;Clean TOV1
RET ;Back to BEGIN
END:
RET ;Back to MAIN
这是我最初接触汇编的方法之一,因此读起来可能很难看。该代码目前无法正常工作。有什么想法吗?
编辑:
感谢 Spektre 向我指出了这一点,我更正了上面的一段代码。除了
,代码是一样的GENERATE:
.
.
.
LDI R21, 44 ;The amount of PERIODS (not half-periods as before) in 100 ms
.
.
.
BEGIN:
OUT PORTB, R18 ;This was before the BEGIN tag, now it is after it
CALL LOOP_1 ;It counts a halfperiod with output=0
OUT PORTB, R19 ;Now output=volume
CALL LOOP_1 ;It counts a halfperiod with output=volume
CLZ ;Clean Z Flag
DEC R21
BREQ END ;When 100ms have passed, generation is over
JMP BEGIN ;If not, generation continues
我为这样的架构做了很多年......我正在使用 UC3(后继者)并且在 MCU 66 MHz
时钟我可以达到2-5 MHz
轮询频率。但恐怕在您的 Target 平台上这会低很多。因此,首先测量轮询频率以查看 HW.
1 MHz
是输出时钟还是MCU时钟?您需要输出的最大频率是多少?
有更多方法可以实现这一点,显而易见的是:
Timer/Counter
你可以设置Timer,比如1us,在那里做定时和切换。或者您可以使用它来设置新频率。但要注意任何中断都会导致输出信号出现时序问题,如抖动......如果有足够的时间,这些问题是可以避免的,但它通常会大大限制最大输出频率。
主线程轮询
看起来这就是你在做的事情。但是您忘记了所有说明都有自己的时间安排。条件语句可以有不同的时间,所以你应该补偿。
利用接口(如果存在)
查看数据表,看看是否有任何 HW 连接到该端口。 MCUs 通常在引脚上复用更多功能,有时允许利用它们。比如我曾经利用串口、DMA+ABI/SD卡接口做了一个VGA图像生成器。每种方法都正确地产生了 16 色 VGA 信号,但在同一芯片和引脚上具有完全不同的代码和硬件架构......要与 ATMega168 上具有
20 MHz
时钟的平台进行比较(它与 ATMega328) 的核心相同,我能够为 LCD[= 生成 1bit LCD640x480
信号149=](比彩色 VGA 信号低很多倍)。有时你甚至可以在运行时改变引脚的含义......例如为了实现100 000 rpm
BLDC 控制器我改变了几次内部电路和电机的电气周期只是为了能够产生如此高的速度(否则无法通过直接方式实现。
您需要试验哪种方法 fast/accurate/comfortable 对您来说足够了。从第一次看你的代码我偶然发现了这个:
EOR R19, R19 ;It varies between 0 and volume
OUT PORTB, R19 ;It puts the output to the actual value of R19 (0 or volume)
如果我没看错的话,你正在 XORing R19
和 R19
... 在第一次通过后销毁 R19
的内容.所以你一直在输出 0
到 B
端口:
XOR Volume,Volume -> Volume = 0
XOR 0,0 -> 0
相反,我会:
- 出端口量
- 等半期
- 输出端口零
- 等半期
- 如果达到100ms改变频率
- 转到#1
您应该尽可能少地使用端口访问。不确定 ATMega 是否也是这种情况,但在较新的体系结构上,端口由 HW 接口/[=211 访问=] 如果使用不当的话,高速切换真的很慢。
PS.
希望你的 R2R 梯形图在输入电阻之前有二极管,或者至少 PORTB 有集电极开路输出,否则它将无法工作,因为相邻引脚之间的电流,如果都设置为不同的值。
[Edit1] 一些说明
更清楚R2R电流问题
二极管应该有相同的PN势垒电压!!!
1MHz时钟
你写了
I'm running the clock at 1MHz
但是没有说明是MCU/CPU时钟,Timer时钟还是输出方波信号频率。现在很明显,您的输出信号是<130,523> Hz
,这在您的平台上应该很容易实现。我之前不确定......我在 MCU 上生成了甚至大约 30 MHz 的信号,所以 1 MHz 是可能的。时间
大多数平台上的条件指令,如
BREQ END
在条件为真和为假时有不同的时间,因此为了保持同步,你应该考虑到这一点......有时nop
可以处理问题...如果是这样的话,你应该检查文档中的指令时序。你说你正在使用 counter/timer 我在你的代码中没有看到它。为了正确使用你应该
- 为您想要的模式和频率配置硬件
- 注册中断子程序
- 启用中断和/或触发定时器计数器事件...
内部中断事件处理程序
如果需要,重置计数器值并清除中断标志,以便发生另一个事件。如果您接近芯片功能的边缘,有时甚至需要更正计数器值以获得正确的时序。
我看到的是 timer/counter 的一些配置,不确定是否正确,因为我已经很长时间没有使用该平台了。我在任何地方都看不到中断处理程序子例程......可能您正在轮询计数器寄存器但再次不确定它是否是正确的方法(并非所有硬件寄存器都可以读取)您应该面对文档。但无论如何它正在轮询,所以指令的时间很重要......
如果有帮助,我会为 ATMega168 挖掘我的一个古老的发电机(在 PortC 上生成方波)asm 代码:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; generator ver: 0.00 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
CPU: ATMega168 8MHz internal RC
OUT: PC
*/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ATMega168 startup: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; fuses:BOOTRST=1 disabled
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.equ stack =0x04FF
.equ cpuclk =8000000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.device ATmega168 ; device selection
.equ PINB =0x03 ; IO reg adresses definition in/out
.equ DDRB =0x04
.equ PORTB =0x05
.equ PINC =0x06
.equ DDRC =0x07
.equ PORTC =0x08
.equ PIND =0x09
.equ DDRD =0x0A
.equ PORTD =0x0B
.equ TIFR0 =0x15
.equ TIFR1 =0x16
.equ TIFR2 =0x17
.equ PCIFR =0x1B
.equ EIFR =0x1C
.equ EIMSK =0x1D
.equ GPIOR0=0x1E
.equ EECR =0x1F
.equ EEDR =0x20
.equ EEARL =0x21
.equ EEARH =0x22
.equ GTCCR =0x23
.equ TCCROA=0x24
.equ TCCROB=0x25
.equ TCNT0 =0x26
.equ OCROA =0x27
.equ OCROB =0x28
.equ GPIOR1=0x2A
.equ GPIOR2=0x2B
.equ SPCR =0x2C
.equ SPSR =0x2D
.equ SPDR =0x2E
.equ ACSR =0x30
.equ SMCR =0x33
.equ MCUSR =0x34
.equ MCUCR =0x35
.equ SPMCSR=0x37
.equ SPL =0x3D
.equ SPH =0x3E
.equ SREG =0x3F
.equ WDTCSR=0x60 ; sts,lds
.equ CLKPR =0x61
.equ PRR =0x64
.equ OSCCAL=0x66
.equ PCICR =0x68
.equ EICRA =0x69
.equ PCMSK0=0x6B
.equ PCMSK1=0x6C
.equ PCMSK2=0x6D
.equ TIMSK0=0x6E
.equ TIMSK1=0x6F
.equ TIMSK2=0x70
.equ ADCL =0x78
.equ ADCH =0x79
.equ ADCSRA=0x7A
.equ ADCSRB=0x7B
.equ ADMUX =0x7C
.equ DIDR0 =0x7E
.equ DIDR1 =0x7F
.equ TCCR1A=0x80
.equ TCCR1B=0x81
.equ TCCR1C=0x82
.equ TCNT1L=0x84
.equ TCNT1H=0x85
.equ ICR1L =0x86
.equ ICR1H =0x87
.equ OCR1AL=0x88
.equ OCR1AH=0x89
.equ OCR1BL=0x8A
.equ OCR1BH=0x8B
.equ TCCR2A=0xB0
.equ TCCR2B=0xB1
.equ TCNT2 =0xB2
.equ OCR2A =0xB3
.equ ASSR =0xB6
.equ TWBR =0xB8
.equ TWSR =0xB9
.equ TWAR =0xBA
.equ TWDR =0xBB
.equ TWCR =0xBC
.equ TWAMR =0xBD
.equ UCSR0A=0xC0
.equ UCSR0B=0xC1
.equ UCSR0C=0xC2
.equ UBRR0L=0xC4
.equ UBRR0H=0xC5
.equ UDR0 =0xC6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.cseg ; Start of code segment
.org 0x0000 ; interrupts, highest priority first
jmp RESET ; 3 Reset Handler
jmp EXT_INT0 ; 3 IRQ0 Handler
jmp EXT_INT1 ; 3 IRQ1 Handler
jmp PCINT0 ; 3 PCINT0 Handler
jmp PCINT1 ; 3 PCINT1 Handler
jmp PCINT2 ; 3 PCINT2 Handler
jmp WDT ; 3 Watchdog Timer Handler
jmp TIM2_COMPA ; 3 Timer2 Compare A Handler
jmp TIM2_COMPB ; 3 Timer2 Compare B Handler
jmp TIM2_OVF ; 3 Timer2 Overflow Handler
jmp TIM1_CAPT ; 3 Timer1 Capture Handler
jmp TIM1_COMPA ; 3 Timer1 Compare A Handler
jmp TIM1_COMPB ; 3 Timer1 Compare B Handler
jmp TIM1_OVF ; 3 Timer1 Overflow Handler
jmp TIM0_COMPA ; 3 Timer0 Compare A Handler
jmp TIM0_COMPB ; 3 Timer0 Compare B Handler
jmp TIM0_OVF ; 3 Timer0 Overflow Handler
jmp SPI_STC ; 3 SPI Transfer Complete Handler
jmp USART_RXC ; 3 USART, RX Complete Handler
jmp USART_UDRE ; 3 USART, UDR Empty Handler
jmp USART_TXC ; 3 USART, TX Complete Handler
jmp ADC_DONE ; 3 ADC Conversion Complete Handler
jmp EE_RDY ; 3 EEPROM Ready Handler
jmp ANA_COMP ; 3 Analog Comparator Handler
jmp TWI ; 3 2-wire Serial Interface Handler
jmp SPM_RDY ; 3 Store Program Memory Ready Handler
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RESET: cli ; Reset Handler
ldi r16,high(stack)
out SPH,r16
ldi r16,low(stack)
out SPL,r16
ldi r16, 0xFF ; DDRB 1 - output, 0 - input direction
out DDRB, r16
ldi r16, 0xFF ; DDRC 1 - output, 0 - input direction
out DDRC, r16
ldi r16, 0xFF ; DDRD 1 - output, 0 - input direction
out DDRD, r16
ldi r16, 0xFF ; all outputs high,and all inputs Pull Up to Ucc
out PORTB,r16
out PORTC,r16
out PORTD,r16
jmp main
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EXT_INT0: ; IRQ0 Handler
reti
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EXT_INT1: ; IRQ1 Handler
reti
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PCINT0: ; PCINT0 Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PCINT1: ; PCINT1 Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PCINT2: ; PCINT2 Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
WDT: ; Watchdog Timer Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM2_COMPA: ; Timer2 Compare A Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM2_COMPB: ; Timer2 Compare B Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM2_OVF: ; Timer2 Overflow Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM1_CAPT: ; Timer1 Capture Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM1_COMPA: ; Timer1 Compare A Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM1_COMPB: ; Timer1 Compare B Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM1_OVF: ; Timer1 Overflow Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM0_COMPA: ; Timer0 Compare A Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM0_COMPB: ; Timer0 Compare B Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIM0_OVF: ; Timer0 Overflow Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SPI_STC: ; SPI Transfer Complete Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
USART_RXC: ; USART, RX Complete Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
USART_UDRE: ; USART, UDR Empty Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
USART_TXC: ; USART, TX Complete Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ADC_DONE: ; ADC Conversion Complete Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
EE_RDY: ; EEPROM Ready Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ANA_COMP: ; Analog Comparator Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TWI: ; 2-wire Serial Interface Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SPM_RDY: ; Store Program Memory Ready Handler
reti ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
main: ldi r16,0
main0: out PORTC,r16 ; 1 8T / 8MHz = 1MHz PC0, 0.5MHz PC1, ...
inc r16 ; 1
nop ; 1
nop ; 1
nop ; 1
nop ; 1
rjmp main0 ; 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
waitms: push r31 ; 2 wait cca r31 [ms] +(15+r31*4)T
push r30 ; 2
push r29 ; 2
waitms2:ldi r30,cpuclk/200000;1 1ms
waitms1:ldi r29,49 ; 1 200T
waitms0:nop ; 1
dec r29 ; 1
brne waitms0 ; 2/1
dec r30 ; 1
brne waitms1 ; 2/1
dec r31 ; 1
brne waitms2 ; 2/1
pop r29 ; 2
pop r30 ; 2
pop r31 ; 2
ret ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ID: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; end. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
这是我当时用于这些设备的标准模板...看看 waitms
子例程,它尽可能接近 r31 [ms]
等待。存在指令时序,是的,条件指令在该芯片上具有不同的时序。由于 ATMega328 具有相同的内核,因此您的芯片也相同。
呵呵,我的带有 C++ 示波器的 ATMega 模拟器仍然可以在 Win7 上运行...(写在 w9x 或 w2k 上,现在不确定)(与从我发布的源代码中编译的十六进制一起使用)。