Перейти к содержанию

    

Универсальный LCD идикатор(GPIO,I2C)

Добрый день, в одном из проектов, подключил LCD индикатор через расширитель порта по I2C, но код получился, как-то не очень надежный, в общем сейчас появилось по больше времени, и решил ещё раз все обдумать и сделать нормальный модуль LCD, который можно в любой проект пихать. Но пока только какая то каша в голове.

В общем вот примерно как-то так было, во время всех while, переключался на другие процессы через ОС.

Теперь меня интересует, как бы все это сделать без ОС,( для самообразования!!).

ну и желательно, прозрачный код, но это уже не обязательно.

//================================================================================
void DisplayUpdate(unsigned char *buf){
unsigned char LcdCountChar;
unsigned char GetCharTemp;
for(LcdCountChar=0;LcdCountChar<40;LcdCountChar++){
	if(LcdCountChar==0){
		WriteLCDcommand(ADDRESS_FIRST_ROW);
	}
	if(LcdCountChar==20){
		WriteLCDcommand(ADDRESS_SECOND_ROW);
	}
	GetCharTemp=UTF_conversion(buf[LcdCountChar]);
	WriteLCDdata(GetCharTemp);
}
}
//================================================================================
void WriteLCDdata(unsigned char data){
SetRS;
WriteLCD_Nibble(data);
data=data<<4;
WriteLCD_Nibble(data);
}
//================================================================================
void WriteLCD_Nibble(unsigned char data){
SetE;
SetW;
//------------------------
   GlobalData_Lcd=data;
Pcf8575_Send_data(GlobalData_Lcd,GlobalData_Led_key_lcd);
//PortLCD=(PortLCD&0xFF0F)|(data&0xF0);
while(Pcf8575_Send_ok==1){} //Передача завершилась
//------------------------
ClrE;
}

Как вот это:

while(Pcf8575_Send_ok==1){} //Передача завершилась

вынести в самый верх что бы можно было по опрашивать в main?

 

Была идея все данные закинуть в стек, а потом, их выдавать.

где все функции будут запускаться в том порядке как их и устанавливали, но тогда получается на каждый чих( SetE;)

внутри WriteLCD_Nibble надо делать тоже функцию, и все это в большую очередь выстроиться, ну и тут так же стала проблема в том что разные параметры передается,и помимо установки в стек обработчиков, надо ещё и параметры устанавливать.

void Lcd_init(void){
    Lcd_pin_init();
    LCD_ON();
    //----------------
    RTOS_SetTask(lcd_set_8bit_mode(0),0,100);      
    RTOS_SetTask(WriteLCDcommand,0,100);         
    RTOS_SetTask(WriteLCDcommand(0x30),0,0);     
    RTOS_SetTask(WriteLCDcommand(0x30),0,0);      
    RTOS_SetTask(WriteLCDcommand(0x3C),0,0);      
    RTOS_SetTask(lcd_cursor_mode(LCD_ENABLE,CURSOR_ENABLE,BLINK),0,0);
    RTOS_SetTask(WriteLCDcommand(0x1),0,0);  
    
    RTOS_SetTask(Start_UpadeDispaly,0,0);  
    return 
}

 

Думал сделать на КА, жестко прописать какие функции за кем вызываються, но тут

1) теряется читабельность(хотя с этим я уже мерился, так 1 раз все это прописать и больше туда возвращаться не планирую).

2) когда следуют одинаковые функции друг за другом типа 2 раза вызов функции WriteLCDcommand с разным параметром.

как тут быть даже не знаю, делал ещё пред настройку(как бы буфер на 2 функции вперед), установку функции Next, которая будет выполнятся после установленной, но тут получилось все запутано совсем.

 

Подскажите в какую сторону двигаться?.

 

 

 

 

 

 

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если хотите уйти от "расширителей", крайне неэффективных для I2C.

Предлагается к рассмотрению.

1. На каком-либо процессоре реализовать свой "расширитель", а более правильно - универсальный контроллер LCD

