Круглая светодиодная петля в Atmega2560 с использованием сборки

Я работаю над простым светодиодным проектом, используя микроконтроллер Atmega2560. Светодиоды должны вращаться по кругу соответственно.

Константы DEF:

LED_AMNT : Сколько светодиодов загорится

LED_DATA : Какие светодиоды будут работать

Светодиодный дизайн макета:

layout

LED_AMNT = 1 Анимация

anim1

LED_AMNT = 2 Анимация

anim2

Код, который я написал, работает немного неправильно.

1) L7 и L0 не работают вместе. В конце первый бит смещается в 0.

Шаг 0: L0 и L1 -> 0000 0011

...

Шаг 6: L6 и L7 -> 1100 0000

Шаг 7: L7 и L0 -> 1000 0001 (должны быть такими)

Шаг 7: L7 и L0 -> // пропущены к шагу 0

anim-  неправильно

2) Когда я нажимаю кнопку LED_AMNT_INCREASE, количество светодиодов увеличивается, но когда тур завершен. Он не увеличивается мгновенно. Я жду окончания текущего тура. (когда 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  

person Dentrax    schedule 21.11.2017    source источник
comment
Для 1) вы должны реализовать правильный поворот, всегда просто возвращая верхний бит внизу. Ваш работает только для случая с одним светодиодом. Для 2) вам, конечно, нужно обновить текущий шаблон. Совершенно непонятно, что вы вообще делаете с rol LED_AMOUNT; cpi LED_AMOUNT, 0x1f... почему это не инкремент?   -  person Jester    schedule 21.11.2017
comment
Это означает, что я не хочу вращать минимум 1 светодиод, максимум 4 светодиода. Я знаю, его плохой дизайн. Как я могу это сделать ?   -  person Dentrax    schedule 21.11.2017


Ответы (1)


В основном вы хотите повернуть значение байта. Вы можете сделать это с помощью следующего псевдокода:

// 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.

Чтобы обновить шаблон выполнения, вам нужно посмотреть на текущее состояние, чтобы найти бит, который нужно установить.

Это можно сделать «умным» способом: перед сдвигом LED_DATA проверьте, не следует ли добавить еще один бит светодиода. Если нет, просто сдвиньте, как указано выше. Если да, запомните старое значение LED_DATA, сдвиньте LED_DATA, как указано выше, а затем установите LED_DATA = LED_DATA | prevLedData.

«Простым» способом может быть использование четырех разных шаблонов, таких как LED_DATA_1 = 0x00000001, LED_DATA_2 = 0b00000011, LED_DATA_3 = 0b00000111, LED_DATA_4 = 0b00001111. На каждом шаге вы меняете все четыре значения, и в зависимости от того, сколько светодиодов вам действительно нужно включить, вы выводите соответствующее значение из одного из четырех регистров. Вам понадобится еще один регистр для хранения того, сколько светодиодов должно гореть в данный момент, которое изменяется при каждом нажатии кнопки от 1 до 4 (или от 0 до 3) и обратно до 1 (или 0).

person JimmyB    schedule 21.11.2017
comment
Спасибо, вы спасатель :) А вот последний абзац я не понял. Что я должен изменить, когда я нажимаю BUTTON_CLICK_AMOUNT, я не хочу вращать светодиод min-1, max-4. - person Dentrax; 21.11.2017
comment
Извините, я не очень ясно выразился и пропустил случай переполнения с 4 на 1. Добавлю несколько мыслей. - person JimmyB; 22.11.2017
comment
Спасибо, я решил эту проблему с помощью xor и and gate, чтобы изменить количество светодиодов с 4 на 1. Моя функция сброса: mov LED_DATA_PREV, LED_DATA lsl LED_DATA_PREV EOR LED_VERI, LED_DATA_PREV AND LED_DATA, LED_DATA_PREV Но, если LED_DATA 1000 0111, не работает. Потому что я вечерний левый. - person Dentrax; 22.11.2017