芯启元 发表于 2026-7-3 15:32:25

61-DMA-LCM液晶屏接口测试,STC32G144K246实验箱演示程序

61-DMA-LCM液晶屏接口测试,STC32G144K246实验箱演示程序
LCD1602显示程序-硬件M6800并行接口+DMA刷新,主程序main.C代码如下(汇编版本在附件中):


/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/

/*************    功能说明    **************

本例程基于STC32G144K246为主控芯片的实验箱进行编写测试。

LCM1602字符液晶模块,8位数据,全部由LCM DMA驱动,节省CPU时间(LCD1602用IO方式访问速度很慢!)。

使用3.3V的LCD1602模块。

请先别修改程序, 直接下载"LCD1602.hex"测试.

下载时, 选择时钟 48MHz (用户可自行修改频率).

*******************************************/

#include "..\..\comm\STC32G144K246.h"

typedef   unsigned char   u8;
typedef   unsigned int    u16;
typedef   unsigned long   u32;

/****************用户定义宏****************/

#define   MAIN_Fosc      48000000UL    //定义主时钟

/****************IO口定义******************/

sfr   LCD_DATA = 0xa0;    //P0--0x80,P1--0x90,P2--0xa0,P3--0xb0
sbit    LCD_B7= P2^7;   //Pin 14   B3--Pin 10   LED+ -- Pin 15
                            //Pin 13   B2--Pin 9      LED- -- Pin 16
                            //Pin 12   B1--Pin 8      Vo   -- Pin 3
                            //Pin 11   B0--Pin 7      VDD-- Pin 2      VSS -- Pin 1

sbit    LCD_ENA = P4^4;   //Pin 6
sbit    LCD_RW= P4^2;   //Pin 5//LCD_RS      R/W   DB7--DB0      FOUNCTION
sbit    LCD_RS= P4^5;   //Pin 4//    0      0   INPUT      write the command to LCD model
                                     //    0      1   OUTPUT   read BF and AC pointer from LCD model
                                     //    1      0   INPUT      write the data to LCDmodel
                                     //    1      1   OUTPUT   read the data from LCD model

/****************本地常量声明**************/

const u8text1[] = {" LCD1602 8-DATA "};
const u8text2[] = {" STC32G144K246"};
const u8text3[] = {"2025-12-25 18:59"};
const u8text4[] = {" www.STCAI.com"};

/****************本地变量声明**************/

u16 LCM_TxCnt;          //LCM DMA触发发送次数, 一次16字节, 一共64次
bit B_LCM_DMA_busy;   //LCM DMA忙标志, 1标志LCM-DMA忙,LCM DMA中断中清除此标志,使用LCM DMA前要确认此标志为0
bit B_TxCmd;            //已发送命令标志
u8xdata CmdTmp;    //命令缓冲
u8xdata DisTmp1;//第一行显示缓冲,将要显示的内容放在显存里,启动DMA即可.
u8xdata DisTmp2;//第二行显示缓冲,将要显示的内容放在显存里,启动DMA即可.

/****************本地函数声明**************/


/******************************************************************************
               HD44780U    LCD_MODUL DRIVE PROGRAMME
*******************************************************************************

total 2 lines, 16x2= 32
first line address:0~15
second line address: 64~79

total 2 lines, 20x2= 40
first line address:0~19
second line address: 64~83

total 2 lines, 40x2= 80
first line address:0~39
second line address: 64~103
*/

#define C_CLEAR            0x01      //clear LCD
#define C_HOME             0x02      //cursor go home
#define C_CUR_L            0x04      //cursor shift left after input
#define C_RIGHT            0x05      //picture shift right after input
#define C_CUR_R            0x06      //cursor shift right after input
#define C_LEFT             0x07      //picture shift left after input
#define C_OFF            0x08      //turn off LCD
#define C_ON               0x0C      //turn onLCD
#define C_FLASH            0x0D      //turn onLCD, flash
#define C_CURSOR         0x0E      //turn onLCD and cursor
#define C_FLASH_ALL      0x0F      //turn onLCD and cursor, flash
#define C_CURSOR_LEFT      0x10      //single cursor shift left
#define C_CURSOR_RIGHT   0x10      //single cursor shift right
#define C_PICTURE_LEFT   0x10      //single picture shift left
#define C_PICTURE_RIGHT    0x10      //single picture shift right
#define C_BIT8             0x30      //set the data is 8 bits
#define C_BIT4             0x20      //set the data is 8 bits
#define C_L1DOT7         0x30      //8 bits,one line 5*7dots
#define C_L1DOT10          0x34      //8 bits,one line 5*10 dots
#define C_8bitL2DOT7       0x38      //8 bits,tow lines 5*7 dots
#define C_4bitL2DOT7       0x28      //4 bits,tow lines 5*7 dots
#define C_CGADDRESS0       0x40      //CGRAM address0 (addr=40H+x)
#define C_DDADDRESS0       0x80      //DDRAM address0 (addr=80H+x)

//========================================================================
// 函数: void delay_ms(unsigned int ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-11-01
// 备注: 由于芯片使能了Cache功能,软件延时时间可能不太准确
//========================================================================
void delay_ms(u16 ms)
{
    u16 i;
    do{
      i = MAIN_Fosc / 6000;
      while(--i);
    }while(--ms);
}

//******************** LCD40 Module END ***************************