с различными интерфейсами - I2C, SPI, serial итп.

2. Входной интерфейс девайса п.1 - SPI (и/или USART). Первый - простой и надежный. Второй даст возможность

делать "выносной" дисплей, например в виде панели оператора, если использовать RS485.

3. Протокол - делаем какой удобно и надежно. Если это будет MODBUS - получим панель оператора.

 

Такой себе "OPC-сервер" :)

 

4. Алгоритм выдачи инф. на дисплей, для упрощения можно использовать следующий.

4.1. Выдача инф. через SPI интерфейс выполняется задачей с низким приоритетом, по таймеру + флаги обновления (нет смысла выдавать инф. на дисплей, если она уже там отображается). Таймер - 0.2 .... 0.5 с. Выдача идет "пакетом", без квитирования и задержек на максимальной скорости, соотв-но зацикл невозможнн.

4.2. Выдача инф. по 4.1. производится из видео-RAM (образ экрана LCD в требуемом формате).

4.3. Изменение буфера RAM выполняется асинхронно из любой задачи, из которой требуется вывод на LCD.

 

Плюс такого подхода в том, что при выводе на LCD не требуется аппаратно через SPI обновлять содержимое экрана.

Это будет сделано автоматичиески, максимум через таймаут 4.1.

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
1. На каком-либо процессоре реализовать свой "расширитель", а более правильно - универсальный контроллер LCD

LCD, дисплей примитивный hd44780, так что сильно жырно для него использовать отдельный МК, если я правильно понял.

4.2. Выдача инф. по 4.1. производится из видео-RAM (образ экрана LCD в требуемом формате).

4.3. Изменение буфера RAM выполняется асинхронно из любой задачи, из которой требуется вывод на LCD.

Все так и сделано.

4.1. Выдача инф. через SPI интерфейс выполняется задачей с низким приоритетом, по таймеру + флаги обновления (нет смысла выдавать инф. на дисплей, если она уже там отображается). Таймер - 0.2 .... 0.5 с. Выдача идет "пакетом", без квитирования и задержек на максимальной скорости, соотв-но зацикл невозможнн.

Блин а тут не чисто SPI,I2C, управляющие выводы RS,RW,E идут от процессора а через расширитель только данные, изначально было так проше, что бы на 1 символ не приходилось 3 байта гнать

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
(1) LCD, дисплей примитивный hd44780, так что сильно жырно для него использовать отдельный МК, если я правильно понял.

(2) Блин а тут не чисто SPI,I2C, управляющие выводы RS,RW,E идут от процессора а через расширитель только данные, изначально было так проше, что бы на 1 символ не приходилось 3 байта гнать

(1) По стоимости PCF8574 вполне сопоставима с недорогими контроллерами.

(2) Оставить SPI, специфический алгоритм общения с LCD вынести в контроллер. "Тормозные" операции, вроде таймаутов для LCD, не будут выполняться в базовой системе. Подобные

while(Pcf8575_Send_ok==1){}

и прочим, которые упрятаны в Pcf8575_Send_data();

Подскажите в какую сторону двигаться?.

(непонятно, что за RTOS)

void Lcd_init(void){
    Lcd_pin_init();
    LCD_ON();
    //----------------
    RTOS_SetTask(lcd_set_8bit_mode(0),0,100);      
    RTOS_SetTask(WriteLCDcommand,0,100);         
    RTOS_SetTask(WriteLCDcommand(0x30),0,0);     
    RTOS_SetTask(WriteLCDcommand(0x30),0,0);      
    RTOS_SetTask(WriteLCDcommand(0x3C),0,0);      
    RTOS_SetTask(lcd_cursor_mode(LCD_ENABLE,CURSOR_ENABLE,BLINK),0,0);
    RTOS_SetTask(WriteLCDcommand(0x1),0,0);  
    
    RTOS_SetTask(Start_UpadeDispaly,0,0);  
    return 
}

 

примерно так

