FSMC学习笔记
本节使用FSMC来驱动LCD实验,使用LCD模拟8080时序就可以驱动显示器芯片。光与使用8080时序驱动LCD的文章https://blog.csdn.net/qq_42171042/article/details/132236310
FSMC
FSMC,flexible static memory Controller,灵活的静态存储控制器。
用途:用于驱动SRAM,NOR FLASH,NAND FLASH及PC卡类型的存储器。
配置好FSMC,定义一个指向这些地址的指针,通过对指针操作就可以直接修改存储单元的内容,FSMC自动完成读写命令和数据访问操作,不需要程序去实现时序。这里FSMC具体的工作原理还不懂,为什么能够通过读写地址的方式来写数据读数据,通过写命令的方式
F1/ F4(407)系列大容量型号,且引脚数目在100脚以上的芯片都有FSMC接口
F4/F7/H7系列就是FMC接口:他可以驱动sdram
FSMC硬件
FSMC框图介绍
FSMC通信引脚介绍
使用FSMC驱动LCD
FSMC时序介绍
FSMC是Flexible灵活的,可以产生多种时序来控制外部存储器。
NOR/PSRAM控制器产生的异步时序就有5种,总体分为两类:一类是模式1,其他为拓展模式。
拓展模式相对模式1来说读写时序时间参数设置可以不同,满足存储器读写时序不一样需求。
FSMC相关寄存器介绍
对于NOR_FLASH/PSRAM控制器(存储块1)配置工作,通过FSMC_BCRx、FSMC_BTRx和FSMC_BWTRx寄存器设置(其中x=1~4,对应4个区)
FSMC_BCR4(片选控制寄存器。包含存储器块的信息(存储器类型/数据宽度等))
EXTMOD:扩展模式使能位,控制是否允许读写不同的时序。(若读和写用不同的时序,该位设置为1)
WREN:写使能位。向TFTLCD写入数据,该位设置1
MWID[1:0]:存储器数据总线宽度。00,表示8位数据模式;01表示16位数据模式;10和11保留。
MTYP[1:0]:存储器类型。00表示SRAM、ROM;01表示PSRAM;10表示NOR FLASH;11保留。
MBKEN:存储块使能位。该位置1
FSMC_BTR4(片选时序寄存器。设置读操作时序参数(ADDSET/DATAST))
ACCMOD[1:0]:访问模式。00:模式A;01:模式B;10:模式C;11:模式D。
DATAST[7:0]:数据保持时间,等于DATAST(+1)个HCLK时钟周期,DATAST最大为255。
对于ILI9341来说,其实就是RD低电平持续时间,最小为355ns。
对于F1,一个HCLK = 13.9ns(1/72M),设置为15
对于F4,一个HCLK = 6ns(1/168M),设置为60
ADDSET[3:0]:地址建立时间。表示ADDSET(+1)个HCLK时钟周期,ADDSET最大为15。
对于ILI9341来说,相当于RD高电平持续时间,为90ns。
F1即使设置为0,RD也有超过90ns的高电平,这里设置为1。F4对该位设置为15。
如果未设置上一个寄存器的EXTMOD位,则读写共用这个时序寄存器!
FSMC_BWTR4(写时序寄存器。设置写操作时序参数(ADDSET/DATAST))
FSMC寄存器组合说明
在ST官方提供的寄存器定义里面,并没有定义FSMC_BCRx、FSMC_BTRx、FSMC_BWTRx
等这个单独的寄存器,而是将他们进行了一些组合,规则如下:
FSMC_BCRx和FSMC_BTRx,组合成BTCR[8]寄存器组,他们的对应关系如下:
BTCR[0]对应FSMC_BCR1,BTCR[1]对应FSMC_BTR1
BTCR[2]对应FSMC_BCR2,BTCR[3]对应FSMC_BTR2
BTCR[4]对应FSMC_BCR3,BTCR[5]对应FSMC_BTR3
BTCR[6]对应FSMC_BCR4,BTCR[7]对应FSMC_BTR4
FSMC_BWTRx则组合成BWTR[7]寄存器组,他们的对应关系如下:
BWTR[0]对应FSMC_BWTR1,BWTR[2]对应FSMC_BWTR2,
BWTR[4]对应FSMC_BWTR3,BWTR[6]对应FSMC_BWTR4,
BWTR[1]、BWTR[3]和BWTR[5]保留,没有用到
硬件IO连接关系
实验源码
#include "./BSP/LCD/lcd.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LCD/lcd_ex.c"
#include "./BSP/LCD/lcdfont.h"
SRAM_HandleTypeDef g_sram_handle; /* SRAM句柄,初始化FSMC控制LCD) */
/* LCD画笔和背景颜色 */
uint32_t g_point_color = 0XF800; /* »±ÊÑÕÉ« */
uint32_t g_back_color = 0XFFFF; /* ±³¾°É« */
/* LCD重要参数,基本命令和高宽 */
_lcd_dev lcddev;
/* 写命令*/
void lcd_wr_regno(volatile uint16_t cmd)
{
cmd = cmd;
*(uint16_t *)(FSMC_ADDR_CMD) = cmd;
}
/* 写数据 */
void lcd_wr_data(volatile uint16_t data)
{
data = data;
*(uint16_t *)(FSMC_ADDR_DATA) = data;
}
/* 写寄存器 */
void lcd_write_reg(uint16_t regno, uint16_t data)
{
lcd_wr_regno(regno);
lcd_wr_data(data);
}
/* 读数据,通过读取指定地址的数据来读取所需数据,这里的具体工作原来还不太清楚,FSMC,暂时先放着,以后再研究 */
uint16_t lcd_rd_data(void)
{
volatile uint16_t ram;
ram = *(uint16_t *)(FSMC_ADDR_DATA);
return ram;
}
/* 设置坐标*/
void lcd_set_cursor(uint16_t x, uint16_t y)
{
lcd_wr_regno(0x2A);
lcd_wr_data(x >> 8);
lcd_wr_data(x & 0XFF);
lcd_wr_regno(0x2B);
lcd_wr_data(y >> 8);
lcd_wr_data(y & 0XFF);
}
/* 写数据准备 */
void lcd_write_ram_prepare(void)
{
lcd_wr_regno(0x2C);
}
/*画点 */
void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color)
{
lcd_set_cursor(x, y);
lcd_write_ram_prepare();
lcd_wr_data(color);
}
/*读点 */
uint16_t lcd_read_point (uint16_t x, uint16_t y)
{
uint16_t r = 0, g = 0, b = 0; /* ¶¨Òå±äÁ¿ */
lcd_set_cursor(x, y); /*设置坐标 */
lcd_wr_regno(0X2E); /*发送读命令 */
r = lcd_rd_data(); /* 假读 */
r = lcd_rd_data(); /* 读rg */
b = lcd_rd_data(); /* 读b */
g = r & 0XFF; /* µÃµ½gÖµ */
return (((r >> 11) << 11) | ((g >> 2) << 5) | (b >> 11));
}
/* lcd清屏*/
void lcd_clear(uint16_t color)
{
uint32_t totalpoint = 240 * 320;
lcd_set_cursor(0x0000, 0x0000);
lcd_write_ram_prepare();
for (uint32_t index; index < totalpoint; index++)
{
lcd_wr_data(color);
}
}
//初始化FSMC
void lcd_init(void)
{
FSMC_NORSRAM_TimingTypeDef fsmc_read_handle;
FSMC_NORSRAM_TimingTypeDef fsmc_write_handle;
/* FSMC控制的对象 */
g_sram_handle.Instance = FSMC_NORSRAM_DEVICE;
g_sram_handle.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
g_sram_handle.Init.NSBank = FSMC_NORSRAM_BANK4; /* NOR/PSRAM存储块中的BANK4 */
g_sram_handle.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE; /* 数据线和地址线步复用 */
g_sram_handle.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM; /* 存储器类型SRAM */
g_sram_handle.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16; /* 存储器宽度16位 */
g_sram_handle.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE; /* 是否使能突发访问,仅对同步突发存储器有效,此处未用到*/
g_sram_handle.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW; /* 等待信号的极性,仅在突发模式访问下有用*/
g_sram_handle.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS; /* */
g_sram_handle.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE; /*存储器使能 */
g_sram_handle.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE; /* */
g_sram_handle.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE; /* 读写使用不同的时序 */
g_sram_handle.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE; /* */
g_sram_handle.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE; /*禁止突发写 */
/* FSMC读时序控制器*/
fsmc_read_handle.AccessMode = FSMC_ACCESS_MODE_A; /* 模式A */
fsmc_read_handle.AddressSetupTime = 1; /*地址建立时间,HCLK 1/72M = 13.9ns * 2 = 27.8ns (实际 > 200ns) */
fsmc_read_handle.AddressHoldTime = 0; /* 地址保持时间(ADDHLD),模式A没有用到 */
/* */
fsmc_read_handle.DataSetupTime = 15; /*数据保存时间(DATAST)为16个HCLK = 13.9 * 16 = 222.4ns */
/* FSMC写时序控制器,波形一致 */
fsmc_write_handle.AccessMode = FSMC_ACCESS_MODE_A; /* ģʽA */
fsmc_write_handle.AddressSetupTime = 1; /* µØÖ·½¨Á¢Ê±¼ä(ADDSET)Ϊ2¸öHCLK = 27.8 ns */
fsmc_write_handle.AddressHoldTime = 0; /* µØÖ·±£³Öʱ¼ä(ADDHLD) ģʽAÊÇûÓÐÓõ½ */
/* ijЩҺ¾§Çý¶¯ICµÄдÐźÅÂö¿í£¬×îÉÙÒ²µÃ50ns¡£ */
fsmc_write_handle.DataSetupTime = 3; /* 数据保持时间(DATAST)为4个HCLK = 13.9 * 4 = 55.6ns (实际 > 200ns) */
HAL_SRAM_Init(&g_sram_handle, &fsmc_read_handle, &fsmc_write_handle);
delay_ms(50);
/* 读取ID */
lcd_wr_regno(0xD3);
lcddev.id = lcd_rd_data(); /* ¼Ù¶Á */
lcddev.id = lcd_rd_data(); /* 00 */
lcddev.id = lcd_rd_data(); /* 93 */
lcddev.id <<= 8;
lcddev.id |= lcd_rd_data(); /* 41 */
printf("lcddev_id:%#x \r\n", lcddev.id);
/* 初始化LCD芯片 */
if (lcddev.id == 0x9341)
lcd_ex_ili9341_reginit();
else
lcd_ex_st7789_reginit();
/* 对LCD控制结构体赋值 */
lcddev.width = 240;
lcddev.height = 320;
lcddev.setxcmd = 0x2A;
lcddev.setycmd = 0x2B;
lcddev.wramcmd = 0x2C;
lcd_wr_regno(lcddev.setxcmd);
lcd_wr_data(0);
lcd_wr_data(0);
lcd_wr_data((lcddev.width - 1) >> 8);
lcd_wr_data((lcddev.width - 1) & 0XFF);
lcd_wr_regno(lcddev.setycmd);
lcd_wr_data(0);
lcd_wr_data(0);
lcd_wr_data((lcddev.height - 1) >> 8);
lcd_wr_data((lcddev.height - 1) & 0XFF);
/* ÉèÖÃɨÃè·½Ïò */
lcd_write_reg(0x36, 1 << 3);
/* µãÁÁ±³¹â */
LCD_BL(1);
/* lcd_clear */
lcd_clear(0xFFFF);
}
//设置使用FSMC模拟8080时序所需要的外设。包括时钟使能和设置GPIO工作模式。这里的SRAM是不是显示器芯片的存储器。
void HAL_SRAM_MspInit(SRAM_HandleTypeDef *hsram)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_FSMC_CLK_ENABLE(); /* 使能FSMC时钟 */
__HAL_RCC_GPIOD_CLK_ENABLE(); /* ʹÄÜGPIODʱÖÓ */
__HAL_RCC_GPIOE_CLK_ENABLE(); /* ʹÄÜGPIOEʱÖÓ */
LCD_WR_GPIO_CLK_ENABLE();
LCD_RD_GPIO_CLK_ENABLE();
LCD_BL_GPIO_CLK_ENABLE();
LCD_CS_GPIO_CLK_ENABLE();
LCD_RS_GPIO_CLK_ENABLE();
gpio_init_struct.Pin = LCD_CS_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* ÍÆÍ츴Óà */
gpio_init_struct.Pull = GPIO_PULLUP; /* ÉÏÀ */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* ¸ßËÙ */
HAL_GPIO_Init(LCD_CS_GPIO_PORT, &gpio_init_struct); /* ³õʼ»¯LCD_CSÒý½Å */
gpio_init_struct.Pin = LCD_WR_GPIO_PIN;
HAL_GPIO_Init(LCD_WR_GPIO_PORT, &gpio_init_struct); /* ³õʼ»¯LCD_WRÒý½Å */
gpio_init_struct.Pin = LCD_RD_GPIO_PIN;
HAL_GPIO_Init(LCD_RD_GPIO_PORT, &gpio_init_struct); /* ³õʼ»¯LCD_RDÒý½Å */
gpio_init_struct.Pin = LCD_RS_GPIO_PIN;
HAL_GPIO_Init(LCD_RS_GPIO_PORT, &gpio_init_struct); /* ³õʼ»¯LCD_RSÒý½Å */
gpio_init_struct.Pin = LCD_BL_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* ÍÆÍìÊä³ö */
HAL_GPIO_Init(LCD_BL_GPIO_PORT, &gpio_init_struct); /* LCD_BLÒý½ÅģʽÉèÖÃ(ÍÆÍìÊä³ö) */
/* Êý¾ÝÏß³õʼ»¯ */
/* ³õʼ»¯PD0,1,8,9,10,14,15 */
gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 \
| GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15;
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* ÍÆÍ츴Óà */
gpio_init_struct.Pull = GPIO_PULLUP; /* ÉÏÀ */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* ¸ßËÙ */
HAL_GPIO_Init(GPIOD, &gpio_init_struct); /* ³õʼ»¯ */
/* ³õʼ»¯PE7,8,9,10,11,12,13,14,15 */
gpio_init_struct.Pin = 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;
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
}
main.c没有变化。