Электроника / Atmel AVR
Добавить в избранное

Примеры на Ассемблере для микроконтроллеров Atmel AVR

Примеры различных программ на языке Ассемблера для микроконтроллеров Atmel AVR
картинка к записи Примеры на Ассемблере для микроконтроллеров Atmel AVR

Здесь представлены примеры различных программ на языке Ассемблера для микроконтроллеров Atmel AVR. Примеры выложены в виде проектов для AVR Studio под микроконтроллер ATmega16, поэтому при переносе на другие МК семейства AVR это нужно учитывать. Тактовая частота микроконтроллера во всех примерах 8 МГц (используется тактирование от внутреннего генератора). Код примеров разбит на блоки и снабжен комментариями.

Краткое описание команд Ассемблера AVR представлено здесь.

Подробное описание каждой команды представлено в AVR 8bit Instruction Set.

Для более глубокого изучения ассемблера AVR советую к прочтению книгу Юрия Ревича "Практическое программирование микроконтроллеров Atmel AVR на языке ассемблера". С беcплатным фрагментом книги можно ознакомиться здесь. Также можно изучить различные методические пособия ВУЗов, например, вот.

Также на сайте выложены примеры программ на языке Си.

Start

Заготовка стартовой инициализации микроконтроллера, в которую входят инициализация стека, очистка ОЗУ (SRAM) и регистров общего назначения (R0 - R31), а также глобальный запрет прерываний. Пример кода приведён ниже. При использовании следует помнить, что регистры ввода-вывода (порты, периферия и т.д.) не очищаются, поэтому их НЕОБХОДИМО инициализировать отдельно.

.include "m16def.inc"        ; Use AtMega16A
;=================================================
; Имена регистров, а также различные константы
    .equ    XTAL                = 8000000   ; Частота МК
    .equ    UART_BaudRate           = 19200     ; Скорость при связи по UART
    .equ    UART_BaudDivider        = XTAL / (16 * UART_BaudRate) - 1
    .equ    I2C_Frequency           = 80000     ; Частота шины I2C
    .equ    I2C_BaudDivider         = (XTAL / (8 * I2C_Frequency) - 2)
    .equ    Bit0                = 0b00000001
    .equ    Bit1                = 0b00000010
    .equ    Bit2                = 0b00000100
    .equ    Bit3                = 0b00001000
    .equ    Bit4                = 0b00010000
    .equ    Bit5                = 0b00100000
    .equ    Bit6                = 0b01000000
    .equ    Bit7                = 0b10000000
    .def    MulLow              = R0    ; Младший регистр результата умножения
    .def    MulHigh             = R1    ; Старший регистр результата умножения
    .def    Temp0               = R15   ; Регистр с нулевым значением
    .def    Temp1               = R16
    .def    Temp2               = R17
    .def    Temp3               = R18
    .def    Temp4               = R19
    .def    Temp5               = R20
    .def    Temp6               = R21
    .def    Temp7               = R22
    .def    Temp8               = R23
    .def    Counter             = R24   ; Регистр счетчик
    .def    Flags               = R25   ; Флаговый регистр
;=================================================
; Сегмент SRAM памяти
.DSEG               
;=================================================
; Сегмент EEPROM памяти
.ESEG               
;=================================================
; Сегмент FLASH памяти
.CSEG
;=================================================
; Таблица прерываний
    .ORG 0x00
        RJMP    RESET           
;=================================================
; Прерывание по сбросу, стартовая инициализация 
RESET:  
    ; Инициализация стека
    LDI     Temp1, LOW(RAMEND)
    OUT     SPL, Temp1
    LDI     Temp1, HIGH(RAMEND) 
    OUT     SPH, Temp1
    ; Очистка ОЗУ и регистров R0-R31
    LDI     ZL, LOW(SRAM_START)     ; Адрес начала ОЗУ в индекс
    LDI     ZH, HIGH(SRAM_START)
    CLR     Temp1               ; Очищаем R16
RAM_Flush:
    ST      Z+, Temp1               
    CPI     ZH, HIGH(RAMEND + 1)    
    BRNE    RAM_Flush           
    CPI     ZL, LOW(RAMEND + 1) 
    BRNE    RAM_Flush
    LDI     ZL, (0x1F-2)            ; Адрес регистра R29
    CLR     ZH
Reg_Flush:
    ST      Z, ZH
    DEC     ZL
    BRNE    Reg_Flush
    CLR     ZL
    CLR     ZH
    ; Регистры и SRAM полностью очищены (обнулены)
    ; Но регистры ввода-вывода (IO) НЕОБХОДИМО очищать
    ; Глобальный запрет прерываний
    CLI

Скачать AVR Start.asm

Delays

Библиотека с подпрограммами задержки. Все задержки рассчитаны на тактовую частоту МК 8 МГц. Библиотека включает следующие процедуры:

  • Delay1us – Задержка повышенной точности в 1 мкс c учетом длительности RCALL и RET.
  • Delay5us – Задержка повышенной точности в 5 мкс c учетом длительности RCALL и RET.
  • Delay10us – Задержка повышенной точности в 10 мкс c учетом длительности RCALL и RET.
  • Delayus – Задержка высокой точности в несколько десятков микросекунд.
  • Delayms – Задержка высокой точности в несколько миллисекунд.
; БИБЛИОТЕКА ЗАДЕРЖЕК (8 МГц)
;   Delay1us    задержка повышенной точности в 1 мкс c учетом длительности RCALL и RET
;   Delay5us    задержка повышенной точности в 5 мкс c учетом длительности RCALL и RET
;   Delay10us   задержка повышенной точности в 10 мкс c учетом длительности RCALL и RET
;   Delayus     задержка высокой точности в несколько десятков микросекунд
;   Delayms     задержка высокой точности в несколько миллисекунд
;=================================================
; задержка повышенной точности в 1 мкс c учетом длительности RCALL и RET
; RCALL дает 3 + 1 NOP + 4 RET = 8 - 1 микросекунда при 8МГц
Delay1us:                           
    NOP         
RET
;=================================================  
; задержка повышенной точности в 5 мкс c учетом длительности RCALL и RET
Delay5us:   
    PUSH    Temp1   
    LDI     Temp1, 9    
Delay5us_loop:                  
    DEC     Temp1   
    BRNE    Delay5us_loop   
    POP     Temp1
    NOP
    NOP             
RET
;=================================================          
; задержка повышенной точности в 10 мкс c учетом длительности RCALL и RET
Delay10us:  
    PUSH    Temp1   
    LDI     Temp1, 23   
Delay10us_loop:                 
    DEC     Temp1   
    BRNE    Delay10us_loop  
    POP     Temp1               
RET
;=================================================      
; задержка высокой точности в несколько десятков микросекунд
; вход Temp1 количество необходимых десятков микросекунд
Delayus:
    PUSH    Temp2
Delayus_loop1:
    LDI     Temp2, 25
Delayus_loop2:
    DEC     Temp2   
    BRNE    Delayus_loop2
    NOP
    NOP
    DEC     Temp1
    BRNE    Delayus_loop1
    POP     Temp2
RET 
;=================================================
; задержка высокой точности в несколько миллисекунд
; вход Temp1 количество необходимых миллисекунд
Delayms:
    PUSH    Temp2
    MOV     Temp2, Temp1
Delayms_loop:   
    LDI     Temp1, 100
    RCALL   Delayus
    DEC     Temp2
    BRNE    Delayms_loop
    POP     Temp2
RET

Скачать AVR Delay.asm

Math

Библиотека с подпрограммами математических операций, включает следующие процедуры:

  • SUB16X16 – Вычитание 16-разрядных чисел.
  • ADD16X16 – Сложение 16-разрядных чисел.
  • MUL16X16s – Знаковое умножение 16-разрядных чисел.
  • MUL16X16u – Беззнаковое умножение 16-разрядных чисел.
  • DIV16X16s – Знаковое деление 16-разрядных чисел.
  • DIV16X16u – Беззнаковое деление 16-разрядных чисел.
  • DIV16POWER2s – Знаковое деление 16-разрядного числа на степень 2.
  • DIV16POWER2u – Беззнаковое деление 16-разрядного числа на степень 2.
  • SIGN16 – Смена знака 16-разрядного числа.
  • DEC2BCD – Перевол 8-разрядного десятичного числа в двоично-десятичное (BCD).
  • BCD2DEC – Перевол 8-разрядного двоично-десятичного (BCD) числа в десятичное.
  • CP16X16 – Сравнение 16-разрядных чисел.
  • DIGITS8 – Вычисление цифр 8-разрядного числа.
  • DIGITS16 – Вычисление цифр 16-разрядного числа.
; БИБЛИОТЕКА ДЛЯ РАБОТЫ С МАТЕМАТИКОЙ
;   SUB16X16        вычитание 16-разрядных чисел
;   ADD16X16        сложение 16-разрядных чисел 
;   MUL16X16s       знаковое умножение 16-разрядных чисел
;   MUL16X16u       беззнаковое умножение 16-разрядных чисел
;   DIV16X16s       знаковое деление 16-разрядных чисел
;   DIV16X16u       беззнаковое деление 16-разрядных чисел
;   DIV16POWER2s        знаковое деление 16-разрядного числа на степень 2
;   DIV16POWER2u        беззнаковое деление 16-разрядного числа на степень 2
;   SIGN16          смена знака 16-разрядного числа
;   DEC2BCD         перевол 8-разрядного десятичного числа в двоично-десятичное (BCD)
;   BCD2DEC         перевол 8-разрядного двоично-десятичного (BCD) числа в десятичное   
;   CP16X16         сравнение 16-разрядных чисел
;   DIGITS8         вычисление цифр 8-разрядного числа
;   DIGITS16        вычисление цифр 16-разрядного числа
;=======================================================================
; вычитание 16-разрядных чисел
; вход:     Temp1-Temp2 первый аргумент от H к L
;       Temp3-Temp4 второй аргумент от H к L
; выход:    Temp1-Temp2 результат от H к L
SUB16X16:
    SUB     Temp2, Temp4
    SBC     Temp1, Temp3
RET
;=======================================================================
; сложение 16-разрядных чисел
; вход:     Temp1-Temp2 первый аргумент от H к L
;       Temp3-Temp4 второй аргумент от H к L
; выход:    Temp1-Temp2 результат от H к L
ADD16X16:
    ADD     Temp2, Temp4
    ADC     Temp1, Temp3
RET
;=======================================================================
; знаковое умножение 16-разрядных чисел
; вход:     Temp1-Temp2 первый аргумент от H к L
;       Temp3-Temp4 второй аргумент от H к L
; выход:    Temp1-Temp4 результат от H к L
MUL16X16s:
    MULS    Temp3, Temp1        ; (signed)ah * (signed)bh
    MOV     Temp5, MulHigh
    MOV     Temp6, MulLow
    MUL     Temp4, Temp2        ; al * bl
    MOV     Temp7, MulHigh
    MOV     Temp8, MulLow
    MULSU   Temp3, Temp2        ; (signed)ah * bl
    SBC     Temp5, Temp0        ; из-за отриц. чисел
    ADD     Temp7, MulLow
    ADC     Temp6, MulHigh
    ADC     Temp5, Temp0
    MULSU   Temp1, Temp4        ; (signed)bh * al
    SBC     Temp5, Temp0        ; из-за отриц. чисел
    ADD     Temp7, MulLow
    ADC     Temp6, MulHigh
    ADC     Temp5, Temp0
    MOV     Temp1, Temp5        ; move result
    MOV     Temp2, Temp6
    MOV     Temp3, Temp7
    MOV     Temp4, Temp8
