Ввод и вывод данных. Работа со встраиваемыми модулями МК, устройства взаимодействия с пользователем.
Практическое занятие №5
Цель работы
Целью практического занятия является получение практических навыков работы со средой моделирования и разработки микроконтроллерных модулей цифровых платформ.
Теоретическая часть
Модули вывода
В данной работе для вывода информации пользователю используются LCD дисплеи c интерфейсом I²C.
I²C
I²C - двухпроводная шина связи электронных устройств. Состоит из линии тактирующего сигнала SCL (сокращение от Serial Clock) и SDA (Serial Data). Устройства, соединённые шиной I²C, используют иерархию Ведущий-Ведомые. Шина I²C позволяет подключить сразу несколько ведомых устройств к микроконтроллеру, задействуя всего два пина: дисплеи, датчики, контроллеры питания.
Ведущее устройство управляет общением всех устройств на шине, сканирует доступные устройства и опрашивает их по необходимости. Без разрешения ведущего устройства ведомые не могут использовать шину I²C.
Ведомые устройства исполняют команды ведущего устройства, отвечают в установленные интервалы в соответствие с тактированием.
Тактирующие сигналы SCL позволяют синхронизировать работу ведущего и ведомых устройств, чтобы переключения логических уровней происходили одновременно. Сигнальная шина SDA по умолчанию установлена в 1 (используется подтягивающий резистор к питанию). Для "общения" устройства замыкают сигнальную шину на змелю, тем самым переключая логическую 1 в 0. Именно поэтому общение сразу двух ведомых устройств одновременно невозможно - их сигналы смешаются.
Передача информации:
Служебные команды (Start, Stop) производятся путем изменения линии SDA при высоком уровне линии тактов (SCL). Данные же передаются установкой линии SDA при низком уровне линии SCL * Начало передачи начинается с установки состояния Start(высокий уровень SCL, провал уровня SDA). Передача заканчивается с установлением состояния Stop(высокий уровень SCL, установление высокого уровня SDA) * Передача данных Ведущее(Master)->Ведомое(Slave): Ведущий генерирует тактирующий сигнал на SCL и передает бит по SDA. Как только уровень SCL становится высоким - ведомый считывает данные. Ведомый имеет * Передача данных Ведомое(Slave)->Ведущее(Master): Ведущий генерирует тактирующий сигнал на SCL. При SCL == 0 ведомое устройство устанавливает необходимый бит на SDA. При SCL == 1 ведущее устройства считывает данные с SDA.
Преимущества:
- На одной двупроводной линии может находиться до 127 устройств.
- За счет наличия ведущего устройства и тактирующих сигналов протокол передачи данных не зависит от времени.
- Может быть несколько ведущих устройств
- Простая расширяемость
Недостатки:
- Низкая тактовая частота
- Возможные коллизии при неправильно спроектированной программе и наличии нескольких ведущих устройствах

Рисунок 1 – Схема работы I2C протокола
Важно!
Порты ввода-вывода Arduino используют напряжение 5 В. Логические уровни у множества подключаемых ведомых устройств равны 3.3 В. В таком случае необходимо либо устанавливать преобразователь TTL уровней, либо использовать устройства с 5V tolerant пинами. Многие микросхемы, не смотря на питание от 3,3 В, могут использовать 5 В на сигнальных линиях.
I²C Arduino
Arduino имеет специальные контакты для I²C, на которые установлены подтягивающие резисторы в соответствии с требованиями протокола I²C.
Для плат Arduino Uno это контакты A4 и A5.
Пин A4 соответсвует линии SDA. Пин A5 соответсвует линии SCL.
В версии Arduino Uno R3 есть еще один набор контактов I2C рядом с USB-разъемом:

Рисунок 2 – Пины i2c на Arduino
Дисплей LCD 20x4
Данный дисплей представляет собой экран с 4 рядами по 20 символов

Рисунок 3 – Дисплей LCD20x4
На дисплей могут быть выведены как стандартные символы, так и рисунки.
Базовый вывод на дисплей
Создадим проект со следуюещй схемой подключения:

Рисунок 4 – Схема подключения дисплея
Код основной программы:
#include <LiquidCrystal_I2C.h>
// Параметры подключаемого дисплея
#define I2C_ADDR 0x27 ///< Адрес устройства(дисплей)
#define LCD_COLUMNS 20 ///< Количество столбцов в дисплее
#define LCD_LINES 4 ///< Количетсво строк в дисплее
//Создадим объект класса, описывающего дисплей
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
void setup()
{
lcd.init(); // Инициализация дисплея
lcd.backlight(); // Включаем подсветку
// Устанавливаем курсор и выводим текст на дисплей
// setCursor(column index, line index)
lcd.setCursor(4, 0);
lcd.print("Hello, world!");
lcd.setCursor(2, 3);
lcd.print("Enjoy!");
}
void loop()
{
uint8_t i; // переменная-счетчик для цикла
lcd.clear(); // Очищаем дисплей от предыдущего вывода
for (i = 0; i < LCD_COLUMNS; i++)
{// Нарисуем полосы во всю длину дисплея
lcd.setCursor(i,0);
lcd.print("=");
lcd.setCursor(i,2);
lcd.print("=");
}
lcd.setCursor(9,1);
lcd.print(millis()/1000); // Вывод счетчика времени
delay(1000); // Дисплей обновляется раз в 1000мс
}
Код листа описания diagram.json:
{
"version": 1,
"author": "Uri Shaked",
"editor": "wokwi",
"parts": [
{ "type": "wokwi-arduino-uno", "id": "uno", "top": 20, "left": 20, "attrs": {} },
{ "type": "wokwi-lcd2004", "id": "lcd", "top": 246, "left": 50, "attrs": { "pins": "i2c" } }
],
"connections": [
[ "uno:GND.2", "lcd:GND", "black", [ "v14", "*", "h-20" ] ],
[ "uno:5V", "lcd:VCC", "red", [ "v18", "*", "h-16" ] ],
[ "uno:A4", "lcd:SDA", "green", [ "v22", "*", "h-12" ] ],
[ "uno:A5", "lcd:SCL", "blue", [ "v26", "*", "h-8" ] ]
],
"dependencies": {}
}
Код листа использованных библиотек libraries.txt
# Wokwi Library List
# See https://docs.wokwi.com/guides/libraries
# Automatically added based on includes:
LiquidCrystal I2C
Модули ввода
В данной работе модулем ввода будет служить циферная клавиатура (Keypad)
Cхема представлена ниже:

Рисунок 5 – Схема проекта
Основная логика работы модуля KeyPad:
- Когда кнопки не нажаты, все контакты столбцов удерживаются на высоком уровне, а все контакты строк удерживаются на низком уровне:

Рисунок 6 – Состояния с отжатыми кнопками
- При нажатии кнопки уровень вывода столбца, на котором находится кнопка, становится "0".

Рисунок 7 – Произошло нажатие кнопки
- Arduino теперь знает, в каком столбце находится кнопка. Теперь нужно найти строку. Для этого происходит переключениие каждого из выводов строк на высокий уровень "1", при этом происходит чтение вывода найденного на шаге 2 столбца. Если вывод столбца вновь станет высоким "1", то соответсвующая кнопке строка в процессе перебора была установлена в "1". Следовательно строка найдена.

