PDA

Просмотр полной версии : Как вывести цифру на ЖКИ HD44780?



pozdn
02.10.2009, 21:09
Надо вывести цифру на ЖКИ. Вывод массива буквенных символов работает. Строки, связанные с выводом массива букв, закомментированы. А ассемблирование кода для вывода цифры не идет ( ругается на строку 19).
. Для вывода цифры “5” поместил ее в R20 и перевел ее в значение кода по ASCII.
Подскажите,пожалуйст а, где ошибка.

1. #include <mega16.h> //RS = 0 -команда, RS = 1 -символ
2. #include <delay.h> //RW = 0 - данные пересылаются в индикатор
3. #define unsigned char c=r20 //E - данные фиксируются в индикаторе при // импульсе

//unsigned char data[ ]={'В','А','Н','Я'};
//unsigned char i; // параметр цикла

4. void pulse_e(void) // Сформировать импульс на линии Е
5. {
6. delay_us(1);
7. PORTC.2=1; // E=1
8. delay_us(1);
9. PORTC.2=0; // E=0
10. }

11. void send_char(c) // Передать код символа для отображения
12. {
13. PORTC=(c&0b11110000)|(0b00000 001); // Передадим ст.часть байта
14. pulse_e();
15. PORTC=((c<<4)&0b11110000)|(0b00000 001); // Передадим мл. часть байта
16. pulse_e();
17. delay_us(45); // пауза
18. }

19. void send_cmd(c) // Передать индикатору код команды
20. {
21. PORTC=(c&0b11110000); // Передадим ст.часть байта
22. pulse_e();
23. PORTC=((c<<4)&0b11110000); // Передадим мл. часть байта
24. pulse_e();
25. delay_us(45); // пауза
26. }

27. void main(void)
28. {
29. DDRC=0xff; / Настроим линии порта С на вывод
30. PORTC=0b00000000;
31. delay_ms(15); // пауза для инициации индикатора
32. PORTC=0x20; // Установим код 0x20 -переход в четырехбитный режим
33. pulse_e();
34. delay_us(45); // пауза
35. send_cmd(0x28); // Отошлем команду в четырехбитном режиме
36.send_cmd(0x0f); // Команда " вкл. отображение с миг. курсором и // подчеркиванием"
37. send_cmd(0x01); //Команда " Очистить индикатор"
38. delay_ms(70); // пауза
39. send_cmd(0x82); // Команда " Установить позицию для вывода // текста"
40. delay_ms(70); // пауза
//for (i=0;i<4;i++) // Вывести 4 символа
41. #asm;
42. ldi r20,0b00000101; // Цифра 5 для вывода на ЖКИ
43. ori r20,0b00110000; //Преобразование цифры 5 в код ASCII
44. #endasm;
// send_char(data[i]); //код символа для вывода
45. send_char(c);
46. while(1);
47. } // Конец основной программы

leokri
03.10.2009, 09:12
Вот таблица прямых кодов индикатора.

blindman
03.10.2009, 09:56
1. Ругается жена, когда мужик "под мухой" приходит :) Компилятор выдает сообщение об ошибке, которое и надо было привести.

2. Вот этот кусок кода
#asm;
ldi r20,0b00000101; // Цифра 5 для вывода на ЖКИ
ori r20,0b00110000; //Преобразование цифры 5 в код ASCII
#endasm;
// send_char(data[i]); //код символа для вывода
send_char(c);

не имеет смысла, потому что
#define unsigned char c=r20 //E - данные фиксируются в индикаторе при // импульсе - ерунда полная

И какой сакральный смысл хранить переменную в фиксированном регистре (она там не хранится, но по коду видна такая попытка)?

pozdn, Вам хотя бы минимальное понятие нужно о синтаксисе языка, на котором Вы пытаетесь писать программу

pozdn
03.10.2009, 10:23
То,что ерунда, я согласен и насчет синтаксиса Вы правы.Только это эмоции,а хотелось бы по существу. Этой строчкой
#define unsigned char c=r20хотелось записать содержимое регистра r20 в переменную c. Если это неправильно, то как это сделать правильно?
А вообще-то надо выводить не просто какие-нибудь цифры, а результат после АЦП-преобразования, который и будет находиться в каких-то регистрах.

blindman
03.10.2009, 10:48
В данном конкретном случае Вам нужно понять, что c у Вас - не просто переменная, а параметр функции. И имеет смысл только внутри функции. За пределами функции может быть переменная с тем же именем, но это будет уже другая переменная. Всё, что нужно было - просто

send_char('5');, без всяких ассемблеров. И про регистры задумываться не надо, это не ассемблер, компилятор сам разберётся в какие регистры что положить.

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

void send_char(unsigned char c)
{
// ...............
}


А вот этой конструкцией

