В прошлом уроке, используя багаж наших знаний по аппаратной реализации шины I2C в контроллере STM32F1, мы поработали с данной шиной и на запись и на чтение, подключив к ней микросхему памяти EEPROM AT24C32. При этом мы уже использовали возможности библиотеки CMSIS.
Но, думаю, что для более глубокого понимания работы данной шины и её программирования при помощи средств библиотеки CMSIS одного урока будет недостаточно. Поэтому на данном уроке мы попробуем также подключить к данной шине контроллера символьный дисплей на контроллере HD44780 разрешением 16 x 2 символа.
С таким дисплеем мы уже много работали, также много работали и с переходником, а также постоянно продолжаем работать, поэтому рассказывать о дисплее, а также о переходнике и как они работают, не требуется.
Поэтому предлагаю сразу же перейти к практической части, чтобы не терять лишнее время.
Начнём со схемы подключения дисплея, которая у нас остаётся точно такая же, как и была в уроке 151, в котором мы также работали с таким дисплеем, используя библиотеку LL, только для обеспечения лучшего контакта по шине, мы подключим переходник без макетной платы, а также отдельный источник питания на 5 вольт для дисплея мы тоже не будем использовать. А воспользуемся мы выводом 5 вольт прямо из нашего ST-Link, за что отдельное спасибо посетителю ресурса, который этот метод подсказал
Проект был сделан из проекта прошлого занятия с именем CMSIS_I2C_EEPROM и был назван CMSIS_I2C_LCD1602.
Из проекта урока 151 с именем LL_I2C_LCD1602 скопируем в соответствующие папки нашего нового проекта файлы lcd.h, lcd.c, i2c_user.h и i2c_user.c и откроем его в Keil.
Удалим файл led.c из дерева проекта, а затем физически из папки проекта, также из папки удалим файл led.h.
Откроем файл main.c, исправим подключение файла led.h на lcd.h, а заодно подключим также и файл i2c_user.c, а также стандартную библиотеку stdio.h
#include "lcd.h"
#include "i2c_user.h"
#include <stdio.h>
В дерево проекта добавим файлы lcd.c и i2c_user.c
Из файла main.c удалим вот эти макросы
#define I2C_OWNADDRESS1_7BIT 0x00004000U
#define I2C_MODE_I2C 0x00000000U
#define SLAVE_OWN_ADDRESS 0xA0
#define I2C_REQUEST_WRITE 0x00
#define I2C_REQUEST_READ 0x01
Вот эти объявления также удалим
__IO uint8_t tim2_count = 0;
extern uint8_t R1,R2,R3,R4;
extern uint16_t num_gl;
uint8_t rd_value[20] = {0};
uint8_t wr_value[20] = {0x14,0x13,0x12,0x11,0x10,
0x0F,0x0E,0x0D,0x0C,0x0B,
0x0A,0x09,0x08,0x07,0x06,
0x05,0x04,0x03,0x02,0x01};
Функции AT24C_WriteBytes и AT24C_ReadBytes, TIM2_Init, TIM2_IRQHandler и GPIO_Init удалим вместе с телами.
Удалим вызовы следующих функций в main()
TIM2_Init();
GPIO_Init();
Также оттуда удалим вот это
TIM_EnableIT_UPDATE(TIM2);
TIM_EnableCounter(TIM2);
AT24C_WriteBytes (0x008A, wr_value, 20);
//AT24C_ReadBytes (0x008A, rd_value, 20);
for(i=0;i<20;i++)
{
ledprint(wr_value[i]);
//ledprint(rd_value[i]);
delay_ms(1000);
}
Функцию I2C_Init перенесём вместе со всем её телом в файл i2c_user.c, удалив из её заголовка спецификатор static, а затем создадим для неё прототип в заголовочном файле i2c_user.h
1 2 |
void I2C_SendByteByADDR(I2C_TypeDef * i2c, uint8_t c,uint8_t addr); void I2C_Init(void); |
В данном заголовочном файле также удалим подключение данных библиотек
#include «stm32f1xx.h»
#include «stm32f1xx_ll_i2c.h»
Вместо них подключим другую
1 |
#include "stm32f10x.h" |
В файле i2c_user.c добавим следующие макросы
1 2 3 4 |
#include "i2c_user.h" //------------------------------------------------ #define I2C_OWNADDRESS1_7BIT 0x00004000U #define I2C_MODE_I2C 0x00000000U |
Добавим также глобальную переменную
1 2 3 4 |
#define I2C_REQUEST_READ 0x01 //------------------------------------------------ __IO uint32_t tmpreg1; //------------------------------------------------ |
В функции I2C_Init исправим имя переменной вот здесь
tmpreg1 = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);
Также в этой же функции поменяем вот здесь порядок, так как сначала надо включить тактирование порта, а уже затем только настраивать его ножки, иначе работать шина не будет
1 2 3 4 5 6 |
void I2C_Init(void) { //I2C1 GPIO SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPBEN); SET_BIT(GPIOB->CRL, GPIO_CRL_CNF7_1 | GPIO_CRL_CNF6_1 | GPIO_CRL_CNF7_0 | GPIO_CRL_CNF6_0 |\ GPIO_CRL_MODE7_1 | GPIO_CRL_MODE6_1 | GPIO_CRL_MODE7_0 | GPIO_CRL_MODE6_0); |
В функции I2C_SendByteByADDR исправим тело в соответствии с требованиями библиотеки CMSIS. Здесь у нас практически то же самое, что в функции записи в микросхему EEPROM, только здесь мы отправляем в шину один байт
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void I2C_SendByteByADDR(I2C_TypeDef * i2c, uint8_t c,uint8_t addr) { //Disable Pos CLEAR_BIT(i2c->CR1, I2C_CR1_POS); MODIFY_REG(i2c->CR1, I2C_CR1_ACK, I2C_CR1_ACK); SET_BIT(i2c->CR1, I2C_CR1_START); while (!READ_BIT(i2c->SR1, I2C_SR1_SB)){}; (void) i2c->SR1; //I2C_Write_Byte(addr); MODIFY_REG(i2c->DR, I2C_DR_DR, addr | I2C_REQUEST_WRITE); while (!READ_BIT(i2c->SR1, I2C_SR1_ADDR)){}; (void) i2c->SR1; (void) i2c->SR2; //I2C_Write_Byte(c); MODIFY_REG(i2c->DR, I2C_DR_DR,c); while (!READ_BIT(i2c->SR1, I2C_SR1_TXE)){}; //I2C_StopCondition(); SET_BIT(i2c->CR1, I2C_CR1_STOP); } //------------------------------------------------ |
В файле lcd.h удалим подключение следующих библиотек
#include «stm32f1xx.h»
#include «stm32f1xx_ll_utils.h»
Вместо них подключим вот эту
#include "stm32f10x.h"
Перейдём в файл lcd.c и добавим прототип функции задержки
1 2 3 4 |
#include "i2c_user.h" //------------------------------------------------ void delay_ms(uint32_t ms); //------------------------------------------------ |
Вместо вызова задержки LL_mDelay напишем delay_ms во всех встречающихся местах.
Добавим буфер на 1 байт
1 2 |
char str1[100]; uint8_t buf[1]={0}; |
После функции DelayMicro добавим задержку в наносекундах
1 2 3 4 5 6 7 |
//------------------------------------------------ __STATIC_INLINE void DelayNano(__IO uint32_t nanos) { nanos = nanos * (SystemCoreClock / 1000000) / 9000; while (nanos--); } //------------------------------------------------ |
В функции sendhalfbyte вместо данной задержки
DelayMicro(1);
используем вот эту
1 |
DelayNano(200); |
А вот эту задержку в конце тела удалим вообще
DelayMicro(50);
В функции LCD_ini включим запись вот здесь
1 2 |
LCD_WriteByteI2CLCD(0); setwrite();//запись |
А в конце тела удалим
setled();//подсветка
setwrite();//запись
Вот это удалим
sendbyte(0x0C,0);//дисплей включаем (D=1), курсоры никакие не нужны
Вместо этого просто отключим дисплей
sendbyte(0x08,0);//дисплей пока выключаем
А включим его вот здесь
1 2 3 |
sendbyte(0x06,0);// пишем влево delay_ms(1); sendbyte(0x0C,0);//дисплей включаем (D=1), курсоры никакие не нужны |
Из функции LCD_SetPos удалим два ненужных кейса
case 2:
sendbyte((0x14+x)|0x80,0);
break;
case 3:
sendbyte((0x54+x)|0x80,0);
break;
В функции main() файла main.c вызовем инициализацию дисплея и попробуем вывести 2 строки на дисплей
1 2 3 4 5 |
I2C_Init(); LCD_ini(); LCD_String("String 1"); LCD_SetPos(5,1); LCD_String("String 2"); |
Соберём проект, прошьём контроллер и посмотрим результат работы кода
Всё отображается. Отлично.
Добавим в main() локальный строковый массив
1 2 |
uint16_t i; char str1[10]; |
Добавим задержку на пару секунд, очистим надпись во второй строке дисплея и в бесконечном цикле организуем счётчик, который будет отображать постоянно увеличивающуюся цифру в данном месте
1 2 3 4 5 6 7 8 9 10 11 |
LCD_String("String 2"); delay_ms(2000); LCD_SetPos(5,1); LCD_String(" "); while(1) { i++; sprintf(str1,"%5d",i); LCD_SetPos(5,1); LCD_String(str1); delay_ms(500); |
Соберём код, прошьём контроллер и посмотрим, как работает наш счётчик
Всё работает.
Итак, на данном уроке мы подключили дисплей 16×2 посредством переходника, работающего по шине I2C, используя при этом возможности библиотеки CMSIS.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Переходник I2C to LCD можно приобрести здесьI2C to LCD1602 2004
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Один только вопрос, данный дисплей как правило с 5 вольтовой логикой, а STM32 с 3,3 вольтовой. Получается переходник I2C работает с 3,3 или как?
open-drain port
Данный дисплей работает и от 3,3в , надо только подкрутить резистор на плате переходника. Конечно будет тускло, но так мне больше нравится. Если хотите обычный режим(5в), то запитайте переходник от 5в, а данные получайте от 3,3в. Работает. Автору урока огромное спасибо, хотелось бы урок CMSIS. STM32F1. I2C. Переходник для LCD 16×2 на DMA.Именно DMA+CMSIS.