RET
;=======================================================================
; беззнаковое умножение 16-разрядных чисел
; вход:     Temp1-Temp2 первый аргумент от H к L
;       Temp3-Temp4 второй аргумент от H к L
; выход:    Temp1-Temp4 результат от H к L
MUL16X16u:
    MUL     Temp3, Temp1        ; (unsigned)ah * (unsigned)bh
    MOV     Temp5, MulHigh
    MOV     Temp6, MulLow
    MUL     Temp4, Temp2        ; al * bl
    MOV     Temp7, MulHigh
    MOV     Temp8, MulLow
    MUL     Temp3, Temp2        ; (unsigned)ah * bl
    ADD     Temp7, MulLow
    ADC     Temp6, MulHigh
    ADC     Temp5, Temp0
    MUL     Temp1, Temp4        ; (unsigned)bh * al
    ADD     Temp7, MulLow
    ADC     Temp6, MulHigh
    ADC     Temp5, Temp0
    MOV     Temp1, Temp5        ; move result
    MOV     Temp2, Temp6
    MOV     Temp3, Temp7
    MOV     Temp4, Temp8
RET
;=======================================================================
; знаковое деление 16-разрядных чисел
; вход:     Temp1-Temp2 первый аргумент от H к L
;       Temp3-Temp4 второй аргумент от H к L
; выход:    Temp1-Temp2 результат от H к L
;       R13-R14 остаток от H к L
DIV16X16s:  
    MOV     R10, R16    ;вычисляем знак результата
    EOR     R10, R18    ; знак хранится в R10
    SBRS    R16, 7      ; проверяем знак делимого
    RJMP    d16s_1      ; если положительное то идем дальше
    COM     R16     ; иначе меняем знак делимого
    COM     R17     ; преобразуем в доп код
    SUBI    R17, LOW(-1)
    SBCI    R16, HIGH(-1)
d16s_1: 
    SBRS    R18, 7      ; проверяем знак делителя
    RJMP    d16s_2
    COM     R18 
    COM     R19
    SUBI    R19, LOW(-1)
    SBCI    R18, HIGH(-1)
    ; подготовили делимое и делитель
d16s_2: 
    ; очищаем остаток и флаг переноса
    CLR     R14
    CLR     R13
    CLC
    LDI     R31, 17     ; init loop counter
d16s_3:     
    ROL     R17     ; shift left dividend
    ROL     R16
    DEC     R31     ; decrement counter
    BRNE    d16s_5      ; if done
    SBRS    R10, 7      ; if MSB in sign register set
    RJMP    d16s_4
    COM     R16     ; change sign of result
    COM     R17
    SUBI    R17, LOW(-1)
    SBCI    R16, HIGH(-1)
d16s_4: 
    RET
d16s_5: 
    ROL     R14     ; shift dividend into remainder
    ROL     R13
    SUB     R14, R19    ; remainder = remainder - divisor
    SBC     R13, R18
    BRCC    d16s_6      ; if result negative
    ADD     R14, R19    ; restore remainder
    ADC     R13, R18
    CLC             ; clear carry to be shifted into result
    RJMP    d16s_3      ; else
d16s_6: 
    SEC             ; set carry to be shifted into result
    RJMP    d16s_3
;=======================================================================
; беззнаковое деление 16-разрядных чисел
; вход:     Temp1-Temp2 первый аргумент от H к L
;       Temp3-Temp4 второй аргумент от H к L
; выход:    Temp1-Temp2 результат от H к L
;       R13-R14 остаток от H к L
;=================================================
DIV16X16u:  
    ; очищаем остаток и флаг переноса
    CLR     R14
    CLR     R13
    CLC
    LDI     R31, 17     ; init loop counter
d16u_1: 
    ROL     R17     ; shift left dividend
    ROL     R16
    DEC     R31     ; decrement counter
    BRNE    d16u_2      ; if done
    RET             ; return
d16u_2: 
    ROL     R14     ; shift dividend into remainder
    ROL     R13
    SUB     R14, R19    ;remainder = remainder - divisor
    SBC     R13, R18    
    BRCC    d16u_3      ; if result negative
    ADD     R14, R19    ; restore remainder
    ADC     R13, R18
    CLC             ; clear carry to be shifted into result
    RJMP    d16u_1      ; else
d16u_3: 
    SEC             ; set carry to be shifted into result
    RJMP    d16u_1
;=======================================================================
; знаковое деление 16-разрядного числа на степень 2
; вход:     Temp1-Temp2 делимое от H к L
;       Temp5 степень 2
; выход:    Temp1-Temp2 результат от H к L
DIV16POWER2s:
    TST     Temp5
    BREQ    DIV16POWER2s_2
DIV16POWER2s_1:
    ASR     Temp1
    ROR     Temp2
    DEC     Temp5
    BRNE    DIV16POWER2s_1
DIV16POWER2s_2:
RET
;=======================================================================
; беззнаковое деление 16-разрядного числа на степень 2
; вход:     Temp1-Temp2 делимое от H к L
;           Temp5 степень 2
; выход:    Temp1-Temp2 результат от H к L
DIV16POWER2u:
    TST     Temp5
    BREQ    DIV16POWER2u_2
