В сегодняшнем уроке будет рассмотрена работа с символьным LCD дисплеем фирмы Winstar на контроллере HD44780. Следует сразу отметить, что аналогов данного дисплея великое множество и прошивка будет работать со всеми ними. Также была проверена работа данного кода с графическими и символьными OLED дисплеями.
Начнем, как и обычно, с постановки задачи. Необходимо подключить дисплей по 4х-битной шине к отладочной плате STM32F4 и вывести на него любой текст.
Итак, начнем с подключения. Существует два типа подключения подобных дисплеев: по 4х- и 8ми-битным шинам, при этом существенной разницы между ними нет, поэтому остановимся на первой, поскольку она требует меньшего количества проводников.
Схема подключения показана на рисунке ниже.
Следует отметить один очень важный момент: 1 вывод — "+5В" и 2 — "GND", на многих дисплеях поменяны местами, поэтому прежде чем подключить дисплей, прочитайте даташит. Неправильное подключение может привести к выходу дисплея из строя.
Собрать отладочную плату и дисплей в одно устройство можно разными способами. Можно просто распаять проводками, можно развести печатную плату-переходник, а можно собрать переходник на макетной плате, как показано на фото.
Теперь перейдем к прошивке. Выполним ее в виде отдельной библиотеки, чтобы в дальнейшем упростить подключение дисплея в других проектах — добавляете файл в проект и используете. Библиотеку назовем lcd.h. В библиотеке содержится следующий код:
//---Переопределяем порты для подключения дисплея, для удобства---// #define LCM_OUT GPIOB->ODR #define LCM_PIN_RS GPIO_Pin_0 // PB0 #define LCM_PIN_EN GPIO_Pin_1 // PB1 #define LCM_PIN_D7 GPIO_Pin_7 // PB7 #define LCM_PIN_D6 GPIO_Pin_6 // PB6 #define LCM_PIN_D5 GPIO_Pin_5 // PB5 #define LCM_PIN_D4 GPIO_Pin_4 // PB4 #define LCM_PIN_MASK ((LCM_PIN_RS | LCM_PIN_EN | LCM_PIN_D7 | LCM_PIN_D6 | LCM_PIN_D5 | LCM_PIN_D4)) GPIO_InitTypeDef GPIO_InitStructure; //---Функция задержки---// void delay(int a) { int i = 0; int f = 0; while(f < a) { while(i<60) {i++;} f++; } } //---Нужная функция для работы с дисплеем, по сути "дергаем ножкой" EN---// void PulseLCD() { LCM_OUT &= ~LCM_PIN_EN; delay(220); LCM_OUT |= LCM_PIN_EN; delay(220); LCM_OUT &= (~LCM_PIN_EN); delay(220); } //---Отсылка байта в дисплей---// void SendByte(char ByteToSend, int IsData) { LCM_OUT &= (~LCM_PIN_MASK); LCM_OUT |= (ByteToSend & 0xF0); if (IsData == 1) LCM_OUT |= LCM_PIN_RS; else LCM_OUT &= ~LCM_PIN_RS; PulseLCD(); LCM_OUT &= (~LCM_PIN_MASK); LCM_OUT |= ((ByteToSend & 0x0F) << 4); if (IsData == 1) LCM_OUT |= LCM_PIN_RS; else LCM_OUT &= ~LCM_PIN_RS; PulseLCD(); } //---Установка позиции курсора---// void Cursor(char Row, char Col) { char address; if (Row == 0) address = 0; else address = 0x40; address |= Col; SendByte(0x80 | address, 0); } //---Очистка дисплея---// void ClearLCDScreen() { SendByte(0x01, 0); SendByte(0x02, 0); } //---Инициализация дисплея---// void InitializeLCD(void) { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0 | GPIO_Pin_1| GPIO_Pin_4 | GPIO_Pin_5| GPIO_Pin_6| GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, &GPIO_InitStructure); LCM_OUT &= ~(LCM_PIN_MASK); delay(32000); delay(32000); delay(32000); LCM_OUT &= ~LCM_PIN_RS; LCM_OUT &= ~LCM_PIN_EN; LCM_OUT = 0x20; PulseLCD(); SendByte(0x28, 0); SendByte(0x0E, 0); SendByte(0x06, 0); } //---Печать строки---// void PrintStr(char *Text) { char *c; c = Text; while ((c != 0) && (*c != 0)) { SendByte(*c, 1); c++; } }
Пройдемся по основным функциям библиотеки:
InitializeLCD( ) — инициализация дисплея, необходимо выполнять при старте программы.
InitializeLCD(); //Инициализация дисплея
ClearLCDScreen( ) - очистка памяти дисплея.
ClearLCDScreen(); //Очистка памяти дисплея
Cursor(№ строки, № столбца) — установка позиции курсора, отсчет начинается с нулевой строки и нулевого столбца.
Cursor(0,2); //Установка курсора, 0-ая строка, 2-ой столбец
PrintStr(текст) — написание строки на дисплее.
PrintStr("CXEM.NET");
SendByte(байт, режим) — если коротко, то эта функция отправляет байт в дисплей. Если параметр «режим» равен «1», то на дисплее появится символ, а если «0» - то байт будет принят дисплеем в режиме настройки. Например очистка дисплея, установка курсора или выбор типа курсора.
SendByte(0b00001100, 0); //Курсор выключен
С библиотекой закончили. Теперь пора запускать дисплей. Для этого в основном файле main.c надо написать следующий код:
#include "stm32f4xx.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_rcc.h" #include "lcd.h" int main(void) { InitializeLCD(); //Инициализация дисплея ClearLCDScreen(); //Очистка дисплея от мусора Cursor(0,2); //Установка курсора PrintStr("Specially for"); //Написание текста Cursor(1,4); PrintStr("CXEM.NET"); while(1) { } }
Думаю по комментариям все понятно. Теперь остается скомпилировать код и прошить плату. Делаем рестарт и наслаждаемся написанным.
В заключении хотелось бы показать, как менять вид курсора. Существует три режима: без курсора, курсор в виде нижнего подчеркивания и мигающий курсор.
Для выключения курсора выполняем:
SendByte(0b00001100, 0); //Курсор выключен
Для нижнего подчеркивания:
SendByte(0b00001110, 0); //Курсор не мигает
Для мигающего:
SendByte(0b00001111, 0); //Курсор мигает
Прикрепленные файлы:
- Lesson_5.rar (139 Кб)
Комментарии (61) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
[Автор]
char *t="Какой-то текст";
то пршете просто:
PrintStr(t);
Если же хотите вывести числовую переменную, то подключаете библиотеку stdio.h и конвертируете число в строку:
int i=123;
char *t;
snprintf(t, 255, "%d", i);
PrintStr(t);
int i = 123;
sprintf(io_buff, "%d", i);
PrintStr(io_buff);
[Автор]
[Автор]
[Автор]
void CursorBlink(int blinkType)
{
if(blinkType==0) SendByte(0b00001111, 0); //Курсор мигает
else if(blinkType==1) SendByte(0b00001110, 0); //Курсор не мигает
else if(blinkType==2)SendByte(0b00001100, 0); //Курсор выключен
}
и менять вид курсора командой типа CursorBlink(1);
Вопрос: Подскажите, где можно ознакомиться с подробным описанием версии языка CooCox?
Буду очень признателен за любую помощь.
[Автор]
[Автор]
Инициализирую так:
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIOB->CRH |= 0x33333333; // конфигурируем на выход частота 50мгЦ
GPIOB->CRL |= 0x33333333; // конфигурируем на выход частота 50мгЦ
//GPIO_Init(GPIOB, &GPIO_InitStructure);
LCM_OUT &= ~(LCM_PIN_MASK);
delay(32000);
delay(32000);
delay(32000);
LCM_OUT &= ~LCM_PIN_RS;
LCM_OUT &= ~LCM_PIN_EN;
LCM_OUT = 0x20;
PulseLCD();
SendByte(0x28, 0);
SendByte(0x0E, 0);
SendByte(0x06, 0);
}
#define LCM_OUT GPIOB->ODR
#define LCM_PIN_RS GPIO_Pin_3
#define LCM_PIN_EN GPIO_Pin_4
#define LCM_PIN_D7 GPIO_Pin_5
#define LCM_PIN_D6 GPIO_Pin_6
#define LCM_PIN_D5 GPIO_Pin_7
#define LCM_PIN_D4 GPIO_Pin_9
#define LCM_PIN_MASK ((LCM_PIN_RS | LCM_PIN_EN | LCM_PIN_D7 | LCM_PIN_D6 | LCM_PIN_D5 | LCM_PIN_D4))
GPIO_InitTypeDef GPIO_InitStructure;
Переделал так:
#define LCM_OUT GPIOA->ODR
#define LCM_PIN_RS GPIO_Pin_0 // PB0
#define LCM_PIN_EN GPIO_Pin_1 // PB1
#define LCM_PIN_D7 GPIO_Pin_7 // PB7
#define LCM_PIN_D6 GPIO_Pin_6 // PB6
#define LCM_PIN_D5 GPIO_Pin_5 // PB5
#define LCM_PIN_D4 GPIO_Pin_4 // PB4
#define LCM_PIN_MASK ((LCM_PIN_RS | LCM_PIN_EN | LCM_PIN_D7 | LCM_PIN_D6 | LCM_PIN_D5 | LCM_PIN_D4))
GPIO_InitTypeDef GPIO_InitStructure;
void InitializeLCD(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIOA->CRH |= 0x33333333; // конфигурируем на выход частота 50мгЦ
GPIOA->CRL |= 0x33333333; // конфигурируем на выход частота 50мгЦ
//GPIO_Init(GPIOB, &GPIO_InitStructure);
LCM_OUT &= ~(LCM_PIN_MASK);
delay(32000);
delay(32000);
delay(32000);
LCM_OUT &= ~LCM_PIN_RS;
LCM_OUT &= ~LCM_PIN_EN;
LCM_OUT = 0x20;
PulseLCD();
SendByte(0x28, 0);
SendByte(0x0E, 0);
SendByte(0x06, 0);
}
GPIOB->pin_1 = RS
GPIOC->pin_3 = bit4
GPIOA->pin_4 = E
............
............
и т.д. :)
Во всех этих примерах есть ограничения с настройкой порта. А в этом примере даже нельзя к другой ножке одного и того же порта подключить .
[Автор]
2) Проверьте правильность подключения, отсутствие КЗ и разрывов;
3) Сравните инициализацию Вашего дисплея по даташиту с используемым в статье.
1) Прога для дисплея WH2004 подойдет или только для 1602?
2) Есть ли возможность загружать свои символы?
void Cursor(char Row, char Col)
{
char address;
switch ( Row )
{
case 0: address = 0x00; break;
case 1: address = 0x40; break;
case 2: address = 0x14; break;
case 3: address = 0x54; break;
}
address += Col;
SendByte(0x80 | address, 0);
}
Не могу собрать, - startup\startup_stm32f4xx.c(289): error: #29: expected an expression
Keil V4.74.0.22
Следовательно логические уровни совпадать не будут. Дисплей может сжечь порты МК (не защищённые от 5 В), а контроллер в свою очередь не сможет отправлять данные, так как лог единица от 0.7Vdd (согласно даташиту). Как быть, городить полевики на каждый выход МК?
Вот такие вот грабли
LCM_OUT = 0x20;
и сверяйтесь со своими портами.
и с задержкой игрался, а толку ноль
Делал подтягивающие резисторы для дисплея на 5 вольт, малоли может ему 3.3 мало - результат 0. Все коды уже перелопатил включая этот - один хрен ничего. С контрастностью тоже все ок - резистор не крутил, как на атмеле заработал, больше я его не трогал. В чем проблема?
Отдельно пинами СТМ которые настроил для дисплея моргаю. В чем проблема объясните. Или покажите нормальное видео где обычный дисплей на 16 ног подключается в 4 битном режиме.
void ClearLCDScreen()
{
SendByte(0x01, 0);
delay(220);
SendByte(0x02, 0);
delay(220);
}