Министерство науки и высшего образования Российской Федерации ФГАОУ ВО «Севастопольский Государственный Университет» Кафедра «Инновационные телекоммуникационные технологии» ЛАБОРАТОРНАЯ РАБОТА №4 «Последовательный интерфейс I2C(TWI)» По дисциплине «Интерфейсы радиоэлектронных систем» Выполнил: ст.гр. Р/б-21-1-о Волконский И. И. Вариант 3 Проверил: Лукьянчиков А.В. Севастополь 2023 1. РАБОТА 1.1. Цель работы Изучить последовательный интерфейс I2C на примере передачи данных на жидкокристаллический индикатор 1602. 1.2. Задание — В качестве шаблона программы следует использовать файлзаготовку I2C_example.c, в котором содержаться объявления констант и массив команд инициализации ЖКИ. — Пользуясь примерами в методических указаниях, создать в программе функции: init_i2c(); start_i2c(); sendAddr_i2c(); sendByte_i2c(); stop_i2c(); send_lcd(); init_lcd(). — Создать функцию выбора положения символа на экране вида setCursor(char x, char y) , где x = (1…16) — аргумент, задающий положение в строке; y = (1…2) — аргумент, задающий номер строки. После создания функций повторить пример вывода символов, показанный на рис. 1.1; загрузить полученную программу в макет. — Написать и загрузить в макет программу, реализующую вывод строк на дисплей. Вывести свои имя и фамилию латиницей в разных строках. — Создать функцию, производящую очистку дисплея и перевод курсора в начало первой строки. — Написать и загрузить в макет программу, обеспечивающую циклический сдвиг символов влево. Рис. 1.1 — Пример программы, выводящей несколько символов в середине первой строки 1.3. Теоретические сведения Интерфейс I2C (IIC, от англ. Inter-Integrated Circuit — «между интегральными схемами») — это последовательный синхронных интерфейс для связи между интегральными схемами (микросхемами). Применяется в радиоэлектронных системах для соединения периферийных устройств с процессорами и микроконтроллерами. Для передачи данных используются две двунаправленные линии связи: линия тактирования SCL (Serial CLock), и линия передачи данных SDA (Serial DAta), которые представляют собой шину данных, причем на одну шину можно подключать несколько различных устройств. Линии SDA и SCL двунаправленные: устройства, подключаемые к шине должны иметь выводы, перенастраиваемые для работы на вход и на выход. Обе линии интерфейса подсоединены к источнику питания через подтягивающие резисторы. Когда шина свободна, обе линии находятся в высоком логическом состоянии (лог.1). Перевести линию в низкое логическое состояние (лог.0) может любое из подключенных устройств. Таким образом, в интерфейсе реализуется так называемая схема монтажного И — линия интерфейса будет находиться в лог.1 только если все подключенные к ней устройства формируют на своем выходе лог.1. Устройства, подключенные к шине, делятся на ведущего (Master) и ведомого (Slave). Ведущий инициализирует процесс передачи данных и выдает тактовые импульсы на линию SCL, ведомый принимает команды/данные, а также выдает данные по запросу ведущего. В интерфейсе предусмотрена программная адресация устройств, подключенных к шине. Наиболее распространена длина адреса — 7 битов. Каждое устройство имеет свой уникальный адрес, который заложен производителем и указан в технической документации. Адрес устройства может быть как фиксированным, так и аппаратно изменяемым. Для изменения адреса устройство снабжается дополнительными входами: в зависимости от уровня напряжения на этих входах (высокое или низкое), можно получить различные адреса. Обычно количество входов варьируется от одного до трёх. Аппаратная настройка адреса предусмотрена для возможности подключения нескольких однотипных устройств на одну шину. Максимальное количество устройств, объединенных интерфейсом I2C, ограничивается размером адресного пространства, а также суммарной емкостью шины, которая не должна превышать значения 400 пФ. Жидкокристаллические индикаторы (ЖКИ) 1602 широко применяются в радиоэлектронных системах для отображения текстовой и числовой (символьной) информации. В установленном в лабораторном макете ЖКИ 1602 символы сгруппированы в две строки по 16 символов. Управление дисплеем осуществляется контроллером HD44780. Для работы с дисплеем через интерфейс I2C используется микросхема расширителя портов PCF8574. 2. ВЫПОЛНЕНИЕ РАБОТЫ 2.1. Содержимое файла i2c.c #include "main.h" /*Здесь define потому что в h файле их почему-то не видно*/ // Коды состояний шины #define F_CPU 16000000L #define F_SCL 100000L #define START 0x08 #define REP_START 0x10 #define SLA_W_ACK 0x18 #define SLA_W_NACK 0x20 #define DATA_W_ACK 0x28 #define DATA_W_NACK 0x30 #define STATUS (TWSR & 0xF8) #include "i2c.h" void I2C_Init() { TWSR=0; TWBR=((F_CPU/F_SCL)-16)/2; } //В идеале всегда должно возвращать 1 - это означает состояние СТАРТ //Если вывело 0, то что-то пошло не так char I2C_Start() { TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); } //Результатом этой функции является 1, если раб по адресу существует, и 0 - если нет char I2C_Send_Adress(unsigned char adr, unsigned char direct) { TWDR=(adr<<1)|direct; TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); return (STATUS==SLA_W_ACK); } //Отправка одного байта. Если получено 1 - байт принят char I2C_Send_Single(unsigned char data) { TWDR=data; TWCR=(1<<TWINT)|(1<<TWEN); while(!(TWCR&(1<<TWINT))); return (STATUS==DATA_W_ACK); } //Эта функция устанавливает устройству состоянию СТОП void I2C_Stop() { TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO); } 2.2 Содержимое файла i2c.h #ifndef I2C_H #define I2C_H #include "i2c.c" void I2C_Init(); char I2C_Start(); char I2C_Send_Adress(unsigned char address, unsigned char direction); char I2C_Send_Single(unsigned char data); void I2C_Stop(); #endif 2.3. Содержимое файла lcd.c #include "main.h" #define PCF8574_adr 0x27 #define CMD 0 #define DTA 1 #define LCD_RS 0 #define LCD_RW 1 #define LCD_E 2 #define LCD_BL 3 #define LCD_D5 5 #define LCD_ON 0x0C #define LCD_RETURN 0x02 #include "lcd.h" /* ФУНКЦИИ ДЛЯ ДИСПЛЕЯ РЕАЛИЗОВАНЫ ДЛЯ ИНТЕРФЕЙСА I2C */ char lcd_init_commands[] = {0x20,0x28,0x28,0x08,0x01,0x06,0x0c}; //Отправка команды в дисплей. mode 0 - отправить команду. mode 1 отправить байт данных void LCD_Send(char value, char mode) { char LCD; LCD=(value & 0xf0)|(mode<<LCD_RS)|(1<<LCD_E)|(1<<LCD_BL); I2C_Send_Single(LCD); LCD&=~(1<<LCD_E); I2C_Send_Single(LCD); LCD=(value<<4)|(mode<<LCD_RS)|(1<<LCD_E)|(1<<LCD_BL); I2C_Send_Single(LCD); LCD&=~(1<<LCD_E); I2C_Send_Single(LCD); } char LCD_Init() { for(int i=0;i<7;i++){ LCD_Send(lcd_init_commands[i],CMD); _delay_ms(40);} } 2.4. Содержимое файла LCD.h #ifndef LCD_H #define LCD_H #include "lcd.c" void LCD_Send(char value, char mode); char LCD_Init(); #endif 2.5 Содержимое файла main.h #ifndef MAIN_H #define MAIN_H //Ѕиблиотека где объ¤влены красивые имена дл¤ регистров и их битов #include <avr/io.h> //Ѕиблиотека дл¤ функции задержки #include <util/delay.h> //Ѕиблиотека дл¤ красивого объ¤влени¤ переменных #include <stdint.h> //Ѕиблиотека ввода-вывода #include <stdio.h> /* ѕодключаем все остальные библиотеки */ #include "i2c.h" #include "lcd.h" #endif 2.6 Код main.c для задания 1 #include "main.h" void main(void){ I2C_Init(); I2C_Start(); I2C_Send_Adress(PCF8574_adr,0x00); LCD_Init(); LCD_Send(0x85,CMD); LCD_Send('R',DTA); LCD_Send(0x86,CMD); LCD_Send('=',DTA); LCD_Send(0x87,CMD); LCD_Send('U',DTA); LCD_Send(0x88,CMD); LCD_Send('/',DTA); LCD_Send(0x89,CMD); LCD_Send('I',DTA); I2C_Stop(); } 2.7. Код для задания 2 Продемонстрируем реализацию функции выбора адреса ячейки на LCD отдельно: void LCD_SetCursor(char x, char y) { /* Защита пользователя от самого себя */ //1 <= x < =16. Индексы от 0 до 15 if(x>16) x=16; if(x<1) x=1; x--; //1 <= y <= 2. Индексы от 0 до 1 if(y=>2) y=0xC0; if(y<=1) y=0x80; /* Превращение двух чисел в координату */ //В 16-м виде 1-я строка имеет цифру 8, 2-я C (12) //Колонки от 0 до F //Любое число хранится в двоичном виде, запись через 0b или 0x нужно просто для удобства восприятия int Cord = x+y; LCD_Send(Cord; CMD) } Содержимое main.c void main(void){ I2C_Init(); I2C_Start(); I2C_Send_Adress(PCF8574_adr,0x00); LCD_Init(); LCD_SetCursor(6,1); LCD_Send('R',DTA); LCD_SetCursor(7,1); LCD_Send('=',DTA); LCD_SetCursor(8,1); LCD_Send('U',DTA); LCD_SetCursor(9,1); LCD_Send('/',DTA); LCD_SetCursor(10,1); LCD_Send('I',DTA); I2C_Stop(); } 2.8. Код для задания 3 Продемонстрируем реализацию функции вывода строк на LCD отдельно: void LCD_send_string(*char data, char row) { //Ширина строки 16 позиций (0…15) char Limit = 0; for(char* char_ptr = data; *char_ptr != '\0'; ++char_ptr) { LCD_SetCursor(Limit, row); LCD_Send(*char_ptr, DTA); Limit++; if(Limit >= 16) break; } } Содержимое main.c void main(void){ I2C_Init(); I2C_Start(); I2C_Send_Adress(PCF8574_adr,0x00); LCD_Init(); LCD_send_string("Vladimir ",1); LCD_send_string("Saltanovsky ",2); I2C_Stop(); } 2.9. Код для задания 4 Продемонстрируем реализацию функции очистки содержимого LCD дисплея void LCD_Clear() { for(char x=1; x<=16; x++) { for(char y=1; y<=2; y++) { LCD_SetCursor(x,y); LCD_Send(' ',DTA); } } LCD_SetCursor(1,1); } 2.10. Код для задания 5 Продемонстрируем реализацию функции сдвига содержимого экрана void LCD_Shift_Left() { //Для этого есть отдельная команда //0001 S/C R/L - LCD_Send(0b00010100, CMD); } Содержимое main.c void main(void){ I2C_Init(); I2C_Start(); I2C_Send_Adress(PCF8574_adr,0x00); LCD_Init(); LCD_send_string("Vladimir ",1); LCD_send_string("Saltanovsky ",2); while(1) { LCD_Shift_Left(); _delay_ms(1000); } } ВЫВОДЫ В данной лабораторной работе был изучен последовательный интерфейс I2C на примере передачи данных на жидкокристаллический индикатор 1602.