Программы на ассемблере для avr

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

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

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

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

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

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

  • Delay1us – Задержка повышенной точности в 1 мкс c учетом длительности RCALL и RET.
  • Delay5us – Задержка повышенной точности в 5 мкс c учетом длительности RCALL и RET.
  • Delay10us – Задержка повышенной точности в 10 мкс c учетом длительности RCALL и RET.
  • Delayus – Задержка высокой точности в несколько десятков микросекунд.
  • Delayms – Задержка высокой точности в несколько миллисекунд.

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-разрядного числа.

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.

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

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

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

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

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

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

  • Delay1us – Задержка повышенной точности в 1 мкс c учетом длительности RCALL и RET.
  • Delay5us – Задержка повышенной точности в 5 мкс c учетом длительности RCALL и RET.
  • Delay10us – Задержка повышенной точности в 10 мкс c учетом длительности RCALL и RET.
  • Delayus – Задержка высокой точности в несколько десятков микросекунд.
  • Delayms – Задержка высокой точности в несколько миллисекунд.

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-разрядного числа.

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 здесь.

Читайте также:  Медио плеер для виндовс 10

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

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

Приведен и подробно разобран пример простой программы для AVR микроконтроллера на языке Ассемблер (Assembler). Собираем простую схему на микроконтроллере для мигания светодиодами, компилируем программу и прошиваем ее в микроконтроллер под ОС GNU Linux.

Подготовка

Итак, у нас уже есть настроенный и подключенный к микроконтроллеру программатор, также мы разобрались с программой avrdude, изучили ее настройки и примеры использования. Пришло время разработать свою первую программу, которая будет выполнять какие-то реальные действия с AVR микроконтроллером (МК).

Писать программу мы будем на языке программирования Ассемблер (Assembler, Asm). Основной ее задачей будет заставить поочередно и с установленной задержкой мигать два разноцветных светодиода (красный и синий), имитируя таким образом полицейскую мигалку.

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

Понятное дело что подобную мигалку можно реализовать на основе простого мультивибратора на двух транзисторах с конденсаторами. Микроконтроллер же вам предоставляет намного больше возможностей.

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

Первый инструмент, который нам понадобится – редактор исходного кода, здесь можно использовать любой текстовый редактор. В одной из прошлых статей мы рассматривали настройку среды разработки программ Geany для программирования AVR микроконтроллеров с использованием языков Ассемблера и Си.

В принципе там уже все готово, останется написать код программы и поочередным нажатием двух кнопок (Compile-Flash) скомпилировать и прошить программу в микроконтроллер.

Несмотря на то что у вас уже может быть настроена среда Geany, я приведу все консольные команды которые необходимы для компиляции и прошивки нашей программы в МК.

Принципиальная схема и макет

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

Рис. 1. Принципиальная схема мигалки на светодиодах и микроконтроллере ATmega8.

Примечание: принципиальная схема нарисована за несколько минут в программе Eeschema, которая входит в комплекс программ EDA(Electronic Design Automation) KiCAD (для Linux, FreeBSD, Solaris, Windows). Очень мощный профессиональный инструмент, и что не мало важно – свободный!

Схема устройства состоит из микроконтроллера ATmega8 и двух светодиодов, которые подключены через гасящие резисторы. К микроконтроллеру подключен ISP-коннектор для осуществления программирования через программатор. Также предусмотрены клеммы для подключения внешнего источника питания напряжением 5В.

То как выглядит данная схема в сборе на макетной баспаечной панели (BreadBoard) можно посмотреть на рисунке ниже:

Рис. 2. Конструкция светодиодной мигалки на микроконтроллере ATmega8.

К микроконтроллеру подключен программатор USBAsp, используя ISP интерфейс, от него же и будет питаться наша экспериментальная конструкция. Если нужно запитать конструкцию от внешнего источника питания напряжением 5В то достаточно его подключить к + и – линиям питания панели.

Исходный код программы на Ассемблере

Разработанная нами программа будет попеременно зажигать и гасить два светодиода. Светодиоды подключены к двум пинам PD0 и PD1 микроконтроллера.