//========================================================================
// 函数: void LCM_Config(void)
// 描述: LCM配置函数。ST7920 @540KHz,清屏指令典型时间为1.6ms,读忙信号0us, 写入、读出数据、命令72us. 本例子DMA刷新时间84ms @24MHz.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2024-8-17
// 备注:
//========================================================================
//LCMIFCFG(7EFE50H)    TFT彩屏接口配置寄存器
#define    LCMIFIE         (0<<7)   //TFT彩屏接口中断使能控制位(bit7), 1->允许中断,0->禁止
#define    LCMIFIP         (0<<4)   //TFT彩屏接口中断优先级控制位(bit5~bit4), (最低)0~3(最高).
#define    LCMIFDPS      (0<<2)   //TFT彩屏接口数据脚选择位(bit3~bit2), D18_D8=0: 0或2->8位数据在P2, D18_D8=1: 0: P2-高字节, P0低字节, 2: P2-高字节,低字节P0 P4.7 P4.6 P4.3 P4.1.
#define    LCM_D16_D8      (0<<1)   //TFT彩屏接口数据宽度控制位(bit1), 0: 8位数据,1:16位数据
#define    LCM_M68_I80   1      //TFT彩屏接口数据模式选择位(bit0), 0: I8080模式,1:M6800模式

//LCMIFCFG2(7EFE51H)    TFT彩屏接口配置寄存器2
#define    LCMIFCPS      (0<<5)   //TFT彩屏接口控制脚选择位(bit6~bit5), RS RD(E) WR(RW), 0->P4.5 P4.4 P4.2,1->P4.5 P3.7 P3.6, 2->P4.0 P4.4 P4.2, 3->P4.0 P3.7 P3.6
#define    LCMSETUPT       (4<<2)   //TFT彩屏接口数据建立时间控制位(bit4~bit2), 0~7, 对应1~8个LCMIF时钟, 对于6800模式就是E高电平时间, 至少20us.
#define    LCMHOLDT      3      //TFT彩屏接口数据保持时间控制位(bit1~bit0), 0~3, 对应1~4个LCMIF时钟

//LCMIFCR(7EFE52H)    TFT彩屏接口控制寄存器
#define    ENLCMIF         (1<<7)   //TFT彩屏接口使能控制位(bit7), 1->允许TFT彩屏接口功能, 0->禁止
#define    LCM_WRCMD       4      //TFT彩屏接口触发命令(bit2~bit0), 4->写命令
#define    LCM_WRDAT       5      //TFT彩屏接口触发命令(bit2~bit0), 5->写数据
#define    LCM_RDCMD       6      //TFT彩屏接口触发命令(bit2~bit0), 6->读命令/状态
#define    LCM_RDDAT       7      //TFT彩屏接口触发命令(bit2~bit0), 7->读数据

void LCM_Config(void)
{
    LCMIFCFG= LCMIFIE| LCMIFIP   | LCMIFDPS | LCM_D16_D8 | LCM_M68_I80;
    LCMIFCFG2 = LCMIFCPS | LCMSETUPT | LCMHOLDT;    //RS:P45,E:P44,RW:P42; Setup Time,HOLD Time
    LCMIFSTA= 0x00;    //TFT彩屏接口中断请求标志,软件清0
    LCMIFPSCR = (u8)(MAIN_Fosc/250000UL-1);    //TFT彩屏接口时钟预分频0~255, LCMIF时钟频率 = SYSclk/(LCMIFPSCR+1), LCD1602速度慢,最快250KHz
    LCMIFCR   = ENLCMIF;
}

void CheckBusy(void)    //检测忙标志
{
    LCMIFSTA = 0;       //清除完成标志
    do
    {
      LCMIFCR   = ENLCMIF | LCM_RDCMD;    //读状态
      while((LCMIFSTA & 1) == 0);         //等待读完成
      LCMIFSTA = 0;   //清除完成标志
      LCD_ENA = 0;
    }while(LCMIFDATL & 0x80);   //bit7==0则空闲
}

/**********************************************/
void IniSendCMD(u8 cmd) //write the command to LCD
{
    LCMIFDATL = cmd;
    LCMIFCR   = ENLCMIF | LCM_WRCMD;    //写命令
    while((LCMIFSTA & 1) == 0);         //等待写完成
    LCMIFSTA = 0;   //清除完成标志
}

/**********************************************/
void Write_CMD(u8 cmd)//write the command to LCD
{
    CheckBusy();    //检测忙
    LCMIFDATL = cmd;
    LCMIFCR   = ENLCMIF | LCM_WRCMD;    //写命令
    while((LCMIFSTA & 1) == 0);         //等待写完成
    LCMIFSTA = 0;   //清除完成标志
}

/********* 初始化函数 **************************/
void Initialize_LCD(void)   //intilize LCD, input none, output none
{
    LCD_ENA = 0;
    LCD_RS= 0;
    LCD_RW= 0;

    delay_ms(100);
    IniSendCMD(C_BIT8);         //set the data is 4 bits

    delay_ms(10);
    IniSendCMD(C_BIT8);         //set the data is 4 bits

    delay_ms(10);
    IniSendCMD(C_8bitL2DOT7);   //tow lines 5*7 dots

    delay_ms(6);
    Write_CMD(C_CLEAR);         //clear LCD RAM
    Write_CMD(C_CUR_R);         //Curror Shift Right
    Write_CMD(C_ON);            //turn onLCD
}

//========================================================================
// 函数: void DMA_Config(u8 xdata *pic)
// 描述: LCM DMA配置函数。
// 参数: *pic: 数据首地址.
// 返回: none.
// 版本: V1.0, 2024-8-17
// 备注:
//========================================================================
//DMA_LCM_CR (7EfA71H)   LCM_DMA控制寄存器
#define      DMA_ENLCM   (1<<7)    // LCM DMA功能使能控制位,    bit7, 0:禁止LCM DMA功能,1:允许LCM DMA功能。
#define      LCM_TRIGWC    (1<<6)    // LCM DMA触发写命令,bit6, 0:写0无效,          1:写1开始LCM DMA开始写命令。
#define      LCM_TRIGWD    (1<<5)    // LCM DMA触发写数据,bit5, 0:写0无效,          1:写1开始LCM DMA开始写数据。
#define      LCM_TRIGRC    (1<<4)    // LCM DMA触发读命令,bit4, 0:写0无效,          1:写1开始LCM DMA开始读命令。
#define      LCM_TRIGRD    (1<<3)    // LCM DMA触发读数据,bit3, 0:写0无效,          1:写1开始LCM DMA开始读数据。
#define      LCM_CLRFIFO   0         // 清除LCM DMA接收FIFO控制位,bit0, 0:写0无效,1:写1复位FIFO指针。

