Праздник жизни по гиковски: с ARM и TFT LCD

    Введение


    Результат Увидев посты от dlinyj, goodic и Hoshi я в очередной раз ощутил, что Хабр — торт.

    Первый пост касался написания драйвера символьного дисплея на базе HD44780 для Linux (Создание собственных драйверов под Linux от dlinyj); отличными ответами на него послужили посты хабраюзеров goodic (Поздравление по гиковски, без написания дров) и Hoshi (Новогодняя малина — прикручиваем экран HD44780 к Raspberry Pi).

    Мне тоже захотелось поучаствовать в этом празднике жизни и реализовать свой аппаратный vt52-like терминал. Символьного дисплея у меня не оказалось, но был китайский dev-board на базе ARM Cortex-M3 с полноценным TFT-дисплеем 240х320, частичной документацией.

    Запас энтузиазма в наличии имелся, поэтому, проснувщись в воскресенье днем (~17 MSK) я приступил к написанию embedded драйвера для данного LCD.

    Если вам интересно embedded-программирование по ARM, электроника или просто результат — прошу под кат.

    Железо


    В моем распоряжении была простая отладочная плата из Поднебесной (стоимостью около $20) на базе микроконтроллера ST STM32F103RB с аппаратным мостом USB-to-UART Prolific PL-2303HX, кучей мелкой периферии и TFT LCD с контроллером Ilitek ILI9320 с неведомой схемой подключения.

    В качестве внутрисхемного отладчика и программатора использовался Olimex JTAG ARM-TINY-USB-H. Хороший девайс, нормально работает с OpenOCD.

    devboard
    devboard


    Точнее сказать, изначально даже не было известно, что за контроллер стоит на LCD. Все, что можно было узнать из дисплейного модуля, что он подключен по 16-bit шине, имеет сигналы nCS, nWR, nRD, BL_EN и RS,
    назначение которых было угадать не сложно:
    • nCS — активация шины дисплея (здесь и далее префикс n означает, что активный уровень сигнала — 0)
    • BL_EN — управление подсветкой
    • nWR — записи
    • nRD — чтение
    • RS — выбор регистра


    В одном из архивов с документацией, найденных на просторах Китайского сегмента интернета была схожая плата с
    модулем Ilitek 932x.

    Программные интерфейсы


    Низкоуровневый интерфейс

    Так как в рунете описаний работы с этим LCD-контроллером не много, я, пожалуй, опишу низкоуровневый интерфейс.

    Их у данного контроллера по сути 4: i80-system (параллельный интерфейс, a-la обычная память, похожий на интерфейс HD44780), SPI, VSYNC (system + VSYNC, с внутренним тактированием) и RGB (VSYNC, HSYNC, ENABLE, с внешним тактированием DOTCLK). В моём случае доступен i80-system и, возможно, SPI (не проверял).

    Т. к. я использовал только system, то его описание и займемся. Дабы сильно не загружать в статью — будет в спойлере.

    Электрический интерфейс ILI9320
    На электрическом уровне работа с цифровой техникой обычно описывается timing-диаграммами. В нашем случае есть пять управляющих сигналов и 16-ти битная шина данных.

    Перед передачей контроллеру какой-либо информации следует активировать интерфейс сигналом nCS, выставив его в 0.

    Далее, при выставленном в 0 RS записывается адрес регистра в который будет записываться информация (фактическая запись осуществляется активацией сигнала nWR. Сигнал RS выставляется обратно в 1.

    После этого выполняется фактическая операция чтения или записи (с помощью nRD и nWR соответственно).

    Диаграммы этих процессов выглядят следующим образом:
    op
    read LCD read op
    write LCD write op


    При записи/чтении из GRAM используется специальный регистр 0x22. Кроме того, контроллер может делать автоинкремент
    адреса GRAM, что позволяет читать/писать её содержимое последовательно.

    Диаграммы:
    op
    GRAM read LCD GRAM read op
    GRAM write LCD GRAM write op


    После выполнения операций nCS выставляется обратно в 1.

    Для рисования timing-диаграмм нашел прекрасный проект wavedrom, работающий в браузере. Тестировать тут (здесь же были подготовлены схемы выше).


    На основе электрического интерфейса были написаны низкоуровневые функции:

    lcd_ll_funcs
    void _lcd_select(void) { GPIO_ResetBits(GPIOC, GPIO_Pin_9); }
    void _lcd_deselect(void) { GPIO_SetBits(GPIOC, GPIO_Pin_9); }
    void _lcd_rs_set(void) { GPIO_SetBits(GPIOC, GPIO_Pin_8); }
    void _lcd_rs_reset(void) { GPIO_ResetBits(GPIOC, GPIO_Pin_8); }
    void _lcd_rd_en(void) { GPIO_ResetBits(GPIOC, GPIO_Pin_11); }
    void _lcd_rd_dis(void) { GPIO_SetBits(GPIOC, GPIO_Pin_11); }
    void _lcd_wr_en(void) { GPIO_ResetBits(GPIOC, GPIO_Pin_10); }
    void _lcd_wr_dis(void) { GPIO_SetBits(GPIOC, GPIO_Pin_10); }
    
    void _lcd_bl_en(void) { GPIO_SetBits(GPIOC, GPIO_Pin_12); }
    void _lcd_bl_dis(void) { GPIO_ResetBits(GPIOC, GPIO_Pin_12); }
    
    // changes DB[15:0] GPIO pins mode
    void lcd_gpio_conf(GPIOMode_TypeDef mode);
    
    void _lcd_put_data(u16 data) {
        // data[0-7] -> GPIOC[0-7], data[8-15] -> GPIOB[8-15]
        GPIOB->ODR = (GPIOB->ODR&0x00ff)|(data&0xff00);
        GPIOC->ODR = (GPIOC->ODR&0xff00)|(data&0x00ff);
    }
    
    u16 _lcd_read_data(void) {
        lcd_gpio_conf(GPIO_Mode_IN_FLOATING);
        u16 result = (GPIOB->IDR&0xff00)|(GPIOC->IDR&0x00ff);
        lcd_gpio_conf(GPIO_Mode_Out_PP);
        return result;
    }
    
    // assume that lcd_select() was done before it
    void _lcd_tx_reg(u8 addr) {
        _lcd_put_data(addr);
        _lcd_rs_reset();
        _lcd_wr_en();
        _lcd_wr_dis();
        _lcd_rs_set();
    }
    
    // assume that _lcd_tx_reg(u8) was done before it
    void _lcd_tx_data(u16 data) {
        _lcd_put_data(data);
        _lcd_wr_en();
        _lcd_wr_dis();
    }
    
    // assume that _lcd_tx_reg(u8) was done before it
    u16 _lcd_rx_data(void) {
        _lcd_rd_en();
        u16 result = _lcd_read_data();
        _lcd_rd_dis();
        return result;
    }
    


    Для ускорения можно заинлайнить эти функции и преобразовать в макросы (с которыми Eclipse не очень дружит, к сожалению).

    На основе этих функций реализованы функции записи в регистр, чтения из регистра, блиттинг изображения.

    Высокоуровневый интерфейс

    Функции LCD-дисплея для основной части программы доступны через следующее API:

    u16 lcd_init(void);
    
    void lcd_set_cursor(u16 x, u16 y);
    void lcd_set_window(u16 left, u16 top, u16 right, u16 bottom);
    
    void lcd_fill(u32 color);
    void lcd_rect(u16 left, u16 top, u16 right, u16 bottom);
    void lcd_put_char_at(u32 data, u16 x, u16 y);
    
    u32 lcd_get_fg(void);
    u32 lcd_get_bg(void);
    void lcd_set_fg(u32 color);
    void lcd_set_bg(u32 color);
    


    Функции терминала используют этот интерфейс для всех своих операций.

    Наиболее интересной частью является функция рисования символа, т. к. за ней скрывается вся работа со шрифтами. Выглядит она следующим образом:

    lcd_put_char_at
    void lcd_put_char_at(u32 data, u16 x, u16 y) {
        u8 xsize, ysize;
        u8 *char_img;
        lcd_get_char(data, &xsize, &ysize, &char_img);
    
        lcd_set_cursor(x, y);
        lcd_set_window(x, y, x + xsize, y + ysize);
    
        _lcd_select();
        _lcd_tx_reg(0x22);
        // works only for 8xN fonts
        for(u8 i = 0; i < ysize; i++) {
            u8 str = char_img[i];
            for(u8 j = 0; j < xsize; j++) {
                _lcd_tx_data((str&(1<<(xsize-j-1)))?fg_color:bg_color);
            }
        }
        _lcd_deselect();
    }
    


    Как можно увидеть, ссылка на битмап символа и его размеры приходит из функции lcd_get_char по коду символа (он 32-х битный, чтобы дополнительными символами не трограть ASCII-часть).

    В текущий момент используется шрифт, содержащий нижнюю часть ASCII-таблицы, плюс «ёлочка». Желающие могут попробовать её найти ,)

    debug
    debug


    Наименее интересной и наиболее затратной (в смысле времени написания) явлется функция инициализации дисплея:
    lcd_init: для тех, кто хочет испугаться
    u16 lcd_init(void) {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);
    
        GPIO_InitTypeDef gpio_conf;
    
        gpio_conf.GPIO_Speed = GPIO_Speed_50MHz;
        gpio_conf.GPIO_Mode = GPIO_Mode_Out_PP;
        gpio_conf.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
        GPIO_Init(GPIOC, &gpio_conf);
    
        lcd_gpio_conf(GPIO_Mode_Out_PP);
    
        // to init state (0xffff on db0-15, backlit is disabled, nCS, nWR, nRD and RS are high)
        _lcd_bl_dis();
        _lcd_put_data(0xffff);
        _lcd_deselect();
        _lcd_wr_dis();
        _lcd_rd_dis();
        _lcd_rs_set();
    
        // osc enable
        _lcd_bl_dis();
        lcd_write_reg(0x00, 0x0001);
        delay_ms(100);
        u16 lcd_code = lcd_read_reg(0x00);
        delay_ms(100);
    
        // driver output control (S720-S1)
        lcd_write_reg(0x01, 0x0100);
    
        // driving wave control (line inv)
        lcd_write_reg(0x02, 0x0700);
    
        // entry mode (horiz, dir(h+,v+), hwm-, bgr+)
        lcd_write_reg(0x03, 0x1030);
    
        // resize (off)
        lcd_write_reg(0x04, 0x0000);
    
        // display control 2 (skip 2 lines on front porch and on back porch)
        lcd_write_reg(0x08, 0x0202);
    
        // display control 3-4 (scan mode normal, fmark off)
        lcd_write_reg(0x09, 0x0000);
        lcd_write_reg(0x0a, 0x0000);
    
        // RGB disp iface control (int clock, sys int, 16bit)
        lcd_write_reg(0x0c, 0x0001);
    
        // frame marker  position (isn't used)
        lcd_write_reg(0x0d, 0x0000);
    
        // RGB disp iface control 2 (all def, we don't use rgb)
        lcd_write_reg(0x0f, 0x0000);
    
        // power on seq
        lcd_write_reg(0x07, 0x0021);
        delay_ms(10);
    
        // turn on power supply and configure it (enable sources, set contrast, power supply on)
        lcd_write_reg(0x10, 0x16b0);
        // set normal voltage and max dcdc freq
        lcd_write_reg(0x11, 0x0007);
        // internal vcomh (see 0x29), pon, gray level (0x08)
        lcd_write_reg(0x12, 0x0118);
        // set vcom to 0.92 * vreg1out
        lcd_write_reg(0x13, 0x0b00);
        // vcomh = 0.69 * vreg1out
        lcd_write_reg(0x29, 0x0000);
    
        // set x and y range
        lcd_write_reg(0x50, 0);
        lcd_write_reg(0x51, LCD_WIDTH-1);
        lcd_write_reg(0x52, 0);
        lcd_write_reg(0x53, LCD_HEIGHT-1);
    
        // gate scan control (scan direction, display size)
        lcd_write_reg(0x60, 0x2700);
        lcd_write_reg(0x61, 0x0001);
        lcd_write_reg(0x6a, 0x0000);
    
        // partial displays off
        for(u8 addr = 0x80; addr < 0x86; addr++) {
            lcd_write_reg(addr, 0x0000);
        }
    
        // panel iface control (19 clock/line)
        lcd_write_reg(0x90, 0x0013);
    
        // lcd timings
        lcd_write_reg(0x92, 0x0000);
        lcd_write_reg(0x93, 0x0001);
        lcd_write_reg(0x95, 0x0110);
        lcd_write_reg(0x97, 0x0000);
        lcd_write_reg(0x98, 0x0000);
    
        lcd_write_reg(0x07, 0x0133);
    
        // turn on backlit after init done
        _lcd_bl_en();
    
        return lcd_code;
    }
    


    Реализация терминала


    Эта часть ничем особым не примечательна. Реализован unbuffered-терминал, с частью кодов из предыдущих статей.

    escape sequences
    Escape-последовательности:
    • \033[A = Переместить курсор на одну строку вверх
    • \033[B = Переместить курсор на одну строку вниз
    • \033[C = Сдвинуть курсор на одну позицию вправо
    • \033[D = Сдвинуть курсор на одну позицию влево
    • \033[H = Переместить курсор в левый верхний угол — домой (позиция 0,0)
    • \033[J = Очистить всё, НЕ возвращает курсор домой!
    • \033[K = Стирает до конца строки, НЕ возвращает курсор домой!
    • \033[M = Новая карта символов — не реализована
    • \033[Y = Позиция, принимает Y-X
    • \033[X = Позиция, принимает X-Y
    • \033[R = CGRAM Выбор ячейки памяти — не реализована, т. к. нет CGRAM
    • \033[V = Прокрутка включена — не реализована
    • \033[W = Прокрутка вылючена — не реализована
    • \033[b = Подсветка включена-выключена — не реализована


    Другие полезные коды:
    • \r = Возврат каретки (возвращают курсор в позицию 0 на текущей линии!)
    • \n = Новая линия
    • \t = Табуляция (по умолчанию 3 символа)



    Коммуникации


    Для взаимодействия с внешним миром используется USART1 в асинхронном режиме через преобразователь USB-to-UART PL-2303HX.

    С точки зрения хоста с Linux на борту это /dev/ttyUSBx. К сожалению, драйвера для pl2303 оказались довольно нестабильными. Но, как только подцепятся, работают прилично.

    Чтобы не опрашивать UART в основном цикле (который пустой), работа с ним реализована на прерываниях.

    С программной точки зрения это значит, что после инициализации USART1 необходимо настроить соответствующий вектор прерывания в NVIC.

    Выглядит это следующим образом:
    NVIC_InitTypeDef nvic_conf;
    nvic_conf.NVIC_IRQChannel = USART1_IRQn;
    nvic_conf.NVIC_IRQChannelPreemptionPriority = 0;
    nvic_conf.NVIC_IRQChannelSubPriority = 2;
    nvic_conf.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic_conf);
    
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    


    Последней коммандой разрешаем событие заполнения приемного регистра USART1.

    Соответственно, обработка выглядит так:
    void USART1_IRQHandler(void) {
        u8 data = USART1->DR;
        uart_write_byte(data);
        handle_byte(data);
    }
    


    Отправляем байт обратно (echo) и вызываем обработчик, который является простым конечным автоматом.
    handle_byte(u8)
    // escape sequence handling vars
    u8 escape_seq = 0;
    u8 buf[10];
    
    void handle_byte(u8 data) {
        if((!escape_seq) && (data == 0x1b)) {
            escape_seq = 1;
        } else if (escape_seq == 1) {
            buf[escape_seq] = data;
            escape_seq++;
            if(data != '[') {
                escape_seq = 0;
            }
        } else if (escape_seq == 2) {
            switch(data) {
            case 'A':
                lcd_term_set_cursor(lcd_term_row()-1, lcd_term_col());
                break;
            case 'B':
                lcd_term_set_cursor(lcd_term_row()+1, lcd_term_col());
                break;
            case 'C':
                lcd_term_set_cursor(lcd_term_row(), lcd_term_col()+1);
                break;
            case 'D':
                lcd_term_set_cursor(lcd_term_row(), lcd_term_col()-1);
                break;
            case 'H':
                lcd_term_set_cursor(0, 0);
                break;
            case 'J':
                lcd_term_clear();
                break;
            case 'K':
                lcd_term_flush_str();
                break;
            case 'X':
            case 'Y':
                buf[escape_seq] = data;
                escape_seq++;
                return;
            }
            escape_seq = 0;
        } else if(escape_seq == 3) {
            buf[escape_seq] = data;
            escape_seq++;
        } else if(escape_seq == 4) {
            u8 row = (buf[2] == 'Y') ? buf[3] - 037 : data - 037;
            u8 col = (buf[2] == 'Y') ? data - 037 : buf[3] - 037;
            lcd_term_set_cursor(row, col);
            escape_seq = 0;
        } else {
            lcd_term_put_str(&data, 1);
        }
    }
    


    Весь код опубликован в репозитории на гитхабе.

    P. S.


    Написание этого поста заняло почти 6 часов. Написание и отладка железячно-софтовой части — около 13 часов.

    Спасибо всем, кто дочитал. О всяких очепятках и прочих насекомых пишите в личку.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 16

      +1
      У меня на похожей плате можно управлять дисплеем через FSMC, при этом дисплей, по сути, маппится в память. Этот способ взаимодействия мне кажется более простым и элегантным, чем дёрганье ножками МК. Может быть и у вас так можно?
        +1
        Да, в теории возможно, хочу попробовать. Просто не факт, что успею до конца года, т. к. рабочий аврал может поставить крест на хоббийных делах.

        Datasheet контроллера дисплея местами очень shit, надо корректно выбрать тайминги. Объем конфигурации можно оценить в функции lcd_init. =)

        Как реализую на FMSC — напишу ещё пост. Будет повод прикрутить поддержку microSD с DMA-блиттингом. С точки зрения описанных выше интерфейсов — поменяется низкоуровневый API, что не критично.
          +1
          Datasheet контроллера дисплея местами очень shit, надо корректно выбрать тайминги. Объем конфигурации можно оценить в функции lcd_init. =)

          Только у вас он маленький весьма получился. А вот у меня в примере работы с контроллером написано вот это:
          LCD_Initializtion()
          void LCD_Initializtion()
          {
            LCD_Configuration();
            LCD_Delay(5);  /* delay 50 ms */		
            DeviceCode = LCD_ReadReg(0x0000);		/* ╢┴╚í╞┴ID	*/
            if(DeviceCode==0x9325 || DeviceCode==0x9328)	/* ▓╗═¼╞┴╟²╢»IC │⌡╩╝╗»▓╗═¼ */
            {
              LCD_WriteReg(0x00e7,0x0010);      
              LCD_WriteReg(0x0000,0x0001);  	/* start internal osc */
              LCD_WriteReg(0x0001,0x0100);     
              LCD_WriteReg(0x0002,0x0700); 	/* power on sequence */
          	LCD_WriteReg(0x0003,(1<<12)|(1<<5)|(1<<4)|(0<<3) ); 	/* importance */
              LCD_WriteReg(0x0004,0x0000);                                   
              LCD_WriteReg(0x0008,0x0207);	           
              LCD_WriteReg(0x0009,0x0000);         
              LCD_WriteReg(0x000a,0x0000); 	/* display setting */        
              LCD_WriteReg(0x000c,0x0001);	/* display setting */        
              LCD_WriteReg(0x000d,0x0000); 			        
              LCD_WriteReg(0x000f,0x0000);
              /* Power On sequence */
              LCD_WriteReg(0x0010,0x0000);   
              LCD_WriteReg(0x0011,0x0007);
              LCD_WriteReg(0x0012,0x0000);                                                                 
              LCD_WriteReg(0x0013,0x0000);                 
              LCD_Delay(5);  /* delay 50 ms */		
              LCD_WriteReg(0x0010,0x1590);   
              LCD_WriteReg(0x0011,0x0227);
              LCD_Delay(5);  /* delay 50 ms */		
              LCD_WriteReg(0x0012,0x009c);                  
              LCD_Delay(5);  /* delay 50 ms */		
              LCD_WriteReg(0x0013,0x1900);   
              LCD_WriteReg(0x0029,0x0023);
              LCD_WriteReg(0x002b,0x000e);
              LCD_Delay(5);  /* delay 50 ms */		
              LCD_WriteReg(0x0020,0x0000);                                                            
              LCD_WriteReg(0x0021,0x0000);           
              LCD_Delay(5);  /* delay 50 ms */		
              LCD_WriteReg(0x0030,0x0007); 
              LCD_WriteReg(0x0031,0x0707);   
              LCD_WriteReg(0x0032,0x0006);
              LCD_WriteReg(0x0035,0x0704);
              LCD_WriteReg(0x0036,0x1f04); 
              LCD_WriteReg(0x0037,0x0004);
              LCD_WriteReg(0x0038,0x0000);        
              LCD_WriteReg(0x0039,0x0706);     
              LCD_WriteReg(0x003c,0x0701);
              LCD_WriteReg(0x003d,0x000f);
              LCD_Delay(5);  /* delay 50 ms */		
              LCD_WriteReg(0x0050,0x0000);        
              LCD_WriteReg(0x0051,0x00ef);   
              LCD_WriteReg(0x0052,0x0000);     
              LCD_WriteReg(0x0053,0x013f);
              LCD_WriteReg(0x0060,0xa700);        
              LCD_WriteReg(0x0061,0x0001); 
              LCD_WriteReg(0x006a,0x0000);
              LCD_WriteReg(0x0080,0x0000);
              LCD_WriteReg(0x0081,0x0000);
              LCD_WriteReg(0x0082,0x0000);
              LCD_WriteReg(0x0083,0x0000);
              LCD_WriteReg(0x0084,0x0000);
              LCD_WriteReg(0x0085,0x0000);
                
              LCD_WriteReg(0x0090,0x0010);     
              LCD_WriteReg(0x0092,0x0000);  
              LCD_WriteReg(0x0093,0x0003);
              LCD_WriteReg(0x0095,0x0110);
              LCD_WriteReg(0x0097,0x0000);        
              LCD_WriteReg(0x0098,0x0000);  
              /* display on sequence */    
              LCD_WriteReg(0x0007,0x0133);
              
              LCD_WriteReg(0x0020,0x0000);  /* ╨╨╩╫╓╖0 */                                                          
              LCD_WriteReg(0x0021,0x0000);  /* ┴╨╩╫╓╖0 */     
            }
            else if(DeviceCode==0x9320 || DeviceCode==0x9300)
            {
              LCD_WriteReg(0x00,0x0000);
          	LCD_WriteReg(0x01,0x0100);	/* Driver Output Contral */
          	LCD_WriteReg(0x02,0x0700);	/* LCD Driver Waveform Contral */
          	LCD_WriteReg(0x03,0x1018);	/* Entry Mode Set */
          	
          	LCD_WriteReg(0x04,0x0000);	/* Scalling Contral */
              LCD_WriteReg(0x08,0x0202);	/* Display Contral */
          	LCD_WriteReg(0x09,0x0000);	/* Display Contral 3.(0x0000) */
          	LCD_WriteReg(0x0a,0x0000);	/* Frame Cycle Contal.(0x0000) */
              LCD_WriteReg(0x0c,(1<<0));	/* Extern Display Interface Contral */
          	LCD_WriteReg(0x0d,0x0000);	/* Frame Maker Position */
          	LCD_WriteReg(0x0f,0x0000);	/* Extern Display Interface Contral 2. */
          	
              LCD_Delay(10);  /* delay 100 ms */		
          	LCD_WriteReg(0x07,0x0101);	/* Display Contral */
              LCD_Delay(10);  /* delay 100 ms */		
          
          	LCD_WriteReg(0x10,(1<<12)|(0<<8)|(1<<7)|(1<<6)|(0<<4));	/* Power Control 1.(0x16b0)	*/
          	LCD_WriteReg(0x11,0x0007);								/* Power Control 2 */
          	LCD_WriteReg(0x12,(1<<8)|(1<<4)|(0<<0));				/* Power Control 3.(0x0138)	*/
          	LCD_WriteReg(0x13,0x0b00);								/* Power Control 4 */
          	LCD_WriteReg(0x29,0x0000);								/* Power Control 7 */
          	
          	LCD_WriteReg(0x2b,(1<<14)|(1<<4));
          		
          	LCD_WriteReg(0x50,0);       /* Set X Start */
          	LCD_WriteReg(0x51,239);	    /* Set X End */
          	LCD_WriteReg(0x52,0);	    /* Set Y Start */
          	LCD_WriteReg(0x53,319);	    /* Set Y End */
          	
          	LCD_WriteReg(0x60,0x2700);	/* Driver Output Control */
          	LCD_WriteReg(0x61,0x0001);	/* Driver Output Control */
          	LCD_WriteReg(0x6a,0x0000);	/* Vertical Srcoll Control */
          	
          	LCD_WriteReg(0x80,0x0000);	/* Display Position? Partial Display 1 */
          	LCD_WriteReg(0x81,0x0000);	/* RAM Address Start? Partial Display 1 */
          	LCD_WriteReg(0x82,0x0000);	/* RAM Address End-Partial Display 1 */
          	LCD_WriteReg(0x83,0x0000);	/* Displsy Position? Partial Display 2 */
          	LCD_WriteReg(0x84,0x0000);	/* RAM Address Start? Partial Display 2 */
          	LCD_WriteReg(0x85,0x0000);	/* RAM Address End? Partial Display 2 */
          	
              LCD_WriteReg(0x90,(0<<7)|(16<<0));	/* Frame Cycle Contral.(0x0013)	*/
          	LCD_WriteReg(0x92,0x0000);	/* Panel Interface Contral 2.(0x0000) */
          	LCD_WriteReg(0x93,0x0001);	/* Panel Interface Contral 3. */
              LCD_WriteReg(0x95,0x0110);	/* Frame Cycle Contral.(0x0110)	*/
          	LCD_WriteReg(0x97,(0<<8));	
          	LCD_WriteReg(0x98,0x0000);	/* Frame Cycle Contral */
          
              LCD_WriteReg(0x07,0x0173);
            }
            else if(DeviceCode==0x9331)
            {
          	LCD_WriteReg(0x00E7, 0x1014);
          	LCD_WriteReg(0x0001, 0x0100);   /* set SS and SM bit */
          	LCD_WriteReg(0x0002, 0x0200);   /* set 1 line inversion */
          	LCD_WriteReg(0x0003, 0x1030);   /* set GRAM write direction and BGR=1 */
          	LCD_WriteReg(0x0008, 0x0202);   /* set the back porch and front porch */
              LCD_WriteReg(0x0009, 0x0000);   /* set non-display area refresh cycle ISC[3:0] */
          	LCD_WriteReg(0x000A, 0x0000);   /* FMARK function */
          	LCD_WriteReg(0x000C, 0x0000);   /* RGB interface setting */
          	LCD_WriteReg(0x000D, 0x0000);   /* Frame marker Position */
          	LCD_WriteReg(0x000F, 0x0000);   /* RGB interface polarity */
          	/* Power On sequence */
          	LCD_WriteReg(0x0010, 0x0000);   /* SAP, BT[3:0], AP, DSTB, SLP, STB	*/
          	LCD_WriteReg(0x0011, 0x0007);   /* DC1[2:0], DC0[2:0], VC[2:0] */
          	LCD_WriteReg(0x0012, 0x0000);   /* VREG1OUT voltage	*/
          	LCD_WriteReg(0x0013, 0x0000);   /* VDV[4:0] for VCOM amplitude */
              LCD_Delay(20);                  /* delay 200 ms */		
          	LCD_WriteReg(0x0010, 0x1690);   /* SAP, BT[3:0], AP, DSTB, SLP, STB	*/
          	LCD_WriteReg(0x0011, 0x0227);   /* DC1[2:0], DC0[2:0], VC[2:0] */
              LCD_Delay(5);                   /* delay 50 ms */		
          	LCD_WriteReg(0x0012, 0x000C);   /* Internal reference voltage= Vci	*/
              LCD_Delay(5);                    /* delay 50 ms */		
          	LCD_WriteReg(0x0013, 0x0800);   /* Set VDV[4:0] for VCOM amplitude */
          	LCD_WriteReg(0x0029, 0x0011);   /* Set VCM[5:0] for VCOMH */
          	LCD_WriteReg(0x002B, 0x000B);   /* Set Frame Rate */
              LCD_Delay(5);                   /* delay 50 ms */		
          	LCD_WriteReg(0x0020, 0x0000);   /* GRAM horizontal Address */
          	LCD_WriteReg(0x0021, 0x0000);   /* GRAM Vertical Address */
          	/* Adjust the Gamma Curve */
          	LCD_WriteReg(0x0030, 0x0000);
          	LCD_WriteReg(0x0031, 0x0106);
          	LCD_WriteReg(0x0032, 0x0000);
          	LCD_WriteReg(0x0035, 0x0204);
          	LCD_WriteReg(0x0036, 0x160A);
          	LCD_WriteReg(0x0037, 0x0707);
          	LCD_WriteReg(0x0038, 0x0106);
          	LCD_WriteReg(0x0039, 0x0707);
          	LCD_WriteReg(0x003C, 0x0402);
          	LCD_WriteReg(0x003D, 0x0C0F);
          	/* Set GRAM area */
          	LCD_WriteReg(0x0050, 0x0000);   /* Horizontal GRAM Start Address */
          	LCD_WriteReg(0x0051, 0x00EF);   /* Horizontal GRAM End Address */
          	LCD_WriteReg(0x0052, 0x0000);   /* Vertical GRAM Start Address */
          	LCD_WriteReg(0x0053, 0x013F);   /* Vertical GRAM Start Address */
          	LCD_WriteReg(0x0060, 0x2700);   /* Gate Scan Line */
          	LCD_WriteReg(0x0061, 0x0001);   /*  NDL,VLE, REV */
          	LCD_WriteReg(0x006A, 0x0000);   /* set scrolling line */
          	/* Partial Display Control */
          	LCD_WriteReg(0x0080, 0x0000);
          	LCD_WriteReg(0x0081, 0x0000);
          	LCD_WriteReg(0x0082, 0x0000);
          	LCD_WriteReg(0x0083, 0x0000);
          	LCD_WriteReg(0x0084, 0x0000);
          	LCD_WriteReg(0x0085, 0x0000);
          	/* Panel Control */
          	LCD_WriteReg(0x0090, 0x0010);
          	LCD_WriteReg(0x0092, 0x0600);
          	LCD_WriteReg(0x0007,0x0021);		
              LCD_Delay(5);                   /* delay 50 ms */		
          	LCD_WriteReg(0x0007,0x0061);
              LCD_Delay(5);                   /* delay 50 ms */		
          	LCD_WriteReg(0x0007,0x0133);    /* 262K color and display ON */
              LCD_Delay(5);                   /* delay 50 ms */		
            }
            else if(DeviceCode==0x9919)
            {
              /* POWER ON &RESET DISPLAY OFF */
          	LCD_WriteReg(0x28,0x0006);
          	LCD_WriteReg(0x00,0x0001);		
          	LCD_WriteReg(0x10,0x0000);		
          	LCD_WriteReg(0x01,0x72ef);
          	LCD_WriteReg(0x02,0x0600);
          	LCD_WriteReg(0x03,0x6a38);	
          	LCD_WriteReg(0x11,0x6874);
          	LCD_WriteReg(0x0f,0x0000);    /* RAM WRITE DATA MASK */
          	LCD_WriteReg(0x0b,0x5308);    /* RAM WRITE DATA MASK */
          	LCD_WriteReg(0x0c,0x0003);
          	LCD_WriteReg(0x0d,0x000a);
          	LCD_WriteReg(0x0e,0x2e00);  
          	LCD_WriteReg(0x1e,0x00be);
          	LCD_WriteReg(0x25,0x8000);
          	LCD_WriteReg(0x26,0x7800);
          	LCD_WriteReg(0x27,0x0078);
          	LCD_WriteReg(0x4e,0x0000);
          	LCD_WriteReg(0x4f,0x0000);
          	LCD_WriteReg(0x12,0x08d9);
          	/* Adjust the Gamma Curve */
          	LCD_WriteReg(0x30,0x0000);
          	LCD_WriteReg(0x31,0x0104);	 
          	LCD_WriteReg(0x32,0x0100);	
              LCD_WriteReg(0x33,0x0305);	
              LCD_WriteReg(0x34,0x0505);	 
          	LCD_WriteReg(0x35,0x0305);	
              LCD_WriteReg(0x36,0x0707);	
              LCD_WriteReg(0x37,0x0300);	
          	LCD_WriteReg(0x3a,0x1200);	
          	LCD_WriteReg(0x3b,0x0800);		 
              LCD_WriteReg(0x07,0x0033);
            }
            else if(DeviceCode==0x1505)
            {
              /* second release on 3/5  ,luminance is acceptable,water wave appear during camera preview */
              LCD_WriteReg(0x0007,0x0000);
              LCD_Delay(5);                   /* delay 50 ms */		
              LCD_WriteReg(0x0012,0x011C);    /* why need to set several times?	*/
              LCD_WriteReg(0x00A4,0x0001);    /* NVM */
              LCD_WriteReg(0x0008,0x000F);
              LCD_WriteReg(0x000A,0x0008);
              LCD_WriteReg(0x000D,0x0008);    
              /* GAMMA CONTROL */
              LCD_WriteReg(0x0030,0x0707);
              LCD_WriteReg(0x0031,0x0007); 
              LCD_WriteReg(0x0032,0x0603); 
              LCD_WriteReg(0x0033,0x0700); 
              LCD_WriteReg(0x0034,0x0202); 
              LCD_WriteReg(0x0035,0x0002); 
              LCD_WriteReg(0x0036,0x1F0F);
              LCD_WriteReg(0x0037,0x0707); 
              LCD_WriteReg(0x0038,0x0000); 
              LCD_WriteReg(0x0039,0x0000); 
              LCD_WriteReg(0x003A,0x0707); 
              LCD_WriteReg(0x003B,0x0000); 
              LCD_WriteReg(0x003C,0x0007); 
              LCD_WriteReg(0x003D,0x0000); 
              LCD_Delay(5);                   /* delay 50 ms */		
              LCD_WriteReg(0x0007,0x0001);
              LCD_WriteReg(0x0017,0x0001);    /* Power supply startup enable */
              LCD_Delay(5);                   /* delay 50 ms */		
              /* power control */
              LCD_WriteReg(0x0010,0x17A0); 
              LCD_WriteReg(0x0011,0x0217);    /* reference voltage VC[2:0]   Vciout = 1.00*Vcivl */
              LCD_WriteReg(0x0012,0x011E);    /* Vreg1out = Vcilvl*1.80   is it the same as Vgama1out ?	*/
              LCD_WriteReg(0x0013,0x0F00);    /* VDV[4:0]-->VCOM Amplitude VcomL = VcomH - Vcom Ampl */
              LCD_WriteReg(0x002A,0x0000);  
              LCD_WriteReg(0x0029,0x000A);    /* Vcomh = VCM1[4:0]*Vreg1out    gate source voltage?? */
              LCD_WriteReg(0x0012,0x013E);    /* power supply on */
              /* Coordinates Control */
              LCD_WriteReg(0x0050,0x0000);
              LCD_WriteReg(0x0051,0x00EF); 
              LCD_WriteReg(0x0052,0x0000); 
              LCD_WriteReg(0x0053,0x013F); 
              /* Pannel Image Control */
              LCD_WriteReg(0x0060,0x2700); 
              LCD_WriteReg(0x0061,0x0001); 
              LCD_WriteReg(0x006A,0x0000); 
              LCD_WriteReg(0x0080,0x0000); 
              /* Partial Image Control */
              LCD_WriteReg(0x0081,0x0000); 
              LCD_WriteReg(0x0082,0x0000); 
              LCD_WriteReg(0x0083,0x0000); 
              LCD_WriteReg(0x0084,0x0000); 
              LCD_WriteReg(0x0085,0x0000); 
              /* Panel Interface Control */
              LCD_WriteReg(0x0090,0x0013);      /* frenqucy */	
              LCD_WriteReg(0x0092,0x0300); 
              LCD_WriteReg(0x0093,0x0005); 
              LCD_WriteReg(0x0095,0x0000); 
              LCD_WriteReg(0x0097,0x0000); 
              LCD_WriteReg(0x0098,0x0000); 
            
              LCD_WriteReg(0x0001,0x0100); 
              LCD_WriteReg(0x0002,0x0700); 
              LCD_WriteReg(0x0003,0x1030); 
              LCD_WriteReg(0x0004,0x0000); 
              LCD_WriteReg(0x000C,0x0000); 
              LCD_WriteReg(0x000F,0x0000); 
              LCD_WriteReg(0x0020,0x0000); 
              LCD_WriteReg(0x0021,0x0000); 
              LCD_WriteReg(0x0007,0x0021); 
              LCD_Delay(20);                   /* delay 200 ms */		
              LCD_WriteReg(0x0007,0x0061); 
              LCD_Delay(20);                   /* delay 200 ms */		
              LCD_WriteReg(0x0007,0x0173); 
              LCD_Delay(20);                   /* delay 200 ms */		
            }							 
            else if(DeviceCode==0x8989)
            {
              LCD_WriteReg(0x0000,0x0001);    LCD_Delay(5);   /* ┤≥┐¬╛º╒± */
              LCD_WriteReg(0x0003,0xA8A4);    LCD_Delay(5);   
              LCD_WriteReg(0x000C,0x0000);    LCD_Delay(5);   
              LCD_WriteReg(0x000D,0x080C);    LCD_Delay(5);   
              LCD_WriteReg(0x000E,0x2B00);    LCD_Delay(5);   
              LCD_WriteReg(0x001E,0x00B0);    LCD_Delay(5);   
              LCD_WriteReg(0x0001,0x2B3F);    LCD_Delay(5);   /* ╟²╢»╩Σ│÷┐╪╓╞320*240 0x2B3F */
              LCD_WriteReg(0x0002,0x0600);    LCD_Delay(5);
              LCD_WriteReg(0x0010,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0011,0x6070);    LCD_Delay(5);   /* ╢¿╥σ╩²╛▌╕±╩╜ 16╬╗╔½ ║ß╞┴ 0x6070 */
              LCD_WriteReg(0x0005,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0006,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0016,0xEF1C);    LCD_Delay(5);
              LCD_WriteReg(0x0017,0x0003);    LCD_Delay(5);
              LCD_WriteReg(0x0007,0x0133);    LCD_Delay(5);         
              LCD_WriteReg(0x000B,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x000F,0x0000);    LCD_Delay(5);   /* ╔¿├Φ┐¬╩╝╡╪╓╖ */
              LCD_WriteReg(0x0041,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0042,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0048,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0049,0x013F);    LCD_Delay(5);
              LCD_WriteReg(0x004A,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x004B,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0044,0xEF00);    LCD_Delay(5);
              LCD_WriteReg(0x0045,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0046,0x013F);    LCD_Delay(5);
              LCD_WriteReg(0x0030,0x0707);    LCD_Delay(5);
              LCD_WriteReg(0x0031,0x0204);    LCD_Delay(5);
              LCD_WriteReg(0x0032,0x0204);    LCD_Delay(5);
              LCD_WriteReg(0x0033,0x0502);    LCD_Delay(5);
              LCD_WriteReg(0x0034,0x0507);    LCD_Delay(5);
              LCD_WriteReg(0x0035,0x0204);    LCD_Delay(5);
              LCD_WriteReg(0x0036,0x0204);    LCD_Delay(5);
              LCD_WriteReg(0x0037,0x0502);    LCD_Delay(5);
              LCD_WriteReg(0x003A,0x0302);    LCD_Delay(5);
              LCD_WriteReg(0x003B,0x0302);    LCD_Delay(5);
              LCD_WriteReg(0x0023,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0024,0x0000);    LCD_Delay(5);
              LCD_WriteReg(0x0025,0x8000);    LCD_Delay(5);
              LCD_WriteReg(0x004f,0);        /* ╨╨╩╫╓╖0 */
              LCD_WriteReg(0x004e,0);        /* ┴╨╩╫╓╖0 */
            }
            LCD_Delay(5);  /* delay 50 ms */		
          }
          

            +1
            Только у вас он маленький весьма получился.
            Это смотря с чем сравнивать. У символьных всё проще =)

            В вашем примере настраиваются 6 вариантов дисплеев. У меня — только ili9320.

            Плюс, я не использую partial images (соответствующие регистры выставляются в 0x0000 в цикле), не трогаю настройки гамма-коррекцию (10 регистров).
          0
          Посмотрел в datasheet более внимательно. У меня STM32F103RB (он medium-density, 64 ноги) нет FSMC. Он доступен только на high-density с количеством выводов 100 (т. е. STM32F103VC, STM32F103VD, STM32F103VE).
          +1
          Круто, спасибо за пост. Особая ценность поста — пример работы с графическими дисплеями!
            +2
            Приятно видеть как новогодняя тусовка пополняется всё новыми людми и решениями, Спасибо!
              0
              Я просто оставлю это здесь. AVR+QVGA TFT.
                0
                К сожалению, на adafruit нет этого продукта и, соответственно, документации и примеров.

                Но, честно говоря, не верится, чтобы atmega32 при своих 16 MHz и без DMA могла тянуть инфу с sd и обновлять экран столь быстро, разве что на пределе возможностей. В общем, жду исходников.
                  0
                  Продукт был не adafruit'овской разработки, документация у lady Ada сохранилась в wiki, а вот все посты, связанные с разработкой этого чудо-устройства канули в лету вместе с сайтом posterous.

                  PS Было бы здорово, если бы кто-нибудь написал про этот пик инженерного мастерства пост, пока это не кануло в лету окончательно.
                    0
                    Здесь исходники софтовой части github.com/rossumur/microtouch, а здесь github.com/adafruit/microtouch — железной.

                    Бегло глянул исходники, там реализованы:
                    — «драйвера» дисплея, тачскирина, sd, fat, usb cdc;
                    — однозадачная ОС.

                    Из интересных решений увидел:
                    — ручное разворачивание цикла для выровненной на 4 байта записи в дисплей;
                    — использование аппаратных возможностей контроллера дисплея для скролла.

                    Если есть желание — напишите статью. У меня нет ни железки, ни времени, к сожалению.
                0
                Это же космолет 1 (сэнчжоу1), разве нет? К нему дают диск на китайском, я перевел названия каталогов через гугло-транслейт, стало возможно ориентироваться. Все хочу оформить плату под coocox, но руки не доходят. =(
                  0
                  Это клон mini-stm32. Диска в комплекте не было. Архивы со схемой и примерами искал, сравнивал с реальным подключением, и в какой-то момент нашел более-менее соответствующий. Но всё равно, datasheet + тестер необходимы. Надписи на плате плюс чуть-чуть реверса спасают =)
                    0
                      0
                      Копий этой платы довольно много. На той, что по ссылке, написано armjishu, на моей — mcujishu. Других отличий не видно.
                        0
                        Я про свою тоже ничего не смог нагуглить, потом пошел на страницу описания на ебее (уже не найти), увидел фотку диска и меня осенило. )
                        Посмотрите, может это такая же поделка.

                Only users with full accounts can post comments. Log in, please.