void SystemGo_init(void)
{
    Lcd_pin_init(); 
    OtherHW_pin_init(); 
    Lcd_Test(); delay(500); Lcd_Clear();
    //----------------
    RTOS_SetTask( TimersTask(), PRTY_0 );      // Реализация софт-таймеров на базе аппаратных таймеров
    RTOS_SetTask( LCDUpdateTask(), PRTY_2 );      // Выдача на LCD, если есть что  
    RTOS_SetTask( MainTask(), PRTY_1 );                // Управление нашим папилацем.
    // ---------------
    RTOS_Run();
    //return 
}

ps - в реальности скобочки после функций-задач надо убрать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
(2) Оставить SPI, специфический алгоритм общения с LCD вынести в контроллер. "Тормозные" операции, вроде таймаутов для LCD, не будут выполняться в базовой системе.
Благодарю, в следующий раз по думаю в этом направлении, но пока увы, уже плата готова.

(непонятно, что за RTOS)

Это не RTOS, а просто объектный такой код, для понятия алгоритма, вот поспешил и не исправил, правильнее было так написать

STACK_SetTask(lcd_set_8bit_mode(0),0,100);      
STACK_SetTask(WriteLCDcommand,0,100);        
STACK_SetTask(WriteLCDcommand(0x30),0,0);    
STACK_SetTask(WriteLCDcommand(0x30),0,0);      
STACK_SetTask(WriteLCDcommand(0x3C),0,0);      
STACK_SetTask(lcd_cursor_mode(LCD_ENABLE,CURSOR_ENABLE,BLINK),0,0);
STACK_SetTask(WriteLCDcommand(0x1),0,0);

Это я хотел, забуферизировать порядок вызова функций.

примерно так

Это то понятно, а дальше как уходить от while, которая появляется в третей вложенной функции? Особенно интересно без средств ОС(переключения контекста).

 

Как по максимуму сгруппировать все while , что бы потом спихнул все на DMA и ждешь 1 флаг завершения инициализации/обновления LCD ?

 

 

Изменено пользователем pokk

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Есть давно известная схема подключения HD44780-совместимого дисплея к МК с помощью 74HC164 в 8-бит режиме(только запись) по 3 GPIO. Есть библиотеки для разных МК, искать по запросу "74hc164 hd44780". Цена в розницу для 74HC164 ~0.2$

avr2lcd.jpg

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да и младшие STM8 не сильно дороже (оптом 30, в розницу 50 центов). Чем с расширителем плохо - межбайтовые времянки все равно надо выдерживать. Вероятно, в ниббловом режиме через расширитель или параллельно этим можно и DMA с таймером озадачить, конечно (если есть). Если есть свободный UART - логичнее бы туда строкой плюнуть и пусть уже отдельный контроллер разбирается. Мне вот понравилось навесить модуль на прерывание таймера (125 us), по запросу копирую буфер экрана в память индикатора без каких-то непроизводительных задержек. На этом же прерывании и тех же линиях данных и опрос клавиатуры с авторепитером.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
. . . .

(1) Это то понятно, а дальше как уходить от while, которая появляется в третей вложенной функции? Особенно интересно без средств ОС(переключения контекста).

 

(2) Как по максимуму сгруппировать все while , что бы потом спихнул все на DMA и ждешь 1 флаг завершения инициализации/обновления LCD ?

 

1. Использование работы периферии процессора в режиме прерываний "на полную".

В частности - SPI + IRQ + DMA. Достаточно "толкнуть" процесс и не трогать его пока не завершится передача.

(Никаких wile. Никаких RTOS). Это, конечно, процессоро-зависимое (запуск "в паре" DMA+USART/SPI + IRQ).

 

2. см. п.1 + все, что было мною высказано выше.

Если работать с LCD по его протоколу, то скорость ограничена, иногда требуются таймауты. DMA "не рулит".

Может это и можно реализовать, "рваный" режим DMA. Не знаю. И имеет ли это смысл.

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
Авторизация