//DMA_LCM_CFG(7EfA70H)   LCM_DMA配置寄存器
#define      DMA_LCMIE   (1<<7)    // LCM DMA中断使能控制位,bit7, 0:禁止SPI DMA中断,   1:允许中断。
#define      DMA_LCMIP   (0<<2)    // LCM DMA中断优先级控制位,bit3~bit2, (最低)0~3(最高).
#define      DMA_LCMPTY    0         // LCM DMA数据总线访问优先级控制位,bit1~bit0, (最低)0~3(最高).

//DMA_LCM_STA(7EfA72)   LCM_DMA状态寄存器
#define      LCM_TXOVW   (1<<1)    // LCM DMA数据覆盖标志位,bit1, 软件清0.
#define      DMA_LCMIF   1         // LCM DMA中断请求标志位,bit0, 软件清0.

void LCM_DMA_Trig(void)
{
    DMA_LCM_STA= 0x00;
    DMA_LCM_CFG= DMA_LCMIE | DMA_LCMIP | DMA_LCMPTY;;
    DMA_LCM_ITVH = 0;         //设置传输间隔时间(高8位),对应N+1个LCMIF时钟(1~65536个LCMIF时钟)
    DMA_LCM_ITVL = 9-1;         //设置传输间隔时间(低8位)

    B_TxCmd   = 0;            //已发送命令标志
    LCM_TxCnt = 0;            //LCM DMA触发发送次数, 一次16字节, 一共64次
    B_LCM_DMA_busy = 1;         //标志LCM-DMA忙,LCM DMA中断中清除此标志,使用LCM DMA前要确认此标志为0
    DMA_LCM_STA = DMA_LCMIF;    //软件触发LCM DMA中断,启动发送
}

//========================================================================
// 函数: void LCMIF_DMA_ISR(void) interrupt DMA_LCM_VECTOR
// 描述: LCM DMA中断函数。中断处理时间:2.3us @24MHz. 刷新时间:84ms @24MHz.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2024-8-17
// 备注:
//========================================================================
void LCMIF_DMA_ISR(void) interrupt DMA_LCM_VECTOR
{
    if(DMA_LCM_STA & DMA_LCMIF)
    {
      if(LCM_TxCnt >= 2)      //判断发送是否完毕
      {
            DMA_LCM_CR = 0;
            B_LCM_DMA_busy = 0; //清除LCM-DMA忙标志,LCM DMA中断中清除此标志,使用LCM DMA前要确认此标志为0
      }
      else      //仍有数据要发送
      {
            if(!B_TxCmd)    //还没有发设置地址命令,则先发设置地址命令
            {
                B_TxCmd = 1;            //指示已发地址命令
                if(LCM_TxCnt == 0)      //第一行地址设置命令
                {
                  LCD_RS = 0;         //写命令
                  LCD_RW = 0;
                  CmdTmp = 0x80;   //第一行AC地址
                }
                else if(LCM_TxCnt == 1) //第二行地址设置命令
                {
                  LCD_RS = 0;         //写命令
                  LCD_RW = 0;
                  CmdTmp = 0x80 | 64;//第二行AC地址
                }
                DMA_LCM_TXAH = (u8)((u16)CmdTmp >> 8);//LCM DMA发送命令首地址
                DMA_LCM_TXAL = (u8)CmdTmp;
                DMA_LCM_AMTH = 0;       //设置传输总字节数(高8位), 设置传输总字节数 = N+1
                DMA_LCM_AMT= 1-1;   //设置传输总字节数(低8位).
                DMA_LCM_CR   = DMA_ENLCM | LCM_TRIGWC;    //启动LCM DMA发送命令
            }
            else    //写数据
            {
                LCD_RS = 1;   //写显示数据
                LCD_RW = 0;
                B_TxCmd = 0;    //清除已发地址命令
                if(LCM_TxCnt == 0)   //第一行显示缓冲地址
                {
                  DMA_LCM_TXAH = (u8)((u16)DisTmp1 >> 8);    //LCM DMA发送数据首地址
                  DMA_LCM_TXAL = (u8)DisTmp1;
                }
                else //if(LCM_TxCnt == 1)   //第二行显示缓冲地址
                {
                  DMA_LCM_TXAH = (u8)((u16)DisTmp2 >> 8);    //LCM DMA发送数据首地址
                  DMA_LCM_TXAL = (u8)DisTmp2;
                }
                DMA_LCM_AMTH = 0;       //设置传输总字节数(高8位), 设置传输总字节数 = N+1
                DMA_LCM_AMT= 16-1;    //设置传输总字节数(低8位).
                DMA_LCM_CR   = DMA_ENLCM | LCM_TRIGWD;//启动LCM DMA发送数据
                LCM_TxCnt++;      //发送行数+1
            }
      }
    }
    DMA_LCM_STA = 0;
}

/*************** 主函数 *******************************/

void main(void)
{
    u16 i;

    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 1; //避免 DMA 与 CPU 同时访问 xdata 时产生总线冲突

    P4M0 |= 0x34;       //RS,WR,RD设置成推挽输出
    P4M1 &= ~0x34;

    P5M0 |= 0x02;       //RST设置成推挽输出
    P5M1 &= ~0x02;

    P2BP &= ~0xff;      //使能LCM的D8~D15数据脚硬件自动设置端口模式

    delay_ms(100);    //等待一下,等LCD讲入工作状态
    LCM_Config();
    Initialize_LCD();    //初始化LCD
    EA = 1;

    while (1)
    {
      if(!B_LCM_DMA_busy)      //DMA空闲
      {
            for(i=0; i<16; i++)    //将要显示的文本装载到显存
            {
                DisTmp1= text1;//加载第一行显存
                DisTmp2 = text2;    //加载第二行显存
            }
            LCM_DMA_Trig();      //启动DMA,显示文本
      }
      delay_ms(2000);

      if(!B_LCM_DMA_busy)      //DMA空闲
      {
            for(i=0; i<16; i++)    //将要显示的文本装载到显存
            {
                DisTmp1 = text3;    //加载第一行显存
                DisTmp2 = text4;    //加载第二行显存
            }
            LCM_DMA_Trig();      //启动DMA,显示文本
      }
      delay_ms(2000);
    }
}