#define unsigned char c=r20
Вы говорите компилятору: везде, где в тексте программы встречается "unsigned", заменяй его на "char c=r20". Бессмыслица, верно?

pozdn
03.10.2009, 12:03
blindman, спасибо Вам за ответ на первую половину вопроса.
А еще хотелось бы, получить ответ и на вторую.т.е. как вывести на ЖКИ результат АЦП-преобразования? Если можно,конечно.

blindman
03.10.2009, 14:11
конечно, можно. нужно перевести число в строку символов, например, функцией itoa(), а потом посимвольно выдать на индикатор

pozdn
03.10.2009, 17:09
blindman, еще раз Вам большое спасибо! Немного прояснилось.
Буду двигаться дальше.

UN9GW
03.10.2009, 17:14
Надо вывести цифру на ЖКИ

В CVAVR для этого типа контроллеров есть целая библиотека с полным набором функций.

Содержимое lcd.h


void _lcd_ready(void);
void _lcd_write_data(unsi gned char data);
// write a byte to the LCD character generator or display RAM
void lcd_write_byte(unsig ned char addr, unsigned char data);
// read a byte from the LCD character generator or display RAM
unsigned char lcd_read_byte(unsign ed char addr);
// set the LCD display position x=0..39 y=0..3
void lcd_gotoxy(unsigned char x, unsigned char y);
// clear the LCD
void lcd_clear(void);
void lcd_putchar(char c);
// write the string str located in SRAM to the LCD
void lcd_puts(char *str);
// write the string str located in FLASH to the LCD
void lcd_putsf(char flash *str);
// initialize the LCD controller
unsigned char lcd_init(unsigned char lcd_columns);

pozdn
04.10.2009, 13:39
Подправил программу, но все равно при ассемблировании CodeVision выдает ошибку в строке 31: void itoa(a,*string,10);
“отсутствует ;”
Вот код:

1. #include <mega16.h>
2. #include <delay.h>
3. #include <stdlib.h>
4. unsigned char string; //Объявление символьной переменной
5. unsigned int a=35; //Объявление числовой переменной

6. void pulse_e(void) // Сформировать импульс на линии Е
7. {
8. delay_us(1);
9. PORTC.2=1; // E=1
10. delay_us(1);
11. PORTC.2=0; // E=0
12. }

13. void send_char(unsigned char c) // Передать код символа для отображения
14. {
15. PORTC=(c&0b11110000)|(0b00000 001); // Передадим ст.часть байта
16. pulse_e();
17. PORTC=((c<<4)&0b11110000)|(0b00000 001); // Передадим мл. часть байта
18. pulse_e();
19. delay_us(45); // Пауза
20. }

21. void send_cmd(unsigned char c) // Передать индикатору код команды
22. {
23. PORTC=(c&0b11110000); // Передадим ст. часть байта
24. pulse_e();
25. PORTC=((c<<4)&0b11110000); // Передадим мл. часть байта
26. pulse_e();
27. delay_us(45); // пауза
28. }

29. void main(void)
30. {
31. void itoa(a,*string,10); //Преобразуем число а в символ string
32. DDRC=0xff; // Настроим линии порта С на вывод
33. PORTC=0b00000000;
34. delay_ms(15); // пауза для инициации индикатора
35. PORTC=0x20; // Установим код 0x20 -переход в четырехбитный режим
36. pulse_e();
37. delay_us(45); // пауза
38. send_cmd(0x28); // Отошлем команду для четырехбитного режима
39. send_cmd(0x0f); // Команда " вкл. отображение с миг. курсором и подчеркиванием"
40. send_cmd(0x01); //Команда " Очистить индикатор"
41. delay_ms(70); // пауза
42. send_cmd(0x82); // Команда " Установить позицию для вывода текста"
43. delay_ms(70); // пауза
44. send_char(string); //код символа для вывода
45. while(1);
46. } // Конец основной программы

leokri
04.10.2009, 16:35
Вы заморачиваете себе голову.
Откройте буфер для дисплея
char disp[6];//Буфер дисплея
А дальше свое число выводите.
sprintf(disp,"%03u",а);
lcd_puts(disp) ;
Все больше ничего не нужно.
Только с буфером не жадничайте.
Успехов!

M0TLN
04.10.2009, 16:50
Подправил программу, но все равно при ассемблировании CodeVision выдает ошибку в строке 31: void itoa(a,*string,10);
“отсутствует ;”
Вот код:

.....