DIV16POWER2u_1:
    LSR     Temp1
    ROR     Temp2
    DEC     Temp5
    BRNE    DIV16POWER2u_1
DIV16POWER2u_2:
RET
;=======================================================================
; смена знака 16-разрядного числа
; вход:     Temp1-Temp2 число от H к L
; выход:    Temp1-Temp2 результат от H к L
SIGN16:
    COM     Temp1
    COM     Temp2
    SUBI    Temp2, LOW(-1)
    SBCI    Temp2, HIGH(-1)
RET 
;=======================================================================
; перевол 8-разрядного десятичного числа в двоично-десятичное (BCD)
; вход:     Temp1 десятичное число
; выход:    Temp1 BCD число
DEC2BCD: 
    PUSH    Temp2
    PUSH    Temp3
    PUSH    Temp4
    CPI     Temp1, 10
    BRLO    Dec2Bcd_exit
    PUSH    Temp1
    CLR     Temp3
    LDI     Temp2, 10   
Dec2Bcd_1: 
    SUB     Temp1,Temp2 
    INC     Temp3
    CPI     Temp1, 10    
    BRGE    Dec2Bcd_1
    CLR     Temp4
    CLR     Temp1 
Dec2Bcd_2: 
    ADD     Temp4, Temp2 
    INC     Temp1
    CP      Temp1, Temp3
    BRNE    Dec2Bcd_2  
    
    POP     Temp1
    SUB     Temp1, Temp4 
    SWAP    Temp3
    ADD     Temp1, Temp3
Dec2Bcd_exit:
    POP     Temp4
    POP     Temp3
    POP     Temp2
RET
;=======================================================================
; перевол 8-разрядного двоично-десятичного (BCD) числа в десятичное
; вход:     Temp1 BCD число
; выход:    Temp1 десятичное число
BCD2DEC: 
    PUSH    Temp2
    PUSH    Temp3
    PUSH    Temp1
    SWAP    Temp1
    CLR     Temp3
    CBR     Temp1, 0b11110000
    MOV     Temp3, Temp1
    CLR     Temp2
Bcd2Dec_1:
    ADD     Temp1, Temp3
    INC     Temp2
    CPI     Temp2, 9
    BRNE    Bcd2Dec_1
    MOV     Temp2, Temp1
    POP     Temp1 
    CBR     Temp1, 0b11110000
    ADD     Temp1, Temp2
    POP     Temp3
    POP     Temp2
RET
;=======================================================================
; сравнение 16-разрядных чисел
; вход:     Temp1-Temp2 первый аргумент от H к L
;       Temp3-Temp4 второй аргумент от H к L
; выход:    смотри флаги
CP16X16:
    CP      Temp2, Temp4
    CPC     Temp1, Temp3
RET
;=======================================================================
; вычисление цифр 8-разрядного числа
; вход:     Temp1 аргумент
; выход:    Temp1-Temp3 цифры от H к L
DIGITS8:
    CLR     R26
    CLR     R27
    CLR     R28
    CLR     R29
    CLR     R30
    LDI     Temp2,100
DIG8_1:
    CP      Temp1, Temp2
    BRLO    DIG8_2
    SUB     Temp1, Temp2
    ; сотни
    INC     R26 
    RJMP    DIG8_1
DIG8_2: 
    LDI     Temp2,10
DIG8_3:
    CP      Temp1, Temp2
    BRLO    DIG8_4
    SUB     Temp1, Temp2
    ; тысячи
    INC     R27     
    RJMP    DIG8_3
DIG8_4:
    ; в Temp1 остались только единицы
    MOV     Temp3, Temp1
    MOV     Temp1, R26      
    MOV     Temp2, R27
RET
;=======================================================================
; вычисление цифр 16-разрядного числа
; вход:     Temp1-Temp2 аргумент от H к L
; выход:    Temp1-Temp5 цифры от H к L
DIGITS16:
    CLR     R26
    CLR     R27
    CLR     R28
    CLR     R29
    CLR     R30
    LDI     Temp3, HIGH(10000)
    LDI     Temp4, LOW(10000)
DIG16_1:
    RCALL   CP16X16 
    BRLO    DIG16_2
    RCALL   SUB16X16
    ; десятки тысяч
    INC     R26 
    RJMP    DIG16_1
DIG16_2:    
    LDI     Temp3, HIGH(1000)
    LDI     Temp4, LOW(1000)
DIG16_3:
    RCALL   CP16X16 
    BRLO    DIG16_4
    RCALL   SUB16X16
    ; тысячи
    INC     R27     
    RJMP    DIG16_3
DIG16_4:
    LDI     Temp3, HIGH(100)
    LDI     Temp4, LOW(100)
DIG16_5:
    RCALL   CP16X16 
    BRLO    DIG16_6
    RCALL   SUB16X16
    ; сотни
    INC     R28 
    RJMP    DIG16_5
DIG16_6:
    LDI     Temp3, HIGH(10)
    LDI     Temp4, LOW(10)
DIG16_7:
    RCALL   CP16X16 
    BRLO    DIG16_8
    RCALL   SUB16X16
    ; десятки
    INC     R29
    RJMP    DIG16_7
DIG16_8:
    ; в Temp1-Temp2 остались только единицы
    MOV     Temp5, Temp2
    MOV     Temp1, R26      
    MOV     Temp2, R27
    MOV     Temp3, R28
    MOV     Temp4, R29
RET

Скачать AVR Math.asm

Несколько интересных примеров

