日韩一区二区三区精品,欧美疯狂xxxxbbbb牲交,热99re久久免费视精品频,人妻互换 综合,欧美激情肉欲高潮视频

STM32CubeMX | 35-使用硬件FSMC驅(qū)動TFT-LCD屏幕

發(fā)布者:敬亭山人最新更新時間:2021-07-26 來源: eefocus關(guān)鍵字:STM32CubeMX 手機(jī)看文章 掃描二維碼
隨時隨地手機(jī)看文章

本篇詳細(xì)的記錄了如何使用STM32CubeMX配置 STM32f407ZGT6 的硬件FSMC外設(shè)驅(qū)動TFT-LCD屏幕。


1. 準(zhǔn)備工作

硬件準(zhǔn)備

  • 開發(fā)板
    首先需要準(zhǔn)備一個開發(fā)板,這里我準(zhǔn)備的是STM32F407ZGT6的開發(fā)板。

  • TFT-LCD
    開發(fā)板底板接正點(diǎn)原子4.3寸TFT-LCD。

2. STM32 FSMC外設(shè)概述

2.1. 什么是FSMC

FSMC全稱 Flexible static memory controller,靈活的靜態(tài)內(nèi)存控制器,顧名思義,其主要作用是:負(fù)責(zé)向外部擴(kuò)展的存儲類設(shè)備提供控制信號。


FSMC內(nèi)存控制器支持的存儲設(shè)備有:

  • Nor Flash、SRAM、PSRAM

  • Nand Flash

  • 類SRAM設(shè)備

2.2. FSMC外設(shè)的功能框圖

在這里插入圖片描述

2.3. 外部設(shè)備的地址映射(重點(diǎn))

從FSMC的角度來看,外部的存儲設(shè)備被分為幾個固定大小的Bank,每個bank 256 MB。

整個FSMC外設(shè)映射地址的劃分如圖:

2.3.1. Bank1

Bank1的地址空間為:0x6000 0000 - 0x6FFF FFFF,支持外接Nor Flash、PSRAM、SRAM等設(shè)備,還可以外接DM9000等類存儲設(shè)備。


整個Bank1的地址空間被劃分為四個子bank,每個子bank的大小為64MB,剛好對應(yīng)FSMC外設(shè)的地址總線(FSMC_A[0:25])有26條(2^26=64MB)。


FSMC還有兩條內(nèi)部總線ADDR[27:26],用這兩路控制片選信號,如下表:

BANK1控制時序模型

接下來講述BANK1控制外部存儲器的時序模式,BANK1又稱為Nor Flash/SRAM/PSRAM控制器,后續(xù)暫且叫它SRAM控制器。


SRAM控制器支持兩種控制模式:

  • 同步模式

  • 異步模式

對于異步模式,F(xiàn)SMC主要設(shè)置三個時序參數(shù):

  • 地址建立時間:ADDSET

  • 數(shù)據(jù)建立時間:DATASET

  • 地址保持時間:ADDHLD

根據(jù)SRAM、PSRAM、Nor Flash的綜合特點(diǎn),F(xiàn)MC定義了四種不同的異步時序模型,如下表:

本文中控制TFT-LCD使用的就是異步ModeA時序模型。


異步ModeA時序模型

模式A時序模型的優(yōu)勢在于:支持獨(dú)立的讀寫時序控制。這一點(diǎn)對于控制TFT-LCD來說,非常符合。因為TFT-LCD在讀的時候,一般比較慢,而在寫入的時候一般比較快。

模式A的讀操作時序如圖:

模式A的寫操作時序如圖:

圖中ADDSET和DATASET兩個時序的值,后續(xù)配置的時候會詳細(xì)講述。


2.3.2. Bank2、3/4

只能外接Nand Flash設(shè)備和PC Card設(shè)備:

3. 使用STM32CubeMX生成工程

選擇芯片型號

打開STM32CubeMX,打開MCU選擇器:

搜索并選中芯片STM32F407ZGT6:

