张春来 发表于 6 天前

STC32G144K256 LCM i8080 DMA数据发送问题

自己设计了 i8080接口的TFT板子, 16位并口,
使用AICUBE生成了工程,发现里边没有关于DMA发送数据的库函数,
在官网也没有找到,有专家指点下该怎么找到库函数,是官方还没出吗?

梁工 发表于 6 天前

可以参考Ai8051U的实验箱的例程。

神农鼎 发表于 6 天前



LVGL@STC32G144K246,Hello World ! 51的 LVGL,GUI - LVGL,LiteOS,@STC32G144K246 实现启动中 国芯人工智能技术交流网站 - AI32位8051交流社区

神农鼎 发表于 6 天前






深圳国芯人工智能有限公司-实验箱

神农鼎 发表于 6 天前



AiCube 图形化自动生成程序,TFT-彩屏@Ai8051U, 驱动 ST7735S,ST7739, ST7789 - TFT彩屏,触摸屏,DMA-i8080/M6800并口自动刷屏,DMA-SPI刷屏,外设直接到外设 国芯人工智能技术交流网站 - AI32位8051交流社区

这个帖子中咨询比较好

神农鼎 发表于 6 天前

需要 STC32G144K246工作在100MHz的 DMA-i8080-16位模式
涮TFT彩屏的参考程序,数据从 QSPI的FLASH取, 速度12MHz以下

神农鼎 发表于 6 天前


神农鼎 发表于 6 天前

<p>好的,基于手册和论坛信息,我已经掌握了所有关键参数。下面给出完整的参考程序。</p>
<hr />
<h2>参考程序:STC32G144K246 @100MHz,DMA-i8080-16位涮TFT,QSPI Flash取数据,QSPI时钟≤12MHz</h2>
<blockquote>
<p><strong>信息来源</strong>:</p>
<ul>
<li>手册第8章「时钟管理」:HPLL配置、CLKSEL、CLKDIV</li>
<li>手册第26章「四线SPI(QSPI)」:PSCR分频</li>
<li>手册第32章「TFT彩屏接口」:LCMIFPSCR、SETUPT、HOLDT</li>
<li>手册第33章「TFT彩屏与存储器之间的数据读写(LCM_DMA)」</li>
<li>论坛TID=20601:HPLL产生60/80/120MHz例程</li>
<li>论坛TID=24732:QSPI+TFT DMA P2P例程</li>
</ul>
</blockquote>
<hr />
<h3>时钟方案</h3>
<table>
<thead>
<tr>
<th>项目</th>
<th>值</th>
<th>计算</th>
</tr>
</thead>
<tbody>
<tr>
<td>下载IRC</td>
<td>48MHz</td>
<td>ISP下载时选择</td>
</tr>
<tr>
<td>HPLL输入</td>
<td>48MHz IRC</td>
<td>HPLLCKS=1</td>
</tr>
<tr>
<td>HPLL预分频</td>
<td>HPLLPDIV=8</td>
<td>48/8=6MHz基频</td>
</tr>
<tr>
<td>HPLL倍频</td>
<td>HPLLDIV=10(72倍)</td>
<td>6×72=432MHz</td>
</tr>
<tr>
<td>MCK2SEL</td>
<td>HPLL1输出/2</td>
<td>432/2=216MHz</td>
</tr>
<tr>
<td>CLKDIV</td>
<td>2</td>
<td>216/2=<strong>108MHz</strong>(接近100MHz)</td>
</tr>
<tr>
<td><strong>或CLKDIV=3</strong></td>
<td>216/3=<strong>72MHz</strong></td>
<td>如需精确100MHz可调</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意</strong>:STC32G144K246的HPLL输出频率为432MHz时,MCK2SEL选择HPLL1输出/2得216MHz,再经CLKDIV分频。要精确得到100MHz,可调整HPLLDIV或使用外部晶振。以下代码以 <strong>HPLL→216MHz→CLKDIV=2→108MHz</strong> 为例,您可根据需要微调CLKDIV。</p>
</blockquote>
<p><strong>QSPI时钟 ≤12MHz</strong>:108MHz / 9 = 12MHz(PSCR=8),或108MHz / 10 = 10.8MHz(PSCR=9)</p>
<p><strong>LCMIF时钟</strong>:108MHz / 9 = 12MHz(LCMIFPSCR=8),SETUPT=0→Tsetup=1个LCMIF时钟≈83ns,HOLDT=0→Thold=1个LCMIF时钟≈83ns</p>
<hr />
<h3>文件1:config.h</h3>
<pre><code class="language-c">/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/