Несколько примеров программ на языке ассемблера для микроконтроллеров Atmel AVR. Эти примеры демонстрируют основные операции, такие как загрузка данных, арифметические операции и ввод-вывод:

  1. Пример программы для загрузки константы в регистр:
ldi r16, 0x0A ; Загрузить значение 0x0A в регистр R16
  1. Пример программы для сложения двух чисел и сохранения результата:
ldi r16, 0x0A ; Загрузить значение 0x0A в регистр R16
ldi r17, 0x05 ; Загрузить значение 0x05 в регистр R17
add r16, r17  ; Сложить R16 и R17 и сохранить результат в R16
  1. Пример программы для условного перехода:
ldi r16, 0x0A ; Загрузить значение 0x0A в регистр R16
cpi r16, 0x0F ; Сравнить R16 с 0x0F
breq equal    ; Если равно, перейти к метке equal
; Если не равно, выполнить другие операции
equal:
; Код, выполняемый при равенстве
  1. Пример программы для ввода и вывода данных через порты ввода-вывода:
in r16, PORTB ; Загрузить данные с порта ввода-вывода PORTB в регистр R16
out PORTB, r16 ; Вывести данные из регистра R16 на порт ввода-вывода PORTB
  1. Пример программы для цикла:
ldi r16, 0   ; Загрузить начальное значение в регистр R16
loop:
    ; Код, выполняемый внутри цикла
    inc r16   ; Увеличить значение R16 на 1
    cpi r16, 10 ; Сравнить R16 с 10
    brne loop ; Если R16 не равно 10, вернуться к метке loop

Обратите внимание, что это базовые примеры, и реальные программы для микроконтроллеров AVR могут быть более сложными, включать в себя обработку прерываний, взаимодействие с периферийными устройствами и другие задачи.


Более сложные примеры программ на языке ассемблера для микроконтроллеров Atmel AVR:

  1. Пример программы для инициализации порта ввода-вывода и мигания светодиодом:
.include <avr/io.h>

.equ LED_PORT, PORTB
.equ LED_DDR,  DDRB
.equ LED_PIN,  PINB
.equ LED_BIT,  PB0

ldi r16, 0xFF   ; Установить все биты порта B как выходы
out LED_DDR, r16

main_loop:
    sbi LED_PORT, LED_BIT   ; Установить бит PB0 в 1 (включить светодиод)
    _delay_ms(1000)         ; Подождать 1 секунду
    cbi LED_PORT, LED_BIT   ; Установить бит PB0 в 0 (выключить светодиод)
    _delay_ms(1000)         ; Подождать еще 1 секунду
    rjmp main_loop          ; Бесконечный цикл
  1. Пример программы для обработки прерывания от внешнего источника (например, кнопки):
.include <avr/io.h>
.def  button = r18

.equ LED_PORT, PORTB
.equ LED_DDR,  DDRB
.equ LED_BIT,  PB0

.equ BUTTON_PORT,  PORTC
.equ BUTTON_PIN,   PINC
.equ BUTTON_DDR,   DDRC
.equ BUTTON_BIT,   PC0

.org 0x00  ; Начальный вектор прерывания
    rjmp reset

.org INT0addr  ; Вектор прерывания от внешнего источника
    in button, BUTTON_PIN  ; Считать состояние кнопки
    brne button_pressed    ; Если кнопка нажата, перейти к обработке
    rjmp button_released

button_pressed:
    sbi LED_PORT, LED_BIT   ; Включить светодиод
    rjmp button_done

button_released:
    cbi LED_PORT, LED_BIT   ; Выключить светодиод
    rjmp button_done

button_done:
    reti  ; Завершить обработку прерывания

reset:
    ldi r16, 0xFF   ; Установить все биты порта B как выходы
    out LED_DDR, r16
    ldi r16, 0x00   ; Установить все биты порта C как входы
    out BUTTON_DDR, r16
    ldi r16, (1 << INT0)   ; Включить прерывание INT0
    out GICR, r16
    sei  ; Разрешить глобальные прерывания

main_loop:
    rjmp main_loop  ; Бесконечный цикл

Обратите внимание, что второй пример включает в себя обработку внешнего прерывания (INT0), которое срабатывает при нажатии на кнопку, и светодиод мигает в зависимости от состояния кнопки.


Ещё более объемные примеры программ на языке ассемблера для микроконтроллеров Atmel AVR:

  1. Пример программы для чтения с датчика температуры DS18B20 и вывода значения на серийный порт USART:
.include <avr/io.h>

.equ DS18B20_PORT, PORTD
.equ DS18B20_DDR, DDRD
.equ DS18B20_PIN, PIND
.equ DS18B20_DQ,  PD2  ; Пин для подключения DS18B20

.equ USART_BAUD_RATE, 9600

.org 0x00
    rjmp reset  ; Вектор сброса

reset:
    ldi r16, 0xFF   ; Установить все биты порта D как выходы
    out DS18B20_DDR, r16

    ser r16          ; Установить R16 в 0xFF (все биты в 1)
    out UBRRL, r16   ; Установить скорость передачи данных USART
    ldi r16, (1 << TXEN) | (1 << RXEN)  ; Включить передачу и прием через USART
    out UCSRB, r16
    ldi r16, (1 << UCSZ0) | (1 << UCSZ1)  ; Установить формат кадра 8N1
    out UCSRC, r16

main_loop:
    call read_temperature  ; Вызов функции для считывания температуры
    call send_temperature  ; Вызов функции для отправки температуры через USART
    rcall _delay_ms      ; Вызов функции задержки
    rjmp main_loop

read_temperature:
    ; Код для инициализации и чтения температуры с DS18B20
    ; Результат сохраняется в регистре R16
    ret