芯启元 发表于 2026-7-3 15:34:52

61-DMA-LCM液晶屏接口测试,STC32G144K246实验箱演示程序
TFT320240显示程序-硬件I8080并行接口+DMA刷新,主程序main.C代码如下(汇编版本在附件中):


/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/

/*************功能说明    **************

本例程基于STC32G144K246为主控芯片的实验箱进行编写测试。

使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。

edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。

LCM接口+DMA驱动液晶屏程序

16bit I8080模式, P0口接D0~D7, P2口接D8~D15

sbit    LCD_RS      =   P4^5;
sbit    LCD_WR      =   P4^2;
sbit    LCD_RD      =   P4^4;
sbit    LCD_RESET   =   P5^1;

DMA设置长度2048字节,通过中断方式判断传输完成

下载时, 选择IRC频率 48MHz,主时钟使用 PLL 90MHz 时钟频率.

******************************************/

#include "..\..\comm\STC32G144K246.h"
#include "font.h"

typedef   unsigned char   u8;
typedef   unsigned int    u16;
typedef   unsigned long   u32;

/****************用户定义宏****************/

#define SEL_HPLL1       1
#define SEL_HPLL2       2
#define HPLL_SEL      SEL_HPLL2

#if(HPLL_SEL==SEL_HPLL1)
    #define MAIN_Fosc   120000000L //定义主时钟(超频)
#else
    #define MAIN_Fosc   90000000L//定义主时钟
#endif

/****************IO口定义******************/

sbit    LCD_RS      =   P4^5;
sbit    LCD_WR      =   P4^2;
sbit    LCD_RD      =   P4^4;
sbit    LCD_RESET   =   P5^1;

#define LCD_CS_H()P9SETB= 0x08//P93 = 1
#define LCD_CS_L()P9CLRB= 0x08//P93 = 0

//IO连接
//#defineLCD_DataPortH P2   //高8位数据口,8位模式下只使用高8位
//#defineLCD_DataPortL P0   //低8位数据口,8位模式下低8位可以不接线

/****************本地常量声明**************/

//支持横竖屏快速定义切换
#define USE_HORIZONTAL      3   //定义液晶屏顺时针旋转方向         0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转

//画笔颜色
#define WHITE                  0xFFFF
#define BLACK                  0x0000         
#define BLUE             0x001F
#define BRED             0XF81F
#define GRED             0XFFE0
#define GBLUE            0X07FF
#define RED                  0xF800
#define MAGENTA                0xF81F
#define GREEN                  0x07E0
#define CYAN                   0x7FFF
#define YELLOW               0xFFE0
#define BROWN            0XBC40 //棕色
#define BRRED            0XFC07 //棕红色
#define GRAY             0X8430 //灰色

#define DARKBLUE               0X01CF      //深蓝色
#define LIGHTBLUE               0X7D7C      //浅蓝色
#define GRAYBLUE                0X5458 //灰蓝色
#define LIGHTGREEN            0X841F //浅绿色
#define LGRAY            0XC618 //浅灰色(PANNEL),窗体背景色
#define LGRAYBLUE      0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE         0X2B12 //浅棕蓝色(选择条目的反色)

//定义LCD的尺寸
#define LCD_W 240
#define LCD_H 320

#define DMA_AMT_LEN2047//n+1

/****************本地变量声明**************/

u16 POINT_COLOR=0x0000;      //画笔颜色

u16 index;
u16 xdata Color;
bit DmaFlag;
bit LcmFlag;

typedef struct
{                                                                                    
      u16 width;                        //LCD 宽度
      u16 height;                        //LCD 高度
      u16 id;                              //LCD ID
      u8dir;                        //横屏还是竖屏控制:0,竖屏;1,横屏。      
      u8 wramcmd;                  //开始写gram指令
      u8 rramcmd;         //开始读gram指令
      u8 setxcmd;                  //设置x坐标指令
      u8 setycmd;                  //设置y坐标指令         
}_lcd_dev;         

_lcd_dev lcddev;

/****************本地函数声明**************/

void HPLL_config(void);
void delay_ms(u16 ms);
void GPIO_Init(void);
void LCM_Config(void);
void DMA_Config(void);
void LCD_Init(void);
void Test_Color(void);
void LCD_WR_DATA_16Bit(u16 Data);
void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd);
void Show_Str(u16 x, u16 y, u16 fc, u16 bc, u8 *str,u8 size,u8 mode);
void LCD_direction(u8 direction);

/**********************************************/
void main(void)
{
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 1; //避免 DMA 与 CPU 同时访问 xdata 时产生总线冲突

    HPLL_config();
      GPIO_Init();
      LCM_Config();
      DMA_Config();
      EA = 1;
      
      LCD_Init(); //LCM初始化

      while(1)
      {
                Test_Color();
      }
}

