找回密码
 立即注册
查看: 67|回复: 8

STC32G144K256 LCM i8080 DMA数据发送问题

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2026-07-16 11:04:19

1

主题

0

回帖

5

积分

新手上路

积分
5
发表于 6 天前 | 显示全部楼层 |阅读模式
自己设计了 i8080接口的TFT板子, 16位并口,
使用AICUBE生成了工程,发现里边没有关于DMA发送数据的库函数,
在官网也没有找到,有专家指点下该怎么找到库函数,是官方还没出吗?

回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:410
  • 最近打卡:2026-07-21 09:39:09

83

主题

7972

回帖

1万

积分

超级版主

积分
17025
发表于 6 天前 | 显示全部楼层
可以参考Ai8051U的实验箱的例程。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 6 天前 | 显示全部楼层
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 6 天前 | 显示全部楼层
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 6 天前 | 显示全部楼层
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 6 天前 | 显示全部楼层
需要 STC32G144K246工作在100MHz的 DMA-i8080-16位模式
涮TFT彩屏的参考程序,数据从 QSPI的FLASH取, 速度12MHz以下
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 6 天前 | 显示全部楼层
截图202607161315035831.jpg
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 6 天前 | 显示全部楼层

好的,基于手册和论坛信息,我已经掌握了所有关键参数。下面给出完整的参考程序。


参考程序: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周期,四线数据

重要说明

  1. 关于P2P DMA的限制:LCM_DMA的源地址寄存器(DMA_LCM_TXAH/TXAL)是16位,只能访问XRAM空间(0x0000~0xFFFF),无法直接访问QSPI_DR(0x7EF920)。因此采用 QSPI→XRAM缓冲区→LCM_DMA→TFT 的两步法。
  2. 如需精确100MHz:可将CLKDIV从2改为3(216/3=72MHz),或调整HPLLDIV倍频数。例如HPLLDIV=8(68倍)→6×68=408MHz→/2=204MHz→CLKDIV=2→102MHz。
  3. QSPI时钟调整:如果Flash支持更高速度,可减小PSCR值。例如PSCR=5→108/6=18MHz(仍建议≤20MHz)。
  4. 编译环境:使用 Keil C251,Memory Model推荐 XSmall,大数组加 xdata 关键字。

🔍 搜索 "需要 STC32G144K246工作在100MHz的 DMA-i8080-16位模式
涮TFT彩屏的参考程序 数据从 Q"

📌 相关论坛讨论

  1. STC32G144K256 LCM i8080 DMA数据发送问题
  2. 指令在 i-Cache 里面运行, STC32G144K246, 64MHz / 72M / 90M / 100M / 120M
  3. LVGL@STC32G144K246,Hello World ! 51的 LVGL,GUI

⏱️ 查找用时 23.3s | 总回答用时 75.9s

回复

使用道具 举报 送花

  • 打卡等级:以坛为家III
  • 打卡总天数:604
  • 最近打卡:2026-07-22 08:56:28
已绑定手机

115

主题

4992

回帖

1万

积分

超级版主

无情的代码机器

积分
10893
发表于 6 天前 | 显示全部楼层
AiCube生成的直接修改要发送的内容到buffer,调用这个触发函数即可


截图202607161343313993.jpg
截图202607161343506288.jpg
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-22 09:10 , Processed in 0.080524 second(s), 78 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表