|
|
  |
Помогите разобраться с символьным ЖКИ, HD44780 |
|
|
|
Feb 20 2007, 10:49
|

Местный
  
Группа: Свой
Сообщений: 211
Регистрация: 3-06-06
Пользователь №: 17 742

|
Уже второй день пытаюсь оживить символьный ЖКИ под IAR. До этого момента использовал CodeVision и встроенную в него библиотеку, которая всегда работала как часы. С IAR пришлось разбираться второпях, до этого с ним дела не имел. Нашел кучу исходников библиотек для работы с HD44780, но толком ни одна так и не заработала. Пришлось разбираться самому, в результате чего была создана некая компиляция из кусков разных библиотек. Она даже заработала и стала выводить на дисплей данные. Но радость моя была недолгая - программа работает крайне нестабильно. То все нормально, то выводится куча левых символов без какой-либо закономерности. С железом это никак не может быть связано - с CodeVision все работало хорошо. Много игрался с задержками, думал из-за них, но они как будто не влияют на этот глюк. К сообщению прицепляю код, помогите, пожалуйста, разобраться! Или, быть может, у кого-то есть готовая библиотека для работы с этими ЖКИ? Контроллер - ATmega48, частота 1 МГц.
Сообщение отредактировал Pavel V. - Feb 20 2007, 10:52
Прикрепленные файлы
main.txt ( 303 байт )
Кол-во скачиваний: 105
lcd.txt ( 4.59 килобайт )
Кол-во скачиваний: 162
--------------------
Good News Everyone!
|
|
|
|
|
Feb 20 2007, 11:18
|

Гуру
     
Группа: Свой
Сообщений: 2 720
Регистрация: 24-03-05
Пользователь №: 3 659