//========================================================================
// 函数: void HPLL_config(void)
// 描述: PLL时钟配置函数。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-11-01
// 备注:
//========================================================================
void HPLL_config(void)
{
#if(HPLL_SEL==SEL_HPLL2)
    WTST = 2;         //通过WTST增加等待时钟控制Flash读取速度在33MHz以内, 主时钟:360MHz/2/2=90MHz, 读Flash速度:90MHz/(1+2)=30MHz

    HPLLCR |= 0x80;   //使能HPLL(高速外设默认使用HPLL时钟,切换到HPLL2前要先使能HPLL,切换完再关闭HPLL)

                        //首先需要将HIRC主频调节到48MHz
    HPLL2CR &= ~0x10;   //选择HPLL2输入时钟源为HIRC
//    HPLL2CR |= 0x10;    //选择HPLL2输入时钟源为IRCM
    HPLL2PDIV = 8;      //设置HPLL2输入时钟预分频为8(HPLL输入频率必须为6MHz)
//    HPLL2CR |= 0x00;    //HPLL2=6MHz*52=312MHz
//    HPLL2CR |= 0x01;    //HPLL2=6MHz*54=324MHz
//    HPLL2CR |= 0x02;    //HPLL2=6MHz*56=336MHz
//    HPLL2CR |= 0x03;    //HPLL2=6MHz*58=348MHz
    HPLL2CR |= 0x04;    //HPLL2=6MHz*60=360MHz
//    HPLL2CR |= 0x05;    //HPLL2=6MHz*62=372MHz
//    HPLL2CR |= 0x06;    //HPLL2=6MHz*64=384MHz
//    HPLL2CR |= 0x07;    //HPLL2=6MHz*66=396MHz
//    HPLL2CR |= 0x08;    //HPLL2=6MHz*68=408MHz
//    HPLL2CR |= 0x09;    //HPLL2=6MHz*70=420MHz
//    HPLL2CR |= 0x0a;    //HPLL2=6MHz*72=432MHz
//    HPLL2CR |= 0x0b;    //HPLL2=6MHz*74=444MHz
//    HPLL2CR |= 0x0c;    //HPLL2=6MHz*76=456MHz
//    HPLL2CR |= 0x0d;    //HPLL2=6MHz*78=468MHz
//    HPLL2CR |= 0x0e;    //HPLL2=6MHz*80=480MHz
//    HPLL2CR |= 0x0f;    //HPLL2=6MHz*82=492MHz
    HPLL2CR |= 0x20;    //高速外设时钟选择 HPLL2/2
    HPLL2CR |= 0x80;    //使能HPLL2

    HPLLCR &= ~0x80;    //关闭HPLL(高速外设默认使用HPLL时钟,切换到HPLL2前要先使能HPLL,切换完再关闭HPLL)
   
    CLKDIV = 2;         //系统时钟 = 主时钟源/2 = HPLL2/2/2 = 90MHz
    CLKSEL = 0x08;      //选择HPLL2/2作为主时钟源

#else
    WTST = 3;         //通过WTST增加等待时钟控制Flash读取速度在33MHz以内, 主时钟:480MHz/2/2=120MHz, 读Flash速度:120MHz/(1+3)=30MHz
                        //首先需要将HIRC主频调节到48MHz
    HPLLCR &= ~0x10;    //选择HPLL输入时钟源为HIRC
//    HPLLCR |= 0x10;   //选择HPLL输入时钟源为IRCM
    HPLLPDIV = 8;       //设置HPLL输入时钟预分频为8(HPLL输入频率必须为6MHz)
//    HPLLCR |= 0x00;   //HPLL=6MHz*52=312MHz
//    HPLLCR |= 0x01;   //HPLL=6MHz*54=324MHz
//    HPLLCR |= 0x02;   //HPLL=6MHz*56=336MHz
//    HPLLCR |= 0x03;   //HPLL=6MHz*58=348MHz
//    HPLLCR |= 0x04;   //HPLL=6MHz*60=360MHz
//    HPLLCR |= 0x05;   //HPLL=6MHz*62=372MHz
//    HPLLCR |= 0x06;   //HPLL=6MHz*64=384MHz
//    HPLLCR |= 0x07;   //HPLL=6MHz*66=396MHz
//    HPLLCR |= 0x08;   //HPLL=6MHz*68=408MHz
//    HPLLCR |= 0x09;   //HPLL=6MHz*70=420MHz
//    HPLLCR |= 0x0a;   //HPLL=6MHz*72=432MHz
//    HPLLCR |= 0x0b;   //HPLL=6MHz*74=444MHz
//    HPLLCR |= 0x0c;   //HPLL=6MHz*76=456MHz
//    HPLLCR |= 0x0d;   //HPLL=6MHz*78=468MHz
    HPLLCR |= 0x0e;   //HPLL=6MHz*80=480MHz
//    HPLLCR |= 0x0f;   //HPLL=6MHz*82=492MHz
    HPLL2CR &= ~0x60;   //高速外设时钟选择 HPLL/2
    HPLLCR |= 0x80;   //使能HPLL

    CLKDIV = 2;         //系统时钟=主时钟源/2 = HPLL/2/2 = 120MHz
    CLKSEL = 0x04;      //选择HPLL/2作为主时钟源
#endif

//    P5M0 |= 0x10; P5M1 &= ~0x10;//设置P5.4口为推挽输出
//    P5SR &= ~0x10;            //设置P5.4口为快速模式
//    HIRCCR |= 0x10;             //输出系统时钟
//    MCLKOCR = 10;               //系统时钟 10 分频到P5.4
}

void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
{         
      u16 i,j;                        
      u16 width=ex-sx+1;               //得到填充的宽度
      u16 height=ey-sy+1;                //高度
      LCD_SetWindows(sx,sy,ex,ey);//设置显示窗口

      for(j=0,i=0;i<=DMA_AMT_LEN;i++)
      {
                Color= color;
      }
      index = 75;   //(320 * 240 * 2) / 2048 = 75
      LCD_CS_L();
    DmaFlag = 1;
      DMA_LCM_CR = 0xa0;      //Write dat
      while(DmaFlag);
}

void Test_Color(void)
{
      u8 buf = {0};

      LCD_Fill(0,0,lcddev.width,lcddev.height,WHITE);
      Show_Str(20,30,BLUE,YELLOW,"LCM Test",16,1);delay_ms(800);
      LCD_Fill(0,0,lcddev.width,lcddev.height,RED);
      Show_Str(20,30,BLUE,YELLOW,"RED ",16,1);delay_ms(800);
      LCD_Fill(0,0,lcddev.width,lcddev.height,GREEN);
      Show_Str(20,30,BLUE,YELLOW,"GREEN ",16,1);delay_ms(800);
      LCD_Fill(0,0,lcddev.width,lcddev.height,BLUE);
      Show_Str(20,30,RED,YELLOW,"BLUE ",16,1);delay_ms(800);
}

