--- title: "Подключение дисплея от Siemens C55/A55/A52 к AVR" categories: ["mcu", "archive"] date: 2016-02-19T00:00:00+03:00 draft: false featured_image: miniature.jpg --- Вот на днях решил порыться в своих запасах запчастей от мобильных телефонах. И заметил дисплей с маркировкой LPH8694-3. Погуглив, я выяснил что это дисплей от телефонов Siemens C55/A55/A52. Ну что ж добру пропадать, будем подключать. > :hint: Статья не закончена. > Когда-нибудь она будет доработана. ![Маркировка экрана|400](lcd.jpg) Экран имеет весьма странное разрешение экрана – 101 на 64 точки (в [даташите](PCF8812.pdf) вообще написано, что 102x64). Это намного больше, чем у дисплея, которым я обычно пользуюсь (у nokia 5110 – 64 на 48 точек). За основу я решил использовать [библиотеку](https://github.com/gresolio/N3310Lib) уважаемого товарища [gresolio](https://github.com/gresolio). Дисплеи очень похожи, но немного отличается инициализация. Да и доработать библиотеку под новое разрешение экрана тоже нужно. ![Распиновка дисплея (вид со стороны контактов)|338](lph8684.png) Главное отличие в инициализации, без чего дисплей не запустится вообще – способ питания. Нужно найти вот эту строку в **LcdInit**: ``` c LcdSend( 0x13, LCD_CMD ); // Настройка питания (LCD bias mode 1:48) ``` И заменить на: ```c LcdSend( 0x16, LCD_CMD ); ``` **LcdGotoXYFont**, до: ```c byte LcdGotoXYFont ( byte x, byte y ) { // Проверка границ if( x > 13 || y > 5 ) return OUT_OF_BORDER; // Вычисление указателя. Определен как адрес в пределах 504 байт LcdCacheIdx = x * 6 + y * 84; return OK; } ``` После: ```c byte LcdGotoXYFont ( byte x, byte y ) { // Проверка границ if( x > LCD_COLS || y > LCD_LINES ) return OUT_OF_BORDER; // Вычисление указателя. Определен как адрес в пределах 504 байт LcdCacheIdx = x * LCD_LINES + y * LCD_X_RES; return OK; } ``` **LcdChr**, до: ```c else if ( size == FONT_2X ) { tmpIdx = LcdCacheIdx - 84; ``` После: ```c else if ( size == FONT_2X ) { tmpIdx = LcdCacheIdx - LCD_X_RES; ``` До: ```c // Копируем две части в кэш LcdCache[tmpIdx++] = b1; LcdCache[tmpIdx++] = b1; LcdCache[tmpIdx + 82] = b2; LcdCache[tmpIdx + 83] = b2; ``` После: ```c LcdCache[tmpIdx++] = b1; LcdCache[tmpIdx++] = b1; LcdCache[tmpIdx + LCD_X_RES - 2] = b2; LcdCache[tmpIdx + LCD_X_RES - 1] = b2; ``` **LcdPixel**, до: ```c // Пересчет индекса и смещения index = ( ( y / 8 ) * 84 ) + x; offset = y - ( ( y / 8 ) * 8 ); ``` После ```c // Пересчет индекса и смещения index = ( ( y / LCD_LINES ) * LCD_X_RES ) + x; offset = y - ( ( y / 8) * LCD_LINES ); ``` **n3310.h**, до: ```c // Разрешение дисплея в пикселях #define LCD_X_RES 84 // разрешение по горизонтали #define LCD_Y_RES 48 // разрешение по вертикали ``` После: ```c // Разрешение дисплея в пикселях #define LCD_X_RES 102 // разрешение по горизонтали #define LCD_Y_RES 65 // разрешение по вертикали #define LCD_LINES (LCD_Y_RES / 8) // количество строк #define LCD_COLS (LCD_X_RES / 5) // количество столбцов ``` Возможно, это не всё. Вот то, что получилось: sc55.h: ```c /* * Имя : n3310.h * * Описание : Это заголовочный файл для драйвера графического LCD от Nokia 3310, а также его китайских клонов. * Базируется на коде библиотек написанных Sylvain Bissonnette и Fandi Gunawan: * http://www.microsyl.com/index.php/2010/03/24/nokia-lcd-library/ * http://fandigunawan.wordpress.com/2008/06/18/lcd-nokia-3310-pcd8544-driver-in-winavravr-gcc/ * Основные отличия между оригиналом и клоном хорошо описаны в статье от Aheir: * http://radiokot.ru/articles/29/ * * Автор : Xander Gresolio * Веб-страница : http://we.easyelectronics.ru/profile/XANDER/ * * Лицензия : GPL v3.0 * * Компилятор : WinAVR, GCC for AVR platform */ #ifndef _SC55_H_ #define _SC55_H_ #include // Порт к которому подключен LCD (здесь пример распиновки для ATmega8) #define LCD_PORT PORTB #define LCD_DDR DDRB // Распиновка порта #define LCD_DC_PIN PB1 #define LCD_CE_PIN PB2 #define LCD_DATA_PIN PB3 #define LCD_RST_PIN PB4 #define LCD_CLK_PIN PB5 // Разрешение дисплея в пикселях #define LCD_X_RES 102 // разрешение по горизонтали #define LCD_Y_RES 65 // разрешение по вертикали #define LCD_LINES (LCD_Y_RES / 8) // количество строк #define LCD_COLS (LCD_X_RES / 5) // количество столбцов // Настройки для рисования группы прямоугольников функцией LcdBars ( byte data[], byte numbBars, byte width, byte multiplier ) #define EMPTY_SPACE_BARS 2 // расстояние между прямоугольниками #define BAR_X 30 // координата x #define BAR_Y 47 // координата y // Размер кэша ( 84 * 48 ) / 8 = 504 байта #define LCD_CACHE_SIZE ( ( LCD_X_RES * LCD_Y_RES ) / 8 ) #define FALSE 0 #define TRUE 1 // Для возвращаемых значений #define OK 0 // Безошибочная отрисовка #define OUT_OF_BORDER 1 // Выход за границы дисплея #define OK_WITH_WRAP 2 // Переход на начало (ситуация автоинкремента указателя курсора при выводе длинного текста) typedef unsigned char byte; // Перечисления typedef enum { LCD_CMD = 0, // Команда LCD_DATA = 1 // Данные } LcdCmdData; typedef enum { PIXEL_OFF = 0, // Погасить пиксели дисплея PIXEL_ON = 1, // Включить пиксели дисплея PIXEL_XOR = 2 // Инвертировать пиксели } LcdPixelMode; typedef enum { FONT_1X = 1, // Обычный размер шрифта 5x7 FONT_2X = 2 // Увеличенный размер шрифта } LcdFontSize; // Прототипы функций, детальную информацию смотрим внутри n3310lcd.c void LcdInit ( void ); // Инициализация void LcdClear ( void ); // Очистка буфера void LcdUpdate ( void ); // Копирование буфера в ОЗУ дисплея void LcdImage ( const byte *imageData ); // Рисование картинки из массива в Flash ROM void LcdContrast ( byte contrast ); // Установка контрастности дисплея byte LcdGotoXYFont ( byte x, byte y ); // Установка курсора в позицию x,y byte LcdChr ( LcdFontSize size, byte ch ); // Вывод символа в текущей позиции byte LcdStr ( LcdFontSize size, byte dataArray[] ); // Вывод строки сохраненной в RAM byte LcdFStr ( LcdFontSize size, const byte *dataPtr ); // Вывод строки сохраненной в Flash ROM byte LcdPixel ( byte x, byte y, LcdPixelMode mode ); // Точка byte LcdLine ( byte x1, byte y1, byte x2, byte y2, LcdPixelMode mode ); // Линия byte LcdCircle ( byte x, byte y, byte radius, LcdPixelMode mode); // Окружность byte LcdRect ( byte x1, byte y1, byte x2, byte y2, LcdPixelMode mode ); // Прямоугольник byte LcdSingleBar ( byte baseX, byte baseY, byte height, byte width, LcdPixelMode mode ); // Один byte LcdBars ( byte data[], byte numbBars, byte width, byte multiplier ); // Несколько /* * Таблица для отображения символов (ASCII[0x20-0x7F] + CP1251[0xC0-0xFF] = всего 160 символов) */ static const byte FontLookup [][5] PROGMEM= { { 0x00, 0x00, 0x00, 0x00, 0x00 }, // 0x20 32 { 0x00, 0x00, 0x5F, 0x00, 0x00 }, // ! 0x21 33 { 0x00, 0x07, 0x00, 0x07, 0x00 }, // " 0x22 34 { 0x14, 0x7F, 0x14, 0x7F, 0x14 }, // # 0x23 35 { 0x24, 0x2A, 0x7F, 0x2A, 0x12 }, // $ 0x24 36 { 0x4C, 0x2C, 0x10, 0x68, 0x64 }, // % 0x25 37 { 0x36, 0x49, 0x55, 0x22, 0x50 }, // & 0x26 38 { 0x00, 0x05, 0x03, 0x00, 0x00 }, // ' 0x27 39 { 0x00, 0x1C, 0x22, 0x41, 0x00 }, // ( 0x28 40 { 0x00, 0x41, 0x22, 0x1C, 0x00 }, // ) 0x29 41 { 0x14, 0x08, 0x3E, 0x08, 0x14 }, // * 0x2A 42 { 0x08, 0x08, 0x3E, 0x08, 0x08 }, // + 0x2B 43 { 0x00, 0x00, 0x50, 0x30, 0x00 }, // , 0x2C 44 { 0x10, 0x10, 0x10, 0x10, 0x10 }, // - 0x2D 45 { 0x00, 0x60, 0x60, 0x00, 0x00 }, // . 0x2E 46 { 0x20, 0x10, 0x08, 0x04, 0x02 }, // / 0x2F 47 { 0x3E, 0x51, 0x49, 0x45, 0x3E }, // 0 0x30 48 { 0x00, 0x42, 0x7F, 0x40, 0x00 }, // 1 0x31 49 { 0x42, 0x61, 0x51, 0x49, 0x46 }, // 2 0x32 50 { 0x21, 0x41, 0x45, 0x4B, 0x31 }, // 3 0x33 51 { 0x18, 0x14, 0x12, 0x7F, 0x10 }, // 4 0x34 52 { 0x27, 0x45, 0x45, 0x45, 0x39 }, // 5 0x35 53 { 0x3C, 0x4A, 0x49, 0x49, 0x30 }, // 6 0x36 54 { 0x01, 0x71, 0x09, 0x05, 0x03 }, // 7 0x37 55 { 0x36, 0x49, 0x49, 0x49, 0x36 }, // 8 0x38 56 { 0x06, 0x49, 0x49, 0x29, 0x1E }, // 9 0x39 57 { 0x00, 0x36, 0x36, 0x00, 0x00 }, // : 0x3A 58 { 0x00, 0x56, 0x36, 0x00, 0x00 }, // ; 0x3B 59 { 0x08, 0x14, 0x22, 0x41, 0x00 }, // < 0x3C 60 { 0x14, 0x14, 0x14, 0x14, 0x14 }, // = 0x3D 61 { 0x00, 0x41, 0x22, 0x14, 0x08 }, // > 0x3E 62 { 0x02, 0x01, 0x51, 0x09, 0x06 }, // ? 0x3F 63 { 0x32, 0x49, 0x79, 0x41, 0x3E }, // @ 0x40 64 { 0x7E, 0x11, 0x11, 0x11, 0x7E }, // A 0x41 65 { 0x7F, 0x49, 0x49, 0x49, 0x36 }, // B 0x42 66 { 0x3E, 0x41, 0x41, 0x41, 0x22 }, // C 0x43 67 { 0x7F, 0x41, 0x41, 0x22, 0x1C }, // D 0x44 68 { 0x7F, 0x49, 0x49, 0x49, 0x41 }, // E 0x45 69 { 0x7F, 0x09, 0x09, 0x09, 0x01 }, // F 0x46 70 { 0x3E, 0x41, 0x49, 0x49, 0x7A }, // G 0x47 71 { 0x7F, 0x08, 0x08, 0x08, 0x7F }, // H 0x48 72 { 0x00, 0x41, 0x7F, 0x41, 0x00 }, // I 0x49 73 { 0x20, 0x40, 0x41, 0x3F, 0x01 }, // J 0x4A 74 { 0x7F, 0x08, 0x14, 0x22, 0x41 }, // K 0x4B 75 { 0x7F, 0x40, 0x40, 0x40, 0x40 }, // L 0x4C 76 { 0x7F, 0x02, 0x0C, 0x02, 0x7F }, // M 0x4D 77 { 0x7F, 0x04, 0x08, 0x10, 0x7F }, // N 0x4E 78 { 0x3E, 0x41, 0x41, 0x41, 0x3E }, // O 0x4F 79 { 0x7F, 0x09, 0x09, 0x09, 0x06 }, // P 0x50 80 { 0x3E, 0x41, 0x51, 0x21, 0x5E }, // Q 0x51 81 { 0x7F, 0x09, 0x19, 0x29, 0x46 }, // R 0x52 82 { 0x46, 0x49, 0x49, 0x49, 0x31 }, // S 0x53 83 { 0x01, 0x01, 0x7F, 0x01, 0x01 }, // T 0x54 84 { 0x3F, 0x40, 0x40, 0x40, 0x3F }, // U 0x55 85 { 0x1F, 0x20, 0x40, 0x20, 0x1F }, // V 0x56 86 { 0x3F, 0x40, 0x38, 0x40, 0x3F }, // W 0x57 87 { 0x63, 0x14, 0x08, 0x14, 0x63 }, // X 0x58 88 { 0x07, 0x08, 0x70, 0x08, 0x07 }, // Y 0x59 89 { 0x61, 0x51, 0x49, 0x45, 0x43 }, // Z 0x5A 90 { 0x00, 0x7F, 0x41, 0x41, 0x00 }, // [ 0x5B 91 { 0x02, 0x04, 0x08, 0x10, 0x20 }, // \ 0x5C 92 { 0x00, 0x41, 0x41, 0x7F, 0x00 }, // ] 0x5D 93 { 0x04, 0x02, 0x01, 0x02, 0x04 }, // ^ 0x5E 94 { 0x40, 0x40, 0x40, 0x40, 0x40 }, // _ 0x5F 95 { 0x00, 0x01, 0x02, 0x04, 0x00 }, // ` 0x60 96 { 0x20, 0x54, 0x54, 0x54, 0x78 }, // a 0x61 97 { 0x7F, 0x48, 0x44, 0x44, 0x38 }, // b 0x62 98 { 0x38, 0x44, 0x44, 0x44, 0x20 }, // c 0x63 99 { 0x38, 0x44, 0x44, 0x48, 0x7F }, // d 0x64 100 { 0x38, 0x54, 0x54, 0x54, 0x18 }, // e 0x65 101 { 0x08, 0x7E, 0x09, 0x01, 0x02 }, // f 0x66 102 { 0x0C, 0x52, 0x52, 0x52, 0x3E }, // g 0x67 103 { 0x7F, 0x08, 0x04, 0x04, 0x78 }, // h 0x68 104 { 0x00, 0x44, 0x7D, 0x40, 0x00 }, // i 0x69 105 { 0x20, 0x40, 0x44, 0x3D, 0x00 }, // j 0x6A 106 { 0x7F, 0x10, 0x28, 0x44, 0x00 }, // k 0x6B 107 { 0x00, 0x41, 0x7F, 0x40, 0x00 }, // l 0x6C 108 { 0x7C, 0x04, 0x18, 0x04, 0x78 }, // m 0x6D 109 { 0x7C, 0x08, 0x04, 0x04, 0x78 }, // n 0x6E 110 { 0x38, 0x44, 0x44, 0x44, 0x38 }, // o 0x6F 111 { 0x7C, 0x14, 0x14, 0x14, 0x08 }, // p 0x70 112 { 0x08, 0x14, 0x14, 0x18, 0x7C }, // q 0x71 113 { 0x7C, 0x08, 0x04, 0x04, 0x08 }, // r 0x72 114 { 0x48, 0x54, 0x54, 0x54, 0x20 }, // s 0x73 115 { 0x04, 0x3F, 0x44, 0x40, 0x20 }, // t 0x74 116 { 0x3C, 0x40, 0x40, 0x20, 0x7C }, // u 0x75 117 { 0x1C, 0x20, 0x40, 0x20, 0x1C }, // v 0x76 118 { 0x3C, 0x40, 0x30, 0x40, 0x3C }, // w 0x77 119 { 0x44, 0x28, 0x10, 0x28, 0x44 }, // x 0x78 120 { 0x0C, 0x50, 0x50, 0x50, 0x3C }, // y 0x79 121 { 0x44, 0x64, 0x54, 0x4C, 0x44 }, // z 0x7A 122 { 0x00, 0x08, 0x36, 0x41, 0x00 }, // { 0x7B 123 { 0x00, 0x00, 0x7F, 0x00, 0x00 }, // | 0x7C 124 { 0x00, 0x41, 0x36, 0x08, 0x00 }, // } 0x7D 125 { 0x08, 0x04, 0x08, 0x10, 0x08 }, // ~ 0x7E 126 { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, //  0x7F 127 { 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 }; #endif /* _N3310_H_ */ ``` sc55.c: ```c /* * Имя : n3310.c * * Описание : Это драйвер для графического LCD от Nokia 3310, а также его китайских клонов. * Базируется на коде библиотек написанных Sylvain Bissonnette и Fandi Gunawan: * http://www.microsyl.com/index.php/2010/03/24/nokia-lcd-library/ * http://fandigunawan.wordpress.com/2008/06/18/lcd-nokia-3310-pcd8544-driver-in-winavravr-gcc/ * Основные отличия между оригиналом и клоном хорошо описаны в статье от Aheir: * http://radiokot.ru/articles/29/ * * Автор : Xander Gresolio * Веб-страница : http://we.easyelectronics.ru/profile/XANDER/ * * Лицензия : GPL v3.0 * * Компилятор : WinAVR, GCC for AVR platform * * История : * Версия 1.0 (06.08.2011) * + Первая версия * + Добавлена поддержка китайских клонов LCD Nokia 3310 * + Полный перевод комментариев к исходному коду драйвера * + Таблица символов драйвера дополнена кириллицей (упрощенная Windows-1251) * + Добавлена функция рисования окружностей LcdCircle * - Исправлены ошибки в проверке корректности координат при вызове функций рисования * - Исправлена ошибка в функции LcdSingleBar (неверная отрисовка по y) */ #include #include #include "sc55.h" // Прототипы приватных функций драйвера static void LcdSend ( byte data, LcdCmdData cd ); static void Delay ( void ); // Глобальные переменные // Кэш в ОЗУ 84*48 бит или 504 байта static byte LcdCache [ LCD_CACHE_SIZE ]; // Чтобы не обновлять весь дисплей, а лишь ту часть что изменилась, // будем отмечать две границы кэша где произошли изменения. Затем // можно копировать эту часть кэша между границами в ОЗУ дисплея. static int LoWaterMark; // нижняя граница static int HiWaterMark; // верхняя граница // Указатель для работы с LcdCache[] static int LcdCacheIdx; // Флаг изменений кэша static byte UpdateLcd; /* * Имя : LcdInit * Описание : Производит инициализацию порта МК и контроллера LCD * Аргумент(ы) : Нет * Возвращаемое значение : Нет */ void LcdInit ( void ) { // Pull-up на вывод подключенный к reset дисплея LCD_PORT |= _BV ( LCD_RST_PIN ); // Устанавливаем нужные биты порта на выход LCD_DDR |= _BV( LCD_RST_PIN ) | _BV( LCD_DC_PIN ) | _BV( LCD_CE_PIN ) | _BV(LCD_DATA_PIN) | _BV(LCD_CLK_PIN); // Некалиброванная задержка Delay(); // Дергаем reset LCD_PORT &= ~( _BV( LCD_RST_PIN ) ); Delay(); LCD_PORT |= _BV ( LCD_RST_PIN ); // Отключаем LCD контроллер - высокий уровень на SCE LCD_PORT |= _BV( LCD_CE_PIN ); // Отправляем команды дисплею LcdSend( 0x21, LCD_CMD ); // Включаем расширенный набор команд (LCD Extended Commands) LcdSend( 0xC8, LCD_CMD ); // Установка контрастности (LCD Vop) LcdSend( 0x06, LCD_CMD ); // Установка температурного коэффициента (Temp coefficent) LcdSend( 0x16, LCD_CMD ); // Настройка питания (Bias n=2), siemens LcdSend( 0x20, LCD_CMD ); // Включаем стандартный набор команд и горизонтальную адресацию (LCD Standard Commands,Horizontal addressing mode) LcdSend( 0x0C, LCD_CMD ); // Нормальный режим (LCD in normal mode) // Первичная очистка дисплея LcdClear(); LcdUpdate(); } /* * Имя : LcdClear * Описание : Очищает дисплей. Далее необходимо выполнить LcdUpdate * Аргумент(ы) : Нет * Возвращаемое значение : Нет */ void LcdClear ( void ) { // // Очистка кэша дисплея // int i; // for ( i = 0; i < LCD_CACHE_SIZE; i++ ) // { // LcdCache[i] = 0x00; // } // Оптимизация от Jakub Lasinski (March 14 2009) memset( LcdCache, 0x00, LCD_CACHE_SIZE ); // Сброс указателей границ в максимальное значение LoWaterMark = 0; HiWaterMark = LCD_CACHE_SIZE - 1; // Установка флага изменений кэша UpdateLcd = TRUE; } /* * Имя : LcdUpdate * Описание : Копирует кэш в ОЗУ дисплея * Аргумент(ы) : Нет * Возвращаемое значение : Нет */ void LcdUpdate (void) { int i; if ( LoWaterMark < 0 ) LoWaterMark = 0; else if ( LoWaterMark >= LCD_CACHE_SIZE ) LoWaterMark = LCD_CACHE_SIZE - 1; if ( HiWaterMark < 0 ) HiWaterMark = 0; else if ( HiWaterMark >= LCD_CACHE_SIZE ) HiWaterMark = LCD_CACHE_SIZE - 1; // Устанавливаем начальный адрес в соответствии к LoWaterMark LcdSend( 0x80 | ( LoWaterMark % LCD_X_RES ), LCD_CMD ); LcdSend( 0x40 | ( LoWaterMark / LCD_X_RES ), LCD_CMD ); // Обновляем необходимую часть буфера дисплея for ( i = LoWaterMark; i <= HiWaterMark; i++ ) { // Для оригинального дисплея не нужно следить за адресом в буфере, // можно просто последовательно выводить данные LcdSend( LcdCache[i], LCD_DATA ); } // Сброс указателей границ в пустоту LoWaterMark = LCD_CACHE_SIZE - 1; HiWaterMark = 0; // Сброс флага изменений кэша UpdateLcd = FALSE; } /* * Имя : LcdSend * Описание : Отправляет данные в контроллер дисплея * Аргумент(ы) : data -> данные для отправки * cd -> команда или данные (смотри enum в n3310.h) * Возвращаемое значение : Нет */ static void LcdSend ( byte data, LcdCmdData cd ) { // Включаем контроллер дисплея (низкий уровень активный) LCD_PORT &= ~( _BV( LCD_CE_PIN ) ); byte i; if (cd == LCD_DATA) LCD_PORT |= _BV(LCD_DC_PIN); else LCD_PORT &= ~_BV(LCD_DC_PIN); for (i = 0; i < 8; i++) { if ((data >> (7 - i)) & 1) { LCD_PORT |= _BV(LCD_DATA_PIN); } else { LCD_PORT &= ~_BV(LCD_DATA_PIN); } LCD_PORT |= _BV(LCD_CLK_PIN); LCD_PORT &= ~_BV(LCD_CLK_PIN); } LCD_PORT |= _BV(LCD_DATA_PIN); LCD_PORT |= _BV(LCD_DC_PIN); // Отключаем контроллер дисплея LCD_PORT |= _BV( LCD_CE_PIN ); } /* * Имя : LcdContrast * Описание : Устанавливает контрастность дисплея * Аргумент(ы) : контраст -> значение от 0x00 к 0x7F * Возвращаемое значение : Нет */ void LcdContrast ( byte contrast ) { LcdSend( 0x21, LCD_CMD ); // Расширенный набор команд LcdSend( 0x80 | contrast, LCD_CMD ); // Установка уровня контрастности LcdSend( 0x20, LCD_CMD ); // Стандартный набор команд, горизонтальная адресация } /* * Имя : Delay * Описание : Некалиброванная задержка для процедуры инициализации LCD * Аргумент(ы) : Нет * Возвращаемое значение : Нет */ static void Delay ( void ) { int i; for ( i = -32000; i < 32000; i++ ); } /* * Имя : LcdGotoXYFont * Описание : Устанавливает курсор в позицию x,y относительно стандартного размера шрифта * Аргумент(ы) : x,y -> координаты новой позиции курсора. Значения: 0,0 .. 13,5 * Возвращаемое значение : смотри возвращаемое значение в n3310.h */ byte LcdGotoXYFont ( byte x, byte y ) { // Проверка границ if( x > LCD_COLS || y > LCD_LINES ) return OUT_OF_BORDER; // Вычисление указателя. Определен как адрес в пределах 504 байт LcdCacheIdx = x * LCD_LINES + y * LCD_X_RES; return OK; } /* * Имя : LcdChr * Описание : Выводит символ в текущей позиции курсора, затем инкрементирует положение курсора * Аргумент(ы) : size -> размер шрифта. Смотри enum в n3310.h * ch -> символ для вывода * Возвращаемое значение : смотри возвращаемое значение в n3310lcd.h */ byte LcdChr ( LcdFontSize size, byte ch ) { byte i, c; byte b1, b2; int tmpIdx; if ( LcdCacheIdx < LoWaterMark ) { // Обновляем нижнюю границу LoWaterMark = LcdCacheIdx; } if ( (ch >= 0x20) && (ch <= 0x7F) ) { // Смещение в таблице для символов ASCII[0x20-0x7F] ch -= 32; } else if ( ch >= 0xC0 ) { // Смещение в таблице для символов CP1251[0xC0-0xFF] ch -= 96; } else { // Остальные игнорируем (их просто нет в таблице для экономии памяти) ch = 95; } if ( size == FONT_1X ) { for ( i = 0; i < 5; i++ ) { // Копируем вид символа из таблицы в кэш LcdCache[LcdCacheIdx++] = pgm_read_byte( &(FontLookup[ch][i]) ) << 1; } } else if ( size == FONT_2X ) { tmpIdx = LcdCacheIdx - LCD_X_RES; if ( tmpIdx < LoWaterMark ) { LoWaterMark = tmpIdx; } if ( tmpIdx < 0 ) return OUT_OF_BORDER; for ( i = 0; i < 5; i++ ) { // Копируем вид символа из таблицы у временную переменную c = pgm_read_byte(&(FontLookup[ch][i])) << 1; // Увеличиваем картинку // Первую часть b1 = (c & 0x01) * 3; b1 |= (c & 0x02) * 6; b1 |= (c & 0x04) * 12; b1 |= (c & 0x08) * 24; c >>= 4; // Вторую часть b2 = (c & 0x01) * 3; b2 |= (c & 0x02) * 6; b2 |= (c & 0x04) * 12; b2 |= (c & 0x08) * 24; // Копируем две части в кэш LcdCache[tmpIdx++] = b1; LcdCache[tmpIdx++] = b1; LcdCache[tmpIdx + LCD_X_RES - 2] = b2; LcdCache[tmpIdx + LCD_X_RES - 1] = b2; } // Обновляем x координату курсора LcdCacheIdx = (LcdCacheIdx + 11) % LCD_CACHE_SIZE; } if ( LcdCacheIdx > HiWaterMark ) { // Обновляем верхнюю границу HiWaterMark = LcdCacheIdx; } // Горизонтальный разрыв между символами LcdCache[LcdCacheIdx] = 0x00; // Если достигли позицию указателя LCD_CACHE_SIZE - 1, переходим в начало if(LcdCacheIdx == (LCD_CACHE_SIZE - 1) ) { LcdCacheIdx = 0; return OK_WITH_WRAP; } // Иначе просто инкрементируем указатель LcdCacheIdx++; return OK; } /* * Имя : LcdStr * Описание : Эта функция предназначена для печати строки которая хранится в RAM * Аргумент(ы) : size -> размер шрифта. Смотри enum в n3310.h * dataArray -> массив содержащий строку которую нужно напечатать * Возвращаемое значение : смотри возвращаемое значение в n3310lcd.h */ byte LcdStr ( LcdFontSize size, byte dataArray[] ) { byte tmpIdx=0; byte response; while( dataArray[ tmpIdx ] != '\0' ) { // Выводим символ response = LcdChr( size, dataArray[ tmpIdx ] ); // Не стоит волноваться если произойдет OUT_OF_BORDER, // строка будет печататься дальше из начала дисплея if( response == OUT_OF_BORDER) return OUT_OF_BORDER; // Увеличиваем указатель tmpIdx++; } return OK; } /* * Имя : LcdFStr * Описание : Эта функция предназначена для печати строки которая хранится в Flash ROM * Аргумент(ы) : size -> размер шрифта. Смотри enum в n3310.h * dataPtr -> указатель на строку которую нужно напечатать * Возвращаемое значение : смотри возвращаемое значение в n3310lcd.h * Пример : LcdFStr(FONT_1X, PSTR("Hello World")); * LcdFStr(FONT_1X, &name_of_string_as_array); */ byte LcdFStr ( LcdFontSize size, const byte *dataPtr ) { byte c; byte response; for ( c = pgm_read_byte( dataPtr ); c; ++dataPtr, c = pgm_read_byte( dataPtr ) ) { // Выводим символ response = LcdChr( size, c ); if(response == OUT_OF_BORDER) return OUT_OF_BORDER; } return OK; } /* * Имя : LcdPixel * Описание : Отображает пиксель по абсолютным координатам (x,y) * Аргумент(ы) : x,y -> абсолютные координаты пикселя * mode -> Off, On или Xor. Смотри enum в n3310.h * Возвращаемое значение : смотри возвращаемое значение в n3310lcd.h */ byte LcdPixel ( byte x, byte y, LcdPixelMode mode ) { int index; byte offset; byte data; // Защита от выхода за пределы if ( x >= LCD_X_RES || y >= LCD_Y_RES) return OUT_OF_BORDER; // Пересчет индекса и смещения index = ( ( y / LCD_LINES ) * LCD_X_RES ) + x; offset = y - ( ( y / 8) * LCD_LINES ); data = LcdCache[ index ]; // Обработка битов // Режим PIXEL_OFF if ( mode == PIXEL_OFF ) { data &= ( ~( 0x01 << offset ) ); } // Режим PIXEL_ON else if ( mode == PIXEL_ON ) { data |= ( 0x01 << offset ); } // Режим PIXEL_XOR else if ( mode == PIXEL_XOR ) { data ^= ( 0x01 << offset ); } // Окончательный результат копируем в кэш LcdCache[ index ] = data; if ( index < LoWaterMark ) { // Обновляем нижнюю границу LoWaterMark = index; } if ( index > HiWaterMark ) { // Обновляем верхнюю границу HiWaterMark = index; } return OK; } /* * Имя : LcdLine * Описание : Рисует линию между двумя точками на дисплее (алгоритм Брезенхэма) * Аргумент(ы) : x1, y1 -> абсолютные координаты начала линии * x2, y2 -> абсолютные координаты конца линии * mode -> Off, On или Xor. Смотри enum в n3310.h * Возвращаемое значение : смотри возвращаемое значение в n3310lcd.h */ byte LcdLine ( byte x1, byte y1, byte x2, byte y2, LcdPixelMode mode ) { int dx, dy, stepx, stepy, fraction; byte response; // dy y2 - y1 // -- = ------- // dx x2 - x1 dy = y2 - y1; dx = x2 - x1; // dy отрицательное if ( dy < 0 ) { dy = -dy; stepy = -1; } else { stepy = 1; } // dx отрицательное if ( dx < 0 ) { dx = -dx; stepx = -1; } else { stepx = 1; } dx <<= 1; dy <<= 1; // Рисуем начальную точку response = LcdPixel( x1, y1, mode ); if(response) return response; // Рисуем следующие точки до конца if ( dx > dy ) { fraction = dy - ( dx >> 1); while ( x1 != x2 ) { if ( fraction >= 0 ) { y1 += stepy; fraction -= dx; } x1 += stepx; fraction += dy; response = LcdPixel( x1, y1, mode ); if(response) return response; } } else { fraction = dx - ( dy >> 1); while ( y1 != y2 ) { if ( fraction >= 0 ) { x1 += stepx; fraction -= dy; } y1 += stepy; fraction += dx; response = LcdPixel( x1, y1, mode ); if(response) return response; } } // Установка флага изменений кэша UpdateLcd = TRUE; return OK; } /* * Имя : LcdCircle * Описание : Рисует окружность (алгоритм Брезенхэма) * Аргумент(ы) : x, y -> абсолютные координаты центра * radius -> радиус окружности * mode -> Off, On или Xor. Смотри enum в n3310.h * Возвращаемое значение : смотри возвращаемое значение в n3310lcd.h */ byte LcdCircle(byte x, byte y, byte radius, LcdPixelMode mode) { signed char xc = 0; signed char yc = 0; signed char p = 0; if ( x >= LCD_X_RES || y >= LCD_Y_RES) return OUT_OF_BORDER; yc = radius; p = 3 - (radius<<1); while (xc <= yc) { LcdPixel(x + xc, y + yc, mode); LcdPixel(x + xc, y - yc, mode); LcdPixel(x - xc, y + yc, mode); LcdPixel(x - xc, y - yc, mode); LcdPixel(x + yc, y + xc, mode); LcdPixel(x + yc, y - xc, mode); LcdPixel(x - yc, y + xc, mode); LcdPixel(x - yc, y - xc, mode); if (p < 0) p += (xc++ << 2) + 6; else p += ((xc++ - yc--)<<2) + 10; } // Установка флага изменений кэша UpdateLcd = TRUE; return OK; } /* * Имя : LcdSingleBar * Описание : Рисует один закрашенный прямоугольник * Аргумент(ы) : baseX -> абсолютная координата x (нижний левый угол) * baseY -> абсолютная координата y (нижний левый угол) * height -> высота (в пикселях) * width -> ширина (в пикселях) * mode -> Off, On или Xor. Смотри enum в n3310.h * Возвращаемое значение : смотри возвращаемое значение в n3310lcd.h */ byte LcdSingleBar ( byte baseX, byte baseY, byte height, byte width, LcdPixelMode mode ) { byte tmpIdxX,tmpIdxY,tmp; byte response; // Проверка границ if ( ( baseX >= LCD_X_RES) || ( baseY >= LCD_Y_RES) ) return OUT_OF_BORDER; if ( height > baseY ) tmp = 0; else tmp = baseY - height + 1; // Рисование линий for ( tmpIdxY = tmp; tmpIdxY <= baseY; tmpIdxY++ ) { for ( tmpIdxX = baseX; tmpIdxX < (baseX + width); tmpIdxX++ ) { response = LcdPixel( tmpIdxX, tmpIdxY, mode ); if(response) return response; } } // Установка флага изменений кэша UpdateLcd = TRUE; return OK; } /* * Имя : LcdBars * Описание : Рисует группу закрашенных прямоугольников (в режиме PIXEL_ON) * Аргумент(ы) : data[] -> данные которые нужно отобразить * numbBars -> количество прямоугольников * width -> ширина (в пикселях) * multiplier -> множитель для высоты * Возвращаемое значение : смотри возвращаемое значение в n3310lcd.h * Примечание : Пожалуйста проверьте значения EMPTY_SPACE_BARS, BAR_X, BAR_Y в n3310.h * Пример : byte example[5] = {1, 2, 3, 4, 5}; * LcdBars(example, 5, 3, 2); */ byte LcdBars ( byte data[], byte numbBars, byte width, byte multiplier ) { byte b; byte tmpIdx = 0; byte response; for ( b = 0; b < numbBars ; b++ ) { // Защита от выхода за пределы if ( tmpIdx > LCD_X_RES - 1 ) return OUT_OF_BORDER; // Расчет значения x tmpIdx = ((width + EMPTY_SPACE_BARS) * b) + BAR_X; // Рисуем один прямоугольник response = LcdSingleBar( tmpIdx, BAR_Y, data[b] * multiplier, width, PIXEL_ON); if(response == OUT_OF_BORDER) return response; } // Установка флага изменений кэша UpdateLcd = TRUE; return OK; } /* * Имя : LcdRect * Описание : Рисует незакрашенный прямоугольник * Аргумент(ы) : x1 -> абсолютная координата x левого верхнего угла * y1 -> абсолютная координата y левого верхнего угла * x2 -> абсолютная координата x правого нижнего угла * y2 -> абсолютная координата y правого нижнего угла * mode -> Off, On или Xor. Смотри enum в n3310.h * Возвращаемое значение : смотри возвращаемое значение в n3310lcd.h */ byte LcdRect ( byte x1, byte y1, byte x2, byte y2, LcdPixelMode mode ) { byte tmpIdx; // Проверка границ if ( ( x1 >= LCD_X_RES) || ( x2 >= LCD_X_RES) || ( y1 >= LCD_Y_RES) || ( y2 >= LCD_Y_RES) ) return OUT_OF_BORDER; if ( ( x2 > x1 ) && ( y2 > y1 ) ) { // Рисуем горизонтальные линии for ( tmpIdx = x1; tmpIdx <= x2; tmpIdx++ ) { LcdPixel( tmpIdx, y1, mode ); LcdPixel( tmpIdx, y2, mode ); } // Рисуем вертикальные линии for ( tmpIdx = y1; tmpIdx <= y2; tmpIdx++ ) { LcdPixel( x1, tmpIdx, mode ); LcdPixel( x2, tmpIdx, mode ); } // Установка флага изменений кэша UpdateLcd = TRUE; } return OK; } /* * Имя : LcdImage * Описание : Рисует картинку из массива сохраненного в Flash ROM * Аргумент(ы) : Указатель на массив картинки * Возвращаемое значение : Нет */ void LcdImage ( const byte *imageData ) { // // Инициализация указателя кэша // LcdCacheIdx = 0; // // В пределах кэша // for ( LcdCacheIdx = 0; LcdCacheIdx < LCD_CACHE_SIZE; LcdCacheIdx++ ) // { // // Копируем данные из массива в кэш // LcdCache[LcdCacheIdx] = pgm_read_byte( imageData++ ); // } // Оптимизация от Jakub Lasinski (March 14 2009) memcpy_P( LcdCache, imageData, LCD_CACHE_SIZE ); // Тоже самое что и выше, но занимает меньше памяти и быстрее выполняется // Сброс указателей границ в максимальное значение LoWaterMark = 0; HiWaterMark = LCD_CACHE_SIZE - 1; // Установка флага изменений кэша UpdateLcd = TRUE; } ``` main.c: ```c #include #include "sc55.h" int main() { LcdInit(); LcdContrast(0x45); while (1) { LcdClear(); LcdRect(0, 0, LCD_X_RES - 2, LCD_Y_RES - 2, PIXEL_ON); LcdLine(0, 0, LCD_X_RES - 2, LCD_Y_RES - 2, PIXEL_ON); LcdLine(0, LCD_Y_RES - 2, LCD_X_RES - 2, 0, PIXEL_ON); LcdUpdate(); _delay_ms(1000); LcdClear(); LcdGotoXYFont(0, 2); LcdStr(FONT_2X, "Привет,"); LcdGotoXYFont(0, 4); LcdStr(FONT_2X, "мир!"); LcdUpdate(); _delay_ms(1000); } } ``` ![|400](helloworld.jpg) ![|400](lines.jpg)