send_temperature:
    ; Код для отправки температуры через USART
    ; Значение температуры передается через R16
    ret

_delay_ms:
    ; Код для создания задержки в миллисекундах
    ret

.include "uart.inc"  ; Включение файла с подпрограммами для USART
  1. Пример программы для управления шаговым двигателем через драйвер шагового двигателя A4988:
.include <avr/io.h>

.equ A4988_PORT, PORTD
.equ A4988_DDR,  DDRD

.equ STEP_PIN,  PD2
.equ DIR_PIN,   PD3
.equ MS1_PIN,   PD4
.equ MS2_PIN,   PD5
.equ ENABLE_PIN, PD6

.org 0x00
    rjmp reset

reset:
    ldi r16, (1 << STEP_PIN) | (1 << DIR_PIN) | (1 << MS1_PIN) | (1 << MS2_PIN) | (1 << ENABLE_PIN)
    out A4988_DDR, r16  ; Установить все пины управления A4988 как выходы

main_loop:
    ; Управление двигателем, включая изменение направления вращения, шагов и режима микрошага
    ; Здесь можно добавить логику для движения двигателя в нужном направлении и количестве шагов
    rjmp main_loop

Эти примеры программ представляют собой более крупные приложения для микроконтроллеров AVR, и они могут потребовать дополнительных библиотек и настроек, чтобы полностью функционировать.


Добавлю ещё пару примеров программ на ассемблере для микроконтроллеров AVR:

  1. Пример программы для создания простого таймера и обработки прерываний:
.include <avr/io.h>

.org 0x00
    rjmp reset

.org TIMER1_COMPA_vect
    ; Обработка прерывания от TIMER1
    ; Ваш код обработки здесь
    reti

reset:
    ldi r16, (1 << WGM12) | (1 << CS11)  ; Настройка TIMER1 в режим CTC и делитель на 8
    out TCCR1B, r16
    ldi r16, 62500  ; Значение для сравнения, чтобы создать интервал в 1 секунду
    out OCR1A, r16
    sei  ; Разрешить глобальные прерывания

main_loop:
    ; Основной код программы
    rjmp main_loop

Этот пример настраивает TIMER1 для создания прерывания через 1 секунду и обрабатывает его в функции прерывания.

  1. Пример программы для использования внутренней EEPROM для хранения и чтения данных:
.include <avr/io.h>

.equ EEPROM_DATA_ADDR, 0x10  ; Адрес внутренней EEPROM, с которого начинается хранение данных

.org 0x00
    rjmp reset

reset:
    ldi r16, 0x55  ; Значение для записи в EEPROM
    sts EEPROM_DATA_ADDR, r16  ; Записать значение в EEPROM

read_eeprom:
    lds r16, EEPROM_DATA_ADDR  ; Прочитать значение из EEPROM
    ; Использовать значение в регистре R16
    ret

main_loop:
    ; Основной код программы
    rcall read_eeprom  ; Чтение данных из EEPROM
    ; Дополнительная логика
    rjmp main_loop

Этот пример демонстрирует, как записывать и читать данные во внутренней EEPROM микроконтроллера AVR.

Учтите, что эти примеры представляют собой базовую структуру программы. Реальные программы могут включать в себя более сложные операции и логику в зависимости от конкретных задач.

Ассемблер для микроконтроллеров ATMEL AVR может быть довольно сложным, но в данном контексте показываю пример, который демонстрирует некоторые основные аспекты ассемблерного программирования на AVR. Этот пример будет включать в себя работу с прерываниями, работу с портами ввода-вывода и использование множества инструкций.

Пример: Программа, которая мигает светодиодом на микроконтроллере ATMega328P (одном из популярных контроллеров AVR) с использованием внешних прерываний.

.include "m328pdef.inc" ; Включение файла с определениями регистров для ATMega328P

; Определение макросов для удобства работы с портами ввода-вывода
.equ LED_DDR = DDRB      ; Регистр для управления направлением порта B
.equ LED_PORT = PORTB    ; Регистр для управления состоянием порта B
.equ LED_PIN = PINB      ; Регистр для чтения состояния порта B

; Определение вектора прерывания INT0
.equ INT0_vect = 2

; Начальное состояние регистров
.def led_state = r16   ; Регистр для хранения состояния светодиода

; Начало программы
.org 0x0000
    rjmp Main

; Обработчик внешнего прерывания INT0
.org INT0_vect
    ; Инвертировать состояние светодиода
    lds led_state, LED_PIN ; Загрузить текущее состояние порта B
    ldi r17, 1            ; Константа 1
    eor led_state, r17    ; Инвертировать бит в led_state
    sts LED_PORT, led_state ; Установить новое состояние порта B
    reti

Main:
    ; Настроить порт B, бит 0 (PB0) как выход (светодиод)
    sbi LED_DDR, 0

    ; Настроить внешнее прерывание INT0
    ldi r17, (1<<ISC00)  ; Настроить прерывание по изменению уровня
    sts EICRA, r17
    sbi EIMSK, INT0      ; Разрешить прерывание INT0

    sei ; Разрешить глобальные прерывания

    ; Бесконечный цикл
Loop:
    rjmp Loop

.end

Этот пример программы для микроконтроллера ATMega328P на языке ассемблера демонстрирует использование внешнего прерывания (INT0) для переключения состояния светодиода, подключенного к порту B, бит 0 (PB0). Программа также настраивает обработчик прерывания, включает глобальные прерывания и выполняет бесконечный цикл.


Рассмотрим ещё один интересный пример

.include "m128pdef.inc" ; Включение файла с определениями регистров для ATMega128

