14.1實驗內容
通過本實驗主要學習以下內容:
- LCD顯示原理
- EXMC NOR/SRAM模式時序和8080并口時序
- LCD顯示控制
14.2實驗原理
使用MCU的EXMC外設實現8080并口時序,和TFT-LCD控制器進行通信,控制LCD顯示圖片、字符、色塊等。
14.2.1TFT-LCD介紹
薄膜晶體管液晶顯示器(英語:Thin film transistor liquid crystal display,常簡稱為TFT-LCD)是多數液晶顯示器的一種,它使用薄膜晶體管技術改善影象品質。雖然TFT-LCD被統稱為LCD,不過它是種主動式矩陣LCD,被應用在電視、平面顯示器及投影機上。
簡單說,TFT-LCD面板可視為兩片玻璃基板中間夾著一層液晶,上層的玻璃基板是彩色濾光片、而下層的玻璃則有晶體管鑲嵌于上。當電流通過晶體管產生電場變化,造成液晶分子偏轉,藉以改變光線的偏極性,再利用偏光片決定像素的明暗狀態。此外,上層玻璃因與彩色濾光片貼合,形成每個像素各包含紅藍綠三顏色,這些發出紅藍綠色彩的像素便構成了面板上的視頻畫面。
為了對TFT-LCD的顯示進行控制,需要通過接口和液晶屏通信,但所謂與液晶屏通信,實際上還是與液晶屏驅動控制芯片在通信,而主控制器需要按控制芯片支持的通信進行交互,通常有UART、IIC、SPI、8080、MIPI等各類接口。另外需要注意的一點是:一般支持普通MCU接口的LCD驅動芯片,都需要內置GRAM(Graphics RAM),至少能存儲一個屏幕的數據。
這這里,我們使用了8080接口通過并行總線傳輸控制命令和數據,并通過往LCD液晶模組自帶的GRAM更新數據實現屏幕的刷新。
GD32H757海棠派開發板TFT-LCD如下圖所示,采用了ILI9488 LCD驅動器,分辨率320*480,支持多種通信接口,在GD32H757上,適合使用16-bit Parallel MCU Interface接口進行通信,開發板配套的LCD模塊也采用了該接口設計和開發板進行連接。
14.2.2LCD 8080并口時序介紹(16-bit Parallel MCU Interface)
8080接口是由英特爾設計,是一種并行、異步、半雙工通信協議,作用是用于外擴RAM、ROM,后面也用于LCD接口。并行接口又分為8位/16位/24位 三種,顧名思義,就是數據總線的位寬。
- 如下圖所示是16-bit Parallel MCU Interface的接口和MCU的連接信號:
- 如下圖所示是LCD驅動器16BIT 8080并口讀寫時序:
- CS拉低后,并口DATA IO在WR的上升沿被采樣;
- 可以理解為16線的SPI,而WR是寫“CLK”,RD是讀“CLK”;
- 但這里還多了D/C引腳用于選擇傳輸命令或數據
- 8080接口的RGB顏色數據編碼
- 像素信息用RGB三原色表示,所以向液晶屏傳輸的數據幀主要也就是傳輸的RGB顏色數據。
- 像素的顏色數據并不總是用 8R8G8B的24位真彩色 表示,共有下面幾種表示情況:
- 12-bits/pixel (R 4-bit, G 4-bit, B 4-bit), 4,096 Colors, 簡稱444;
- 16-bits/pixel (R 5-bit, G 6-bit, B 5-bit), 65,536 Colors, 簡稱565;
- 18-bits/pixel (R 6-bit, G 6-bit, B 6-bit), 262,144 Colors, 簡稱666;
- 24-bits/pixel (R 8-bit, G 8-bit, B 8-bit), 16,777,216 Colors, 簡稱888;
不同顏色表示方法和不同的總線位寬相組合,就會組合成多種RGB顏色數據編碼。 - 綜合顯示效果、內存資源開銷等,我們采取了RGB565像素格式,這樣16BIT 8080每次傳輸就是一個像素點的像素值,傳輸數據就為像素顏色值。
14.2.3EXMC外設和EXMC NOR/SRAM模式實現8080時序
- 這里我們使用EXMC中時序和接口類似的NOR/SRAM模式,來實現8080接口驅動TFT-LCD顯示。
- 這里我們使用SRAM模式異步模式A(擴展模式),時序如下圖:
我們對比上面的時序和16-bit Data Bus 8080 LCD時序,發現一些信號的時序是類似的,我們可以將這些信號進行對應:
EXMC_NEx -> CSx
EXMC_NOE->RDx
EXMC_NWE->WR
EXMC_D[15:0]->DB[15:0]
EXMC_Ax->D/C
這里巧妙的是使用EXMC_Ax引腳實現D/C的數據/命令切換功能,所以我們只需要選擇一個方便布線的EXMC_Ax引腳,然后在軟件中對該引腳對應的EXMC邏輯地址進行操作就可以實現程序讀寫不同地址時,D/C引腳的狀態切換,從而實現訪問一個EXMC地址時是數據或命令類似,訪問該地址位反向的任意地址就是另外一個類型。對于程序中邏輯地址的影響,除了Ax引腳的選擇外還有NEx引腳的選擇。NE[0]-NE[3]對應如下圖的NOR/SRAM BANK下的Region0-Region3。
14.3硬件設計
在紅楓派開發板設計中,我們使用EXMC_NE1引腳作為CS,EXMC_A12引腳作為D/C,同時LCD觸摸接口使用SPI,LCD_BL為背光控制引腳,這里的引腳選用了帶PWM的引腳,可以實現LCD的背光亮度調節。
14.4代碼解析
14.4.1CSx、D/C、BL相關功能定義和注冊;
在EXMC LCD驅動代碼中存在和電路設計匹配的變更點,往往讓開發者頭大,需要詳細閱讀用戶手冊來進行配置調整、讀寫地址調整;而在我們的驅動文件bsp_8080_lcd.c中定義注冊背光引腳、Ax、NEx引腳,當硬件設計變更時只需要在這里調整,驅動就可以在新的硬件中正常使用。
C //用戶配置定義區 #define BACK_LIGHT_DUTY 80 TIMER_CH_DEF(LCD_8080_BL,TIMER3,1,TIMER_CH_PWM_HIGH,D,13,AF_PP,GPIO_AF_2); #define EXMC_Ax 12 AFIO_DEF(EXMC_Ax_GPIO,G,2,AF_PP,GPIO_AF_12); #define EXMC_NEx 1 AFIO_DEF(EXMC_NEx_GPIO,G,9,AF_PP,GPIO_AF_12); GPIO_DEF(LCD_8080_RST,D,12,OUT_PP,RESET,NULL); //驅動內部定義區 #define EXMC_LCD_D REG16(((uint32_t)(EXMC_BANK0_NORSRAM_REGIONx_ADDR(EXMC_NEx)))|BIT(EXMC_Ax)*2) #define EXMC_LCD_C REG16(((uint32_t)(EXMC_BANK0_NORSRAM_REGIONx_ADDR(EXMC_NEx)))) |
14.4.2gpio和exmc初始化:
exmc使用了擴展模式,這樣讀和寫的時序可以單獨配置,因為LCD對讀和寫的要求時間是不同的,讀的時候速率不能太高,如果使用一種參數類型就會為了滿足讀的要求而降低寫的速率,影響最終刷屏的性能。這里主要調整讀和寫時的地址建立、數據建立時間,通常和硬件設計也有較大關系。
C static void driver_exmc_lcd_16bit_gpio_init(void) { /* EXMC clock enable */ rcu_periph_clock_enable(RCU_EXMC); /* EXMC enable */ rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_GPIOD); rcu_periph_clock_enable(RCU_GPIOE); rcu_periph_clock_enable(RCU_GPIOF); rcu_periph_clock_enable(RCU_GPIOG); /* configure GPIO D[0-15] */ gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15); gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15); gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15); gpio_af_set(GPIOE, GPIO_AF_12, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15); /* configure NOE NWE */ gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_4 | GPIO_PIN_5); gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_4 | GPIO_PIN_5); gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_4 | GPIO_PIN_5); } void driver_exmc_lcd_init(uint32_t norsram_region) { #define LCD_READ_DATA_SETTIME_NS 50 #define LCD_READ_ADDR_SETTIME_NS 10 #define LCD_WRITE_DATA_SETTIME_NS 20 #define LCD_WRITE_ADDR_SETTIME_NS 10 uint32_t ahb_clk=0; exmc_norsram_parameter_struct nor_init_struct; exmc_norsram_timing_parameter_struct nor_timing_read_init_struct; exmc_norsram_timing_parameter_struct nor_timing_write_init_struct; mpu_region_init_struct mpu_init_struct; mpu_region_struct_para_init(&mpu_init_struct); mpu_config(EXMC_BANK0_NORSRAM_REGIONx_ADDR(norsram_region),MPU_REGION_SIZE_64MB,MPU_AP_FULL_ACCESS,MPU_ACCESS_BUFFERABLE,MPU_ACCESS_NON_CACHEABLE,MPU_ACCESS_NON_SHAREABLE,MPU_INSTRUCTION_EXEC_NOT_PERMIT,MPU_TEX_TYPE0); /* EXMC clock enable */ rcu_periph_clock_enable(RCU_EXMC); driver_exmc_lcd_16bit_gpio_init(); nor_init_struct.read_write_timing = &nor_timing_read_init_struct; nor_init_struct.write_timing = &nor_timing_write_init_struct; exmc_norsram_struct_para_init(&nor_init_struct); nor_init_struct.read_write_timing = &nor_timing_read_init_struct; nor_init_struct.write_timing = &nor_timing_write_init_struct; SystemCoreClockUpdate(); ahb_clk=rcu_clock_freq_get(CK_AHB); /* config timing parameter */ nor_timing_read_init_struct.asyn_access_mode = EXMC_ACCESS_MODE_A; nor_timing_read_init_struct.syn_data_latency = EXMC_DATALAT_2_CLK; nor_timing_read_init_struct.syn_clk_division = EXMC_SYN_CLOCK_RATIO_2_CLK; nor_timing_read_init_struct.bus_latency = 1; nor_timing_read_init_struct.asyn_data_setuptime = LCD_READ_DATA_SETTIME_NS/(float)(1000000000.0f/ahb_clk); nor_timing_read_init_struct.asyn_address_holdtime = 1; nor_timing_read_init_struct.asyn_address_setuptime = LCD_READ_ADDR_SETTIME_NS/(float)(1000000000.0f/ahb_clk); /* config timing parameter */ nor_timing_write_init_struct.asyn_access_mode = EXMC_ACCESS_MODE_A; nor_timing_write_init_struct.syn_data_latency = EXMC_DATALAT_2_CLK; nor_timing_write_init_struct.syn_clk_division = EXMC_SYN_CLOCK_RATIO_2_CLK; nor_timing_write_init_struct.bus_latency = 1; nor_timing_write_init_struct.asyn_data_setuptime = LCD_WRITE_DATA_SETTIME_NS/(float)(1000000000.0f/ahb_clk); nor_timing_write_init_struct.asyn_address_holdtime = 1; nor_timing_write_init_struct.asyn_address_setuptime =LCD_WRITE_ADDR_SETTIME_NS/(float)(1000000000.0f/ahb_clk); /* config EXMC bus parameters */ nor_init_struct.norsram_region = norsram_region; nor_init_struct.write_mode = EXMC_ASYN_WRITE; nor_init_struct.extended_mode = ENABLE; nor_init_struct.asyn_wait = DISABLE; nor_init_struct.nwait_signal = DISABLE; nor_init_struct.memory_write = ENABLE; nor_init_struct.nwait_config = EXMC_NWAIT_CONFIG_BEFORE; // nor_init_struct.wrap_burst_mode = DISABLE; nor_init_struct.nwait_polarity = EXMC_NWAIT_POLARITY_LOW; nor_init_struct.burst_mode = DISABLE; nor_init_struct.databus_width = EXMC_NOR_DATABUS_WIDTH_16B; nor_init_struct.memory_type = EXMC_MEMORY_TYPE_SRAM; nor_init_struct.address_data_mux = DISABLE; exmc_norsram_init(&nor_init_struct); /* enable the EXMC bank0 NORSRAM */ exmc_norsram_enable(norsram_region); } |
14.4.3LCD數據、命令讀寫:
LCD的命令、數據的讀寫都通過EXMC來實現,在讀寫EXMC的邏輯地址時對應的波形會發送到LCD上,命令/數據目前通過地址引腳控制,所以我們需要定義兩個地址分別對應命令、數據地址,對這兩個地址讀和寫就可以實現LCD讀寫數據、寫命令功能。
C /** * 說明 LCD寫數據 * 輸入 data: 要寫入的數據 * 返回值 無 */ void bsp_8080_lcd_wr_data(__IO uint16_t data) { // delay_sysclk(1); EXMC_LCD_D = data; } /** * 說明 LCD寫寄存器編號/地址函數 * 輸入 regno: 寄存器編號/地址 * 返回值 無 */ void bsp_8080_lcd_wr_regno(__IO uint16_t regno) { // delay_sysclk(1); EXMC_LCD_C = regno; /* 寫入要寫的寄存器序號 */ } /** * 說明 LCD寫寄存器 * 輸入 regno:寄存器編號/地址 * 輸入 data:要寫入的數據 * 返回值 無 */ void bsp_8080_lcd_write_reg(__IO uint16_t regno,__IO uint16_t data) { // delay_sysclk(1); EXMC_LCD_C = regno; /* 寫入要寫的寄存器序號 */ // delay_sysclk(1); EXMC_LCD_D = data; /* 寫入數據 */ } /** * 說明 LCD讀數據 * 輸入 無 * 返回值 讀取到的數據 */ static uint16_t bsp_8080_lcd_read_data(void) { // delay_sysclk(1); return EXMC_LCD_D; } |
14.4.4LCD初始化
LCD初始化序列通常LCD驅動器廠家會提供相關寄存器配置,為了兼容不同的LCD,可以讀取LCD ID后執行不同的驅動芯片初始化:
C /** * 說明 初始化LCD * @note 該初始化函數可以初始化各種型號的LCD(詳見本.c文件最前面的描述) * * 輸入 無 * 返回值 無 */ uint32_t bsp_8080_lcd_init(void) { bsp_8080_lcd_port_init(); driver_gpio_general_init(&LCD_8080_RST); driver_gpio_pin_reset(&LCD_8080_RST); delay_ms(10); driver_gpio_pin_set(&LCD_8080_RST); delay_ms(10); bsp_8080_lcd_backlight_duty_set(BACK_LIGHT_DUTY); bsp_8080_lcd_backlight_on(); /* 點亮背光 */ /* LCD的畫筆顏色和背景色 */ bsp_8080_lcd_parameter.g_point_color = WHITE; /* 畫筆顏色 */ bsp_8080_lcd_parameter.g_back_color = BLACK; /* 背景色 */ delay_ms(1); /* 初始化FSMC后,必須等待一定時間才能開始初始化 */ /* 嘗試9341 ID的讀取 */ bsp_8080_lcd_wr_regno(0XD3); bsp_8080_lcd_parameter.id = bsp_8080_lcd_read_data(); /* dummy read */ bsp_8080_lcd_parameter.id = bsp_8080_lcd_read_data(); /* 讀到0X00 */ bsp_8080_lcd_parameter.id = bsp_8080_lcd_read_data(); /* 讀取0X93 */ bsp_8080_lcd_parameter.id <<= 8; bsp_8080_lcd_parameter.id |= bsp_8080_lcd_read_data(); /* 讀取0X41 */ if (bsp_8080_lcd_parameter.id == 0X9488) { lcd_ex_ili9488_reginit(); /* 執行ILI9388初始化 */ } else if (bsp_8080_lcd_parameter.id == 0X9341) { lcd_ex_ili9341_reginit(); /* 執行ILI9341初始化 */ }else{ bsp_8080_lcd_parameter.id = 0X9488; bsp_8080_lcd_display_dir(0); /* 默認為豎屏 */ bsp_8080_lcd_scan_dir(DFT_SCAN_DIR); /* 默認掃描方向 */ return DRV_ERROR; } bsp_8080_lcd_display_dir(0); /* 默認為豎屏 */ bsp_8080_lcd_scan_dir(DFT_SCAN_DIR); /* 默認掃描方向 */ bsp_8080_lcd_clear(WHITE); return DRV_SUCCESS; } /** * @brief ILI9488寄存器初始化代碼 * @param 無 * @retval 無 */ void lcd_ex_ili9488_reginit(void) { //************* Start Initial Sequence **********// bsp_lcd_wr_regno(0XF7); bsp_lcd_wr_data(0xA9); bsp_lcd_wr_data(0x51); bsp_lcd_wr_data(0x2C); bsp_lcd_wr_data(0x82); bsp_lcd_wr_regno(0XEC); bsp_lcd_wr_data(0x00); bsp_lcd_wr_data(0x02); bsp_lcd_wr_data(0x03); bsp_lcd_wr_data(0x7A); bsp_lcd_wr_regno(0xC0); bsp_lcd_wr_data(0x13); bsp_lcd_wr_data(0x13); bsp_lcd_wr_regno(0xC1); bsp_lcd_wr_data(0x41); bsp_lcd_wr_regno(0xC5); bsp_lcd_wr_data(0x00); bsp_lcd_wr_data(0x28); bsp_lcd_wr_data(0x80); bsp_lcd_wr_regno(0xB1); //Frame rate 70HZ bsp_lcd_wr_data(0xB0); bsp_lcd_wr_data(0x11); bsp_lcd_wr_regno(0xB4); bsp_lcd_wr_data(0x02); bsp_lcd_wr_regno(0xB6); //RGB/MCU Interface Control bsp_lcd_wr_data(0x02); //MCU bsp_lcd_wr_data(0x22); bsp_lcd_wr_regno(0xB7); bsp_lcd_wr_data(0xc6); bsp_lcd_wr_regno(0xBE); bsp_lcd_wr_data(0x00); bsp_lcd_wr_data(0x04); bsp_lcd_wr_regno(0xE9); bsp_lcd_wr_data(0x00); bsp_lcd_wr_regno(0xF4); bsp_lcd_wr_data(0x00); bsp_lcd_wr_data(0x00); bsp_lcd_wr_data(0x0f); bsp_lcd_wr_regno(0xE0); bsp_lcd_wr_data(0x00); bsp_lcd_wr_data(0x04); bsp_lcd_wr_data(0x0E); bsp_lcd_wr_data(0x08); bsp_lcd_wr_data(0x17); bsp_lcd_wr_data(0x0A); bsp_lcd_wr_data(0x40); bsp_lcd_wr_data(0x79); bsp_lcd_wr_data(0x4D); bsp_lcd_wr_data(0x07); bsp_lcd_wr_data(0x0E); bsp_lcd_wr_data(0x0A); bsp_lcd_wr_data(0x1A); bsp_lcd_wr_data(0x1D); bsp_lcd_wr_data(0x0F); bsp_lcd_wr_regno(0xE1); bsp_lcd_wr_data(0x00); bsp_lcd_wr_data(0x1B); bsp_lcd_wr_data(0x1F); bsp_lcd_wr_data(0x02); bsp_lcd_wr_data(0x10); bsp_lcd_wr_data(0x05); bsp_lcd_wr_data(0x32); bsp_lcd_wr_data(0x34); bsp_lcd_wr_data(0x43); bsp_lcd_wr_data(0x02); bsp_lcd_wr_data(0x0A); bsp_lcd_wr_data(0x09); bsp_lcd_wr_data(0x33); bsp_lcd_wr_data(0x37); bsp_lcd_wr_data(0x0F); bsp_lcd_wr_regno(0xF4); bsp_lcd_wr_data(0x00); bsp_lcd_wr_data(0x00); bsp_lcd_wr_data(0x0f); bsp_lcd_wr_regno(0x36); bsp_lcd_wr_data(0x08); bsp_lcd_wr_regno(0x3A); //Interface Mode Control bsp_lcd_wr_data(0x55); //0x66 18bit; 0x55 16bit bsp_lcd_wr_regno(0x20); bsp_lcd_wr_regno(0x11); delay_ms(120); bsp_lcd_wr_regno(0x29); delay_ms(50); } |
14.4.5LCD畫點函數實現
LCD在任意點顯示想要的顏色值,需要設置顯示光標到目標位置,然后就可以從該光標進行顏色數據寫入,顏色信息將顯示到LCD的指定坐標上。
C /** * 說明 畫點 * 輸入 x,y: 坐標 * 輸入 color: 點的顏色(32位顏色,方便兼容LTDC) * 返回值 無 */ void bsp_8080_lcd_draw_point(uint16_t x, uint16_t y, uint32_t color) { if(x>bsp_8080_lcd_parameter.width || y>bsp_8080_lcd_parameter.height) { return; } if(bsp_8080_lcd_parameter.display_mode == LCD_MODE) { bsp_8080_lcd_set_cursor(x, y); /* 設置光標位置 */ EXMC_LCD_C = bsp_8080_lcd_parameter.wramcmd; /* 開始寫入GRAM */ EXMC_LCD_D = color; }else { *(__IO uint16_t*)(((uint32_t)(bsp_8080_lcd_parameter.display_ram)) + 2*((bsp_8080_lcd_parameter.width*y) + x)) = color; } } |
14.4.6窗口設置和色塊填充
LCD可以設置需顯示的窗口,設置窗口后可以連續的寫數據,像素信息會從窗口起始坐標開始自動遞增和換行顯示顏色。通過圖塊設置函數可以顯示圖片,移植到GUI等;這里我們通過DMA的MEM TO MEM模式可以降低色塊填充過程的CPU占用率,同時提升刷屏速率。
C /** * 說明 在指定區域內填充指定顏色塊 * 輸入 (sx,sy),(ex,ey):填充矩形對角坐標,區域大小為:(ex - sx + 1) * (ey - sy + 1) * 輸入 color: 要填充的顏色數組首地址 * 返回值 無 */ void bsp_8080_lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color) { uint16_t height, width; if(sx>bsp_8080_lcd_parameter.width || ex>bsp_8080_lcd_parameter.width || sy>bsp_8080_lcd_parameter.height || ey>bsp_8080_lcd_parameter.height) { return; } // uint32_t i; width = ex - sx + 1; /* 得到填充的寬度 */ height = ey - sy + 1; /* 高度 */ if(bsp_8080_lcd_parameter.display_mode == LCD_MODE) { bsp_8080_lcd_set_window(sx,sy,width,height); EXMC_LCD_C = bsp_8080_lcd_parameter.wramcmd; driver_dma_mem_to_exmclcd_start((void*)&EXMC_LCD_D,&color[0],DMA_Width_16BIT,height*width); // for(uin32_t i = 0; i < height*width; i++) // { // EXMC_LCD_D = color[i]; // } } else { for(uint16_t y = sy; y <= ey; y++) { for(uint16_t x = sx; x <= ex; x++) { bsp_8080_lcd_parameter.display_ram[y*width+x]=color[(y-sy)*width+(x-sx)]; } } } } |
14.4.7字符顯示和LCD Printf
實現上述功能后,通過字庫信息結合打點函數就可以實現字符的顯示,我們同時實現了在lcd上打印信息,以printf的形式更輕易便捷的輸出信息到LCD上。
C /** * 說明 LCD打印 * 輸入 ...和printf相同用法,自動換行 * 返回值 無 */ void bsp_8080_lcd_printf(const char * sFormat, ...) { #define PRINTF_MAX_LEN 100 char printf_buffer[PRINTF_MAX_LEN]; char* p=printf_buffer; uint16_t len=0,count=0; va_list ParamList; va_start(ParamList, sFormat); vsprintf(printf_buffer,sFormat, ParamList); va_end(ParamList); len=strlen(printf_buffer); while( ( ((*p <= '~') && (*p >= ' ')) || (*p =='\r') || (*p =='\n') ) && (count { if((*p =='\r')) { bsp_8080_lcd_pritnf_parameter.x_now = bsp_8080_lcd_pritnf_parameter.x_start; } else if((*p =='\n')) { bsp_8080_lcd_pritnf_parameter.y_now += bsp_8080_lcd_pritnf_parameter.size; bsp_8080_lcd_fill(bsp_8080_lcd_pritnf_parameter.x_now,bsp_8080_lcd_pritnf_parameter.y_now,bsp_8080_lcd_pritnf_parameter.x_end,bsp_8080_lcd_pritnf_parameter.y_now+bsp_8080_lcd_pritnf_parameter.size,bsp_8080_lcd_pritnf_parameter.back_color); } else if( (bsp_8080_lcd_pritnf_parameter.x_now + bsp_8080_lcd_pritnf_parameter.size/2) > bsp_8080_lcd_pritnf_parameter.x_end) { bsp_8080_lcd_pritnf_parameter.x_now = bsp_8080_lcd_pritnf_parameter.x_start; bsp_8080_lcd_pritnf_parameter.y_now += bsp_8080_lcd_pritnf_parameter.size; bsp_8080_lcd_fill(bsp_8080_lcd_pritnf_parameter.x_now,bsp_8080_lcd_pritnf_parameter.y_now,bsp_8080_lcd_pritnf_parameter.x_end,bsp_8080_lcd_pritnf_parameter.y_now+bsp_8080_lcd_pritnf_parameter.size,bsp_8080_lcd_pritnf_parameter.back_color); } else if ( (bsp_8080_lcd_pritnf_parameter.y_now+bsp_8080_lcd_pritnf_parameter.size) > bsp_8080_lcd_pritnf_parameter.y_end) { bsp_8080_lcd_pritnf_parameter.x_now = bsp_8080_lcd_pritnf_parameter.x_start; bsp_8080_lcd_pritnf_parameter.y_now = bsp_8080_lcd_pritnf_parameter.y_start; bsp_8080_lcd_fill(bsp_8080_lcd_pritnf_parameter.x_start,bsp_8080_lcd_pritnf_parameter.y_start,bsp_8080_lcd_pritnf_parameter.x_end,bsp_8080_lcd_pritnf_parameter.y_end,bsp_8080_lcd_pritnf_parameter.back_color); } if((*p !='\r')&&(*p !='\n')) { bsp_8080_lcd_show_char(bsp_8080_lcd_pritnf_parameter.x_now, bsp_8080_lcd_pritnf_parameter.y_now, *p, bsp_8080_lcd_pritnf_parameter.size, 0, bsp_8080_lcd_pritnf_parameter.point_color,bsp_8080_lcd_pritnf_parameter.back_color); bsp_8080_lcd_pritnf_parameter.x_now += bsp_8080_lcd_pritnf_parameter.size / 2; } p++; count++; } } |
14.4.8主函數
主函數如下所示,進入主函數后,首先進行驅動初始化,包括串口、LED、以及8080接口LCD初始化,之后顯示log圖片以及字符串信息,進入主循環后,循環顯示當前系統tick。
C int main(void) { uint64_t tick_temp=0; uint16_t frames_speed=0; driver_init(); /* 延時和公共驅動部分初始化 */ bsp_uart_init(&BOARD_UART); /* 打印串口初始化 */ bsp_led_group_init(); /* 初始化LED組 */ bsp_led_on(&LED1); bsp_led_off(&LED2); delay_ms(100); printf("TFT 8080 LCD examples.\r\n"); bsp_8080_lcd_init(); /* 初始化LCD */ //計算刷整屏速度 tick_temp=driver_tick; bsp_8080_lcd_clear(WHITE); frames_speed=driver_tick-tick_temp; //顯示log圖片 bsp_8080_lcd_color_fill(60,0,233,99,(uint16_t*)gd_log_picture); //設置打印窗口 bsp_8080_lcd_printf_init(10,109,bsp_8080_lcd_parameter.width-1,369,FONT_ASCII_24_12,WHITE,BLUE); //打印到LCD bsp_8080_lcd_printf("GD32H757ZMT6\r\n"); bsp_8080_lcd_printf("EXMC LCD Brush Test\r\n"); bsp_8080_lcd_printf("LCD ID:%04X\r\n", bsp_8080_lcd_parameter.id); bsp_8080_lcd_printf("* Suzhou Juwo Electronic\r\n"); bsp_8080_lcd_printf("* web: www.gd32bbs.com\r\n"); bsp_8080_lcd_printf("* B station: 475462605\r\n"); bsp_8080_lcd_printf("* QQ Group: 859440462\r\n"); bsp_8080_lcd_printf("* Taobao: juwo.taobao.com\r\n"); bsp_8080_lcd_printf("* Official ID: gd32bbs\r\n"); bsp_8080_lcd_printf("frames brush:%d ms\r\n", frames_speed); //重新設置新的打印窗口 bsp_8080_lcd_printf_init(0,370,bsp_8080_lcd_parameter.width-1,bsp_8080_lcd_parameter.height-1,FONT_ASCII_12_6,BLUE,~BLUE); //將搖桿采集結果處理控制LCD亮度 bsp_8080_lcd_backlight_duty_set(90); while (1) { bsp_8080_lcd_printf(" "); //打印系統tick到LCD bsp_8080_lcd_printf("* system tic is %lld \r\n",driver_tick); delay_ms(200); } } |
14.5實驗結果
復位后顯示聚沃和GD LOG圖片,大字顯示LCD ID,刷屏時間、聚沃相關鏈接地址等。在下方設置了一個printf區窗口,循環打印亮度信息和系統tic信息。
-
單片機
+關注
關注
6035文章
44554瀏覽量
634631 -
mcu
+關注
關注
146文章
17123瀏覽量
350980 -
嵌入式
+關注
關注
5082文章
19104瀏覽量
304797 -
開發板
+關注
關注
25文章
5032瀏覽量
97371 -
GD32
+關注
關注
7文章
403瀏覽量
24326
發布評論請先 登錄
相關推薦
評論