29. void main(void)
30. {
31. void itoa(a,*string,10); //Преобразуем число а в символ string
....


В строке 31 никакого вызова нет.
void в данной строке не нужен.
От куда берется значение string?
Вы просто смешали объявление функции с вызовом ее.

Вы никогда на Си не программировали?
Попробуйте сначала для ДОСа что-то наваять, а не для контроллера.

Типа "Hello, world!"

А лучше возьмите готовый проект от Геннадия из темы про Приемник Мечты - там есть
вариант клавиатуры, подключенный к АЦП Меги, и вывод на экран...

Кукин Николай Николаевич
05.10.2009, 09:41
pozdn
У Вас получилось следующее.
В строке 31 вместо вызова стандартной функции itoa, Вы объявляете внутри функции main заголовок НОВОЙ , уже своей функции itoa. При этом само тело функции itoa не описываете.
На такое компилятор как минимум должен сказать, что эта функция уже определена ранее в библиотеке stdlib.h
Строковую переменную string Вы тоже нигде не описали(вернее выделили для нее всего лишь один байт памяти). А она здесь у Вас применяется как входной параметр для вызываемой функции itoa.
Все переменные, подставляемые в качестве параметров в вызываемые функции, должны быть либо объявлены явно до вызова этих функций, либо сформированы(т.е. для них должна быть выделена оперативная память).
В частности применимо к строковой переменной string, ее можно объявить разными способами, и в зависимости от способа объявления, обрабатывать придется по-разному.
Можно задать переменную до вызова main, либо внутри main:
unsigned char string[6];
// для строки выделено 5 байт + последний, 6 байт надо заполнить 0, для того, чтобы можно было вести обработку этой переменной как строковой.

или

unsigned char string =”abcdef”;
//здесь уже после f имеется 0, таким образом, для строки выделено 7
байт


будет :

unsigned char string =”abcdef”;
main()
{

}
или
main()
{
char string =”abcdef”;


}

Если Вы объявите только указатель на эту переменную, те.
unsigned char *string;
main()
{

}
, то в этом случае компилятор предусмотрит выделение памяти только для АДРЕСА первого символа этой строки, а места для самой строки не выделит в памяти. Для того, чтобы пользоваться строками таким способом, необходимо в программе динамически( то есть по ходу программы)явно выделять память для строки. Для этого используется функция работы с памятью malloc(), calloc(). После того , как отпадет необходимость пользования областью памяти для размещения строки, ее нужно освободить функцией free().
Теперь о том, что получится при использовании этих разных способов.
Использование itoa() занимает объем около 200 байтов программной памяти.
Применение сalloc, malloc потребует около 1,5 кбайт программной памяти.
Могучая функция sprintf()занимает около 1,5 кб программной памяти.
(предложено leokri, кстати, если будете копировать текст прямо из его сообщения, можете получить ошибку компилирования, параметр ‘a’ копируется как русское ‘а’. Учтите это – поиск причины ошибки может отнять много времени, если эти буквы выглядят на экране одинаково)
Поэтому, если испытываете дефицит программной памяти, лучше использовать itoa(), а строковую переменную объявить явно, выделив ей необходимое количество байт сразу.
Язык С среди всех языков программирования является самым низкоуровневым, поэтому необходимо четкое понимание каждого действия, иначе грабли будут вставать непрерывно. 99% граблей происходит при операциях с оперативной памятью, на четкое понимание обращения к памяти придется обратить все усилия. Пока оно не придет, будет непрерывно трудно. Следует отметить, что нормальной русскоязычной литературы по С для микроконтроллеров пока не написано (во всяком случае я еще не встречал). Поэтому Вам придется это дело проходить сначала «классический» С для «нормальных» компьютеров, а потом уже применительно к микроконтроллерам.
При переходе на контроллеры выяснится, что для написания компактных по объему программ, большинство стандартных функций языка не сильно подходят, т.к. дают код очень большого размера. Но это уже другая песня.
Николай.

UN9GW
05.10.2009, 10:34
Всё, как уже говорилось, намного проще.
Вот библиотечная функция: void lcd_putchar(char c);
Вывести любой символ -
lcd_putchar("A");
или
lcd_putchar("2");

Для вывода цифры нужно определить её формат %u, %f и т.д., а потом уже выводить с помощью библиотечных функций.
Так же выводятся строки, форматированные строки и пр., в общем, всё придумано до нас. :)

blindman
05.10.2009, 10:43
Если нужно всего лишь преобразовать целое в строку, printf()/sprintf() - расточительство. Достаточно itoa()

