Следующим шагом на пути создания усилителя я выбрал подключение жк дисплея и вывод некоторой информации на него. В данной статье в частности будут рассмотрены следующие варианты:
С этим этапом разработки у меня есть один небольшой негативный момент. Дело в том, что я сначала купил дисплей фирмы sunlike sc1602. Я на него убил кучу времени. Мне кажется что я уже перепробовал все возможные способы инициализации всех возможных процессоров, и ничего так мне и не удалось увидеть. Потом я плюнул и купил новый дисплей фирмы Winstar, который завелся сразу же по стандартной процедуре инициализации хитачевского проца, которая была рассмотрена ранее.
Дисплей подключен, но в старом коде меня некоторые пункты не устраивали, вот то что внутри lcd.h:
#include <htc.h> #ifndef _LCD_H_ #define _LCD_H_ #ifndef _XTAL_FREQ // Unless specified elsewhere, 4MHz system frequency is assumed #define _XTAL_FREQ 4000000 #endif #define LCD_RS RD2 #define LCD_EN RD3 #define lcd4b RD4 #define lcd5b RD5 #define lcd6b RD6 #define lcd7b RD7 #define lcd_cursor(x) lcd_write(((x)&0x7F)|0x80) #define LCD_STROBE() ((LCD_EN = 1),(LCD_EN=0)) #define testbit(data,bitno) ((data>>bitno)&0x01) extern void lcd_write(unsigned char c); extern void lcd_clear(void); extern void lcd_puts(const char * s); extern void lcd_putch(char c); extern void lcd_goto(unsigned char pos); extern void lcd_init(); #endif
Главное отличие в этом файле – это то что для данных я не использую весь порт, а в прошлой версии хотя и использовалось всего 4 линии, но присваивание шло на весь порт. Раз изменилась система присваивания данных, то изменению должны подвергнуться и две функции: lcd_write и lcd_init
Внутренности lcd.c
#include "lcd.h" void lcd_write(unsigned char c) { __delay_us(40); if (testbit(c,4)) lcd4b = 1; else lcd4b = 0; if (testbit(c,5)) lcd5b = 1; else lcd5b = 0; if (testbit(c,6)) lcd6b = 1; else lcd6b = 0; if (testbit(c,7)) lcd7b = 1; else lcd7b = 0; LCD_STROBE(); if (testbit(c,0)) lcd4b = 1; else lcd4b = 0; if (testbit(c,1)) lcd5b = 1; else lcd5b = 0; if (testbit(c,2)) lcd6b = 1; else lcd6b = 0; if (testbit(c,3)) lcd7b = 1; else lcd7b = 0; LCD_STROBE(); } void lcd_clear(void) { LCD_RS = 0; lcd_write(0x1); __delay_ms(2); } void lcd_puts(const char * s) { LCD_RS = 1; // write characters while(*s) lcd_write(*s++); } void lcd_putch(char c) { LCD_RS = 1; // write characters lcd_write( c ); } void lcd_goto(unsigned char pos) { LCD_RS = 0; lcd_write(0x80+pos); } void lcd_init() { LCD_RS = 0; LCD_EN = 0; __delay_ms(15); // wait 15mSec after power applied, lcd4b = 1; lcd5b = 1; lcd6b = 0; lcd7b = 0; LCD_STROBE(); __delay_ms(5); LCD_STROBE(); __delay_us(200); LCD_STROBE(); __delay_us(200); lcd4b = 0; lcd5b = 1; lcd6b = 0; lcd7b = 0; // Four bit mode LCD_STROBE(); lcd_write(0x28); // Set interface length lcd_write(0xF); // Display On, Cursor On, Cursor Blink lcd_clear(); // Clear screen lcd_write(0x6); // Set entry Mode }
Для проверки работоспособности используем следующий код:
TRISD = 0x00; lcd_init(); lcd_goto(0x00); lcd_puts("LCD 16x2 test"); lcd_goto(0x40); lcd_puts("Diymicro.ru");
Успех 🙂
Прежде всего придется смириться с тем, что придется проворачивать похожие манипуляции как с семисегментными дисплеями, а именно создать массив определений символов. Пока мне нужны только цифры:
const unsigned char digits[10] = { 0b00110000, //0 0b00110001, //1 0b00110010, //2 0b00110011, //3 0b00110100, //4 0b00110101, //5 0b00110110, //6 0b00110111, //7 0b00111000, //8 0b00111001, //9 };
Все, можно писать функции дежурного отображения:
void display_tt() { //дежурная функция отображения информации на LCD unsigned char d; get_temp(); //берем значение температуры LCD_RS = 0; lcd_write(0b00001100); //выключаем курсор __delay_us(100); lcd_clear(); d = ReadHour(); lcd_goto(0x05); lcd_putch(digits[d/10]); //выводим значение часов d = d - ((d/10)*10); lcd_putch(digits[d]); d = 0b00111010; lcd_putch(d); d = ReadMin(); lcd_putch(digits[d/10]); //выводим значение минут d = d - ((d/10)*10); lcd_putch(digits[d]); lcd_goto(0x44); if (!sign) lcd_putch(0b00101011); else lcd_putch(0b10110000); //знак температуры d=temperature/10; lcd_putch(digits[d]); //целое значение температуры d=temperature-((temperature/10)*10); lcd_putch(digits[d]); d = 0b00101110; lcd_putch(d); //точка lcd_putch(digits[temp_drob]); //вывод дробного значения d = 0b11011111; lcd_putch(d); //градус d = 0b01000011; lcd_putch(d); //С }
Вот что получилось, отладка в разгаре 🙂
Я еще вначале загонялся, чтобы сделать менюшку на жк, выбор пунктов в которой осуществлялся бы с компьютера по уарту. Но потом понял, что в этом случае возникнут трудности с прерываниями (в базовом режиме работы устройства оно ждет, когда на него прилетит какой либо символ по уарту, таким образом протокол связи с ПК придется хорошо продумать), а также то, что эта функция фактически бесполезна. Поэтому меню я решил отложить, до описания работы с энкодером – будет хорошей индикацией его работы.
Допилим прерывания по таймеру, которые служат с целью обновления информации на дисплее. Здесь я решил заюзать таймер TMR2, потому что у меня есть смутные подозрения, что другие два таймера мне еще понадобятся в будущем. Так как нам нужны приличные временные промежутки, то ставим все значения на максимум и получаем импульсы длиной около 16 секунд. Далее, я с помощью переменной флага увеличил этот промежуток в два раза. То есть инфа на дисплея будет обновляться раз в 32 секунды, для меня самое то.
interrupt isr() { if (TMR2IF) { update++; if (update == 255) { flag++; update = 0; } TMR2 = 0x01; T2CKPS0 = 1; T2CKPS1 = 1; //1 делитель не делит входную частоту TOUTPS2 = 1; TOUTPS1 = 1; TOUTPS3 = 1; TOUTPS0 = 1; //2 делитель делит на 5 TMR2IF = 0; //сброс флага } }
Не забываем то, что переменные используемые и в прерываниях и в главной функции должны быть объявлены как volatile.
Закидываю сюда также весь проект, будет и в качестве бэкапа для меня.
Вывод цифр можно сделать проще, без масива.
lcd_clear();
d = ReadHour();
lcd_goto(0x05);
lcd_putch(0x30+d/10);
С адреса 0x30 знакогенератора расположенны коды цифр от 0 до 9
последовательно.
Спасибо, не знал об этом. Приму способ на вооружение 🙂
Если не затруднит прошу растолковать для чего ((d/10)*10) вот это деленье умноженье?
Не затруднит, например, нам надо вывести число 25 на дисплей, в моей функции отображается только один разряд, то есть:
1. Выводим цифру 2 = 25/10 (целочисленный тип переменной)
2. Выводим цифру 5 = 25 – (25/10)*10 (=20)
А если в моем случае по UARTу приходит 9 байт данных и я их хочу вывести в 16-ричной форме на экран, то есть по две цифры, например AA FF 1D 3A 55 4D 3E 2E 4A, то как лучше сделать. Cоздать 9 переменных и потом их последовательно в одну строчку выстроить ?
Я бы просто переделал свою функцию отображения с помощью струкутры switch(), и последовательно бы все выпихивал на экран.