Ниже приведен исходный код программы на Ассебмлере(Assembler, Asm) для микроконтроллера ATmega8. Сохраните этот код в файл под названием leds_blinking.asm для последующей работы.

Кратко рассмотрим приведенный выше код и построчно разберем его структуру. Выполнение программы происходит по порядку – с верху кода и к низу, учитывая при этом метки, переходы с возвратами и условия.

Все строки и части строк, которые начинаются с символа ";" – это комментарии. При компиляции и выполнении программы такие строчки игнорируются, они служат для документирования и примечаний.

При помощи директивы ".INCLUDEPATH" мы указываем путь "/usr/share/avra/", по которому компилятору нужно искать файлы для включения их в текущий файл с использованием директив ".INCLUDE". В нашем примере подключается файл, полный путь к которому будет выглядеть вот так: "/usr/share/avra/m8def.inc".

Директива ".LIST" указывает компилятору о необходимости генерирования листинга с текущего места в коде, отключить генерирование можно директивой ".NOLIST". Листинг представляет собой файл в котором содержится комбинация ассемблерного кода, адресов и кодов операций. Используется для отладки и других полезных нужд.

Директива ".CSEG" (CodeSEGment) определяет начало программного сегмента (код программы что записан во флешь-память) – сегмента кода. Соответственно все что размещено ниже этой директивы относится к программному коду.

Для определения сегмента данных (RAM, оперативная память) или памяти EEPROM используются директивы ".DSEG" и ".ESEG" соответственно. Таким образом выполняется распределение памяти по сегментам.

Каждый из сегментов может использоваться в программном коде только раз, по умолчанию если не указана ни одна из директив используется сегмент кода (CSEG).

При помощи директивы ".ORG" компилятору указывается начальный адрес "0x0000" сегмента, в данном случае мы указали начальный адрес сегмента кода. В данной программе эту директиву можно было бы и не использовать, поскольку по умолчанию адрес программного кода всегда 0x0000.

Дальше в коде происходит инициализация стека. Стек (Stack) – это область памяти (как правило у всех AVR чипов размещается в SRAM), которая используется микропроцессором для хранения и последующего считывания адресов возврата из подпрограмм, а также для других пользовательских нужд.

При вызове подпрограммы flhtc nt записывается в стек и начинается выполнение кода подпрограммы. По завершению подпрограммы (директива RET)

Стек работает по принципу LIFO (Last In – First Out, последним пришёл – первым вышел). Для адресации вершины стека используется указатель стека – SP (Stack Pointer), это может быть однобайтовое или двухбайтовое значение в зависимости от доступного количества SRAM памяти в МК.

При помощи инструкции "LDI" мы загружаем в регистр R16 значение младшего байта конечного адреса ОЗУ "Low(RAMEND)" (предопределенная константа в файле m8def.inc что содержит адрес последней ячейки SRAM), а потом при помощи инструкции OUT выполняем загрузку данного значения из регистра R16 в порт SPL (Stack Pointer Low). Таким же образом производится инициализация старшего байта адреса в указателе стека SPH.

Инструкция LDI используется для загрузки старшего и младшего значений из константы в регистр общего назначения. А инструкция OUT позволяет выполнить операцию загрузки с немного иной спецификой – из регистра общего назначения в регистр периферийного устройства МК, порт ввода-вывода и т.п.

Если не произвести инициализацию стека то возврат из подпрограмм станет невозможным, к примеру в приведенном коде после выполнения инструкции перехода к подпрограмме "RCALL Wait" возврат не будет выполнен и программа не будет работать как нужно.

Директива ".equ" выполняет присвоение указанному символьному имени "Delay" числового значения "5", по сути мы объявили константу. Имя константы должно быть уникальным, а присвоенное значение не может быть изменено в процессе работы программы.

Дальше мы устанавливает два канала (пины PD0, PD1) порта DDRD (PortD) на вывод, делается это загрузкой двоичного значения 0b00000011 (0x3, число 3) в регистр R16 с последующим выводом этого значения из него в порт DDRD при помощи команды OUT.