Кукин Николай Николаевич
05.10.2009, 15:26
Игорь (UN9GW),
Ведь у товарища как в этом и вопрос, как преобразовать целое число в строковую переменную. Приведенные Вами форматы используются строковыми функциями, каждая из которых занимает не менее1,5 кб программной памяти. Это действительно расточительство.
( Если судить по приведенным Вами функциям для LCD из библиотеки CodeVision'а, они не преобразовывают hex в ascii). Если пользовать стандартный С, то тогда уж оптимальней использовать itoa() (при условии, что больше нигде в программе не будет использования функций типа prinf/sprintf), тем более, что выходной результат одинаков.
На мой взгляд еще меньше программной памяти сожрется при следующем алгоритме.
1.преобразовываем исходное число в BCD формат. На асме это 22 команды(44 байта).
На С потянет байт на 60-70.
2. преобразование каждой тетрады BCD формата в ascii коды путем суммирования с 0x30, либо табличным, но уже тогда можно в любой ascii код, прописанный в таблице. Это порядка 20-30 байт максимум.
Итого такое преобразование может занять меньше программной памяти, чем itoa().
Получается ascii код, который либо сразу отправлять побайтно в функции вывода на экран, либо сначала формировать строку целиком, а потом строкой в экран.
Николай.

Кукин Николай Николаевич
05.10.2009, 16:04
Вот нашел у старших товарищей реализацию этого алгоритма, правда для char, для int надо добавить вычитание тысяч и десятков тысяч. Для беззнаковых можно еще ужать.
Щас код при оптимизации около ста байт (WinAvr).
//---------преобразование в BCD
void CharToBCD ( signed int Number )
{
// сотни
BCDRes100 = -1;
do
{
Number -=100;
BCDRes100++;
} while ( Number >= 0 );
Number +=100;

// десятки
BCDRes10 = -1;
do
{
Number -=10;
BCDRes10++;
} while ( Number >= 0 );
Number +=10;
// единицы
BCDRes1 = -1;
do
{
Number -=1;
BCDRes1++;
} while ( Number >= 0 );
Number +=1;

// коррекция в ASCII
BCDRes100 += 0x30; //сотни
BCDRes10 += 0x30; //десятки
BCDRes1 += 0x30; //единицы
}

pozdn
05.10.2009, 20:10
С учетом всех замечаний переделал еще раз программу.Как оказалось,она работает.Всем откликнувшимся большое спасибо,особенно blindman,leokri и Кукину Н.Н.
Если будут комментарии, пожалуйста.
Вот код


1. #include <mega16.h>
2. #include <delay.h>
3. #include <stdlib.h>

4. unsigned char c[3]; // кол-во цифр в выводимом числе
5. unsigned int temp=125; //выводимое число
6. unsigned char i; //счетчик передаваемых на вывод цифр

7. void pulse_e(void)
8. {
9. delay_us(1);
10.PORTC.2=1; // E=1
11.delay_us(1);
12.PORTC.2=0; // E=0
13.} // pulse_e

14.void send_char(unsigned char c) // Передать код символа для отображения
15.{

16.PORTC=(c&0b11110000)|(0b00000 001); // Передадим ст.часть байта
17.pulse_e();
18.PORTC=((c<<4)&0b11110000)|(0b00000 001); //Передадим мл.часть байта
19.pulse_e();
20.delay_us(45); // пауза
21.}

22.void send_cmd(unsigned char c) // Передать индикатору код к-ды
23.{
24.PORTC=(c&0b11110000); // Передадим ст.часть байта
25.pulse_e();
26.PORTC=((c<<4)&0b11110000); // Передадим мл. часть байта
27.pulse_e();
28.delay_us(45); // пауза
29.}

30.void main(void)
31.{
32.itoa(temp,c); //Преобразование числа в символы
33.DDRC=0xff; // Настроим линии порта С на вывод
34.PORTC=0b00000000;
35.delay_ms(15); // пауза для инициации индикатора
36.PORTC=0x20; // Переход в четырехбитный режим
37.pulse_e();
38.delay_us(45); // пауза
39.send_cmd(0x28); // Отошлем команду в четырехбитном режиме
40.send_cmd(0x0f); // Команда " вкл.миг. курсор и подчеркивание"
41.send_cmd(0x01); //Команда " Очистить индикатор"
42.delay_ms(70); // пауза
43.send_cmd(0x82); // Команда " Установить позицию для вывода "
44.delay_ms(70); // пауза
45.for (i=0;i<4;i++) // Вывести 3 символа
46. send_char(c[i]); //код символа для вывода
47.while(1);
48.} // main

Genadi Zawidowski
05.10.2009, 23:36
перевод в 4-ч битный режим лучше по другому...


_delay_ms(20); /* 40 ms needed for Vcc = 2.7 V */
_delay_ms(20);

// switch interface to 4-bit wide mode
ws1602_wrcmd_nowait( 0x33);
ws1602_wrcmd_nowait( 0x32);

ws1602_wrcmd(0x28); // two lines

ws1602_wrcmd(0x0c); // ВКЛ.LCD , КУРСОР ОТКЛ., МИГАНИЕ КУРС. ОТКЛ.
ws1602_wrcmd(0x06); // автомат перемещение курсора вправо

ws1602_clear(); // обязательно требуется для нормальной работы, например, адресации.