Рисунок 8 – Перебор строк
#include <Keypad.h>
static const uint8_t ROWS = 4;
static const uint8_t COLS = 4;
static char keys[ROWS][COLS] = { ///< Матрица символов KeyPad'a
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
static uint8_t colPins[COLS] = { 5, 4, 3, 2 }; //< Arduino pin's соотвествующие выводам столбцов KeyPad'a
static uint8_t rowPins[ROWS] = { 9, 8, 7, 6 }; ///< Arduino pin's соотвествующие выводам строк KeyPad'a
/*
Соответствие подключенных пинов Arduino и Keypad
| Keypad pin | Arduino Pin |
|------------|-------------|
| R0 | 2 |
| R1 | 3 |
| R2 | 4 |
| R3 | 5 |
| C0 | 6 |
| C1 | 7 |
| C2 | 8 |
| C3 | 9 |
*/
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); //Объекта класса, описывающего keypad
void setup()
{
Serial.begin(9600); // Открытие последовательного порта на скорости 9600 бод
}
void loop()
{
char key = keypad.getKey(); //Опрос состояния клавиатуры
if (key != NO_KEY)
{ // Если какая-либо клавиша была нажата
Serial.println(key); // Вывод нажатой клавиши в последовательный порт
}
}
Код листа описания diagram.json:
{
"version": 1,
"author": "Uri Shaked",
"editor": "wokwi",
"parts": [
{ "type": "wokwi-arduino-uno", "id": "uno", "top": 402.94, "left": 25, "attrs": {} },
{
"type": "wokwi-membrane-keypad",
"id": "keypad",
"top": -11.91,
"left": 112.06,
"attrs": {}
}
],
"connections": [
[ "uno:2", "keypad:C4", "brown", [] ],
[ "uno:3", "keypad:C3", "gray", [] ],
[ "uno:4", "keypad:C2", "orange", [] ],
[ "uno:5", "keypad:C1", "pink", [] ],
[ "uno:6", "keypad:R4", "blue", [] ],
[ "uno:7", "keypad:R3", "green", [] ],
[ "uno:8", "keypad:R2", "purple", [ "v-14", "h0" ] ],
[ "uno:9", "keypad:R1", "gold", [ "v-18", "h0" ] ]
],
"dependencies": {}
}
Код листа использованных библиотек libraries.txt
# Wokwi Library List
# See https://docs.wokwi.com/guides/libraries
# Automatically added based on includes:
Keypad
Индивидуальное задание
Разработать программный код, при котором микроконтроллерный модуль должен опрашивать подключаемый датчик с периодичностью 10 секунд; информация о проведенном опросе (результат измерения и его номер) должны выводиться на подключенный модуль сегментного дисплея.
Вариант выполнения задания определяется остатком от деления номера студента внутри списка группы на 3.
| Вариант | 0 | 1 | 2 |
|---|---|---|---|
| Датчик | HC-SR04 | LDR | RTC |
Требования к оформлению отчёта
Отчет оформляется в виде файла в формате Microsoft Word. Скриншоты добавляются в файл отчета с необходимыми пояснениями.
Требования к оформлению отчета в формате Microsoft Word: * пользуйтесь правилами оформления лабораторных работ; * при написании кода используйте стили программирования (делайте код удобным для прочтения).
Требования к наименованию файлов отчета: * файл отчета именуется «ЛабХХФамилияИООтчет.doc», где ХХ – двухзначный номер лабораторной работы, например, 02; ФамилияИО – фамилия и инициалы студента, например, ИвановИИ. Пример наименования файла отчета для третьей лабораторной работы выполненной Ивановым И.И. – «Лаб03ИвановИИ.doc»; * дополнительные файлы именуются аналогичным образом, но с указанием, что это на отчет, приложение к нему с добавлением номера приложения «ЛабХХФамилияИОПриложениеYY.doc», где YY – двухзначный номер приложения. Например, для второго приложения наименование, будет следующее «Лаб03ИвановИИПриложение02.doc»; Все отчетные материалы загружаются в домашние работы ОРИОКС.
Список рекомендуемой литературы
- https://wokwi.com
- https://docs.wokwi.com/parts/wokwi-arduino-uno
- https://docs.wokwi.com/parts/wokwi-lcd2004
- https://docs.wokwi.com/parts/wokwi-membrane-keypad