МИНОБРНАУКИ РОССИИ Санкт-Петербургский государственный электротехнический университет «ЛЭТИ» им. В.И. Ульянова (Ленина)
Кафедра биотехнических систем и технологий
ОТЧЁТ
по индивидуальному домашнему заданию №1
по дисциплине «Медицинские микропроцессорные системы»
Студенты гр. 2503 |
|
Кузнецов Д.А. |
Преподаватель |
|
Алексеев Б.Э. |
Санкт-Петербург
2025
ИНДИВИДУАЛЬНОЕ ДОМАШНЕЕ ЗАДАНИЕ №1.
Введение.
Цель работы: Исследование принципов работы микроконтроллера ATtiny104 на базе отладочной платы “ATtiny104 - Xplained Nano”. Получение начальных навыков программирования МК на языке Assembler и C, пользования средой разработки AVR Atmel Studio 7.
Используемые инструменты: Отладочная плата ATtiny104 - Xplained Nano; интегрированная среда разработки AVR Atmel Studio 7; методические указания к выполнению работы.
Atmel ATtiny104 - это CMOS-8-разрядный микроконтроллер с низким энергопотреблением, основанный на усовершенствованной RISC-архитектуре AVR. Выполняя мощные инструкции за один такт, ATtiny104 достигают производительности, близкой к 1 миллисекунде на МГц. Это позволяет разработчику системы оптимизировать энергопотребление устройства в зависимости от скорости обработки.
Прошивку для микроконтроллера можно писать в различных средах разработки, одной из которых является AVR Atmel Studio 7. В этой среде разработки код для прошивки может быть скомпилирован из кода, написанного либо на языке Ассемблера, либо на языке С. В данной работе будут разобраны оба варианта как примеры работы с микроконтроллером.
Теоретические сведения.
Рисунок 1 – Структура памяти МК.
Память программ предназначена для хранения команд, управляющих функционированием микроконтроллера. Память программ также часто используется для хранения таблиц констант, не меняющихся во время работы программы.
Регистры ввода-вывода отвечают за управление МК и его периферией. Основные регистры В/В для управления пинами МК:
Регистр DDRx (Data Direction Register) - порт направления данных, определяет будет порт работать на вход данных или на выход. (1 - выход, 0 - вход)
Регистр PORTx - определяет текущее значение порта, если он работает на выход, то есть подаётся на выход напряжение (1) или нет (0).
Регистр PUEx (pull-up enabled) - определяет включена ли подтяжка через резистор на порте (на рисунке был ключ К1) (1 - включен, 0 - выключен).
Регистр PINx - чисто входной, позволяет считывать напряжение на входе (в нём лежит 1, если есть напряжение и 0, если нет)
Рисунок 2 – Расположение стека в памяти МК.
Стек представляет собой область памяти, которую микроконтроллерное ядро использует для сохранения и восстановления адресов возврата и подпрограмм прерываний. Практически у всех МК AVR стек размещается в оперативной памяти.
Схема устройства каждого порта ввода-вывода упрощённо выглядит так:
Рисунок 3 – Схема порта ввода-вывода.
На входе стоит небольшая защита из диодов, которая призвана защитить вход микроконтроллера от превышения напряжения питания. Если напряжение на входе будет выше напряжения питания, то верхний диод откроется, и это напряжение уйдёт на шину питания, где с ним будет бороться источник питания и его фильтры. Если на вход попадет отрицательное напряжение (ниже уровня земли), то оно будет нейтрализовано через нижний диод и уйдет на землю. Управляющая логика, определяющая конфигурацию порта (направление передачи данных) при этом изображена в виде простейших переключателей.
Режимы работы портов:
Таблица 1. Режимы работы портов.
Отчёт по практической работе.
Создать проект на языке Assembler, скопировать приведённый ниже код примера (приложение 1), проверить его работоспособность.
В ходе работы программы в консоль была выдана следующая строка.
Рисунок 4 – Сообщение в консоли.
Это сообщение говорит о том, что код был успешно скомпилирован, а в ходе его работы не было вызвано ошибок. Если смотреть по значениям регистров, то получаем следующие данные:
Таблица 2. Регистры действующей программы.
Регистры |
После строки подачи 1 |
После строки подачи 0 |
R16 |
|
|
Как видно из таблицы выше, на регистр действительно подаётся значения, соответствующие уровню HIGH на порте A5 и уровню LOW на всех портах, а значит программа работает правильно.
В коде на Assembler'e изменить основной цикл так, чтобы в нём не содержалось инструкций LDI. Для этого можно использовать инструкции SBI/CBI или SBR/CBR.
Проведя аналогичные заданию 1 действия, можно убедиться, что код действительно работает, а значит задание выполнено правильно.
В коде на Assembler'e изменить подпрограмму задержки так, чтобы для создания секундной задержки было достаточно использования только 2 РОН.
Полученный
код представлен в приложении 3. Логика
базируется на том, что вместо добавления
3-го цикла, использующего 3-й регистр
общего назначения, можно второй раз
вызвать исходный цикл, таким образом
количество операций составит
операций, как и до модифицирования кода,
а значит задание выполнено правильно.
Создать проект на языке C, скопировать приведённый ниже код примера, проверить его работоспособность.
В ходе работы программы в консоль была выдана следующая строка.
Рисунок 5 – Сообщение в консоли.
Это сообщение говорит о том, что код был успешно скомпилирован, а в ходе его работы не было вызвано ошибок, а значит программа работает.
В коде на С настроить ножку PB1 в режим работы input pull-up. В бесконечном цикле, кроме мигания диодом, опрашивать состояние ножки. При нажатой кнопке светодиод должен всегда гореть, не мигая. Время реакции на нажатие в пределах периода мигания допустимо. Для выполнения задания потребуется использовать регистры PUEB и PINB, а также условный оператор if ().
Для выполнения этого задания, исходный код был переписан. Если следовать логике кода, то в начале цикла основной программы опрашивается кнопка на ножке PB1. Если кнопка не нажата, происходит один цикл мигания, иначе кнопка просто загорается и горит, пока кнопка нажата.
Приложение 1.
Код на языке Assembler.
// Инициализация периферии
SBR r16, (1 << ddra5) // Устанавливаем бит во временном регистре памяти
OUT DDRA, R16 // Загружаем данные из временного рег. в рег. Направления данных
EOR r16, r16 // XOR r16, т.е. обнуляем r16
// Основной цикл программы
Main:
SBR r16, (1 << PORTA5)
OUT PORTA, r16 // Аналогично грузим в рег. выхода высокий выход
RCALL Delay // Вызываем подпрограмму Delay дважды
RCALL Delay
CBR r16, -1
OUT PORTA, r16 // Аналогично грузим в рег. выхода низкий выход
RCALL Delay // Вызываем подпрограмму Delay дважды
RCALL Delay
RJMP Main // Возвращаемся к метке Main
// Подпрограмма задержки
.equ LowByte = 255 // Говорим заменять все LowByte на 255
.equ MedByte = 255 // Говорим заменять все MedByte на 255
Delay: // Начало подпрограммы Delay
LDI R16, LowByte // Загружаем во временные регистры соответствующие
LDI R17, MedByte // значения, вместо названий автоматически подст. цифры
loop: // Метка Loop, к которой возвращаемся
DEC R16 // Уменьшаем R16
BRNE loop // Если он не 0, возвращаемся к loop
DEC R17 // Уменьшаем R17
BRNE loop // Если он не 0, возвращаемся к loop
RET // Если оба регистра = 0, возвращаемся из подпрограммы
Приложение 2.
Код программы на языке C.
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRA |= (1 << DDRA5); // Говорим что пин A5 работает на выход
DDRB &= ~(1 << DDRB1); // Говорим что пин B1 работает на вход
PUEB |= (1 << PUEB1); // Включаем подтяжку для пина B1
while (1) // Бесконечный цикл
{
if (PINB & (1 << PINB1)) { // Если кнопка нажата, моргаем
PORTA |= (1 << PORTA5); // Светодиод включили
_delay_ms(500); // Ожидание 500 мс
PORTA &= ~(1 << PORTA5); // Светодиод включили
_delay_ms(500); // Ожидание 500 мс
}
else { // Если кнопка не нажата
PORTA &= ~(1 << PORTA5); // Светодиод выключили
}
}
}