По умолчанию все каналы (пины) порта настроены на ввод. При помощи двоичного числа 0b00000011, где последние биты установлены в 1, мы переводим каналы PD0 и PD1 в режим вывода.

Начиная с метки "Start:" начинается основной рабочий цикл нашей программы, эта метка послужит нам для обозначения начального адреса основного цикла и позже будет использована для возврата.

Читайте также:  Nzxt kraken x62 liquid cooler

При помощи инструкции "SBI" выполняем установку бита PORTD0 (предопределен в файле m8def.inc) в порте PORTD чем установим на пине PD0 высокий уровень. Используя инструкцию "CBI" выполняется очистка указанного (PORTD1) бита в порте PORTD и тем самым устанавливается низкий уровень на пине PD1.

Дальше с помощью инструкции RCALL выполняем относительный вызов подпрограммы которая начинается с метки "Wait:". Здесь для запоминания адреса возврата уже используется стек, который мы инициализировали в начале программы.

После завершения подпрограммы (в нашем случае ее функция – задержка по времени) программа вернется к позиции где был выполнен вызов подпрограммы (адрес возврата будет получен из стека) и с этого места продолжится выполнение последующих операторов.

После вызова подпрограммы задержки "Wait" следуют вызовы инструкций SBI и CBI в которых выполняется установка битов порта PORTD таким образом, что теперь на пине PD0 у нас будет низкий уровень, а на пине PD1 – высокий.

По завершению этих инструкций следует еще один вызов подпрограммы задержки "Wait", а дальше следует инструкция "RJMP" которая выполнит относительный переход к указанной метке – "Start", после чего программа снова начнет установку битов в порте с задержками по времени.

Таким образом выполняется реализация бесконечного цикла в котором будут "дергаться" пины порта PORTD микроконтроллера и поочередно зажигаться/гаснуть светодиоды которые подключены к каналам данного порта (пины PD0, PD1).

После основного цикла программы следует наша подпрограмма задержки по времени. Принцип ее работы заключается в выполнении трех вложенных циклов, в каждом из которых происходит вычитание (DEC) единички из числа которое хранится в отдельном регистре, и так до тех пор пока значение не достигнет нуля. Инструкция "DEC" декрементирует значение указанного регистра и требует для этого 1 рабочий такт процессора.

При помощи инструкций "BRNE" (условный переход) выполняется анализ нулевого бита статусных флагов процессора (Zero Flag, ZF). Переход на указанную в инструкции метку будет выполнен если после выполнения предыдущей команды нулевой флаг был установлен. В данном случае проверяется значение нулевого флага после выполнения команд "DEC" над значениями которые хранится в регистрах общего назначения (R17, R18, R19). Инструкция "BRNE" требует 1/2 такта процессора.

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

По умолчанию, без установки фьюзов что задают источник и частоту тактового генератора, в микроконтроллере ATmega8 используется откалиброванный внутренний RC-генератор с частотой 1МГц. Если же мы изменим частоту МК на 4Мгц то наши светодиоды начнут мигать в 4 раза быстрее, поскольку на каждую операцию вычитания и сравнения будет тратиться в 4 раза меньше времени.

Завершается подпрограмма инструкцией "RET", которая выполняет возврат из подпрограммы и продолжение выполнения инструкций с того места, с которого эта подпрограмма была вызвана (на основе сохраненного адреса возвращения, который сохранился в стеке при вызове инструкции "RCALL").

При помощи директивы ".DB" в памяти программ (флешь) резервируется цепочка из байтов под строчку данных "Simple LEDs blinking program", эти данные являются статичными и их нельзя изменять в ходе работы программы. Для резервирования слов (Double Word) нужно использовать директиву ".DW".

В данном случае, у нас во FLASH-память вместе с программным кодом будет записана строка "Simple LEDs blinking program", которая содержит название программы. Данные из этой строчки нигде в программе не используются и приведены в качестве примера.

При каждом резервировании данных с использованием директивы ".DB" или ".DW" должна предшествовать уникальная метка, которая пригодится нам когда нужно будет получить адрес размещаемых данных в памяти для дальнейшего их использования, в нашем случае это "Program_name:".

