使用 Assembly 的 Atmega2560 中的圆形 LED 回路
Circular led loop in Atmega2560 using Assembly
我正在使用 Atmega2560
微控制器进行一个简单的 LED 项目。 LED 应分别以圆形模式旋转。
DEF 常量:
LED_AMNT
: 多少个 LED 会亮
LED_DATA
:哪些 LED 可以工作
LED布局设计:
LED_AMNT = 1 个动画
LED_AMNT = 2 动画
我写的代码有点错误。
1) L7 和 L0 不能一起工作。第一位最后移到 0。
第 0 步:L0 和 L1 -> 0000 0011
...
第 6 步:L6 和 L7 -> 1100 0000
第 7 步:L7 和 L0 -> 1000 0001(必须这样)
第 7 步:L7 和 L0 -> //跳到第 0 步
2) 当我点击 LED_AMNT_INCREASE
按钮时,LED 的数量在增加,但是当游览结束时。它不会立即增加。我在等待当前的巡演结束。 (当0x80变为0x01时)
我写了一个简单的程序,如下所示:
.def LEDS = R16
.def LED_DIRECTION = R17
.def LED_AMOUNT = R19
.def LED_DATA = R21
.org 0
rjmp MAIN
MAIN:
ldi LEDS, 0xFF ; 0xFF = 1111 1111
ldi LED_DATA, 0x01 ; PORTC load register
ldi LED_DIRECTION, 0x01 ; 0x01 ==> Right, 0x00 ==> Left
ldi LED_AMOUNT, 0x01 ; total active led count
out DDRC, LEDS ; make PORTC's all pins to output
sbi PORTB, 0
sbi PORTB, 1
sbi PORTB, 2
LOOP_MAIN:
out PORTC, LED_DATA
call DELAY
call DELAY
call DELAY
call DELAY
call DELAY
sbis PINB, 0
rjmp BUTTON_CLICK_DIRECTION
sbis PINB, 1
rjmp BUTTON_CLICK_AMOUNT
cpi LED_DIRECTION, 0x01
brne LOOP_RIGHT
LOOP_LEFT:
lsl LED_DATA
cpi LED_DATA, 0x80
brne LOOP_MAIN
LEFT_RESET:
lsl LED_DATA
out PORTC, LED_DATA
call DELAY
mov LED_DATA, LED_AMOUNT
;mov LED_DATA, LED_AMOUNT
;brne LEFT_RESET
rjmp LOOP_MAIN
LOOP_RIGHT:
LOOP_MAIN_END:
rjmp LOOP_MAIN
BUTTON_CLICK_DIRECTION:
cpi LED_DIRECTION, 0x00
brne it_is
it_isnt:
ldi LED_DIRECTION, 0x01
rjmp yon_end
it_is:
ldi LED_DIRECTION, 0x00
yon_end:
rjmp LOOP_MAIN
BUTTON_CLICK_AMOUNT:
rol LED_AMOUNT
cpi LED_AMOUNT, 0x1F
breq amount_reset
rjmp amount_end
amount_reset:
ldi LED_AMOUNT, 0x01
amount_end:
mov LED_DATA, LED_AMOUNT
rjmp LOOP_MAIN
DELAY:
push r16
push r17
mov r16,0x40
ldi r17,0x00
ldi r18,0x00
_w0:
dec r18
brne _w0
dec r17
brne _w0
dec r16
brne _w0
pop r17
pop r16
ret
您基本上想要旋转 字节的值。您可以通过以下伪代码执行此操作:
// Check if most significant bit is set:
if ( (LED_DATA & 0x80) != 0 ) {
LED_DATA = LED_DATA << 1 | 1; // Copy msb to lsb after shift.
} else {
LED_DATA = LED_DATA << 1;
}
在汇编器中,你也可以只复制移位后的进位到LSB:
lsl LED_DATA
adc LED_DATA, ZERO_REG ; add 0 + carry (either 0 or 1) to LED_DATA
或者,更详细地说:
lsl LED_DATA
brcc SHIFT_0_IN ; if carry not set, the MSB was not set, so skip setting the LSB
ori LED_DATA, 1 ; set LSB to 1
SHIFT_0_IN:
; keep LSB as 0 -> do nothing, just continue
...
换个方向的旋转也是一样的:
只需右移 (LSR),并将 ori LED_DATA, 1
替换为 ori LED_DATA, 0x80
,设置 MSB 而不是 LSB。
要更新 运行 模式,您必须查看当前状态以找到要设置的位。
这可以通过 'smart' 方式完成:在移动之前 LED_DATA
检查是否应该添加另一个 LED 位。如果不是,只需按上述方式移动即可。如果是,记住LED_DATA
的旧值,如上shift LED_DATA
,然后设置LED_DATA = LED_DATA | prevLedData
.
'easy' 方法可能是使用四种不同的模式,例如 LED_DATA_1 = 0x00000001
、LED_DATA_2 = 0b00000011
、LED_DATA_3 = 0b00000111
、LED_DATA_4 = 0b00001111
。在每一步中,您轮换所有四个值,并根据实际需要打开的 LED 数量,从四个寄存器之一输出相应的值。您需要另一个寄存器来存储此时应点亮的 LED 数量,每次按下按钮时都会发生变化,从 1 到 4(或 0 到 3)再回到 1(或 0)。
我正在使用 Atmega2560
微控制器进行一个简单的 LED 项目。 LED 应分别以圆形模式旋转。
DEF 常量:
LED_AMNT
: 多少个 LED 会亮
LED_DATA
:哪些 LED 可以工作
LED布局设计:
LED_AMNT = 1 个动画
LED_AMNT = 2 动画
我写的代码有点错误。
1) L7 和 L0 不能一起工作。第一位最后移到 0。
第 0 步:L0 和 L1 -> 0000 0011
...
第 6 步:L6 和 L7 -> 1100 0000
第 7 步:L7 和 L0 -> 1000 0001(必须这样)
第 7 步:L7 和 L0 -> //跳到第 0 步
2) 当我点击 LED_AMNT_INCREASE
按钮时,LED 的数量在增加,但是当游览结束时。它不会立即增加。我在等待当前的巡演结束。 (当0x80变为0x01时)
我写了一个简单的程序,如下所示:
.def LEDS = R16
.def LED_DIRECTION = R17
.def LED_AMOUNT = R19
.def LED_DATA = R21
.org 0
rjmp MAIN
MAIN:
ldi LEDS, 0xFF ; 0xFF = 1111 1111
ldi LED_DATA, 0x01 ; PORTC load register
ldi LED_DIRECTION, 0x01 ; 0x01 ==> Right, 0x00 ==> Left
ldi LED_AMOUNT, 0x01 ; total active led count
out DDRC, LEDS ; make PORTC's all pins to output
sbi PORTB, 0
sbi PORTB, 1
sbi PORTB, 2
LOOP_MAIN:
out PORTC, LED_DATA
call DELAY
call DELAY
call DELAY
call DELAY
call DELAY
sbis PINB, 0
rjmp BUTTON_CLICK_DIRECTION
sbis PINB, 1
rjmp BUTTON_CLICK_AMOUNT
cpi LED_DIRECTION, 0x01
brne LOOP_RIGHT
LOOP_LEFT:
lsl LED_DATA
cpi LED_DATA, 0x80
brne LOOP_MAIN
LEFT_RESET:
lsl LED_DATA
out PORTC, LED_DATA
call DELAY
mov LED_DATA, LED_AMOUNT
;mov LED_DATA, LED_AMOUNT
;brne LEFT_RESET
rjmp LOOP_MAIN
LOOP_RIGHT:
LOOP_MAIN_END:
rjmp LOOP_MAIN
BUTTON_CLICK_DIRECTION:
cpi LED_DIRECTION, 0x00
brne it_is
it_isnt:
ldi LED_DIRECTION, 0x01
rjmp yon_end
it_is:
ldi LED_DIRECTION, 0x00
yon_end:
rjmp LOOP_MAIN
BUTTON_CLICK_AMOUNT:
rol LED_AMOUNT
cpi LED_AMOUNT, 0x1F
breq amount_reset
rjmp amount_end
amount_reset:
ldi LED_AMOUNT, 0x01
amount_end:
mov LED_DATA, LED_AMOUNT
rjmp LOOP_MAIN
DELAY:
push r16
push r17
mov r16,0x40
ldi r17,0x00
ldi r18,0x00
_w0:
dec r18
brne _w0
dec r17
brne _w0
dec r16
brne _w0
pop r17
pop r16
ret
您基本上想要旋转 字节的值。您可以通过以下伪代码执行此操作:
// Check if most significant bit is set:
if ( (LED_DATA & 0x80) != 0 ) {
LED_DATA = LED_DATA << 1 | 1; // Copy msb to lsb after shift.
} else {
LED_DATA = LED_DATA << 1;
}
在汇编器中,你也可以只复制移位后的进位到LSB:
lsl LED_DATA
adc LED_DATA, ZERO_REG ; add 0 + carry (either 0 or 1) to LED_DATA
或者,更详细地说:
lsl LED_DATA
brcc SHIFT_0_IN ; if carry not set, the MSB was not set, so skip setting the LSB
ori LED_DATA, 1 ; set LSB to 1
SHIFT_0_IN:
; keep LSB as 0 -> do nothing, just continue
...
换个方向的旋转也是一样的:
只需右移 (LSR),并将 ori LED_DATA, 1
替换为 ori LED_DATA, 0x80
,设置 MSB 而不是 LSB。
要更新 运行 模式,您必须查看当前状态以找到要设置的位。
这可以通过 'smart' 方式完成:在移动之前 LED_DATA
检查是否应该添加另一个 LED 位。如果不是,只需按上述方式移动即可。如果是,记住LED_DATA
的旧值,如上shift LED_DATA
,然后设置LED_DATA = LED_DATA | prevLedData
.
'easy' 方法可能是使用四种不同的模式,例如 LED_DATA_1 = 0x00000001
、LED_DATA_2 = 0b00000011
、LED_DATA_3 = 0b00000111
、LED_DATA_4 = 0b00001111
。在每一步中,您轮换所有四个值,并根据实际需要打开的 LED 数量,从四个寄存器之一输出相应的值。您需要另一个寄存器来存储此时应点亮的 LED 数量,每次按下按钮时都会发生变化,从 1 到 4(或 0 到 3)再回到 1(或 0)。