//========================================================================
// 函数: void delay_ms(unsigned int ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-11-01
// 备注: 由于芯片使能了Cache功能,软件延时时间可能不太准确
//========================================================================
void delay_ms(u16 ms)
{
    u16 i;
    do{
      i = MAIN_Fosc / 6000;
      while(--i);
    }while(--ms);
}

/*****************************************************************************
* @name       :void LCD_WR_REG(u16 Reg)   
* @date       :2018-08-09
* @function   :Write an 16-bit command to the LCD screen
* @parameters :data:Command value to be written
* @retvalue   :None
******************************************************************************/
void LCD_WR_REG(u16 Reg)   
{
    LCMIFDATH = Reg>>8;
      LCMIFDATL = Reg;
      LCD_CS_L();
      LCMIFCR = 0x84;                //Enable interface, write command out
    while(!LCMIFSTA);
    LCMIFSTA = 0x00;
    LCD_CS_H();
}

/*****************************************************************************
* @name       :void LCD_WR_DATA(u16 Data)
* @date       :2018-08-09
* @function   :Write an 16-bit data to the LCD screen
* @parameters :data:data value to be written
* @retvalue   :None
******************************************************************************/
void LCD_WR_DATA(u16 Data)
{
    LCMIFDATH = Data>>8;
      LCMIFDATL = Data;
      LCD_CS_L();
      LCMIFCR = 0x85;                //Enable interface, write data out
    while(!LCMIFSTA);
    LCMIFSTA = 0x00;
    LCD_CS_H();
}

/*****************************************************************************
* @name       :void LCD_WR_DATA_16Bit(u16 Data)
* @date       :2018-08-09
* @function   :Write an 16-bit command to the LCD screen
* @parameters :Data:Data to be written
* @retvalue   :None
******************************************************************************/   
void LCD_WR_DATA_16Bit(u16 Data)
{
      LCD_WR_DATA(Data);
}

/*****************************************************************************
* @name       :void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue)
* @date       :2018-08-09
* @function   :Write data into registers
* @parameters :LCD_Reg:Register address
                LCD_RegValue:Data to be written
* @retvalue   :None
******************************************************************************/
void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue)
{
    LCD_WR_REG(LCD_Reg);
    LCD_WR_DATA(LCD_RegValue);
}

/*****************************************************************************
* @name       :void LCD_WriteRAM_Prepare(void)
* @date       :2018-08-09
* @function   :Write GRAM
* @parameters :None
* @retvalue   :None
******************************************************************************/      
void LCD_WriteRAM_Prepare(void)
{
         LCD_WR_REG(lcddev.wramcmd);         
}

/*****************************************************************************
* @name       :void LCD_DrawPoint(u16 x,u16 y)
* @date       :2018-08-09
* @function   :Write a pixel data at a specified location
* @parameters :x:the x coordinate of the pixel
                y:the y coordinate of the pixel
* @retvalue   :None
******************************************************************************/      
void LCD_DrawPoint(u16 x,u16 y)
{
      LCD_SetWindows(x,y,x,y);//设置光标位置
      LCD_WR_DATA_16Bit(POINT_COLOR);            
}         

/*****************************************************************************
* @name       :void GPIO_Init(void)
* @date       :2018-11-13
* @function   :Set the gpio to push-pull mode
* @parameters :None
* @retvalue   :None
******************************************************************************/      
void GPIO_Init(void)
{
    P4M0 |= 0x34;       //RS,WR,RD设置成推挽输出
    P4M1 &= ~0x34;

    P5M0 |= 0x02;       //RST设置成推挽输出
    P5M1 &= ~0x02;

    P9M0 |= 0x08;       //CS设置成推挽输出
    P9M1 &= ~0x08;

    P0BP &= ~0xff;      //使能LCM的D0~D7 数据脚硬件自动设置端口模式
    P2BP &= ~0xff;      //使能LCM的D8~D15数据脚硬件自动设置端口模式
}

/*****************************************************************************
* @name       :void LCM_Config(void)
* @date       :2025-12-25
* @function   :Config LCM
* @parameters :None
* @retvalue   :None
******************************************************************************/      
void LCM_Config(void)
{
    LCMIFCFG = 0x02;    //16位模式
    LCMIFCFG2 = 0x0d;
    LCMIFSTA = 0x00;
}

/*****************************************************************************
* @name       :void DMA_Config(void)
* @date       :2025-12-09
* @function   :Config DMA
* @parameters :None
* @retvalue   :None
******************************************************************************/      
void DMA_Config(void)
{
      DMA_LCM_AMT = (u8)DMA_AMT_LEN;                              //设置传输总字节数(低8位):n+1
      DMA_LCM_AMTH = (u8)(DMA_AMT_LEN>>8);      //设置传输总字节数(高8位):n+1
      DMA_LCM_TXAH = (u8)((u16)&Color >> 8);
      DMA_LCM_TXAL = (u8)((u16)&Color);
//      DMA_LCM_RXAH = (u8)((u16)&Buffer >> 8);
//      DMA_LCM_RXAL = (u8)((u16)&Buffer);
      DMA_LCM_STA = 0x00;
      DMA_LCM_CFG = 0x82;
      DMA_LCM_CR = 0x00;
}

/*****************************************************************************
* @name       :void LCDReset(void)
* @date       :2018-08-09
* @function   :Reset LCD screen
* @parameters :None
* @retvalue   :None
******************************************************************************/      
void LCDReset(void)
{
      LCD_CS_H();
      delay_ms(50);      
      LCD_RESET=0;
      delay_ms(150);
      LCD_RESET=1;
      delay_ms(50);
}