配置時鐘源

  • 如果選擇使用外部高速時鐘(HSE),則需要在System Core中配置RCC;

  • 如果使用默認(rèn)內(nèi)部時鐘(HSI),這一步可以略過;

這里我都使用外部時鐘:

調(diào)試選項配置

默認(rèn)沒有配置下載引腳,燒錄之后下載器將無法再檢測到,這里我使用ST-Link,所以配置為SW選項:

配置串口

開發(fā)板板載了一個CH340換串口,連接到USART1,但是引腳不是默認(rèn)引腳,需要手動修改。


接下來開始配置USART1:

配置FSMC外設(shè)

本文所使用的開發(fā)板中,將TFT-LCD當(dāng)做SRAM來操作,連接在FSMC的BANK1的第4個區(qū)域。
知識點(diǎn):為什么TFT-LCD可以當(dāng)做SRAM來控制?
因為TFT-LCD和SRAM相比,同樣需要D0-D15數(shù)據(jù)線,WR、RD、CS控制線,唯一不同的就是TFT-LCD需要一條RS信號線(用于控制傳輸?shù)氖敲钸€是數(shù)據(jù)),而SRAM則需要一堆地址線,所以可以巧妙的使用任意一條地址線來當(dāng)做RS信號。

FSMC配置

開發(fā)板上 TFT-LCD 的原理圖如下:

通過原理圖可以看出:

  • LCD D0-D15:使用了16bit:FSMC D0 - FSMC D15;

  • LCD_RS:使用FSMC A6來控制向LCD寫入數(shù)據(jù)還是命令(0-命令,1-數(shù)據(jù));

  • LCD_BL:背光控制,對應(yīng)PB5;

  • LCD_CS:LCD片選信號,F(xiàn)MC_NE4,表示使用Bank1的Bank4子區(qū)域

  • LCD_WR :LCD寫使能,F(xiàn)SMC_NWE;

  • LCD_RD:LCD讀使能,F(xiàn)SMC_NOE;

  • RESET:LCD復(fù)位信號,直接與單片機(jī)復(fù)位信號接在一起;

根據(jù)這些信息,在STM32CubeMX中先配置SRAM4的基本設(shè)置:

此處如果選擇LCD接口類型和SRAM類型的區(qū)別在于:
LCD接口類型只會配置用到的那一個地址引腳,而SRAM類型則會配置所有的地址引腳。

SRAM基本參數(shù)配置

首先設(shè)置基本的參數(shù),允許讀與寫使用不同的模式:

SRAM時序參數(shù)配置

本文中使用的LCD控制器為NT35510控制器,找到其數(shù)據(jù)手冊,查看:

其中主要的時序參數(shù)配置方法如下。

讀時序配置

① HCLK

時序參數(shù)都是以HCLK的周期為單位的,在本文中HCLK=168Mhz,所以一個周期為5.95ns。

② 地址建立時間:Address setup time(ADDSET)

該時序的最大值的15個HCLK,從圖中可以看出,NT35110控制器要求讀的時候最小為10ns,,所以設(shè)為2即可,2x5.95=11.9ns。

③ 數(shù)據(jù)持續(xù)時間:Data setup time(DATASET)

讀時序比較慢,該時序的最大值為255個HCLK,從圖中可以看出,NT35510控制器要求的數(shù)據(jù)建立時間最小為15ns,但因為讀時序比較慢,所以設(shè)為4,4x5.95=23.8ns。

寫時序配置

① HCLK

時序參數(shù)都是以HCLK的周期為單位的,在本文中HCLK=168Mhz,所以一個周期為5.95ns。

② 地址建立時間:Address setup time(ADDSET)

該時序的最大值為15個HCLK,NT35110控制器要求寫的時候最小為0,,所以設(shè)為0即可。

③ 數(shù)據(jù)持續(xù)時間:Data setup time(DATASET)

寫時序比較快,該時序的最大值為255個HCLK,圖中可以看出,NT35510控制器要求的數(shù)據(jù)建立時間最小為15ns,但因為讀時序比較慢,所以設(shè)為3,3x5.95=17.85ns。

綜合上述計算,配置情況如下:

配置背光引腳

