МИНОБРНАУКИ РОССИИ
Санкт-Петербургский государственный
электротехнический университет
«ЛЭТИ» им. В.И. Ульянова (Ленина)
Кафедра БТС
Отчет
по Лабораторной работе №1
Тема: Программирование МК для мигания светодиодом
Студент гр. 7503 _____________________ Марсаль Г. О.
Студент гр. 7503 _____________________ Роганов А. А.
Студентка гр. 7503 _____________________ Соболева Б. Д.
Преподаватель _____________________ Анисимов А. А.
Санкт-Петербург
2020
Цель работы: Исследование принципов работы микроконтроллера ATtiny104 на базе отладочной платы “ATtiny104 - Xplained Nano”. Получение начальных навыков программирования МК на языке Assembler и C, пользования средой разработки AVR Atmel Studio 7.
Используемые инструменты: Отладочная плата ATtiny104 - Xplained Nano; интегрированная среда разработки AVR Atmel Studio 7; методические указания к выполнению работы.
Мигание светодиодом при помощи микроконтроллера подразумевает собой попеременную подачу на один из выходов МК сигналов разных логических уровней.
Логические элементы оперируют сигналами двух типов: "высокий логический уровень" (1) и "низкий логический уровень" (0), которые характеризуются различным уровнем напряжения: полное напряжение питания принимается в качестве уровня "логической единицы", а нулевое напряжение - в качестве уровня "логического нуля".
В идеальном случае все сигналы логических элементов существовали бы в виде этих двух предельных уровней напряжения, и никогда бы от них не отклонялись (например, ниже полного напряжения для "высокого уровня", или выше нуля для "низкого уровня".) Однако в реальности уровни напряжения цифровых сигналов не так часто достигают этих идеальных величин вследствие наличия паразитных падений напряжения в схемах на транзисторах, и поэтому логических схем интерпретируют сигналы как логическую единицу или логический нуль, даже в тех случаях, когда напряжение сигналов лежит в диапазоне между полным напряжением питания и нулём.
Элементы ТТЛ работают при номинальном напряжении питания 5 вольт, +/- 0,25 вольт. В идеале, сигнал высокого логического уровня должен быть равен ровно 5,00 В, а сигнал низкого уровня - ровно 0,00 вольт. Однако в реальных элементах ТТЛ не могут быть обеспечены подобные точные уровни напряжения, поэтому они могут принимать сигналы высокого и низкого уровней даже при значительном отклонении напряжения от идеальных величин. "Приемлемые" напряжения входного сигнала лежат в диапазоне от 0 до 0,8 вольт для низкого логического уровня, и от 2 до 5 вольт для высокого логического уровня. "Приемлемые" напряжения выходного сигнала (уровни напряжения, гарантируемые производителем элемента в указанных вариантах нагрузки) лежат в диапазоне от 0 до 0,5 вольт для низкого логического уровня, и от 2,7 до 5 вольт для высокого логического уровня.
Временные задержки в МК, программно, могут быть реализованы по-разному (например, с помощью счетчика, пропуском команды, библиотек задежрки, либо циклов вычисления). В данной работе был выбран наиболее простой тип задержки – при помощи цикла декрементирования переменных для Assembler и библиотека «delay» для языка С.
Вид используемой платы приведен на рисунке 1.
Рисунок 1 – Схема отладочной платы ATtiny104 Xplained Nano
Код программы:
LDI r16, 0b00100000 ; запись 1 в 5 разр. в РОН r16
OUT DDRA, r16 ; перенос из РОН в DDRA
Main: ; начало главного цикла
LDI r16, 0b00100000 ; запись 1 в 5 разр. в РОН r16
OUT PORTA, r16 ; перенос из РОН в PORTA
RCALL Delay ; вызов метки задержки
LDI r16, 0b00000000 ; запись 0 в 5 разр. в РОН r16
OUT PORTA, r16 ; перенос из РОН в PORTA
RCALL Delay ; вызов метки задержки
RJMP Main ; возврат в начало главного цикла
.equ LowByte = 255 ; задаем переменные
.equ MedByte = 255
.equ HighByte = 4
Delay: ; начало метки задержки
LDI r16, LowByte ; записываем переменные в регистры ОН
LDI r17, MedByte
LDI r18, HighByte
loop: ; вызываем цикл
DEC r16 ; отнимаем единицу в r16
BRNE loop ; если не 0, возвращаемся к началу loop
DEC r17
BRNE loop
DEC r18
BRNE loop
RET ; возврат в место вызова метки Delay
Как можно понять из кода, задержка при мигании светодиодом в данном случае происходит из-за «подвисания» МК во время выполнения операций декрементирования DEC. Количество арифметических операций, для получения различимой глазом задержки должно быть велико, так как тактовая частота равна 1МГц. В данном случае это число равно 255 * 255 * 4 = 260100.
Вывод А5 был выбран потому, что именно к нему, как видно на рис. 1, подключен встроенный в плату светодиод.
Код программы:
#include <avr/io.h>
#include <util/delay.h> // подключили библиотеку задержки
int main(void)
{
DDRA |= (1<<DDRA5); // задали А5 на выход
while (1) // вечный цикл
{
PORTA |= (1<<PORTA5); // высокий логический уровень на А5
_delay_ms(500); // задержка 500 мс
PORTA &=~(1<<PORTA5); // низкий логический уровень на А5
_delay_ms(500); // задержка 500 мс
}
}
Преимущество данного кода заключается в его компактности, простоте написания и легкой читаемости, по сравнению с кодом на Assembler. С другой стороны, данный код не полностью описывает происходящие внутри МК процессы и, в некотором смысле, является менее информативным.
Код программы:
LDI r16, 0b00000000 ; устанавливаем все нули в РОН r16
OUT DDRB, r16 ; присваем регистру DDRB значение r16
LDI r16, 0b00000010 ; устанавливаем 1 во 1 разр. в РОН r16
OUT PUEB, r16 ; присваем регистру PUEB значение r16 (вкл. подтяжку)
LDI r16, 0b00100000 ; устанавливаем 1 во 5 разр. в РОН r16
OUT DDRA, r16 ; присваем регистру DDRA значение r16
Main: ; начало главного цикла
IN r16, PINB ; считываем значения с PINB в r16
SBRC r16, 1 ; если в 1 разр. в r16 единица
LDI r17, 0b00100000 ; присваеваем r17 единицу в 5 разр.
SBRC r16, 1 ; если в 1 разр. в r16 единица
OUT PORTA, r17 ; присваем регистру PORTA значение r17
SBRS r16, 1 ; если в 1 разр. в r16 ноль
LDI r17, 0b00000000 ; присваеваем r17 нули во всех разр.
SBRS r16, 1 ; сли в 1 разр. в r16 ноль
OUT PORTA, r17 ; присваем регистру PORTA значение r17
RJMP Main
Стоит отметить, что очень важной частью программы является включение подтяжки (Pull Up – PUE) для используемой кнопки. В противном случае данный вывод будет находиться в режиме Hi-Z.
Hi-Z - режим высокоимпендансного входа. Этот режим включен по умолчанию. Все вентили разомкнуты, а сопротивление порта очень велико. В принципе, по сравнению с другими режимами, можно его считать бесконечностью. То есть электрически вывод как бы вообще никуда не подключен и ни на что не влияет («висит в воздухе»). В этом случае напряжение будет на нем «скакать» в зависимости от внешних наводок и электромагнитных помех. Очень часто на порту в этом случае нестабильный синус 50Гц — наводка от сети 220В, а в регистре PIN будет меняться 0 и 1 с частотой около 50Гц
Код программы:
#include <avr/io.h>
int main(void)
{
DDRA |= (1<<DDRA5); // установили 1 в 5 разр. DDRA (на вых.)
DDRB &=~ (1<<DDRB1); // установили 0 в 1 разр. DDRB (на вх.)
PUEB |= (1<<PORTB1); // включили подтяжку на 1 кнопке (B1)
PORTB |= (1<<PORTB1);
while (1)
{
if (!(PINB & (1<<PINB1))) { // если кнопка не нажата
PORTA &=~ (1<<PORTA5); // на светодиоде низкий уровень
} else {
PORTA |= (1<<PORTA5); // на светодиоде высокий уровень
}
}
}
Данная программа по выполняемым функциям идентична программе из п.3, но стоит отметить наличие логических побитовых операций, отсутствующих ранее.
Результат работы программ представлен на рис. 2 и рис. 3.
Рисунок 2 – Вид платы (кнопка не нажата)
Рисунок 3 - Вид платы (кнопка нажата)
Код программы:
LDI r16, 0b00000000
OUT DDRB, r16
LDI r16, 0b00000010
OUT PUEB, r16
LDI r16, 0b00100000
OUT DDRA, r16
LDI r16, 0b00000000
OUT PORTA, r16
Main:
IN r16, PINB
SBRS r16, 1
RCALL Blink
SBRC r16, 1
LDI r17, 0b00100000
SBRC r16, 1
OUT PORTA, r17
RJMP Main
.equ LowByte = 255
.equ MedByte = 255
.equ HighByte = 4
Blink:
LDI r18, 0b00000000
OUT PORTA, r18
RCALL Delay
LDI r18, 0b00100000
OUT PORTA, r18
RCALL Delay
RET
Delay:
LDI r19, LowByte
LDI r20, MedByte
LDI r21, HighByte
loop:
DEC r19
BRNE loop
DEC r20
BRNE loop
DEC r21
BRNE loop
RET
По сути, данная программа является комбинацией программ из п. 1 и п. 3, и результат её выполнения заключается в мигании светодиода при нажатой кнопке на плате, и отключение светодиода при её отпускании.
Выводы: Микроконтроллер является сложным, по своей структуре, устройством. Написание даже простых программ для такого устройства требует знания и понимания происходящих внутри процессов. Полезным, в процессе написания алгоритмов, является умение использовать разные подходы к реализации поставленных задач, в частности, выбор языка программирования.
Оба, из использованных языков, имеют свои преимущества и недостатки. Так, в Ассемблере, мы имеем наиболее близкую к машинному языку логику, что позволяет максимально оптимизировать код, но с другой стороны отнимает больше времени и требует больше усилий со стороны разработчика. В Си, в свою очередь, гораздо более простой синтаксис, но так как он является высокоуровневым языком, мы в меньшей степени понимаем логику происходящего внутри МК, что тоже, в некоторых случаях, может быть недостатком.