好的,基于手册和论坛信息,我已经掌握了所有关键参数。下面给出完整的参考程序。
参考程序:STC32G144K246 @100MHz,DMA-i8080-16位涮TFT,QSPI Flash取数据,QSPI时钟≤12MHz
信息来源:
- 手册第8章「时钟管理」:HPLL配置、CLKSEL、CLKDIV
- 手册第26章「四线SPI(QSPI)」:PSCR分频
- 手册第32章「TFT彩屏接口」:LCMIFPSCR、SETUPT、HOLDT
- 手册第33章「TFT彩屏与存储器之间的数据读写(LCM_DMA)」
- 论坛TID=20601:HPLL产生60/80/120MHz例程
- 论坛TID=24732:QSPI+TFT DMA P2P例程
时钟方案
| 项目 |
值 |
计算 |
| 下载IRC |
48MHz |
ISP下载时选择 |
| HPLL输入 |
48MHz IRC |
HPLLCKS=1 |
| HPLL预分频 |
HPLLPDIV=8 |
48/8=6MHz基频 |
| HPLL倍频 |
HPLLDIV=10(72倍) |
6×72=432MHz |
| MCK2SEL |
HPLL1输出/2 |
432/2=216MHz |
| CLKDIV |
2 |
216/2=108MHz(接近100MHz) |
| 或CLKDIV=3 |
216/3=72MHz |
如需精确100MHz可调 |
注意:STC32G144K246的HPLL输出频率为432MHz时,MCK2SEL选择HPLL1输出/2得216MHz,再经CLKDIV分频。要精确得到100MHz,可调整HPLLDIV或使用外部晶振。以下代码以 HPLL→216MHz→CLKDIV=2→108MHz 为例,您可根据需要微调CLKDIV。
QSPI时钟 ≤12MHz:108MHz / 9 = 12MHz(PSCR=8),或108MHz / 10 = 10.8MHz(PSCR=9)
LCMIF时钟:108MHz / 9 = 12MHz(LCMIFPSCR=8),SETUPT=0→Tsetup=1个LCMIF时钟≈83ns,HOLDT=0→Thold=1个LCMIF时钟≈83ns
文件1:config.h
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
#ifndef __CONFIG_H__
#define __CONFIG_H__
//==================================================================
// 时钟配置
// 下载时选择IRC频率 48MHz
// HPLL1: 48MHz -> 预分频/8=6MHz -> 倍频72倍=432MHz
// MCK2SEL = HPLL1输出/2 = 216MHz
// CLKDIV = 2 -> SYSCLK = 216/2 = 108MHz
//==================================================================
#define MAIN_Fosc 108000000L // 系统时钟 108MHz
#define SYS_CLK 108000000UL
//==================================================================
// QSPI时钟配置(要求 ≤12MHz)
// QSPI时钟 = SYSCLK / (PSCR + 1)
// PSCR = 8 -> 108/9 = 12MHz
// PSCR = 9 -> 108/10 = 10.8MHz
// PSCR = 17 -> 108/18 = 6MHz
//==================================================================
#define QSPI_PSCR 8 // QSPI时钟 = 108/9 = 12MHz
//==================================================================
// LCMIF时钟配置
// LCMIF时钟 = SYSCLK / (LCMIFPSCR + 1)
// LCMIFPSCR = 8 -> 108/9 = 12MHz
//==================================================================
#define LCMIF_CLK_DIV 8 // LCMIF时钟 = 108/9 = 12MHz
// TFT时序参数(基于LCMIF时钟=12MHz,周期≈83ns)
// Tsetup = (SETUPT + 1) * LCMIF周期
// Thold = (HOLDT + 1) * LCMIF周期
#define LCM_SETUPT 0 // Tsetup = 1 * 83ns = 83ns
#define LCM_HOLDT 0 // Thold = 1 * 83ns = 83ns
//==================================================================
// TFT彩屏参数(以ILI9341 320x240为例)
//==================================================================
#define TFT_WIDTH 320
#define TFT_HEIGHT 240
#define TFT_PIXEL_BYTES 2 // 16位色,每像素2字节
#define IMG_SIZE ((unsigned long)TFT_WIDTH * TFT_HEIGHT * TFT_PIXEL_BYTES)
// DMA每次传输的字节数(需根据XRAM缓冲区大小调整)
#define DMA_AMT_LEN 51200UL
#define DMA_CNT (IMG_SIZE / DMA_AMT_LEN)
//==================================================================
// 端口定义
// TFT i8080 16位接口(LCMIFDPS=00, D16_D8=1, LCMIFCPS=00)
// DAT[15:8] = P2[7:0], DAT[7:0] = P0[7:0]
// RS=P4.5, RD=P4.4, WR=P4.2
//==================================================================
sbit LCM_CS = P9^3;
sbit LCM_RS = P4^5;
sbit LCM_WR = P4^2;
sbit LCM_RD = P4^4;
sbit LCM_RST = P5^1;
// QSPI Flash接口(QSPI_S=10,P7组)
sbit QSPI_CS = P7^5;
sbit QSPI_IO0 = P7^0;
sbit QSPI_IO1 = P7^2;
sbit QSPI_CLK = P7^1;
sbit QSPI_IO2 = P7^3;
sbit QSPI_IO3 = P7^4;
//==================================================================
// 数据类型定义
//==================================================================
typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef unsigned long DWORD;
// 文件分配表结构
typedef struct {
char strSign[4];
DWORD dwCount;
DWORD dwAddress[62];
} FAT;
#endif
文件2:sys_clk.h
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
#ifndef __SYS_CLK_H__
#define __SYS_CLK_H__
// 时钟寄存器(XFR空间)
#define CLKSEL (*(volatile BYTE *)0x7EFE00)
#define CLKDIV (*(volatile BYTE *)0x7EFE01)
#define HIRCCR (*(volatile BYTE *)0x7EFE02)
#define HPLLCR (*(volatile BYTE *)0x7EFE0C)
#define HPLLPDIV (*(volatile BYTE *)0x7EFE0D)
#define IRCDB (*(volatile BYTE *)0xFE06)
void SYS_Clock_Init(void);
#endif
文件3:sys_clk.c
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
#include "config.h"
#include "sys_clk.h"
/****************************************************************************
* 系统时钟初始化
*
* 目标:系统时钟 = 108MHz
*
* 流程:
* 1. 下载时选择48MHz IRC
* 2. 配置HPLL:输入=48MHz IRC,预分频/8=6MHz,倍频72倍=432MHz
* 3. 选择MCK2SEL=HPLL1输出/2 = 216MHz
* 4. CLKDIV=2 -> SYSCLK = 216/2 = 108MHz
* 5. 关闭HPLL(切换完成后可关闭以省电,或保持开启)
****************************************************************************/
void SYS_Clock_Init(void)
{
// 使能访问XFR
P_SW2 |= 0x80;
// 1. 配置HPLL预分频
// HPLL输入时钟源 = 48MHz IRC (HPLLCKS=1)
// 预分频:48MHz / 8 = 6MHz(必须保证基频为6MHz)
HPLLPDIV = 8;
// 2. 配置HPLL倍频
// HPLLDIV=10 -> 倍频72倍 -> 6MHz * 72 = 432MHz
HPLLCR = 0x9A; // ENHPLL=1, HSI2SCK=00, HPLLCKS=1(48M IRC),
// HPLLDIV=10(72倍)
// 3. 等待HPLL稳定
{
WORD wait;
for (wait = 0; wait < 1000; wait++);
}
// 4. 选择主时钟源为HPLL1输出/2
// MCK2SEL[1:0] = 01 -> HPLL1输出/2 = 432/2 = 216MHz
CLKSEL = 0x10; // MCK2SEL=01, MCKSEL=00(内部IRC, 但被MCK2SEL覆盖)
// 5. 设置系统时钟分频
// CLKDIV=2 -> SYSCLK = 216/2 = 108MHz
CLKDIV = 2;
// 6. 等待时钟切换稳定
{
WORD wait;
for (wait = 0; wait < 100; wait++);
}
// 7. 设置省电模式唤醒等待时钟数(手册建议)
IRCDB = 0x10;
}
文件4:qspi.h
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
#ifndef __QSPI_H__
#define __QSPI_H__
// QSPI寄存器地址(XFR空间)
#define QSPI_CR1 (*(volatile BYTE *)0x7EF900)
#define QSPI_CR2 (*(volatile BYTE *)0x7EF901)
#define QSPI_CR3 (*(volatile BYTE *)0x7EF902)
#define QSPI_CR4 (*(volatile BYTE *)0x7EF903)
#define QSPI_DCR1 (*(volatile BYTE *)0x7EF904)
#define QSPI_DCR2 (*(volatile BYTE *)0x7EF905)
#define QSPI_SR1 (*(volatile BYTE *)0x7EF906)
#define QSPI_SR2 (*(volatile BYTE *)0x7EF907)
#define QSPI_FCR (*(volatile BYTE *)0x7EF908)
#define QSPI_DLR1 (*(volatile BYTE *)0x7EF910)
#define QSPI_DLR2 (*(volatile BYTE *)0x7EF911)
#define QSPI_CCR1 (*(volatile BYTE *)0x7EF914)
#define QSPI_CCR2 (*(volatile BYTE *)0x7EF915)
#define QSPI_CCR3 (*(volatile BYTE *)0x7EF916)
#define QSPI_CCR4 (*(volatile BYTE *)0x7EF917)
#define QSPI_AR1 (*(volatile BYTE *)0x7EF918)
#define QSPI_AR2 (*(volatile BYTE *)0x7EF919)
#define QSPI_AR3 (*(volatile BYTE *)0x7EF91A)
#define QSPI_AR4 (*(volatile BYTE *)0x7EF91B)
#define QSPI_DR (*(volatile BYTE *)0x7EF920)
// QSPI功能脚切换寄存器
#define P_SW4 (*(volatile BYTE *)0xBF)
// W25Qxx常用指令
#define CMD_READ_DATA 0x03 // 单线读
#define CMD_QUAD_READ 0x6B // 四线快速读
void QSPI_Init(void);
void QSPI_ReadFlash(DWORD addr, BYTE *pBuf, WORD len);
#endif
文件5:qspi.c
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
#include "config.h"
#include "qspi.h"
/****************************************************************************
* QSPI初始化
*
* 时钟:SYSCLK=108MHz, PSCR=8 -> QSPI时钟=108/9=12MHz
* 模式:模式0 (CKMODE=0)
* 引脚:QSPI_S=10 (P7组)
****************************************************************************/
void QSPI_Init(void)
{
// 使能访问XFR
P_SW2 |= 0x80;
// 1. 配置QSPI功能脚:QSPI_S[1:0] = 10,选择P7组
P_SW4 &= ~0x03;
P_SW4 |= 0x02;
// 2. 配置时钟预分频
// QSPI时钟 = SYSCLK / (PSCR + 1) = 108 / (8+1) = 12MHz
QSPI_CR4 = QSPI_PSCR;
// 3. 配置器件参数
// CKMODE=0(模式0), CSHT=0(片选高电平1周期)
// QSPI_IP=00(中断优先级最低)
QSPI_DCR1 = 0x00;
// 4. 配置Flash大小(以W25Q128 16MB为例)
// 16MB = 2^24, FSIZE = 24-1 = 23 = 0x17
QSPI_DCR2 = 0x17;
// 5. 使能QSPI
QSPI_CR1 = 0x01; // EN=1
}
/****************************************************************************
* QSPI间接模式 - 四线快速读取Flash数据
*
* 使用0x6B指令:指令单线,地址24位单线,8个dummy周期,数据四线
*
* addr: Flash中的起始地址
* pBuf: 目标缓冲区(XRAM)
* len: 读取字节数
****************************************************************************/
void QSPI_ReadFlash(DWORD addr, BYTE *pBuf, WORD len)
{
WORD i;
// 等待QSPI空闲
while (QSPI_SR1 & 0x20); // BUSY位
// 清除标志
QSPI_FCR = 0x02; // CTCF=1
// 配置数据长度(实际传输 = DL + 1)
QSPI_DLR1 = (BYTE)((len - 1) & 0xFF);
QSPI_DLR2 = (BYTE)((len - 1) >> 8);
// 配置通信格式
// CCR2: IMODE=01(单线指令), ADMODE=01(单线地址),
// ADSIZE=10(24位地址), ABMODE=00(无交替字节)
QSPI_CCR2 = 0x01;
// CCR3: DCYC=8(8个dummy周期), ABSIZE=00
QSPI_CCR3 = 0x20;
// CCR4: FMODE=01(间接读取), DMODE=11(四线数据), SIOO=0
QSPI_CCR4 = 0x13;
// 设置指令:0x6B(四线快速读)
QSPI_CCR1 = CMD_QUAD_READ;
// 设置地址(先写高字节,最后写低字节触发操作)
QSPI_AR4 = 0x00;
QSPI_AR3 = (BYTE)(addr >> 16);
QSPI_AR2 = (BYTE)(addr >> 8);
QSPI_AR1 = (BYTE)(addr); // 写LSB触发操作
// 等待传输完成
while (!(QSPI_SR1 & 0x02)); // TCF标志
// 从FIFO读取数据
for (i = 0; i < len; i++)
{
pBuf[i] = QSPI_DR;
}
// 清除TCF标志
QSPI_FCR = 0x02;
}
文件6:lcm.h
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
#ifndef __LCM_H__
#define __LCM_H__
// LCM寄存器地址(XFR空间)
#define LCMIFCFG (*(volatile BYTE *)0x7EFE50)
#define LCMIFCFG2 (*(volatile BYTE *)0x7EFE51)
#define LCMIFCR (*(volatile BYTE *)0x7EFE52)
#define LCMIFSTA (*(volatile BYTE *)0x7EFE53)
#define LCMIFDATL (*(volatile BYTE *)0x7EFE54)
#define LCMIFDATH (*(volatile BYTE *)0x7EFE55)
#define LCMIFPSCR (*(volatile BYTE *)0x7EFE56)
// LCM_DMA寄存器地址
#define DMA_LCM_CFG (*(volatile BYTE *)0x7EFA70)
#define DMA_LCM_CR (*(volatile BYTE *)0x7EFA71)
#define DMA_LCM_STA (*(volatile BYTE *)0x7EFA72)
#define DMA_LCM_AMTH (*(volatile BYTE *)0x7EFA86)
#define DMA_LCM_AMT (*(volatile BYTE *)0x7EFA73)
#define DMA_LCM_DONEH (*(volatile BYTE *)0x7EFA87)
#define DMA_LCM_DONE (*(volatile BYTE *)0x7EFA74)
#define DMA_LCM_TXAH (*(volatile BYTE *)0x7EFA75)
#define DMA_LCM_TXAL (*(volatile BYTE *)0x7EFA76)
#define DMA_LCM_RXAH (*(volatile BYTE *)0x7EFA77)
#define DMA_LCM_RXAL (*(volatile BYTE *)0x7EFA78)
#define DMA_LCM_ITVH (*(volatile BYTE *)0x7EFA7E)
#define DMA_LCM_ITVL (*(volatile BYTE *)0x7EFA7F)
void LCM_Init(void);
void LCM_WriteCmd(BYTE cmd);
void LCM_WriteData16(WORD dat);
void LCM_SetWindow(WORD x1, WORD y1, WORD x2, WORD y2);
void LCM_DMA_Start(DWORD src_addr, DWORD len);
void LCM_DMA_WaitDone(void);
#endif
文件7:lcm.c
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
#include "config.h"
#include "lcm.h"
/****************************************************************************
* TFT彩屏接口初始化
*
* 模式:i8080,16位数据宽度
* 引脚:LCMIFDPS=00, D16_D8=1, LCMIFCPS=00
* DAT[15:8] = P2[7:0], DAT[7:0] = P0[7:0]
* RS=P4.5, RD=P4.4, WR=P4.2
*
* 时钟:LCMIF时钟 = SYSCLK/(LCMIFPSCR+1) = 108/9 = 12MHz
* 时序:Tsetup = (SETUPT+1)*83ns, Thold = (HOLDT+1)*83ns
****************************************************************************/
void LCM_Init(void)
{
// 使能访问XFR
P_SW2 |= 0x80;
// 1. LCMIFCFG:i8080模式,16位数据宽度,数据脚P2/P0
// B1(D16_D8)=1(16位), B0(M68_I80)=0(i8080)
// B3:B2(LCMIFDPS)=00(P2高字节,P0低字节)
// B7(LCMIFIE)=0(不使能中断)
LCMIFCFG = 0x02;
// 2. LCMIFCFG2:控制脚选择,建立/保持时间
// B6:B5(LCMIFCPS)=00(RS=P4.5,RD=P4.4,WR=P4.2)
// B4:B2(SETUPT)=000 -> Tsetup=1个LCMIF时钟≈83ns
// B1:B0(HOLDT)=00 -> Thold=1个LCMIF时钟≈83ns
LCMIFCFG2 = 0x00;
// 3. LCMIFPSCR:时钟预分频
// LCMIF时钟 = 108MHz / (8+1) = 12MHz
LCMIFPSCR = LCMIF_CLK_DIV;
// 4. 使能TFT彩屏接口
LCMIFCR = 0x80; // ENLCMIF=1
}
/****************************************************************************
* 写命令(8位命令,16位数据总线)
****************************************************************************/
void LCM_WriteCmd(BYTE cmd)
{
while (LCMIFSTA & 0x01); // 等待空闲
LCMIFCR = 0x84; // ENLCMIF=1, CMD=100(写命令)
LCMIFDATL = cmd; // 命令在低字节
LCMIFDATH = 0x00;
}
/****************************************************************************
* 写16位数据
****************************************************************************/
void LCM_WriteData16(WORD dat)
{
while (LCMIFSTA & 0x01); // 等待空闲
LCMIFCR = 0x85; // ENLCMIF=1, CMD=101(写数据)
LCMIFDATL = (BYTE)(dat & 0xFF); // 低字节
LCMIFDATH = (BYTE)(dat >> 8); // 高字节
}
/****************************************************************************
* 设置TFT显示窗口(以ILI9341为例)
****************************************************************************/
void LCM_SetWindow(WORD x1, WORD y1, WORD x2, WORD y2)
{
LCM_WriteCmd(0x2A); // 列地址设置
LCM_WriteData16(x1);
LCM_WriteData16(x2);
LCM_WriteCmd(0x2B); // 行地址设置
LCM_WriteData16(y1);
LCM_WriteData16(y2);
LCM_WriteCmd(0x2C); // 开始写存储器
}
/****************************************************************************
* LCM_DMA启动:从XRAM缓冲区传输数据到TFT接口
*
* src_addr: XRAM源地址(16位)
* len: 传输字节数(实际传输 = len)
*
* 注意:DMA_LCM的TXA是16位地址,只能访问XRAM(0x0000~0xFFFF)
* 不能直接访问QSPI_DR(0x7EF920),因此采用两步法:
* 1. CPU通过QSPI将数据读到XRAM缓冲区
* 2. LCM_DMA将XRAM缓冲区数据发送到TFT
****************************************************************************/
void LCM_DMA_Start(DWORD src_addr, DWORD len)
{
// 使能访问XFR
P_SW2 |= 0x80;
// 1. 配置DMA_LCM_CFG:不使能中断,总线优先级最低
DMA_LCM_CFG = 0x00;
// 2. 设置源地址(XRAM缓冲区地址)
DMA_LCM_TXAH = (BYTE)(src_addr >> 8);
DMA_LCM_TXAL = (BYTE)(src_addr);
// 3. 设置传输总字节数(实际传输 = LCM_AMT + 1)
DMA_LCM_AMTH = (BYTE)((len - 1) >> 8);
DMA_LCM_AMT = (BYTE)((len - 1) & 0xFF);
// 4. 设置传输周期(0=最快速度)
DMA_LCM_ITVH = 0x00;
DMA_LCM_ITVL = 0x00;
// 5. 清除FIFO
DMA_LCM_CR = 0x81; // ENLCM=1, CLRFIFO=1
DMA_LCM_CR = 0x80; // ENLCM=1, CLRFIFO=0
// 6. 设置LCMIFCR为"写数据"模式
LCMIFCR = 0x85; // ENLCMIF=1, CMD=101(写数据)
// 7. 触发发送数据模式
DMA_LCM_CR = 0xA0; // ENLCM=1, TRIGWD=1
}
/****************************************************************************
* 等待LCM_DMA传输完成
****************************************************************************/
void LCM_DMA_WaitDone(void)
{
while (!(DMA_LCM_STA & 0x01)); // 等待LCMIF标志置1
DMA_LCM_STA &= ~0x01; // 清除标志
}
文件8:tft.h
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
#ifndef __TFT_H__
#define __TFT_H__
void TFT_Init(void);
void TFT_Clear(WORD color);
void TFT_DisplayImage(DWORD img_addr);
#endif
文件9:tft.c
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
#include "config.h"
#include "lcm.h"
#include "tft.h"
#include "qspi.h"
// XRAM缓冲区(用于中转QSPI读取的数据)
// 注意:大数组必须加xdata关键字,定义在XRAM空间
BYTE xdata TFT_Buffer[DMA_AMT_LEN];
/****************************************************************************
* TFT初始化(以ILI9341为例)
****************************************************************************/
void TFT_Init(void)
{
WORD delay;
// 硬件复位
LCM_RST = 0;
for (delay = 0; delay < 1000; delay++); // 约1ms
LCM_RST = 1;
for (delay = 0; delay < 10000; delay++); // 约10ms
// ILI9341初始化序列
LCM_WriteCmd(0x01); // 软件复位
for (delay = 0; delay < 50000; delay++); // 约50ms
LCM_WriteCmd(0x11); // 退出睡眠
for (delay = 0; delay < 50000; delay++); // 约50ms
LCM_WriteCmd(0x36); // 存储访问控制
LCM_WriteData16(0x0048); // MX, BGR
LCM_WriteCmd(0x3A); // 像素格式
LCM_WriteData16(0x0055); // 16位色
LCM_WriteCmd(0x29); // 开启显示
}
/****************************************************************************
* 清屏
****************************************************************************/
void TFT_Clear(WORD color)
{
unsigned long i;
LCM_SetWindow(0, 0, TFT_WIDTH - 1, TFT_HEIGHT - 1);
for (i = 0; i < (unsigned long)TFT_WIDTH * TFT_HEIGHT; i++)
{
LCM_WriteData16(color);
}
}
/****************************************************************************
* 从QSPI Flash读取一张图片并显示
*
* 流程:
* 1. 设置TFT窗口为全屏,准备写数据
* 2. 循环:QSPI读取一块数据到XRAM缓冲区
* 3. LCM_DMA将缓冲区数据发送到TFT
*
* img_addr: 图片数据在Flash中的起始地址
****************************************************************************/
void TFT_DisplayImage(DWORD img_addr)
{
DWORD offset;
WORD i;
// 设置TFT窗口为全屏
LCM_SetWindow(0, 0, TFT_WIDTH - 1, TFT_HEIGHT - 1);
offset = img_addr;
for (i = 0; i < DMA_CNT; i++)
{
// 1. QSPI从Flash读取一块数据到XRAM缓冲区
QSPI_ReadFlash(offset, TFT_Buffer, (WORD)DMA_AMT_LEN);
offset += DMA_AMT_LEN;
// 2. LCM_DMA将缓冲区数据发送到TFT
LCM_DMA_Start((DWORD)TFT_Buffer, DMA_AMT_LEN);
LCM_DMA_WaitDone();
}
// 处理剩余数据
{
DWORD remain = IMG_SIZE - (DMA_CNT * DMA_AMT_LEN);
if (remain > 0)
{
QSPI_ReadFlash(offset, TFT_Buffer, (WORD)remain);
LCM_DMA_Start((DWORD)TFT_Buffer, (WORD)remain);
LCM_DMA_WaitDone();
}
}
}
文件10:main.c
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com -----------------------------------------*/
/*---------------------------------------------------------------------*/
/************* 功能说明 **************
*
* STC32G144K246 @108MHz
* QSPI + LCM_DMA 驱动i8080 16位并口TFT彩屏
* QSPI时钟 = 12MHz(PSCR=8)
* LCMIF时钟 = 12MHz(LCMIFPSCR=8)
*
* 硬件连接:
* TFT (i8080 16位):
* DAT[15:8] <- P2[7:0]
* DAT[7:0] <- P0[7:0]
* RS <- P4.5
* RD <- P4.4
* WR <- P4.2
* RST <- P5.1
* CS <- P9.3
*
* QSPI Flash (W25Q64/W25Q128):
* CS <- P7.5
* IO0 <- P7.0
* IO1 <- P7.2
* CLK <- P7.1
* IO2 <- P7.3
* IO3 <- P7.4
*
* 下载时选择IRC频率 48MHz。
* 使用STC-ISP 6.94P以上版本的"串行Flash编程器"工具,
* 将图片数据下载到外部QSPI Flash中。
*
* 编译环境:Keil C251,Memory Model = XSmall
*
******************************************/
#include "config.h"
#include "sys_clk.h"
#include "qspi.h"
#include "lcm.h"
#include "tft.h"
// 全局变量
FAT xdata Fat; // 文件分配表
DWORD xdata dwOffset; // 图片数据偏移地址
int xdata nIndex; // 当前图片索引
int xdata nCount; // 图片总数
/****************************************************************************
* 从Flash读取文件分配表(FAT)
* FAT存储在Flash的0地址处
****************************************************************************/
void Read_FAT(void)
{
QSPI_ReadFlash(0, (BYTE *)&Fat, sizeof(FAT));
// 检查签名
if ((Fat.strSign[0] != 'S') || (Fat.strSign[1] != 'T') ||
(Fat.strSign[2] != 'C') || (Fat.strSign[3] != 'A'))
{
Fat.dwCount = 0;
}
nCount = (int)Fat.dwCount;
nIndex = 0;
}
/****************************************************************************
* 主函数
****************************************************************************/
void main(void)
{
// 使能访问XFR
P_SW2 |= 0x80;
// 1. 初始化系统时钟为108MHz
SYS_Clock_Init();
// 2. 初始化QSPI(时钟=12MHz)
QSPI_Init();
// 3. 初始化TFT彩屏接口(时钟=12MHz)
LCM_Init();
// 4. 初始化TFT液晶
TFT_Init();
// 5. 清屏为黑色
TFT_Clear(0x0000);
// 6. 读取文件分配表
Read_FAT();
// 7. 如果Flash中有图片,循环显示
if (nCount > 0)
{
while (1)
{
dwOffset = Fat.dwAddress[nIndex];
TFT_DisplayImage(dwOffset);
nIndex++;
if (nIndex >= nCount)
{
nIndex = 0;
}
// 帧间延时
{
unsigned long delay;
for (delay = 0; delay < 3000000; delay++);
}
}
}
else
{
TFT_Clear(0xF800); // 无图片则显示红色
}
while (1);
}
关键参数汇总
| 参数 |
值 |
说明 |
| 系统时钟 |
108MHz |
HPLL: 48M→预分频/8→6M→倍频72→432M→/2=216M→CLKDIV=2→108M |
| QSPI时钟 |
12MHz |
PSCR=8, 108M/(8+1)=12M,满足≤12MHz要求 |
| LCMIF时钟 |
12MHz |
LCMIFPSCR=8, 108M/(8+1)=12M |
| Tsetup |
83ns |
SETUPT=0, (0+1)×83ns |
| Thold |
83ns |
HOLDT=0, (1+0)×83ns |
| TFT模式 |
i8080 16位 |
D16_D8=1, M68_I80=0 |
| 数据引脚 |
P2[15:8], P0[7:0] |
LCMIFDPS=00 |
| 控制引脚 |
RS=P4.5, RD=P4.4, WR=P4.2 |
LCMIFCPS=00 |
| QSPI引脚 |
P7组 |
QSPI_S=10 |
| Flash指令 |
0x6B四线快速读 |
8个dummy周期,四线数据 |
重要说明
- 关于P2P DMA的限制:LCM_DMA的源地址寄存器(DMA_LCM_TXAH/TXAL)是16位,只能访问XRAM空间(0x0000~0xFFFF),无法直接访问QSPI_DR(0x7EF920)。因此采用 QSPI→XRAM缓冲区→LCM_DMA→TFT 的两步法。
- 如需精确100MHz:可将CLKDIV从2改为3(216/3=72MHz),或调整HPLLDIV倍频数。例如HPLLDIV=8(68倍)→6×68=408MHz→/2=204MHz→CLKDIV=2→102MHz。
- QSPI时钟调整:如果Flash支持更高速度,可减小PSCR值。例如PSCR=5→108/6=18MHz(仍建议≤20MHz)。
- 编译环境:使用 Keil C251,Memory Model推荐 XSmall,大数组加
xdata 关键字。
🔍 搜索 "需要 STC32G144K246工作在100MHz的 DMA-i8080-16位模式
涮TFT彩屏的参考程序 数据从 Q"
📌 相关论坛讨论
- STC32G144K256 LCM i8080 DMA数据发送问题
- 指令在 i-Cache 里面运行, STC32G144K246, 64MHz / 72M / 90M / 100M / 120M
- LVGL@STC32G144K246,Hello World ! 51的 LVGL,GUI
⏱️ 查找用时 23.3s | 总回答用时 75.9s