При построении программы важно чтобы счетчик выполняемых команд не добрался до адреса с зарезервированными данными, иначе процессор начнет выполнять эти строчки как программный код (поскольку они размещены в сегменте кода). В примере моей программы байты под название программы зарезервированы в конце сегмента кода и за пределами рабочих циклов программы, так что все ОК.

Эти данные можно разместить и в начале кода, использовав операторы перехода для изоляции этих байтов от выполнения:

Документация по Ассемблеру

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

Процесс дальнейшего изучения Ассемблера для AVR микроконтроллеров полностью в ваших руках. Есть достаточно много полезных ресурсов в интернете, книг и материалов с примерами и пояснениями.

Приведу несколько полезных документов, которые вы можете скачать и использовать для справки при разработке программ на AVR ASM.

Справка по Ассемблеру для Atmel AVR (перевод Руслана Шимкевича): atmel-avr-assembler-quick-doc-ru.zip (16Кб, HTML, RU).

Справка по инструкциям Atmel Assembler: atmel-avr-instruction-set-manual-en.pdf.zip (700Кб, PDF, EN, 2015).

Работа с числами в Hex, Bin и Dec

В коде программы для загрузки значений в регистры используются числа и в скобках приведены их значения в шестнадцатеричной системе счисления, например: "50 (0x32, )". В двоичной системе счисления числа указываются в формате "0b00000011". Для удобной переконвертации чисел из шестнадцатеричной системы счисления в десятичную, двоичную и наоборот отлично подходит программный калькулятор из среды рабочего окружения KDE – KCalc.

Рис. 3. KCalc – простое и эффективное решение для пересчета между разными системами счисления.

В настройках (Settings) нужно выбрать режим (Numeral System Mode), после чего программа приобретет вид что на рисунке выше. Переключаться между системами счисления можно устанавливая флажки в полях "Dec", "Hex", "Bin". Для примера: переключаемся в Hex и набираем "FF", потом переключаемся в Dec и видим число в десятичной системе счисления – 255, просто и удобно.

В операционной системе GNU Linux с рабочей средой GNOME (например Ubuntu) также есть подобный калькулятор, это программа – galculator.

Компиляция и прошивка программы в МК

Итак, у нас уже есть полный код программы, который мы сохранили в файл с именем "leds_blinking.asm". Теперь самое время скомпилировать его, делается это нажатием кнопки "Compile" в предварительно настроенной среде Geany или же отдельной командой в консоли:

Если результат выполнения будет без ошибок то мы получим файл прошивки в формате Intel HEX – "leds_blinking.hex", который уже можно прошивать во флешь-память микроконтроллера.

Примечание: опцию "–includepath /usr/share/avra/" можно и не указывать, поскольку в файле с исходным кодом уже была указана директива ".INCLUDEPATH" для поиска файлов с предопределениями для разных моделей МК.

Осталось прошить микроконтроллер используя полученный файл "leds_blinking.hex". В примере я использую программатор USBAsp и микроконтроллер ATmega8, вот так выглядит команда для записи получившегося файла во флешь-память МК:

Примечание: в команде используется относительный путь к файлу leds_blinking.hex, поэтому для успешного выполнения команды нужно перейти в терминале(консоли) в директорию где находится данный файл.

Сразу же после прошивки флешь-памяти на микроконтроллер поступит команда сброса (RESET) и программа начнет выполняться, об єтом будут свидетельствовать два попеременно мелькающих светодиода.

Если же светодиоды не подают признаков жизни, значит что-то пошло не так. Посмотрите внимательно вывод команды для компиляции и прошивки МК, возможно что там увидите сообщения об ошибках которые нужно исправить.

Заключение

Увеличив значение константы "Delay" можно уменьшить частоту мерцания светодиодов, а уменьшив – увеличить частоту. Также можете попробовать добавить несколько светодиодов к свободным каналам порта (PD2-PD7) и модифицировать программу таким образом чтобы получить бегущий огонь из светодиодов.

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

В следующей статье мы разберем программу с похожим функционалом, используя тот-же макет, только выполним ее на языке программирования Си.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock detector