|
Цитата(Pavel V. @ Feb 20 2007, 14:49)  Уже второй день пытаюсь оживить символьный ЖКИ под IAR. До этого момента использовал CodeVision и встроенную в него библиотеку, которая всегда работала как часы.
С IAR пришлось разбираться второпях, до этого с ним дела не имел.
Нашел кучу исходников библиотек для работы с HD44780, но толком ни одна так и не заработала. Пришлось разбираться самому, в результате чего была создана некая компиляция из кусков разных библиотек.
Она даже заработала и стала выводить на дисплей данные. Но радость моя была недолгая - программа работает крайне нестабильно. То все нормально, то выводится куча левых символов без какой-либо закономерности.
С железом это никак не может быть связано - с CodeVision все работало хорошо.
Много игрался с задержками, думал из-за них, но они как будто не влияют на этот глюк.
К сообщению прицепляю код, помогите, пожалуйста, разобраться! Или, быть может, у кого-то есть готовая библиотека для работы с этими ЖКИ?
Контроллер - ATmega48, частота 1 МГц. Не разбирался с тонкостями кода, но все пестрит программными задержками. ИМХО лучше анализировать флаг готовности ЖКИ Вот здесь http://electronix.ru/forum/index.php?showtopic=10934 есть исходники для данного ЖКИ. Есть и мое творение.
--------------------
|
|
|
|
|
Feb 20 2007, 11:35
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(Pavel V. @ Feb 20 2007, 09:49)  К сообщению прицепляю код, помогите, пожалуйста, разобраться! Или, быть может, у кого-то есть готовая библиотека для работы с этими ЖКИ? На первый взгляд больших "плюх" не видно, кроме задержек в инициализации - обратите на них пристальное внимание. Но есть недочеты: Код void LcdWriteNibble (unsigned char a) { E=0; // эта команда не нужна - после выполнения любой команды у вас E возвращается в 0. delay_us(2); // не нужно LCDPORT&=0x0f; LCDPORT|=(a&0xf0); delay_us(2); // не нужно E=1; delay_us(2); // не нужно E=0; delay_us(2); // не нужно. }
void WaitBusy (void) { delay_us(300); // достаточно 40 мкс. }
void LcdWriteCommand (unsigned char a) { E=0; // эта команда не нужна - см. LcdWriteNibble(), в LcdWriteData то же самое. RS=0; LcdWriteNibble(a); LcdWriteNibble(a<<4); WaitBusy(); }
void LcdInit (unsigned char a) { LCDDDR = 0xFF; //3F LCDPORT = 0x00; E=0; // это не нужно - вы его сбросили предыдущей командой. RS=0; // аналогично delay_ms(20); // здесь надо примерно 200 мс LcdWriteNibble(0x30); delay_ms(7); // здесь надо 50 мс минимум LcdWriteNibble(0x30); delay_us(200); // здесь надо 50 мс минимум LcdWriteNibble(0x30); WaitBusy(); // здесь уже можно 200 мкс LcdWriteNibble(0x20); // это лишнее - см. следующую команду WaitBusy(); if (a==1) LcdWriteCommand(0x20); else LcdWriteCommand(0x28); LcdWriteCommand(0x08); LcdWriteCommand(0x01); delay_ms(30); // предыдущая команда исполняется 1.6мс максимум LcdWriteCommand(0x06); LcdWriteCommand(0x0c); delay_ms(30); // предыдущая команда исполняется 40 мкс, дополнительная задержка не нужна. } И подумайте - надо ли вам передавать параметр в эту функцию? Вы по ходу выполнения программы будете его менять? Если нет, напишите две функции инициализации и вызывайте нужную в конкретном проекте - зачем вам лишний код в рабочей программе? В общем видно, что вы надергали код из разных библиотек не разбираясь. Посмотрите, у вас void tlcd_write_byte() делает то же самое, что и LcdWriteCommand(), LcdWriteData().
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Feb 20 2007, 20:11
|

Местный
  
Группа: Свой
Сообщений: 211
Регистрация: 3-06-06
Пользователь №: 17 742

|
Что-то никак не получается ничего.. prottoss, я и Ваш код пробовал, не работает почему-то. У меня вопрос - при подключении по 4-битной шине, куда именно должны на шину подключаться пины DB4,5,6,7? У меня в железе вот какой расклад: PORTD.0 - RS PORTD.1 - R/W PORTD.2 - E PORTD.3 PORTD.4 - DB4 PORTD.5 - DB5 PORTD.6 - DB6 PORTD.7 - DB7 Не подскажете, как должны при этом выглядеть настройки? У меня сейчас вот так: // Шина данных #define LCD_PORTDATA PORTD #define LCD_PINDATA PIND #define LCD_DDRDATA DDRD // Порт управления #define LCD_PORTCTRL PORTD #define LCD_PINCCTRL PIND #define LCD_DDRCTRL DDRD // Линии управления LCD #define LCD_wire_RS (1 << PD0) #define LCD_wire_RW (1 << PD1) #define LCD_wire_E (1 << PD2) #define LCD_wire_BL (1 << PD3) // (BackLight) Правильно? Или в программе подразумевается, что шина данных должна находиться на порту, отличном от порта с линиями управления? UPDATEУра! Заработало! Подправил свой исходный текcт в соответствии с рекомендациями Сергея Борщ. Теперь все стабильно  Видимо, все-таки с задержками у меня какая-то фигня была. Приведу текст к более красивому виду и выложу, мало ли кому пригодится! Теперь на повестке дня чтение данных из АЦП по SPI.
Сообщение отредактировал Pavel V. - Feb 20 2007, 20:33
--------------------
Good News Everyone!
|
|
|
|
|
Aug 21 2007, 09:40
|
Местный
  
Группа: Участник*
Сообщений: 418
Регистрация: 20-08-07
Пользователь №: 29 930

|
Цитата(Pavel V. @ Feb 21 2007, 00:11)  .... Приведу текст к более красивому виду и выложу, мало ли кому пригодится! ... Теперь на повестке дня чтение данных из АЦП по SPI. Не не стоит выкладывать... Везде полно реально работающих примеров для любой конкретной эволюшн борд...
|
|
|
|
|
Apr 9 2008, 10:57
|

Группа: Новичок
Сообщений: 9
Регистрация: 8-04-08
Пользователь №: 36 568

|
Привет всем.
не смог бы обладатель готового решения поделиться опытом, и скинуть готовые библиотеки которые не надо править.
я сталкнулся с той-же проблемой что и автор форума. раньше писал на CVAR и там все было без проблем.
интересно автор решил эту проблему ???
|
|
|
|
|
Jun 4 2008, 07:39
|
Частый гость
 
Группа: Свой
Сообщений: 80
Регистрация: 23-07-07
Из: Украина, г. Сумы
Пользователь №: 29 306

|
не помню где содрал, но у меня работает в иаре Файл LCD.h ------------------------------------------------------------------------------------------------------------------------------ #include "delay.h"
// LCD pins #define DB7 0x80 #define DB6 0x40 #define DB5 0x20 #define DB4 0x10 #define ENABLE 0x04 #define RS1 0x01
// Port #define LCDDDR DDRA #define LCDPORT PORTA #define LCDPIN PINA // General defines #define DATA 0x01 #define CTRL 0x00
//#define LCDPORT PORTD //для AVR PORTB4 .. PORTB7-ШД #define E PORTA_Bit2 //вывод ОМЭВМ для подкл. E #define RS PORTA_Bit0 //---||--- RS
//#define LCDDDR DDRD //#define LCDPIN PIND
//************************************************** //запись старшей тетрады числа
void LcdWriteNibble (unsigned char a) { //E=0; delay_us(30); LCDPORT&=0x0f; LCDPORT|=(a&0xf0); delay_us(50); E=1; delay_us(50); E=0; //delay_us(2); } //************************************************** //ожидание сброса флага ожидания void WaitBusy (void) { delay_us(70); } //************************************************** //запись команды void LcdWriteCommand (unsigned char a) { E=0; RS=0; LcdWriteNibble(a); LcdWriteNibble(a<<4); WaitBusy(); } //************************************************** // инициализация модуля HD44780 //a=1 - однострочный дисплей //a!=1 - двустрочный //курсор выключен,автоинкремент адреса,нет сдвига //экрана void LcdInit (unsigned char a) { DDRA = (1<<DDA2); PORTA_Bit2 = 0; delay_ms(50); LCDDDR = 0xFF; //3F LCDPORT = 0x00; E=0; RS=0; delay_ms(100); delay_ms(100); delay_ms(100); delay_ms(100); LcdWriteNibble(0x30); delay_ms(100); delay_ms(100); delay_ms(100); LcdWriteNibble(0x30); delay_ms(100); delay_ms(100); delay_ms(100); LcdWriteNibble(0x30); WaitBusy(); delay_us(100); delay_us(100); delay_us(100); LcdWriteNibble(0x20); WaitBusy(); if (a==1) LcdWriteCommand(0x20); else LcdWriteCommand(0x28); LcdWriteCommand(0x08); LcdWriteCommand(0x01); delay_ms(2); LcdWriteCommand(0x06); LcdWriteCommand(0x0c); //delay_ms(30); }
//************************************************** //запись символа
void LcdWriteData (unsigned char a) { E=0; RS=1; delay_us(20); LcdWriteNibble(a); LcdWriteNibble(a<<4); WaitBusy(); }
/*********************************************************************** Write a control or data byte to the display. Control: 1=Ctrl, 0=Data If data (control=0) set position with lcd_goto first. Since the LCD is driven in 4-bit modus, we write the MSB nibble first and the the LSB nibble. ***********************************************************************/ void tlcd_write_byte(unsigned char control, unsigned char byte) { if ((byte & 0x80) == 0x80) LCDPORT |= DB7; else LCDPORT &= ~DB7; if ((byte & 0x40) == 0x40) LCDPORT |= DB6; else LCDPORT &= ~DB6; if ((byte & 0x20) == 0x20) LCDPORT |= DB5; else LCDPORT &= ~DB5; if ((byte & 0x10) == 0x10) LCDPORT |= DB4; else LCDPORT &= ~DB4; if (control == 1) LCDPORT |= RS1; else LCDPORT &= ~RS1; LCDPORT |= ENABLE; LCDPORT &= ~ENABLE; if ((byte & 0x08) == 0x08) LCDPORT |= DB7; else LCDPORT &= ~DB7; if ((byte & 0x04) == 0x04) LCDPORT |= DB6; else LCDPORT &= ~DB6; if ((byte & 0x02) == 0x02) LCDPORT |= DB5; else LCDPORT &= ~DB5; if ((byte & 0x01) == 0x01) LCDPORT |= DB4; else LCDPORT &= ~DB4; if (control == 1) LCDPORT |= RS1; else LCDPORT &= ~RS1; LCDPORT |= ENABLE; LCDPORT &= ~ENABLE; WaitBusy(); } /*********************************************************************** Write strings to the display. Set position with tlcd_goto first. Text will wrap if to long to show on one line. ***********************************************************************/ void tlcd_write_string(char *ptr) { while (*ptr != 0x00) tlcd_write_byte(DATA,*ptr++); }
/*********************************************************************** Goto specified column and line. 1,1 is the upper left corner. ***********************************************************************/ void tlcd_goto(unsigned char column, unsigned char line) { unsigned char addr; line--; column--; addr = (line * 64) + column; tlcd_write_byte(CTRL, addr | 0x80); }
//************************************************** //установка курсора в позицию adr
void LcdSetPosition(unsigned char adr) { LcdWriteCommand(adr|0x80); }
//************************************************** //очистка экрана void LcdClrScr() { LcdWriteCommand(0x01); delay_ms(60); }
//------------------------------------------------------------------------------ void LcdSetCGAdress(unsigned char adr) { LcdWriteCommand((adr&0x7f)|0x40); } //------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------
Файл delay.h
------------------------------------------------------------------------------------------------------------------------------
#include <intrinsics.h> //функции циклов
#define f_cpu (16000000) //Частота задающего генератора(ГЦ)
#define delay_us(temp) (__delay_cycles((temp * f_cpu) / 1000000)); /* макрос задержки на "temp" микросекунд. точная задержка при целом значении задающей частоты генератора в МГц (1, 2, 3, 4, ..., 10, 11, ...) */// 571 + 2, 387 + 2
#define delay_ms(temp) (__delay_cycles((temp * f_cpu) / 1000)); /* макрос задержки на "temp" миллисекунд. точная задержка при целом значении задающей частоты генератора в кГц (1, 2, 3, 4, ..., 10, 11, ...) */
#define delay_s(temp) (__delay_cycles(temp * f_cpu)); /* макрос задержки на "temp" секунд. точная задержка при целом значении задающей частоты генератора в Гц (1, 2, 3, 4, ..., 10, 11, ...) */ ------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
|
Jun 11 2008, 22:35
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(alux @ Jun 11 2008, 19:24)  Но обязательно использую этот исходник в следующем проекте. Согласно комментариям в шапках каждого файла этот исходник распространяется под лицензией GPL. Вы готовы раскрыть исходные коды своего следующего проекта? Кода немного, имеет смысл посмотреть как написан этот исходник (для осваивающих плюсы образец неплохой) и написать самому. По коду (смотрел бегло): - вместо передачи в LCD::send() признака команда/данные как параметра лучше написать две функции. Причем одна из них может сбрасывать RS и вызывать вторую, а вторая в конце выставлять RS. Таким образом после любой операции с дисплеем RS в единице и при вызове второй функции его принудительно выставлять не нужно. Хороший компилятор может сгенерить из этих двух функций одну с двумя точками входа. - при загрузке знакосинтезатора нет смысла передавать 8 байтов параметрами, эффективнее передать указатель на массив. Финт: поскольку 3 старших бита не выводятся, можно в старший из них записать единичку, девятым байтом в массив дописать ноль и таким образом превратить массив в С-строку. Потом можно заметить, что загружать можно все символы сразу одной командой (чаще всего требуется один раз при старте загрузить нужное количество символов и в процессе работы они не перегружаются). Потом становится видно, что после посылки команды установки адреса CGRAM дальнейшая загрузка очень похожа на вывод строки. Таким образом вся загрузка вырождается в посылку команды установки CGRAM и вызов функции вывода строки. - функции, которые вызываются только один раз (конструктор, например) можно сделать встроенными. - константы, заданные через #define (команды дисплея) можно перенести внутрь класса как static const члены или enum (что логичнее). В общем критиковать всегда легко
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|