/*****************************************************************************
* @name       :void LCD_Init(void)
* @date       :2018-08-09
* @function   :Initialization LCD screen
* @parameters :None
* @retvalue   :None
******************************************************************************/                  
void LCD_Init(void)
{
      LCDReset(); //初始化之前复位
//      delay_ms(150);                     //根据不同晶振速度可以调整延时,保障稳定显示

//*************3.2inch ILI9341初始化**********//
    LCD_WR_REG(0xCF);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0xD9); //0xC1
    LCD_WR_DATA(0X30);
    LCD_WR_REG(0xED);
    LCD_WR_DATA(0x64);
    LCD_WR_DATA(0x03);
    LCD_WR_DATA(0X12);
    LCD_WR_DATA(0X81);
    LCD_WR_REG(0xE8);
    LCD_WR_DATA(0x85);
    LCD_WR_DATA(0x10);
    LCD_WR_DATA(0x7A);
    LCD_WR_REG(0xCB);
    LCD_WR_DATA(0x39);
    LCD_WR_DATA(0x2C);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x34);
    LCD_WR_DATA(0x02);
    LCD_WR_REG(0xF7);
    LCD_WR_DATA(0x20);
    LCD_WR_REG(0xEA);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_REG(0xC0);    //Power control
    LCD_WR_DATA(0x1B);   //VRH
    LCD_WR_REG(0xC1);    //Power control
    LCD_WR_DATA(0x12);   //SAP;BT 0x01
    LCD_WR_REG(0xC5);    //VCM control
    LCD_WR_DATA(0x08);      //30
    LCD_WR_DATA(0x26);      //30
    LCD_WR_REG(0xC7);    //VCM control2
    LCD_WR_DATA(0XB7);
    LCD_WR_REG(0x36);    // Memory Access Control
    LCD_WR_DATA(0x08);
    LCD_WR_REG(0x3A);   
    LCD_WR_DATA(0x55);
    LCD_WR_REG(0xB1);   
    LCD_WR_DATA(0x00);   
    LCD_WR_DATA(0x1A);
    LCD_WR_REG(0xB6);    // Display Function Control
    LCD_WR_DATA(0x0A);
    LCD_WR_DATA(0xA2);
    LCD_WR_REG(0xF2);    // 3Gamma Function Disable
    LCD_WR_DATA(0x00);
    LCD_WR_REG(0x26);    //Gamma curve selected
    LCD_WR_DATA(0x01);
    LCD_WR_REG(0xE0);    //Set Gamma
    LCD_WR_DATA(0x0F);
    LCD_WR_DATA(0x1D);
    LCD_WR_DATA(0x1A);
    LCD_WR_DATA(0x0A);
    LCD_WR_DATA(0x0D);
    LCD_WR_DATA(0x07);
    LCD_WR_DATA(0x49);
    LCD_WR_DATA(0X66);
    LCD_WR_DATA(0x3B);
    LCD_WR_DATA(0x07);
    LCD_WR_DATA(0x11);
    LCD_WR_DATA(0x01);
    LCD_WR_DATA(0x09);
    LCD_WR_DATA(0x05);
    LCD_WR_DATA(0x04);         
    LCD_WR_REG(0XE1);    //Set Gamma
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x18);
    LCD_WR_DATA(0x1D);
    LCD_WR_DATA(0x02);
    LCD_WR_DATA(0x0F);
    LCD_WR_DATA(0x04);
    LCD_WR_DATA(0x36);
    LCD_WR_DATA(0x13);
    LCD_WR_DATA(0x4C);
    LCD_WR_DATA(0x07);
    LCD_WR_DATA(0x13);
    LCD_WR_DATA(0x0F);
    LCD_WR_DATA(0x2E);
    LCD_WR_DATA(0x2F);
    LCD_WR_DATA(0x05);
    LCD_WR_REG(0x2B);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x01);
    LCD_WR_DATA(0x3f);
    LCD_WR_REG(0x2A);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0xef);   
    LCD_WR_REG(0x11); //Exit Sleep
    delay_ms(120);
    LCD_WR_REG(0x29); //display on   

    //设置LCD属性参数
    LCD_direction(USE_HORIZONTAL);//设置LCD显示方向
}

/*****************************************************************************
* @name       :void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
* @date       :2018-08-09
* @function   :Setting LCD display window
* @parameters :xStar:the bebinning x coordinate of the LCD display window
                                                                yStar:the bebinning y coordinate of the LCD display window
                                                                xEnd:the endning x coordinate of the LCD display window
                                                                yEnd:the endning y coordinate of the LCD display window
* @retvalue   :None
******************************************************************************/
void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
{      
      LCD_WR_REG(lcddev.setxcmd);      
      LCD_WR_DATA((u8)(xStar>>8));
      LCD_WR_DATA((u8)(0x00FF&xStar));               
      LCD_WR_DATA((u8)(xEnd>>8));
      LCD_WR_DATA((u8)(0x00FF&xEnd));

      LCD_WR_REG(lcddev.setycmd);      
      LCD_WR_DATA((u8)(yStar>>8));
      LCD_WR_DATA((u8)(0x00FF&yStar));               
      LCD_WR_DATA((u8)(yEnd>>8));
      LCD_WR_DATA((u8)(0x00FF&yEnd));      

      LCD_WriteRAM_Prepare();      //开始写入GRAM                              
}   