; Определение макросов для удобства работы с портами ввода-вывода
.equ LED_DDR = DDRB      ; Регистр для управления направлением порта B
.equ LED_PORT = PORTB    ; Регистр для управления состоянием порта B
.equ LED_PIN = PINB      ; Регистр для чтения состояния порта B

.equ BUTTON_PIN = PIND  ; Регистр для чтения состояния порта D

; Определение вектора прерывания INT0
.equ INT0_vect = 2

; Определение вектора прерывания TIMER1_COMPA
.equ TIMER1_COMPA_vect = 9

; Начальное состояние регистров
.def led_state = r16      ; Регистр для хранения состояния светодиода
.def button_state = r17   ; Регистр для хранения состояния кнопки

; Начало программы
.org 0x0000
    rjmp Main

; Обработчик внешнего прерывания INT0
.org INT0_vect
    ; Инвертировать состояние светодиода
    lds led_state, LED_PIN ; Загрузить текущее состояние порта B
    ldi r18, 1            ; Константа 1
    eor led_state, r18    ; Инвертировать бит в led_state
    sts LED_PORT, led_state ; Установить новое состояние порта B
    reti

; Обработчик прерывания TIMER1_COMPA
.org TIMER1_COMPA_vect
    ; Увеличить счетчик времени
    lds r19, TCNT1H     ; Загрузить старший байт счетчика TIMER1
    lds r20, TCNT1L     ; Загрузить младший байт счетчика TIMER1
    ldi r21, 1          ; Константа 1
    add r20, r21        ; Увеличить младший байт на 1
    brcc .+2            ; Перейти, если не было переноса
    inc r19             ; Увеличить старший байт при переносе
    sts TCNT1H, r19     ; Сохранить старший байт обратно
    sts TCNT1L, r20     ; Сохранить младший байт обратно

    ; Проверить состояние кнопки
    sbis BUTTON_PIN, 2  ; Проверить бит 2 в PIND
    rjmp ButtonPressed  ; Перейти, если кнопка нажата
    reti

ButtonPressed:
    ; Дополнительные действия, если кнопка нажата
    ; Можно добавить здесь любой код

Main:
    ; Настроить порт B, бит 0 (PB0) как выход (светодиод)
    sbi LED_DDR, 0

    ; Настроить внешнее прерывание INT0
    ldi r18, (1<<ISC00)   ; Настроить прерывание по изменению уровня
    sts EICRA, r18
    sbi EIMSK, INT0       ; Разрешить прерывание INT0

    ; Настроить TIMER1 для генерации прерываний
    ldi r19, 0xC0         ; Настроить TCCR1B для делителя частоты (1024)
    sts TCCR1B, r19
    ldi r20, 0x00         ; Сбросить TCNT1
    sts TCNT1H, r20
    sts TCNT1L, r20

    ldi r21, 0x61         ; Загрузить старший байт для значения сравнения
    sts OCR1AH, r21
    ldi r22, 0x80         ; Загрузить младший байт для значения сравнения
    sts OCR1AL, r22

    ldi r23, (1<<OCIE1A)  ; Разрешить прерывание при сравнении
    sts TIMSK, r23

    sei ; Разрешить глобальные прерывания

    ; Бесконечный цикл
Loop:
    rjmp Loop

.end

Этот пример реализует обработку внешнего прерывания, генерацию прерываний от таймера TIMER1, а также обработку нажатия кнопки. Он включает в себя более сложные операции, такие как настройка таймера, работа с различными регистрами и битами, а также обработка условий внутри прерываний.


IO Ports

В данном примере рассматривается работа с портами ввода-вывода. К порту А подключены 8 светодиодов (линии 0-7). К линии 0 порта С подключена кнопка, с подтяжкой на землю. При нажатии кнопка выдает на линию 0 порта С уровень логической единицы. Цикл программы организован следующим образом: при запуске включается бегущий огонь, сначала загорается светодиод на линии 0 порта А, затем на линии 1 и т.д. По достижении линии 7 направление бегущего огня меняется (от 7 к 0). При нажатии на кнопку бегущий огонь останавливается и загораются одновременно все светодиоды. После повторного нажатия на кнопку бегущий огонь продолжает перемещаться с места остановки.

Скачать пример

Dynamic Indication

В данном примере рассматривается работа с 7-сегментным индикатором. В моём случае он имеет 4 разряда (цифры). Поскольку у меня на плате установлены транзисторы для управления разрядами, то управление осуществляется выводом логической единицы и на разряды и на сегменты. Схема подключения следующая: к линиям 0-7 порта C подключены сегменты индикатора, а к линиям 0-3 порта В разряды индикатора. При запуске на индикатор выводятся цифры 1 2 3 4.

Скачать пример

UART

В данном примере рассматривается периферийного модуля UART (универсальный асинхронный приёмопередатчик). Модуль UART можно настроить как на работу с прерываниями, так и без них (вручную, путём работы с флагами). Пример работает следующим образом: при получении байта, МК переходит в обработчик прерывания (используется только прерывание по приёму данных) и разбирает численное значение байта (0-255) на цифры, которые и выводятся на 7-сегментный индикатор. Схема подключения аналогична предыдущему примеру. Передача осуществляется по двум линиям UART (порт D линии 0-1), к которым необходимо подключить линии RX и TX преобразователя USB-UART. Для настройкки без прерываний необходимо обнулить бит RXCIE в регистре UCSRB и вручную опрашивать интерфейс в основном цикле программы.

Скачать пример

Clock