配置時鐘樹

STM32F407ZGT6的最高主頻到168M,使HCLK = 168Mhz即可:
在這里插入圖片描述

生成工程設(shè)置

代碼生成設(shè)置

最后設(shè)置生成獨(dú)立的初始化文件:

生成代碼

點(diǎn)擊GENERATE CODE即可生成MDK-V5工程:

4. 編寫TFT-LCD驅(qū)動(測試是否可以正常讀寫ID)

特別提醒:STM32CubeMX生成的工程默認(rèn)開啟了-O3優(yōu)化,編寫的驅(qū)動太菜了,會出問題,所以遇到玄學(xué)Bug請改為-O0優(yōu)化!


封裝底層發(fā)送/讀取函數(shù)

LCD的底層無非就是兩個API:發(fā)送命令、發(fā)送數(shù)據(jù),(有的還需要從屏幕讀取數(shù)據(jù)),接下來封裝出這兩(三)個底層API。


之前查看原理圖的時候,表示命令或者數(shù)據(jù)的LCD_RS控制引腳接在FMC_A6上,也就是說地址數(shù)據(jù)的第6位,所以在頭文件lcd-fsmc.h中先定義:


/* 通過地址線控制RS引腳 */

#define LCD_CMD_ADDR            0x6c00007E

#define LCD_DAT_ADDR            0x6c000080


接著開始封裝兩個(三個)底層操作函數(shù):


① 發(fā)送命令函數(shù):


/**

 * @brief    向LCD寫入命令

 * @param    cmd 待寫入命令

 * @retval   none

*/

static void lcd_write_cmd(__IO uint16_t cmd)

{

    *(uint16_t *)(LCD_CMD_ADDR) = cmd;

}


② 發(fā)送數(shù)據(jù)函數(shù):


/**

 * @brief    向LCD寫入數(shù)據(jù)

 * @param    data 待寫入數(shù)據(jù)

 * @retval   none

*/

static void lcd_write_data(__IO uint16_t data)

{

    *(uint16_t *)(LCD_DAT_ADDR) = data;

}


③ 讀取數(shù)據(jù)函數(shù):


/**

 * @brief    從LCD讀取數(shù)據(jù)

 * @param    none

 * @retval   讀取到的數(shù)據(jù)

*/

static uint16_t lcd_read_data(void)

{

    __IO uint16_t data;

    

    data = *(uint16_t *)(LCD_DAT_ADDR);

    

    return data;

}


基于這三個底層API,還可以封裝出讀寫LCD內(nèi)部寄存器的函數(shù):


/**

 * @brief    寫LCD中的寄存器

 * @param    reg  寄存器序號

 * @param    data 要寫入寄存器的值

 * @retval   none

*/

static void lcd_write_reg(__IO uint16_t reg, __IO uint16_t data)

{

    lcd_write_cmd(reg);

    lcd_write_data(data);

}


LCD控制參數(shù)結(jié)構(gòu)體

為了方便驅(qū)動不同的IC,保存不同的控制參數(shù),在lcd_fmc.h中封裝如下數(shù)據(jù)類型:


/**

 * @brief    保存LCD屏幕參數(shù)

 * @param    lcd_width     LCD屏幕寬度

 * @param    lcd_height    LCD屏幕高度

 * @param    lcd_id        LCD 驅(qū)動IC ID

 * @param    lcd_direction LCD橫屏顯示還是豎屏顯示,0-豎屏,1-橫屏

 * @param    wram_cmd      開始寫gram指令

 * @param    set_x_cmd     設(shè)置x坐標(biāo)指令

 * @param    set_y_cmd     設(shè)置y坐標(biāo)指令

*/

typedef struct lcd_params_st {

    uint16_t lcd_width;

    uint16_t lcd_height;

    uint16_t lcd_id;

    uint8_t  lcd_direction;

    uint16_t wram_cmd;

    uint16_t set_x_cmd;

    uint16_t set_y_cmd;

} lcd_params_t;


然后在頭文件中聲明外部變量定義,方便其他程序訪問:


extern lcd_params_t lcd_params;