/*****************************************************************************
* @name       :void LCD_direction(u8 direction)
* @date       :2018-08-09
* @function   :Setting the display direction of LCD screen
* @parameters :direction:0-0 degree
                        1-90 degree
                                                                                                      2-180 degree
                                                                                                      3-270 degree
* @retvalue   :None
******************************************************************************/
void LCD_direction(u8 direction)
{
    lcddev.setxcmd=0x2A;
    lcddev.setycmd=0x2B;
    lcddev.wramcmd=0x2C;
    lcddev.rramcmd=0x2E;
    switch(direction){
      case 0:
            lcddev.width=LCD_W;
            lcddev.height=LCD_H;
            LCD_WriteReg(0x36,(1<<3));
      break;
      case 1:
            lcddev.width=LCD_H;
            lcddev.height=LCD_W;
            LCD_WriteReg(0x36,(1<<3)|(1<<5)|(1<<6));
      break;
      case 2:
            lcddev.width=LCD_W;
            lcddev.height=LCD_H;   
            LCD_WriteReg(0x36,(1<<3)|(1<<4)|(1<<6)|(1<<7));
      break;
      case 3:
            lcddev.width=LCD_H;
            lcddev.height=LCD_W;
            LCD_WriteReg(0x36,(1<<3)|(1<<7)|(1<<5)|(1<<4));
      break;
      default:break;
    }
}         

/*****************************************************************************
* @name       :void LCD_ShowChar(u16 x,u16 y,u16 fc, u16 bc, u8 num,u8 size,u8 mode)
* @date       :2018-08-09
* @function   :Display a single English character
* @parameters :x:the beginning x coordinate of the Character display position
                y:the beginning y coordinate of the Character display position
                                                                fc:the color value of display character
                                                                bc:the background color of display character
                                                                num:the ascii code of display character(0~94)
                                                                size:the size of display character
                                                                mode:0-no overlying,1-overlying
* @retvalue   :None
******************************************************************************/
void LCD_ShowChar(u16 x,u16 y,u16 fc, u16 bc, u8 num,u8 size,u8 mode)
{
      u8 temp;
      u8 pos,t;
      u16 colortemp=POINT_COLOR;      

      num=num-' ';//得到偏移后的值
      LCD_SetWindows(x,y,x+size/2-1,y+size-1);//设置单个文字显示窗口
      if(!mode) //非叠加方式
      {
                for(pos=0;pos<size;pos++)
                {
                        if(size==12)temp=asc2_1206;//调用1206字体
                        else temp=asc2_1608;               //调用1608字体
                        for(t=0;t<size/2;t++)
                  {               
                        if(temp&0x01)LCD_WR_DATA_16Bit(fc);
                              else LCD_WR_DATA_16Bit(bc);
                              temp>>=1;
                              
                  }
                }      
      }
    else//叠加方式
      {
                for(pos=0;pos<size;pos++)
                {
                        if(size==12)temp=asc2_1206;//调用1206字体
                        else temp=asc2_1608;               //调用1608字体
                        for(t=0;t<size/2;t++)
                  {   
                              POINT_COLOR=fc;            
                        if(temp&0x01)LCD_DrawPoint(x+t,y+pos);//画一个点   
                        temp>>=1;
                  }
                }
      }
      POINT_COLOR=colortemp;      
      LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);//恢复窗口为全屏                                 
}

/*****************************************************************************
* @name       :void Show_Str(u16 x, u16 y, u16 fc, u16 bc, u8 *str,u8 size,u8 mode)
* @date       :2018-08-09
* @function   :Display Chinese and English strings
* @parameters :x:the beginning x coordinate of the Chinese and English strings
                y:the beginning y coordinate of the Chinese and English strings
                                                                fc:the color value of Chinese and English strings
                                                                bc:the background color of Chinese and English strings
                                                                str:the start address of the Chinese and English strings
                                                                size:the size of Chinese and English strings
                                                                mode:0-no overlying,1-overlying
* @retvalue   :None
******************************************************************************/                              
void Show_Str(u16 x, u16 y, u16 fc, u16 bc, u8 *str,u8 size,u8 mode)
{                                       
      u16 x0=x;                                                                  
      u8 bHz=0;   //字符或者中文
      while(*str!=0)//数据未结束
      {
                if(!bHz)
                {
                        if(x>(lcddev.width-size/2)||y>(lcddev.height-size))
                        return;
                        if(*str>0x80)      bHz=1;//中文
                        else            //字符
                        {
                              if(*str==0x0D)//换行符号
                              {
                                        y+=size;
                                        x=x0;
                                        str++;
                              }
                              else
                              {
                                        if(size>16)//字库中没有集成12X24 16X32的英文字体,用8X16代替
                                        {
                                                LCD_ShowChar(x,y,fc,bc,*str,16,mode);
                                                x+=8; //字符,为全字的一半
                                        }
                                        else
                                        {
                                                LCD_ShowChar(x,y,fc,bc,*str,size,mode);
                                                x+=size/2; //字符,为全字的一半
                                        }
                              }
                              str++;
                        }
                }
                else//中文
                {
//                        if(x>(lcddev.width-size)||y>(lcddev.height-size))
//                        return;
//                        bHz=0;//有汉字库
//                        if(size==32)
//                        GUI_DrawFont32(x,y,fc,bc,str,mode);
//                        else if(size==24)
//                        GUI_DrawFont24(x,y,fc,bc,str,mode);
//                        else
//                        GUI_DrawFont16(x,y,fc,bc,str,mode);

                        str+=2;
                        x+=size;//下一个汉字偏移
                }
      }
}

/*****************************************************************************
* @name       :void LCM_Interrupt(void)
* @date       :2025-12-25
* @function   :None
* @parameters :None
* @retvalue   :
******************************************************************************/
void LCM_Interrupt(void) interrupt LCM_VECTOR
{
      if(LCMIFSTA & 0x01)
      {
                LCMIFSTA = 0x00;
                LcmFlag = 0;
      }
}

void LCM_DMA_Interrupt(void) interrupt DMA_LCM_VECTOR
{
      if(DMA_LCM_STA & 0x01)
      {
      index--;
      if(index == 0)
      {
            DMA_LCM_CR = 0;
            DmaFlag = 0;
            LCD_CS_H();
      }
      else
      {
            DMA_LCM_CR = 0xa0;      //Write dat
      }

      DMA_LCM_STA = 0;
      }
}



页: [1]
查看完整版本: 61-DMA-LCM液晶屏接口测试,STC32G144K246实验箱演示程序