В данном примере рассматривается реализация простых часов с 7-сегментым индикатором и парой кнопок. Только здесь уже требуется 6 разрядов, хотя секунды можно опустить. Кнопки с подтяжкой на землю. При нажатии кнопка выдает на линию высокий логический уровень. Индикатор подключается как и в предыдущих примерах (сегменты к порту C, разряды к порту B), а кнопки к линиям 2-3 порта D. Кнопка PD2 используется для установки минут, а PD3 для установки часов. По нажатию каждой из кнопок увеличивается значение соответствующего разряда (минуты или часы).

Скачать пример

DS18B20

В данном примере рассматривается работа с цифровым датчиком температуры DS18B20. Показания температуры выводятся на 7-сегментый индикатор. Вывод DQ датчика поключен к линии PC7. Линия должна быть подтянута к плюсу питания резистором на 4.7-10 кОм (согласно документации). Датчик опрашивается каждую секунду. Температура выводится на 4-разрядный индикатор: знак, два разряда на целуюю часть и один на вещественную. Документация к датчику здесь.

Скачать пример

ADC Indication

Данный пример аналогичен примеру с UART. Отличие в том, что байт берется с линии 0 порта А (линия 0 АЦП, ADC0). Микроконтроллер по таймеру производит аналого-цифровое преобразование напряжения на линии 0 порта А, (младшие 2 бита отбрасываются как шум). При измерении используется внутренняя опора 5 В. К линии PD2 порта D подключена кнопка, которая определяет режим вывода показаний. При нажатии на кнопку выводится результат измерений в виде числа от 0 до 255. Если кнопка не нажата, то результат измерений переводится в вольты и выводится на индикатор (с точностью до десятых).

Скачать пример

Fast PWM

В данном примере показана настройка аппаратного ШИМ (широтно-импульсная модуляция, англ. PWM). К линиям 4 и 5 порта D подключены светодиоды, а к линиям 0-3 порта С – кнопки. Кнопки с подтяжкой на землю (при нажатии кнопка выдает на линию порта уровень логической единицы) подключены к линиям 2-5 порта C. Кнопки на линях 2 и 3 соответственно увеличивают и уменьшают коэффициент заполнения ШИМ (меняется яркость светодиода) канала А. Кнопки на линях 4 и 5 соответственно увеличивают и уменьшают коэффициент заполнения ШИМ канала B. Число сравнения для каждого из каналов меняется в диапазоне от 0 до 255. Для канала А шаг изменения равен 10, для канала В шаг равен 5.

Скачать пример

HCSR04

В данном примере рассматривается работа с ультразвуковым датчиком расстояния HCSR04. К линии PD6 подключен вывод Trigger датчика, а к линии PD7 вывод Echo. Поключение 7-сегментного индикатора аналогично предыдущим примерам. По таймеру микроконтроллер раз в секунду опрашивает датчик и определяет расстояние до препятсвия в миллиметрах. После этого число разбивается на цифры и выводится на дисплей. Документация к датчику здесь.

Скачать пример

Matrix Keyboard

В данном примере показана работа с матричной клавиатурой. Микроконтроллер динамически опрашивает клавиатуру, а затем определяет номер нажатой клавиатуры. Размер поля 3 на 3 – получаем 9 кнопок. Нажатие первых 8-ми приводит к зажиганию светодиода на соответствующей линии порта А, нажатие 9-ой кнопки зажигает все светодиоды порта А. Матричная клавиатура подключается к линиям 0-5 порта С (три столбца и три строки). В архиве схема и печатная плата матричной клавиатуры (Diptrace).

Скачать пример

Shift Register

В данном примере рассматривается работа с модулем SPI на примере сдвигового регистра 74HC595. К регистру подключены светодиоды, в качестве линии CS используется линия 4 порта B (вывод not SS). Линия DS (14 нога) регистра идет к MOSI (PB5), линия SHCP (11 нога) к линии SCK (PB7), линия STCP (12 нога) к линии SS (PB4). Линии MR (10 нога) и OE (13 нога) должны быть подтянуты к высокому и низкому логическим уровням соответственно. По таймеру микроконтроллер меняет состояние светодиодов: поочерёдно горят то чётные светодиоды, то нечётные. Если при этом передать байт по UART'у, то он будет выведен в порт на светодиоды. Чтобы обратно переключиться в режим мигания необходимо послать по UART'у 0x00 (ноль). Документация к микросхеме 74HC595 здесь.

Скачать пример

SG-90 Servo

В данном примере рассматривается работа с сервоприводом SG-90. Используется аппаратный ШИМ. Линия ШИМ сервпопривода подключена к каналу А аппаратного ШИМ. Кнопки поворота подключены к линиям PD2 и PD3. Кнопка на линии PD2 увеличивает длительность импульса, кнопка на линии PD3 уменьшает длительность импульса. Длительность импульса меняется от 1 до 2 мс. Описание сервомотора здесь.

Скачать пример

 

RGB Lamp

В данном примере рассматривается работа с трехцветным RGB-светодиодом. Реализовано плавное переливание цветов с использованием программного ШИМ. Линии красного, зеленого и синего цветов подключаются соответственно к линиям 2, 3 и 4 порта D.

Скачать пример

Поделиться
Понравился материал?

Комментарии

Добавить комментарий
Коллаборация

Хорошая подборка примеров, спасибо Вам за тяжкий труд, в своём видео на You tube я дал ссылку на эту Вашу страницу с примерами.

Нравится: 0Не нравится: 0

Ответить

Спасибо за код, Вы его сами писали?

Нравится: 0Не нравится: 0

Ответить

Добрый день!

Ссылки на примеры - не работают! Жаль.

Нравится: 0Не нравится: 0

Ответить

Привет Сергей

Удачно зашел. Гора примеров для атмел, спасибо друг

Нравится: 0Не нравится: 0

Ответить