在lcd_fsmc.c中定義此變量為全局變量:


lcd_params_t lcd_params;


LCD驅(qū)動打印日志的處理

為了方便程序開發(fā),難免要打印一些日志,但是如果printf沒有被重定向,則會導(dǎo)致LCD驅(qū)動卡死。為了避免這個問題,我們使用宏開關(guān)的方式來控制是否打印。


在lcd_fsmc.h中定義此宏開關(guān):


/* 使能此驅(qū)動是否打印調(diào)試日志(需要printf支持) */

#define LCD_LOG_ENABLE          1


接著可以定義一個日志打印函數(shù):


#if LCD_LOG_ENABLE

#include

#define LCD_LOG printf

#else

#define LCD_LOG(format,...)

#endif


之后所以需要打印的地方使用LCD_LOG代替printf即可。


編寫LCD控制器ID讀取函數(shù)

通過主動讀取此控制器ID,可以自動檢測出是哪種類型的控制器,然后執(zhí)行不同的驅(qū)動代碼:


static int lcd_read_id(void)

{

    /* 嘗試執(zhí)行ILI9341控制器ID的讀取流程 */

    lcd_write_cmd(0XD3);    

lcd_params.lcd_id = lcd_read_data();

lcd_params.lcd_id = lcd_read_data();

lcd_params.lcd_id = lcd_read_data();    

lcd_params.lcd_id <<= 8;

lcd_params.lcd_id |= lcd_read_data();

    /* 如果正常讀到,則返回成功 */

    if (lcd_params.lcd_id == 0x9341) {

        return 0;

    }

    

    /* 嘗試執(zhí)行NT35310控制器ID的讀取流程 */

    lcd_write_cmd(0XD4);    

    lcd_params.lcd_id = lcd_read_data();

    lcd_params.lcd_id = lcd_read_data();

    lcd_params.lcd_id = lcd_read_data();

    lcd_params.lcd_id <<= 8;  

    lcd_params.lcd_id |= lcd_read_data();

    /* 如果正常讀到,則返回成功 */

    if (lcd_params.lcd_id == 0x5310) {

        return 0;

    }

    

    /* 嘗試執(zhí)行NT35510控制器ID的讀取流程 */

    lcd_write_cmd(0XDA00);

    lcd_params.lcd_id = lcd_read_data();

    lcd_write_cmd(0XDB00);

    lcd_params.lcd_id = lcd_read_data();

    lcd_params.lcd_id <<= 8;  

    lcd_write_cmd(0XDC00);

    lcd_params.lcd_id |= lcd_read_data();

    /* 如果正常讀到,則返回成功 */

    if (lcd_params.lcd_id == 0x8000) {

        lcd_params.lcd_id = 0x5510;

        return 0;

    }

   

    /* 驅(qū)動IC不支持 */

    lcd_params.lcd_id = 0;

    return -1;

}


編寫LCD初始化函數(shù)

LCD初始化需要發(fā)送大量的命令和數(shù)據(jù),本文限于篇幅,只給出讀LCD 控制IC的ID的部分,用來測試LCD是否能正常讀寫足矣。


void lcd_init(void)

{

    /* 初始化FMC接口 */

    //MX_FSMC_Init();

    

    /* 開啟背光 */

    HAL_GPIO_WritePin(LCD_BL_GPIO_Port, LCD_BL_Pin, GPIO_PIN_SET);

    

  HAL_Delay(50); 

  /* 讀取LCD控制器IC */

    if (lcd_read_id() == -1) {

        LCD_LOG("Not Support LCD IC!rn");

        return;

    } else {

        LCD_LOG("LCD IC ID is:%#xrn", lcd_params.lcd_id);  

    }

return;

}


在lcd_fsmc.h中聲明該函數(shù):


void lcd_init(void);


測試是否可以正常操作LCD

在main.c中包含進(jìn)來頭文件:


/* Private includes ----------------------------------------------------------*/

/* USER CODE BEGIN Includes */

#include

#include "lcd_fsmc.h"

/* USER CODE END Includes */


然后在man函數(shù)中調(diào)用:


