Теперь я расскажу как работать с дисплеем от Нокии 5110.
Матрица состоит из 48 × 84 пикселей. 48 строчек, 84 столбца.
Но питать микросхему отвечающую за работу дисплея нужно от 2.7 В до 3.3 В.
Подсветка также питается от 3.3 В или можно от 5 В через резистор 1К.
Также советуют ставить резисторы 10К на все остальные выводы, если МК питается от 5 В.
Можно работать и с 5 В, но могут быть проблемы с отображением и дисплей быстрее придёт в негодность.
Я заказывал с Ебея и в моём случае для того чтобы включить подсветку нужно было заземлить вывод LED.
Есть несколько вариантов расположения выводов:
Номер вывода | Обозначение | Функция | Примечание |
---|---|---|---|
1 | VCC | Питание | |
2 | GND | Земля | |
3 | SCE (CE) | Выбор чипа | можно просто заземлить |
4 | RST | RESET | активно при лог 0 |
5 | D/C (DC) | выбор режима | команды лог 0 дата лог 1 |
6 | DN (DIN) | Дата | передаётся байт данных |
7 | SCLK (CLK) | Serial clock, линия синхронизации | |
8 | LED (LIGHT) | Подсветка | 3.3V |
Теперь, как же работать с дисплеем. Каждый раз когда нужно общаться с дисплеем необходима на SCE подать лог 0, я его просто заземлил.
команду D/C = 1.
На самом деле это считывается когда мы передаём последний бит данных, но нет разницы когда мы выберем работаем ли мы с данными или командами, главное чтобы при передаче последнего (0) бита стояло нужное нам значение.
Передача данных происходит следующим образом: Мы передаём байт данных по выводу DN, начиная с последнего 7 бита. Нужно выставить этот бит и установить на SCLK лог 1, в этот момент начинает передаваться информация к контроллеру, затем нужно сбросить SCLK, выставить 6 бит на DN, снова выставить 1 на SCLK и продолжать так до тех пор пока не передадим целый байт.
На самом деле это считывается когда мы передаём последний бит данных, но нет разницы когда мы выберем работаем ли мы с данными или командами, главное чтобы при передаче последнего (0) бита стояло нужное нам значение.
Передача данных происходит следующим образом: Мы передаём байт данных по выводу DN, начиная с последнего 7 бита. Нужно выставить этот бит и установить на SCLK лог 1, в этот момент начинает передаваться информация к контроллеру, затем нужно сбросить SCLK, выставить 6 бит на DN, снова выставить 1 на SCLK и продолжать так до тех пор пока не передадим целый байт.
Рис. 1. Передача 1 байта
После этого можно сразу передавать следующий байт, если произошла ошибка и вы передали не тот бит или просто нужно отменить команду последний бит которой ещё не отправлен, то для этого нужно подать лог 0 на RST. Команда не передастся и можно сразу начать передачу новой команды.
Рис. 2. Передача нескольких байт
Рис. 3. Отмена передачи
Не позже чем через 30 мс после подачи питания, необходимо подать отрицательный импульс на RESET длиной 100 нс, иначе можно испортить дисплей ! Таким образом происходит начальная инициализация дисплея.
В принципе ширина всех импульсов <= 100 нс. Поэтому если МК работает на 8 МГц или ниже, можно не ждать при передаче данных, изменении состояний выводов и т.д.
Команда
|
D/C
|
DB7
|
DB6
|
DB5
|
DB4
|
DB3
|
DB2
|
DB1
|
DB0
|
(H = 0 or 1)
|
|||||||||
Выбор функции
|
0
|
0
|
0
|
1
|
0
|
0
|
PD
|
V
|
H
|
Записать данные
|
1
|
D7
|
D6
|
D5
|
D4
|
D3
|
D2
|
D1
|
D0
|
(H = 0)
|
|||||||||
настройка дисплея
|
0
|
0
|
0
|
0
|
0
|
1
|
D
|
0
|
E
|
выставить Y адрес
RAM
|
0
|
0
|
1
|
0
|
0
|
0
|
Y2
|
Y1
|
Y0
|
выставить X адрес
RAM
|
0
|
1
|
X6
|
X5
|
X4
|
X3
|
X2
|
X1
|
X0
|
(H = 1)
|
|||||||||
Настройка
температуры
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
TC1
|
TC0
|
Смещение системы
|
0
|
0
|
0
|
0
|
1
|
0
|
BS2
|
BS1
|
BS0
|
выставить VOP
|
0
|
1
|
VOP6
|
VOP5
|
VOP4
|
VOP3
|
VOP2
|
VOP1
|
VOP0
|
BIT | 0 | 1 |
PD | Чип активен | Режим низкого энергопотребления |
V | горизонтальная адресация | вертикальная адресация |
H | обычные команды | дополнительные команды |
D и E | 00 01 10 11 |
Пустой дисплей Обычный режим Зажечь все пиксели Инвертированный режим |
TC1 и TC0 | 00 01 10 11 |
температурный коэффициент 0 1 2 3 |
VOP - определяет контраст дисплея, у меня всё прекрасно видно при 0xc8.
TC - температурный коэффициент отвечает за изменение напряжения при изменении температуры, может быть полезно если дисплей работает при низких или переменных температурах (дом-улица).
Чтобы выбрать какие пиксели зажечь нужно передать байт данных, мы выбираем как отображать сразу группу из 8 пикселей, информация об изображении на дисплее (о том какие пиксели горят) хранится в RAM, после того как мы передали байт данных мы автоматически переходим к следующей группе пикселей.
Рис. 4. Адресация RAM
Когда мы передаём 1 байт он всегда записывается сверху вниз, дисплей как бы состоит из столбцов по 8 пикселей (1 байт), когда мы записываем столбец то переходим к следующему и тут есть на варианта, либо идти вправо по строчке, дойдя до конца строки опуститься на строку ниже, вернуться к 1 столбцу и продолжить дальше.
(Горизонтальная адресация)
Либо опускаться вниз по столбцу, дойдя до низа, подняться наверх но сдвинуться вправо.
(Вертикальная адресация)
Рис. 5. Горизонтальная адресация
Вертикальная
Рис. 6. Вертикальная адресация
выставить X (Y) адрес RAM - здесь мы численно указываем к какому блоку памяти перейти как указано на рис. 4.
Смещение системы - Не понял что это, в даташите сказано выставлять 0x13.
На Ебее продавец также выставил готовую библиотеку для работы с дисплеем, которую я и буду использовать, она взята с сайта http://icstation.com/
Здесь удобно сделаны define чтобы можно было выставлять или сбрасывать определённые биты.
#define F_CPU 8000000UL #define LCD_RST_set PORTD |= (1<<0) //external reset input #define LCD_RST_clr PORTD &=~ (1<<0) #define LCD_DC_set PORTD |= (1<<1) //data/commande #define LCD_DC_clr PORTD &=~ (1<<1) #define SDIN_set PORTD |= (1<<2) //serial data input #define SDIN_clr PORTD &=~ (1<<2) #define SCLK_set PORTD |= (1<<3) //serial clock input #define SCLK_clr PORTD &= ~(1<<3) #include <avr/io.h> #include <util/delay.h> #include "english_font.h"
В цикле передаём каждый бит: выставляем SDIN в соответствии с последним (крайним левым) битом нашего байта, передаём его и сдвигаем наш байт влево, теперь 6 бит крайний и повторяем процесс до тех пор пока не передадим всё.
void LCD_write_byte(unsigned char dat, unsigned char command) { unsigned char i; if (command == 1) LCD_DC_clr; else LCD_DC_set; for(i=0;i<8;i++) { if(dat&0x80) SDIN_set; else SDIN_clr; SCLK_clr; dat = dat << 1; SCLK_set; } }Весь код
#define F_CPU 8000000UL #define LCD_RST_set PORTD |= (1<<0) //external reset input #define LCD_RST_clr PORTD &=~ (1<<0) #define LCD_DC_set PORTD |= (1<<1) //data/commande #define LCD_DC_clr PORTD &=~ (1<<1) #define SDIN_set PORTD |= (1<<2) //serial data input #define SDIN_clr PORTD &=~ (1<<2) #define SCLK_set PORTD |= (1<<3) //serial clock input #define SCLK_clr PORTD &= ~(1<<3) #include <avr/io.h> #include <util/delay.h> #include "english_font.h" void LCD_write_byte(unsigned char dat, unsigned char command); void LCD_init(); void LCD_clear(); void LCD_set_XY(unsigned char X, unsigned char Y); void LCD_write_char(unsigned char c); void LCD_write_english_string(unsigned char X,unsigned char Y,char *s); int main(void) { DDRD = 0x0F; LCD_init(); //LCD initialization LCD_write_english_string(0,0," Hello World ! "); LCD_write_english_string(0,1," bananas "); LCD_write_english_string(0,2,"dancing potato"); LCD_write_english_string(0,3," tomato"); LCD_write_english_string(0,4," with love "); LCD_write_english_string(0,5," from 4a4ik "); while(1) { } } void LCD_write_byte(unsigned char dat, unsigned char command) { unsigned char i; if (command == 1) LCD_DC_clr; else LCD_DC_set; for(i=0;i<8;i++) { if(dat&0x80) SDIN_set; else SDIN_clr; SCLK_clr; dat = dat << 1; SCLK_set; } } void LCD_init() { LCD_RST_clr; _delay_us(1); LCD_RST_set; _delay_us(1); LCD_write_byte(0x21, 1); // set LCD mode LCD_write_byte(0xc8, 1); // set bias voltage LCD_write_byte(0x06, 1); // temperature correction LCD_write_byte(0x13, 1); // 1:48 LCD_write_byte(0x20, 1); // use bias command, vertical LCD_write_byte(0x0c, 1); // set LCD mode,display normally LCD_clear(); // clear the LCD } void LCD_clear() { unsigned int i; LCD_write_byte(0x0c, 1); LCD_write_byte(0x80, 1); for (i=0; i<504; i++) { LCD_write_byte(0, 0); } } void LCD_set_XY(unsigned char X, unsigned char Y) { LCD_write_byte(0x40 | Y, 1); // column LCD_write_byte(0x80 | X, 1); // row } void LCD_write_char(unsigned char c) { unsigned char line; c -= 32; for (line=0; line<6; line++) LCD_write_byte(font6x8[c][line], 0); } void LCD_write_english_string(unsigned char X,unsigned char Y,char *s) { LCD_set_XY(X,Y); while (*s) { LCD_write_char(*s); s++; } }Github
Также есть удобная прога для того чтобы отображать изображения на дисплее.
LCD Assistant
Но на нашем дисплее правильно отображается только горизонтальная адресация. Из-за этого я написал свою программу обработки .bmp изображений.
Bmp to nokia 5110 Github
Она из изображения создаёт текстовый файл где прописаны все байты по табличкам в 1 массиве. Если попробовать изобразить изображение Бендера (84х48).
полученный файл выглядит следующим образом.
Это изобразить можно с помощью простого цикла:
for(int n = 0; n < 504; n++) { LCD_write_byte( frame_1[ n ], 0); }
Результат
При добавлении изображения, размер прошивки увеличился на 1461 байт.
Также я решил сделать бегущую строку:
Для движения изображения, я использовал вертикальную адресацию и этот код:
Также я решил сделать бегущую строку:
while(1) { for( int i = 83; i >= 0; i-- ) { for(int n = 0; n < 504; n++) { LCD_write_byte( frame_1[ n ], 0); } _delay_ms(50); LCD_set_XY( i, 0); } }
Datasheet
Спасибо сайтам:
Sparkfun
http://icstation.com/
У Вас ошибка тут... Команды это 0, а информация это 1... Исходя из этого при инициализации дисплея нужно использовать параметр 0, а не 1...
ОтветитьУдалитьСпасибо что заметили, забыл исправить.
УдалитьСкажи, а как тут кириллицу использовать?
ОтветитьУдалитьнаписать свой шрифт или найти готовый
УдалитьИ еще вопрос: как здесь вывести строку с UART ?
ОтветитьУдалитьНужно смещение в таблице для русских символов.Вот так работает:
ОтветитьУдалитьvoid lcd_chr(char chr)
{
lcd_base_addr(lcdCacheIdx);
// 5 pixel wide characters and add space
for(unsigned char i=0;i<5;i++)
{
if ( (chr >= 0x20) && (chr <= 0x7F) )
lcd_send(pgm_read_byte(&font5x7[chr-32][i]) << 1, LCD_DATA);
else if ( chr >= 0xC0 )
lcd_send(pgm_read_byte(&font5x7[chr-96][i]) << 1, LCD_DATA);
else
{
// Остальные игнорируем (их просто нет в таблице для экономии памяти)
chr = 95;
}
}
lcd_send(0, LCD_DATA);
lcdCacheIdx += 6;
}
И в таблицу вписать это:
ОтветитьУдалить{ 0x7C, 0x12, 0x11, 0x12, 0x7C }, // А 0xC0 192
{ 0x7F, 0x49, 0x49, 0x49, 0x31 }, // Б 0xC1 193
{ 0x7F, 0x49, 0x49, 0x49, 0x36 }, // В 0xC2 194
{ 0x7F, 0x01, 0x01, 0x01, 0x01 }, // Г 0xC3 195
{ 0x60, 0x3F, 0x21, 0x3F, 0x60 }, // Д 0xC4 196
{ 0x7F, 0x49, 0x49, 0x49, 0x41 }, // Е 0xC5 197
{ 0x77, 0x08, 0x7F, 0x08, 0x77 }, // Ж 0xC6 198
{ 0x22, 0x41, 0x49, 0x49, 0x36 }, // З 0xC7 199
{ 0x7F, 0x10, 0x08, 0x04, 0x7F }, // И 0xC8 200
{ 0x7E, 0x10, 0x09, 0x04, 0x7E }, // Й 0xC9 201
{ 0x7F, 0x08, 0x14, 0x22, 0x41 }, // К 0xCA 202
{ 0x40, 0x3E, 0x01, 0x01, 0x7F }, // Л 0xCB 203
{ 0x7F, 0x02, 0x0C, 0x02, 0x7F }, // М 0xCC 204
{ 0x7F, 0x08, 0x08, 0x08, 0x7F }, // Н 0xCD 205
{ 0x3E, 0x41, 0x41, 0x41, 0x3E }, // О 0xCE 206
{ 0x7F, 0x01, 0x01, 0x01, 0x7F }, // П 0xCF 207
{ 0x7F, 0x09, 0x09, 0x09, 0x06 }, // Р 0xD0 208
{ 0x3E, 0x41, 0x41, 0x41, 0x22 }, // С 0xD1 209
{ 0x01, 0x01, 0x7F, 0x01, 0x01 }, // Т 0xD2 210
{ 0x07, 0x48, 0x48, 0x48, 0x3F }, // У 0xD3 211
{ 0x0E, 0x11, 0x7F, 0x11, 0x0E }, // Ф 0xD4 212
{ 0x63, 0x14, 0x08, 0x14, 0x63 }, // Х 0xD5 213
{ 0x3F, 0x20, 0x20, 0x3F, 0x60 }, // Ц 0xD6 214
{ 0x07, 0x08, 0x08, 0x08, 0x7F }, // Ч 0xD7 215
{ 0x7F, 0x40, 0x7E, 0x40, 0x7F }, // Ш 0xD8 216
{ 0x3F, 0x20, 0x3F, 0x20, 0x7F }, // Щ 0xD9 217
{ 0x01, 0x7F, 0x48, 0x48, 0x30 }, // Ъ 0xDA 218
{ 0x7F, 0x48, 0x30, 0x00, 0x7F }, // Ы 0xDB 219
{ 0x00, 0x7F, 0x48, 0x48, 0x30 }, // Ь 0xDC 220
{ 0x22, 0x41, 0x49, 0x49, 0x3E }, // Э 0xDD 221
{ 0x7F, 0x08, 0x3E, 0x41, 0x3E }, // Ю 0xDE 222
{ 0x46, 0x29, 0x19, 0x09, 0x7F }, // Я 0xDF 223
{ 0x20, 0x54, 0x54, 0x54, 0x78 }, // а 0xE0 224
{ 0x3C, 0x4A, 0x4A, 0x4A, 0x31 }, // б 0xE1 225
{ 0x7C, 0x54, 0x54, 0x28, 0x00 }, // в 0xE2 226
{ 0x7C, 0x04, 0x04, 0x0C, 0x00 }, // г 0xE3 227
{ 0x60, 0x3C, 0x24, 0x3C, 0x60 }, // д 0xE4 228
{ 0x38, 0x54, 0x54, 0x54, 0x18 }, // е 0xE5 229
{ 0x6C, 0x10, 0x7C, 0x10, 0x6C }, // ж 0xE6 230
{ 0x00, 0x44, 0x54, 0x54, 0x28 }, // з 0xE7 231
{ 0x7C, 0x20, 0x10, 0x08, 0x7C }, // и 0xE8 232
{ 0x7C, 0x21, 0x12, 0x09, 0x7C }, // й 0xE9 233
{ 0x7C, 0x10, 0x28, 0x44, 0x00 }, // к 0xEA 234
{ 0x40, 0x38, 0x04, 0x04, 0x7C }, // л 0xEB 235
{ 0x7C, 0x08, 0x10, 0x08, 0x7C }, // м 0xEC 236
{ 0x7C, 0x10, 0x10, 0x10, 0x7C }, // н 0xED 237
{ 0x38, 0x44, 0x44, 0x44, 0x38 }, // о 0xEE 238
{ 0x7C, 0x04, 0x04, 0x04, 0x7C }, // п 0xEF 239
{ 0x7C, 0x14, 0x14, 0x14, 0x08 }, // р 0xF0 240
{ 0x38, 0x44, 0x44, 0x44, 0x00 }, // с 0xF1 241
{ 0x04, 0x04, 0x7C, 0x04, 0x04 }, // т 0xF2 242
{ 0x0C, 0x50, 0x50, 0x50, 0x3C }, // у 0xF3 243
{ 0x08, 0x14, 0x7C, 0x14, 0x08 }, // ф 0xF4 244
{ 0x44, 0x28, 0x10, 0x28, 0x44 }, // х 0xF5 245
{ 0x3C, 0x20, 0x20, 0x3C, 0x60 }, // ц 0xF6 246
{ 0x0C, 0x10, 0x10, 0x10, 0x7C }, // ч 0xF7 247
{ 0x7C, 0x40, 0x7C, 0x40, 0x7C }, // ш 0xF8 248
{ 0x3C, 0x20, 0x3C, 0x20, 0x7C }, // щ 0xF9 249
{ 0x04, 0x7C, 0x50, 0x50, 0x20 }, // ъ 0xFA 250
{ 0x7C, 0x50, 0x20, 0x00, 0x7C }, // ы 0xFB 251
{ 0x00, 0x7C, 0x50, 0x50, 0x20 }, // ь 0xFC 252
{ 0x28, 0x44, 0x54, 0x54, 0x38 }, // э 0xFD 253
{ 0x7C, 0x10, 0x38, 0x44, 0x38 }, // ю 0xFE 254
{ 0x48, 0x54, 0x34, 0x14, 0x7C } // я 0xFF 255
Код актуален и работоспособен .Автору большое спасибо за простоту и не нагроможденность
ОтветитьУдалить