#ifndef __CONFIG_H__
#define __CONFIG_H__

//==================================================================
// 时钟配置
// 下载时选择IRC频率 48MHz
// HPLL1: 48MHz -&gt; 预分频/8=6MHz -&gt; 倍频72倍=432MHz
// MCK2SEL = HPLL1输出/2 = 216MHz
// CLKDIV = 2 -&gt; SYSCLK = 216/2 = 108MHz
//==================================================================
#define MAIN_Fosc      108000000L    // 系统时钟 108MHz
#define SYS_CLK          108000000UL

//==================================================================
// QSPI时钟配置(要求 ≤12MHz)
// QSPI时钟 = SYSCLK / (PSCR + 1)
// PSCR = 8-&gt; 108/9 = 12MHz
// PSCR = 9-&gt; 108/10 = 10.8MHz
// PSCR = 17 -&gt; 108/18 = 6MHz
//==================================================================
#define QSPI_PSCR      8             // QSPI时钟 = 108/9 = 12MHz

//==================================================================
// LCMIF时钟配置
// LCMIF时钟 = SYSCLK / (LCMIFPSCR + 1)
// LCMIFPSCR = 8 -&gt; 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_BYTES2             // 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 = P2, DAT = P0
// 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 charBYTE;
typedef unsigned int   WORD;
typedef unsigned longDWORD;

// 文件分配表结构
typedef struct {
    charstrSign;
    DWORD dwCount;
    DWORD dwAddress;
} FAT;

#endif
</code></pre>
<hr />
<h3>文件2:sys_clk.h</h3>
<pre><code class="language-c">/*---------------------------------------------------------------------*/
/* --- 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
</code></pre>
<hr />
<h3>文件3:sys_clk.c</h3>
<pre><code class="language-c">/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/

#include &quot;config.h&quot;
#include &quot;sys_clk.h&quot;

/****************************************************************************
* 系统时钟初始化
*
* 目标:系统时钟 = 108MHz
*
* 流程:
*   1. 下载时选择48MHz IRC
*   2. 配置HPLL:输入=48MHz IRC,预分频/8=6MHz,倍频72倍=432MHz
*   3. 选择MCK2SEL=HPLL1输出/2 = 216MHz
*   4. CLKDIV=2 -&gt; 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 -&gt; 倍频72倍 -&gt; 6MHz * 72 = 432MHz
    HPLLCR = 0x9A;      // ENHPLL=1, HSI2SCK=00, HPLLCKS=1(48M IRC),
                        // HPLLDIV=10(72倍)

    // 3. 等待HPLL稳定
    {
      WORD wait;
      for (wait = 0; wait &lt; 1000; wait++);
    }

    // 4. 选择主时钟源为HPLL1输出/2
    //    MCK2SEL = 01 -&gt; HPLL1输出/2 = 432/2 = 216MHz
    CLKSEL = 0x10;      // MCK2SEL=01, MCKSEL=00(内部IRC, 但被MCK2SEL覆盖)

    // 5. 设置系统时钟分频
    //    CLKDIV=2 -&gt; SYSCLK = 216/2 = 108MHz
    CLKDIV = 2;

    // 6. 等待时钟切换稳定
    {
      WORD wait;
      for (wait = 0; wait &lt; 100; wait++);
    }

    // 7. 设置省电模式唤醒等待时钟数(手册建议)
    IRCDB = 0x10;
}
</code></pre>
<hr />
<h3>文件4:qspi.h</h3>
<pre><code class="language-c">/*---------------------------------------------------------------------*/
/* --- 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
</code></pre>
<hr />
<h3>文件5:qspi.c</h3>
<pre><code class="language-c">/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/

#include &quot;config.h&quot;
#include &quot;qspi.h&quot;

/****************************************************************************
* QSPI初始化
*
* 时钟:SYSCLK=108MHz, PSCR=8 -&gt; QSPI时钟=108/9=12MHz
* 模式:模式0 (CKMODE=0)
* 引脚:QSPI_S=10 (P7组)
****************************************************************************/
void QSPI_Init(void)
{
    // 使能访问XFR
    P_SW2 |= 0x80;

    // 1. 配置QSPI功能脚:QSPI_S = 10,选择P7组
    P_SW4 &amp;= ~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 &amp; 0x20);    // BUSY位

    // 清除标志
    QSPI_FCR = 0x02;            // CTCF=1

    // 配置数据长度(实际传输 = DL + 1)
    QSPI_DLR1 = (BYTE)((len - 1) &amp; 0xFF);
    QSPI_DLR2 = (BYTE)((len - 1) &gt;&gt; 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 &gt;&gt; 16);
    QSPI_AR2 = (BYTE)(addr &gt;&gt; 8);
    QSPI_AR1 = (BYTE)(addr);      // 写LSB触发操作

    // 等待传输完成
    while (!(QSPI_SR1 &amp; 0x02));   // TCF标志

    // 从FIFO读取数据
    for (i = 0; i &lt; len; i++)
    {
      pBuf = QSPI_DR;
    }

    // 清除TCF标志
    QSPI_FCR = 0x02;
}
</code></pre>
<hr />
<h3>文件6:lcm.h</h3>
<pre><code class="language-c">/*---------------------------------------------------------------------*/
/* --- 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
</code></pre>
<hr />
<h3>文件7:lcm.c</h3>
<pre><code class="language-c">/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/

#include &quot;config.h&quot;
#include &quot;lcm.h&quot;

/****************************************************************************
* TFT彩屏接口初始化
*
* 模式:i8080,16位数据宽度
* 引脚:LCMIFDPS=00, D16_D8=1, LCMIFCPS=00
*   DAT = P2, DAT = P0
*   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 -&gt; Tsetup=1个LCMIF时钟≈83ns
    //    B1:B0(HOLDT)=00   -&gt; 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 &amp; 0x01);                  // 等待空闲
    LCMIFCR = 0x84;                           // ENLCMIF=1, CMD=100(写命令)
    LCMIFDATL = cmd;                            // 命令在低字节
    LCMIFDATH = 0x00;
}

/****************************************************************************
* 写16位数据
****************************************************************************/
void LCM_WriteData16(WORD dat)
{
    while (LCMIFSTA &amp; 0x01);                  // 等待空闲
    LCMIFCR = 0x85;                           // ENLCMIF=1, CMD=101(写数据)
    LCMIFDATL = (BYTE)(dat &amp; 0xFF);             // 低字节
    LCMIFDATH = (BYTE)(dat &gt;&gt; 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 &gt;&gt; 8);
    DMA_LCM_TXAL = (BYTE)(src_addr);

    // 3. 设置传输总字节数(实际传输 = LCM_AMT + 1)
    DMA_LCM_AMTH = (BYTE)((len - 1) &gt;&gt; 8);
    DMA_LCM_AMT= (BYTE)((len - 1) &amp; 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为&quot;写数据&quot;模式
    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 &amp; 0x01));      // 等待LCMIF标志置1
    DMA_LCM_STA &amp;= ~0x01;               // 清除标志
}
</code></pre>
<hr />
<h3>文件8:tft.h</h3>
<pre><code class="language-c">/*---------------------------------------------------------------------*/
/* --- 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
</code></pre>
<hr />
<h3>文件9:tft.c</h3>
<pre><code class="language-c">/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/

#include &quot;config.h&quot;
#include &quot;lcm.h&quot;
#include &quot;tft.h&quot;
#include &quot;qspi.h&quot;

// XRAM缓冲区(用于中转QSPI读取的数据)
// 注意:大数组必须加xdata关键字,定义在XRAM空间
BYTE xdata TFT_Buffer;

/****************************************************************************
* TFT初始化(以ILI9341为例)
****************************************************************************/
void TFT_Init(void)
{
    WORD delay;

    // 硬件复位
    LCM_RST = 0;
    for (delay = 0; delay &lt; 1000; delay++);    // 约1ms
    LCM_RST = 1;
    for (delay = 0; delay &lt; 10000; delay++);   // 约10ms

    // ILI9341初始化序列
    LCM_WriteCmd(0x01);                         // 软件复位
    for (delay = 0; delay &lt; 50000; delay++);   // 约50ms

    LCM_WriteCmd(0x11);                         // 退出睡眠
    for (delay = 0; delay &lt; 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 &lt; (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 &lt; 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 &gt; 0)
      {
            QSPI_ReadFlash(offset, TFT_Buffer, (WORD)remain);
            LCM_DMA_Start((DWORD)TFT_Buffer, (WORD)remain);
            LCM_DMA_WaitDone();
      }
    }
}
</code></pre>
<hr />
<h3>文件10:main.c</h3>
<pre><code class="language-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 &lt;- P2
*   DAT&lt;- P0
*   RS      &lt;- P4.5
*   RD      &lt;- P4.4
*   WR      &lt;- P4.2
*   RST       &lt;- P5.1
*   CS      &lt;- P9.3
*
*   QSPI Flash (W25Q64/W25Q128):
*   CS   &lt;- P7.5
*   IO0&lt;- P7.0
*   IO1&lt;- P7.2
*   CLK&lt;- P7.1
*   IO2&lt;- P7.3
*   IO3&lt;- P7.4
*
* 下载时选择IRC频率 48MHz。
* 使用STC-ISP 6.94P以上版本的&quot;串行Flash编程器&quot;工具,
* 将图片数据下载到外部QSPI Flash中。
*
* 编译环境:Keil C251,Memory Model = XSmall
*
******************************************/

#include &quot;config.h&quot;
#include &quot;sys_clk.h&quot;
#include &quot;qspi.h&quot;
#include &quot;lcm.h&quot;
#include &quot;tft.h&quot;

// 全局变量
FAT    xdata Fat;                      // 文件分配表
DWORDxdata dwOffset;               // 图片数据偏移地址
int    xdata nIndex;                   // 当前图片索引
int    xdata nCount;                   // 图片总数

/****************************************************************************
* 从Flash读取文件分配表(FAT)
* FAT存储在Flash的0地址处
****************************************************************************/
void Read_FAT(void)
{
    QSPI_ReadFlash(0, (BYTE *)&amp;Fat, sizeof(FAT));

    // 检查签名
    if ((Fat.strSign != 'S') || (Fat.strSign != 'T') ||
      (Fat.strSign != 'C') || (Fat.strSign != '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 &gt; 0)
    {
      while (1)
      {
            dwOffset = Fat.dwAddress;
            TFT_DisplayImage(dwOffset);

            nIndex++;
            if (nIndex &gt;= nCount)
            {
                nIndex = 0;
            }

            // 帧间延时
            {
                unsigned long delay;
                for (delay = 0; delay &lt; 3000000; delay++);
            }
      }
    }
    else
    {
      TFT_Clear(0xF800);   // 无图片则显示红色
    }

    while (1);
}
</code></pre>
<hr />
<h2>关键参数汇总</h2>
<table>
<thead>
<tr>
<th>参数</th>
<th>值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>系统时钟</strong></td>
<td><strong>108MHz</strong></td>
<td>HPLL: 48M→预分频/8→6M→倍频72→432M→/2=216M→CLKDIV=2→108M</td>
</tr>
<tr>
<td><strong>QSPI时钟</strong></td>
<td><strong>12MHz</strong></td>
<td>PSCR=8, 108M/(8+1)=12M,满足≤12MHz要求</td>
</tr>
<tr>
<td><strong>LCMIF时钟</strong></td>
<td><strong>12MHz</strong></td>
<td>LCMIFPSCR=8, 108M/(8+1)=12M</td>
</tr>
<tr>
<td><strong>Tsetup</strong></td>
<td><strong>83ns</strong></td>
<td>SETUPT=0, (0+1)×83ns</td>
</tr>
<tr>
<td><strong>Thold</strong></td>
<td><strong>83ns</strong></td>
<td>HOLDT=0, (1+0)×83ns</td>
</tr>
<tr>
<td><strong>TFT模式</strong></td>
<td>i8080 16位</td>
<td>D16_D8=1, M68_I80=0</td>
</tr>
<tr>
<td><strong>数据引脚</strong></td>
<td>P2, P0</td>
<td>LCMIFDPS=00</td>
</tr>
<tr>
<td><strong>控制引脚</strong></td>
<td>RS=P4.5, RD=P4.4, WR=P4.2</td>
<td>LCMIFCPS=00</td>
</tr>
<tr>
<td><strong>QSPI引脚</strong></td>
<td>P7组</td>
<td>QSPI_S=10</td>
</tr>
<tr>
<td><strong>Flash指令</strong></td>
<td>0x6B四线快速读</td>
<td>8个dummy周期,四线数据</td>
</tr>
</tbody>
</table>
<h2>重要说明</h2>
<ol>
<li><strong>关于P2P DMA的限制</strong>:LCM_DMA的源地址寄存器(DMA_LCM_TXAH/TXAL)是16位,只能访问XRAM空间(0x0000~0xFFFF),无法直接访问QSPI_DR(0x7EF920)。因此采用 <strong>QSPI→XRAM缓冲区→LCM_DMA→TFT</strong> 的两步法。</li>
<li><strong>如需精确100MHz</strong>:可将CLKDIV从2改为3(216/3=72MHz),或调整HPLLDIV倍频数。例如HPLLDIV=8(68倍)→6×68=408MHz→/2=204MHz→CLKDIV=2→102MHz。</li>
<li><strong>QSPI时钟调整</strong>:如果Flash支持更高速度,可减小PSCR值。例如PSCR=5→108/6=18MHz(仍建议≤20MHz)。</li>
<li><strong>编译环境</strong>:使用 <strong>Keil C251</strong>,Memory Model推荐 <strong>XSmall</strong>,大数组加 <code>xdata</code> 关键字。</li>
</ol>
<hr />
<p><a href="https://www.stcaimcu.com/plugin.php?id=twpx_xunsearch&amp;mod=forum&amp;q=%E9%9C%80%E8%A6%81%20STC32G144K246%E5%B7%A5%E4%BD%9C%E5%9C%A8100MHz%E7%9A%84%20DMA-i8080-16%E4%BD%8D%E6%A8%A1%E5%BC%8F%0A%E6%B6%AETFT%E5%BD%A9%E5%B1%8F%E7%9A%84%E5%8F%82%E8%80%83%E7%A8%8B%E5%BA%8F%20%E6%95%B0%E6%8D%AE%E4%BB%8E%20Q&amp;f=_all&amp;m=yes&amp;syn=yes&amp;s=relevance&amp;fid=0">🔍 搜索 &quot;需要 STC32G144K246工作在100MHz的 DMA-i8080-16位模式<br />
涮TFT彩屏的参考程序 数据从 Q&quot;</a></p>
<p><strong>📌 相关论坛讨论</strong></p>
<ol>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=24928&amp;highlight=%E9%9C%80%E8%A6%81%20STC32G144K246%E5%B7%A5%E4%BD%9C%E5%9C%A8100MHz%E7%9A%84%20DMA-i8080-16%E4%BD%8D%E6%A8%A1%E5%BC%8F%0A%E6%B6%AETFT%E5%BD%A9%E5%B1%8F%E7%9A%84%E5%8F%82%E8%80%83%E7%A8%8B%E5%BA%8F%EF%BC%8C%E6%95%B0%E6%8D%AE%E4%BB%8E%20QSPI%E7%9A%84FLASH%E5%8F%96%2C%20%E9%80%9F%E5%BA%A612MHz%E4%BB%A5%E4%B8%8B&amp;page=1&amp;extra=#pid224216">STC32G144K256 LCM i8080 DMA数据发送问题</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=20594&amp;highlight=%E9%9C%80%E8%A6%81%20STC32G144K246%E5%B7%A5%E4%BD%9C%E5%9C%A8100MHz%E7%9A%84%20DMA-i8080-16%E4%BD%8D%E6%A8%A1%E5%BC%8F%0A%E6%B6%AETFT%E5%BD%A9%E5%B1%8F%E7%9A%84%E5%8F%82%E8%80%83%E7%A8%8B%E5%BA%8F%EF%BC%8C%E6%95%B0%E6%8D%AE%E4%BB%8E%20QSPI%E7%9A%84FLASH%E5%8F%96%2C%20%E9%80%9F%E5%BA%A612MHz%E4%BB%A5%E4%B8%8B&amp;page=1&amp;extra=#pid190620">指令在 i-Cache 里面运行, STC32G144K246, 64MHz / 72M / 90M / 100M / 120M</a></li>
<li><a href="https://www.stcaimcu.com/forum.php?mod=viewthread&amp;tid=20893&amp;highlight=%E9%9C%80%E8%A6%81%20STC32G144K246%E5%B7%A5%E4%BD%9C%E5%9C%A8100MHz%E7%9A%84%20DMA-i8080-16%E4%BD%8D%E6%A8%A1%E5%BC%8F%0A%E6%B6%AETFT%E5%BD%A9%E5%B1%8F%E7%9A%84%E5%8F%82%E8%80%83%E7%A8%8B%E5%BA%8F%EF%BC%8C%E6%95%B0%E6%8D%AE%E4%BB%8E%20QSPI%E7%9A%84FLASH%E5%8F%96%2C%20%E9%80%9F%E5%BA%A612MHz%E4%BB%A5%E4%B8%8B&amp;page=1&amp;extra=#pid193556">LVGL@STC32G144K246,Hello World ! 51的 LVGL,GUI</a></li>
</ol>
<p>⏱️ 查找用时 23.3s | 总回答用时 75.9s</p>

ercircle 发表于 6 天前

AiCube生成的直接修改要发送的内容到buffer,调用这个触发函数即可




页: [1]
查看完整版本: STC32G144K256 LCM i8080 DMA数据发送问题