/* USER CODE BEGIN 2 */

printf("4.3' TFT-LCD Test By Mculover666rn");

lcd_init();

/* USER CODE END 2 */


編譯,下載,在串口助手中查看結(jié)果:


5. 編寫TFT-LCD驅(qū)動(初始化、刷屏測試)

可以正常讀取ID之后,接下來的工作是:


發(fā)送一堆一堆的命令,初始化屏幕;

設(shè)置坐標(biāo)

清屏

刷屏測試

① LCD開顯示、關(guān)顯示、LCD設(shè)置掃描方向、 LCD設(shè)置顯示方向、LCD設(shè)置光標(biāo)位置這些函數(shù)代碼不多,需要的話請查看源碼。


② 清屏函數(shù):



static void lcd_write_ram_start(void)

{

    lcd_write_cmd(lcd_params.wram_cmd);

}


static void lcd_write_ram(uint16_t rgb_color)

{

    lcd_write_data(rgb_color);

}


void lcd_clear(uint16_t color)

{

uint32_t index = 0;      

uint32_t totalpoint = lcd_params.lcd_width;

    

    /* 計算得到總點(diǎn)數(shù) */

totalpoint *= lcd_params.lcd_height;

    

    /* 設(shè)置光標(biāo)位置 */

lcd_set_cursor(0x00,0x0000);

    

    /* 開始寫入GRAM */

lcd_write_ram_start();

    

    /* 寫入數(shù)據(jù)到GRAM */

for (index = 0; index < totalpoint; index++) {

lcd_write_ram(color);

}

}


③ 初始化函數(shù):代碼過長,請查看源碼。


這三類函數(shù)實現(xiàn)完之后,就可以編寫一個如下的刷屏測試函數(shù):


void lcd_auto_clear(uint16_t period_ms)

{

    lcd_clear(BLACK);

    HAL_Delay(period_ms);

    lcd_clear(BLUE);

    HAL_Delay(period_ms);

    lcd_clear(GREEN);

    HAL_Delay(period_ms);

    lcd_clear(GBLUE);

    HAL_Delay(period_ms);

    lcd_clear(CYAN);

    HAL_Delay(period_ms);

    lcd_clear(GRAY);

    HAL_Delay(period_ms);

    lcd_clear(BROWN);

    HAL_Delay(period_ms);

    lcd_clear(RED);

    HAL_Delay(period_ms);

    lcd_clear(BRED);

    HAL_Delay(period_ms);

    lcd_clear(BRRED);

    HAL_Delay(period_ms);

    lcd_clear(YELLOW);

    HAL_Delay(period_ms);

    lcd_clear(WHITE);

    HAL_Delay(period_ms);

}


在main函數(shù)中調(diào)用此函數(shù),分別給予不同的刷新頻率,測試刷屏速度和效果。


6. 實現(xiàn)打點(diǎn)、畫線、填充函數(shù)(重點(diǎn))

打點(diǎn)函數(shù)

/**

 * @brief    LCD打點(diǎn)函數(shù)

 * @param    x_pos x方向坐標(biāo)

 * @param    y_pos y方向坐標(biāo)

 * @retval   none

*/

void lcd_draw_point(uint16_t x_pos, uint16_t y_pos, uint16_t color)

[1] [2]
關(guān)鍵字:STM32CubeMX 引用地址:STM32CubeMX | 35-使用硬件FSMC驅(qū)動TFT-LCD屏幕

上一篇:STM32CubeMX | 33-使用GPIO讀取溫度傳感器數(shù)據(jù)(DS18B20)
下一篇:移植uc/OS-III最新版到小熊派開發(fā)板(STM32L431)

小廣播
設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦

最新單片機(jī)文章
隨便看看

 
EEWorld訂閱號

 
EEWorld服務(wù)號

 
汽車開發(fā)圈

 
機(jī)器人開發(fā)圈

電子工程世界版權(quán)所有 京B2-20211791 京ICP備10001474號-1 電信業(yè)務(wù)審批[2006]字第258號函 京公網(wǎng)安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved