Продолжаем работу с модулем MSSP, а именно с его возможностью передачи данных по шине I2C.
На прошлом занятии мы подключили дисплей 20×4 на контроллере HD44780 посредством 4-разрядной передачи данных.
Сегодня же мы попробуем аналогичный режим передачи данных на такой дисплей организовать с использованием специального переходника, который преобразует последовательный код I2C в параллельный и который предназначен специально для работы с подобными дисплеями с использованием 4-битного режима.
Выглядит данный переходник вот так и его разъём полностью совместим по ножкам с разъёмом дисплея
У него четыре ножки для соединения с МК — это питание, общий провод и две ножки I2C.
Также мы видим, что к нему подпаяна гребёнка из 16 контактов для соединения с соответствующими контактами модуля дисплея. Так как контакты на переходнике типа «ПАПА», то на модуль дисплея я, соответственно, подпаял гребёнку с контактами типа «МАМА» ну и посредством этих двух контактных площадок я его с модулем и соединил.
Купить данный дисплей можно много где. В описании видеоверсии под роликом находится ссылка, где его приобретал я. Только там, где я приобретал, один было купить невозможно, минимум три, но цена была такова, что я с удовольствием их купил. Сейчас, по-моему, продавец уже предлагает минимальную партию из 5 штук, но цену вроде ещё убавил. Поэтому есть смысл. Я не думаю, что у кого-то, кто постоянно занимается с контроллерами. в наличии есть только один такой дисплей.
Ну что ж, теперь рассмотрим, из чего состоит собственно данный переходник. Там, соответственно, есть регулятор контрастности, поэтому теперь нам не нужно будет заботиться о его отдельном подключении. А сердцем данного переходника служит микросхема PCF8574, преобразующая последовательный код I2C в 8-битные логические состояния на выходе.
Вот, собственно, схема данного переходника (нажмите на картинку для увеличения изображения)
Здесь прекрасно видно подключение микросхемы. У нее существуют три адресных контакта, которые способны изменить адрес устройства для I2C также, как это было в случае микросхемы EEPROM, рассмотренной в предыдущих частях нашего занятия. Соответственно, существуют контакты питания VCC и GND, а также контакты шины I2C — SDA и SCL, ну и, конечно же, восемь контактов логических состояний, управляемых данной микросхемой. Только подключен будет модуль дисплея. как мы видим посредством 4-битного подключения. Это сделано потому, что контактов на шине микросхемы всего 8, а нам нужно ещё оперировать с помощью них управляющими ножками модуля дисплея (RS, RW и E, который здесь назван CS), а также включением подсветки (контакт P3). Подсветка, соответственно, в целях защиты от больших токов контакта параллельного порта микросхемы управляется с помощью ключевого транзистора 8050. Также установлен светодиод через токоограничивающий резистор, сигнализирующий нам о подаче питания на преобразователь, ну и. как уже было выше сказано, регулятор контрастности. Ещё существуют два подтягивающих резистора по 4,7 килоом, подключенные к ножкам шины I2C, назначение которых мы прекрасно знаем.
Ну вот, собственно, и всё насчёт схематического решения переходника.
Теперь немного по техническим характеристикам самой микросхемы PCF8574.
Питание микросхемы осуществляется постоянным напряжением в диапазоне от 2,5 до 7 вольт, так что бояться нам нечего.
Токи по портам входным — максимально 20 милиампер, по выходным — 25.
Также ещё немаловажным для нас является параметр максимальной скорости по I2C — он не должен превышать 100 кГц.
Нам, конечно, спешить некуда, дисплей всё-таки символьный, но если мы захотим на шину I2C повесить ещё что-то и захотим от этого чего-то большего быстродействия, то мы обязаны об этом помнить.
Также посмотрим адрес, по которому мы будем, собственно, с ней общаться по I2C. В даташите их два в зависимости от типа микросхемы. Нам нужен вот этот
Так как мы в данный переходник данные будем только отправлять, то давайте посмотрим диаграмму протокола общения с ним по шине I2C для записи байтов (нажмите на картинку для увеличения изображения)
Запись здесь идёт стандартно. Просто у нас не память EEPROM и никаких ячеек адресных у нас в микросхеме нет. передаём мы сразу данные. Данные можно передавать как по несколько байт. так и по одному. Как только мы передадим условие СТОП, на этом и заканчивается передача. И передавать мы будем только по одному байту.
Скорее всего, вам пока не всё понятно. Это мы исправим, когда будем писать наш код.
Переходим тогда постепенно к нашему проекту, который сделан из проекта урока 19 AT24C и имя мы ему присвоим I2C_LCD80.
Откроем проект в MPLAB.X, сделаем его главным, зайдём в свойства проекта и отключим питание от программатора, так как питать мы будем нашу схему от внешнего блока питания. Скорее всего для того, чтобы настройки применились, придётся перезагрузить среду программирования.
Также удалим файлы led.c и led.h из дерева проекта, а затем и физически, если они не удалятся сами.
Затем откроем файл main.c и удалим подключение данной библиотеки
#include "led.h"
Функционал I2C мы разместим в отдельной библиотеки, поэтому создадим два файла i2c.h и i2c.c следующего содержания:
i2c.h:
1 2 3 4 5 6 7 8 |
#ifndef I2C_H #define I2C_H //-------------------------------------------------------------- #include <xc.h> //-------------------------------------------------------------- #define _XTAL_FREQ 4000000 //-------------------------------------------------------------- #endif /* I2C_H */ |
i2c.c:
1 2 |
#include "i2c.h" //------------------------------------------------ |
Перейдём в файл main.c и подключим также данную библиотеку и там
1 2 |
#include "main.h" #include "i2c.h" |
Добавим глобальный символьный массив для строки
1 2 3 4 |
#include "i2c.h" //------------------------------------------------ char str01[30]={'\0'}; //------------------------------------------------ |
Удалим весь код из файла после данного объявления до функции main() вместе со всеми глобальными переменными. макросами, а также функциями с их телами.
В функции main() оставим локальную переменную, инициализацию ножек портов, далее до бесконечного цикла весь код удалим, в бесконечном цикле задержку также удалим. После всего этого функция примет вот такой вид
1 2 3 4 5 6 7 8 9 10 11 |
void main() { int i; TRISB = 0x00; PORTB = 0xFF; TRISA = 0x00; PORTA = 0x00; while(1) { } } |
Перейдём в файл i2c.h и добавим там макросы для ножек
1 2 3 4 5 6 7 8 |
#define _XTAL_FREQ 4000000 //-------------------------------------------------------------- // Define i2c pins #define SDA RC4 // Data pin for i2c #define SCK RC3 // Clock pin for i2c #define SDA_DIR TRISC4 // Data pin direction #define SCK_DIR TRISC3 // Clock pin direction //-------------------------------------------------------------- |
Перейдём в файл i2c.c и добавим функцию инициализации шины
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//------------------------------------------------ void InitI2C(void) { TRISC=0x18;//RC3 and RC4 Input (SDC and SDA) // SSPM3:SSPM0 = 1000 (I2C Master mode, clock = FOSC/(4 * (SSPADD + 1)) // = 4000000/(4 * (9 + 1)) = 100000 Hz) // CKE=0(Disable SMBus specific inputs) // SKP=1(Release clock) // SMP=1 (Slew rate control disabled for standard speed mode (100 kHz and 1 MHz)) // SSPEN= 1 (Enables the serial port and configures the SDA and SCL pins as the serial port pins) SSPADD=0X09; SSPSTAT = 0x80; // Slew Rate control is disabled SSPCON = 0x38; // Select and enable I2C in master mode } //------------------------------------------------ |
Код инициализации абсолютно такой же, как и в 19 уроке.
Добавим для данной функции прототип в заголовочном файле.
Ниже данной функции добавим ещё несколько служебных функций, которые также имели место в 19 уроке, они нам также могут пригодиться
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
//------------------------------------------------ void I2C_Idle() { while(READ_WRITE); ZERO=0; while(ZERO) { SSPCON2&0x1f; } } //------------------------------------------------ void I2C_IntWait(void) { while(!SSPIF); SSPIF=0; } //------------------------------------------------ void I2C_StartCondition(void) { I2C_Idle(); SEN=1; I2C_IntWait(); } //------------------------------------------------ void I2C_StopCondition(void) { PEN = 1; I2C_IntWait(); } //------------------------------------------------ bit I2C_Write_Byte(unsigned char Byte) { SSPBUF = Byte; I2C_IntWait(); return ACKSTAT; } //------------------------------------------------ |
А ещё добавим функцию записи байта по адресу устройства
1 2 3 4 5 6 7 8 9 |
//------------------------------------------------ void I2C_SendByteByADDR(unsigned char c,unsigned char addr) { I2C_StartCondition(); I2C_Write_Byte(addr); I2C_Write_Byte(c); I2C_StopCondition(); } //------------------------------------------------ |
Добавим также на данную функцию прототип в заголовочном файле.
Теперь добавим файлы lcd.h и lcd.c из проекта предыдущего урока LCD2004_4BIT, переименовав их для отличия соответственно в i2clcd.h и i2clcd.c.
Подключим данную библиотеку в файле main.c
1 2 |
#include "i2c.h" #include "i2clcd.h" |
В файле i2clcd.h подключим библиотеку для работы с шиной I2C
1 2 |
#include <xc.h> #include "i2c.h" |
Далее добавим некоторые макроподстановки в соответствии со схемой на переходник для управляющих ножек и управления подсветкой
1 2 3 4 5 6 7 8 9 |
#define _XTAL_FREQ 4000000 //-------------------------------------------------------------- #define e1 I2C_SendByteByADDR(portlcd|=0x04,0x4E) #define e0 I2C_SendByteByADDR(portlcd&=~0x04,0x4E) #define rs1 I2C_SendByteByADDR(portlcd|=0x01,0x4E) #define rs0 I2C_SendByteByADDR(portlcd&=~0x01,0x4E) #define setled I2C_SendByteByADDR(portlcd|=0x08,0x4E) #define setwrite I2C_SendByteByADDR(portlcd&=~0x02,0x4E) //-------------------------------------------------------------- |
Перейдём в файл i2clcd.c и исправим подключение заголовочного файла, так как мы его переименовали
#include "i2clcd.h"
Удалим макросы
#define rs RC4
#define rw RC5
#define e RC6
Создадим глобальную переменную, которая будет в себе хранить значение состояния контактов на параллельной шине микросхемы, чтобы нам его не читать из шины I2C, а просто помнить его в этой переменной
1 2 3 4 |
#include "i2clcd.h" //-------------------------------------------------------------- unsigned char portlcd = 0; //-------------------------------------------------------------- |
Удалим функцию LCD_delay вместе с телом, так как мы ею все равно не пользуемся.
Также удалим и функцию инициализации портов LCD_PORT_init, так как мы теперь с ними не работаем.
Также у нас несколько изменится функция отправки половины байта в контроллер дисплея sendhalfbyte. Там мы сначала удалим установку значения порта D, так как мы его не используем теперь и он у нас свободен
PORTD&=0b00001111;
PORTD|=c;
Немного исправим вызов команд для управляющих ножек в соответствии с новыми макросами, а также добавим передачу полубайта в шину I2C, используя функцию нами написанной библиотеки для работы с данной шиной
1 2 3 4 5 |
c<<=4; e1; __delay_us(1); I2C_SendByteByADDR ((portlcd&0x0F)|c,0b01001110); e0; |
Мы передаём изменённое значение нашего «виртуального» порта в шину по адресу микросхемы, предварительно очистив там с помощью маски ножки данных.
В функции передачи целого байта мы изменим немного вызов макросов в связи с их изменением
1 2 |
if(mode==0) rs0; else rs1; |
В конце тела функции инициализации дисплея вызовем макросы включения подсветки и режима записи
1 2 3 |
__delay_ms(2); setled; setwrite; |
Перейдём в файл main.c и в функции main() вызовем инициализацию шины и дисплея
1 2 3 |
PORTA = 0x00; InitI2C();// Initialize i2c LCD_Init(); |
И добавим тот же тест, который мы использовали на прошлом занятии
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
LCD_Init(); LCD_String((char*)"String 1"); LCD_SetPos(2,1); LCD_String((char*)"String 2"); LCD_SetPos(4,2); LCD_String((char*)"String 3"); LCD_SetPos(6,3); LCD_String((char*)"String 4"); __delay_ms(2000); LCD_Clear(); while(1) { i++; sprintf(str01,"%5u",i); LCD_SetPos(4,3); LCD_String(str01); sprintf(str01,"%5u",65536-i); LCD_SetPos(10,3); LCD_String(str01); __delay_ms(200); } |
Подключим дисплей с переходником к плате (плату мы используем ту же — от WaveShare), также подключим программатор
Соберём код, прошьём контроллер и, если мы не наделали ошибок, то увидим на дисплее результат нашей работы
Итак, на данном занятии мы научились работать с переходником для дисплея 20×4, подключенным к контроллеру по шине I2C, что позволило сэкономить очень немало ножек портов. Также мы сегодня ещё раз закрепили тему работы с шиной I2C, возможность работы с которой имеется в модуле MSSP.
Всем спасибо за внимание!
Предыдущий урок Программирование МК PIC Следующий урок
Купить программатор (неоригинальный) можно здесь: PICKit3
Купить программатор (оригинальный) можно здесь: PICKit3 original
Отладочную плату PIC Open18F4520-16F877A можно приобрести здесь: PIC Open18F4520-16F877A
Дисплей LCD 20×4 можно приобрести тут: Дисплей LCD 20×4
Переходник I2C to LCD можно приобрести здесьI2C to LCD1602 2004
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий