国学芯用 发表于 2026-7-2 13:11:08

79-使用TFT彩屏8位数据口接R-2R电阻做DAC通过DMA输出波形,AI8051U实验箱演示程序

79-使用TFT彩屏8位数据口接R-2R电阻做DAC通过DMA输出波形,AI8051U实验箱演示程序

主程序main.C代码如下(汇编版本在附件中):

/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序            */
/*---------------------------------------------------------------------*/

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



本例程基于AI8051U为主控芯片的实验箱V1.2版本进行编写测试。

使用Keil C51编译器,为了达到比较高的效率,一般建议选择"Small"模式。

当编译器出现 "error C249: 'DATA': SEGMENT TOO LARGE" 错误时,则需要手动将部分比较大的数组通过"xdata" 强制分配到 XDATA 区域(例如: char xdata buffer ;)

使用TFT彩屏接口+DMA将正弦波数据输出P2口接R-2R做DAC输出正弦波(或任意波形)。
本例为32点正弦波,此法将极大程序减少对CPU的占用,中断频率为输出波形的频率,比如输出2000Hz的正弦波,如果用传统的中断重装方式,将需要64KHz(15.625us)的中断,严重拖累CPU。
而使用本方法,中断率为2000Hz,并且中断仅仅是重启DMA。
注意:重启DMA需要大约1us的时间,会附加到输出波形中,引起失真,当波形小于5000Hz时,影响可以忽略,大于10KHz时,影响开始能检测到。

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

#define MAIN_Fosc      40000000UL

#include "../comm/AI8051U.h"
#include <intrins.h>

typedef         unsigned char        u8;
typedef         unsigned int        u16;
typedef         unsigned long        u32;
#define        Pin0                0x01        //IO引脚 Px.0
#define        Pin1                0x02        //IO引脚 Px.1
#define        Pin2                0x04        //IO引脚 Px.2
#define        Pin3                0x08        //IO引脚 Px.3
#define        Pin4                0x10        //IO引脚 Px.4
#define        Pin5                0x20        //IO引脚 Px.5
#define        Pin6                0x40        //IO引脚 Px.6
#define        Pin7                0x80        //IO引脚 Px.7
#define        PinAll                0xFF        //IO所有引脚
#define P1n_standard(bitn)                        P1M1 &= ~(bitn),        P1M0 &= ~(bitn)
#define P1n_push_pull(bitn)                        P1M1 &= ~(bitn),        P1M0 |=(bitn)
#define P1n_pure_input(bitn)                P1M1 |=(bitn),        P1M0 &= ~(bitn)
#define P1n_open_drain(bitn)                P1M1 |=(bitn),        P1M0 |=(bitn)

#define P2n_standard(bitn)                        P2M1 &= ~(bitn),        P2M0 &= ~(bitn)
#define P2n_push_pull(bitn)                        P2M1 &= ~(bitn),        P2M0 |=(bitn)
#define P2n_pure_input(bitn)                P2M1 |=(bitn),        P2M0 &= ~(bitn)
#define P2n_open_drain(bitn)                P2M1 |=(bitn),        P2M0 |=(bitn)

#define P3n_standard(bitn)                        P3M1 &= ~(bitn),        P3M0 &= ~(bitn)
#define P3n_push_pull(bitn)                        P3M1 &= ~(bitn),        P3M0 |=(bitn)
#define P3n_pure_input(bitn)                P3M1 |=(bitn),        P3M0 &= ~(bitn)
#define P3n_open_drain(bitn)                P3M1 |=(bitn),        P3M0 |=(bitn)

#define P4n_standard(bitn)                        P4M1 &= ~(bitn),        P4M0 &= ~(bitn)
#define P4n_push_pull(bitn)                        P4M1 &= ~(bitn),        P4M0 |=(bitn)
#define P4n_pure_input(bitn)                P4M1 |=(bitn),        P4M0 &= ~(bitn)
#define P4n_open_drain(bitn)                P4M1 |=(bitn),        P4M0 |=(bitn)

//=============================================================================================================
#define        PullUpEnable(port, pu)                port |=(pu)                /* 允许端口内部上拉电阻       PxPU, 要设置的端口对应位为1 */
#define        PullUpDisable(port, pu)                port &= ~(pu)                /* 禁止端口内部上拉电阻(默认) PxPU, 要设置的端口对应位为1 */
#define        PullDownEnable(port, pu)        port |=(pu)                /* 允许端口内部下拉电阻       PxPD, 要设置的端口对应位为1 */
#define        PullDownDisable(port, pu)        port &= ~(pu)                /* 禁止端口内部下拉电阻(默认) PxPD, 要设置的端口对应位为1 */
#define        SchmitEnable(port, smt)                port &= ~(smt)                /* 允许端口施密特触发功能(默认) PxNCS, 要设置的端口对应位为1 */
#define        SchmitDisable(port, smt)        port |=(smt)                /* 允许端口施密特触发功能       PxNCS, 要设置的端口对应位为1 */
#define        SlewRateNormal(port, sr)        port |=(sr)                /* 端口输出设置为普通速度(默认) PxSR, 要设置的端口对应位为1 */
#define        SlewRateHigh(port, sr)                port &= ~(sr)                /* 端口输出设置为高速模式       PxSR, 要设置的端口对应位为1 */
#define        CurrentNormal(port, cu)                port |=(cu)                /* 端口输出电流为普通模式(默认) PxDR, 要设置的端口对应位为1 */
#define        CurrentHigt(port, cu)                port &= ~(cu)                /* 端口输出电流为大电流模式   PxDR, 要设置的端口对应位为1 */
#define        InputEnable(port, ie)                port |=(ie)                /* 允许端口数字输入(默认) PxIE, 要设置的端口对应位为1 */
#define        InputDisable(port, ie)                port &= ~(ie)                /* 禁止端口数字输入       PxIE, 要设置的端口对应位为1 */
#define        BypassEnable(port, bp)                port &= ~(bp)                /* 允许外设自动控制端口       PxBP, 要设置的端口对应位为1 */
#define        BypassDisable(port, bp)                port |=(bp)                /* 禁止外设自动控制端口(默认) PxBP, 要设置的端口对应位为1 */
//=============================================================================================================

/****************************** 用户定义宏 ***********************************/
        sbit    LCD_RS = P4^5;//悬空不用
        sbit    LCD_RW = P3^6;//悬空不用
        sbit    LCD_E= P3^7;        //悬空不用
        sbit    LCD_RESET   =   P4^7;   //悬空
        #define LCD_Data P2


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

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


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

u8        xdata T_SINE={
128,
153,
177,
199,
218,
234,
245,
253,
255,
253,
245,
234,
218,
199,
177,
153,
128,
103,
79,
57,
38,
22,
11,
3,
1,
3,
11,
22,
38,
57,
79,
103,
};


/*************本地函数声明    **************/
void         LCM_Config(void);
void         DMA_Config(u8 xdata *wav);

/********************* 主函数 *************************/
void main(void)
{
        EAXSFR();        //扩展寄存器(XFR)访问使能
        WTST= 0;        //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
        CKCON = 0;        //提高访问XRAM速度

        P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
        P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
        P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
        P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
        P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
        P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
        P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
        P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

        LCM_Config();
        EA = 1;

        DMA_Config(T_SINE);
        P40 = 0;                //接R-2R的地(这样接方便,但接GND更好).
        P2n_pure_input(0xff);                // P2要设置为高阻,否则IO输出电平会干扰数据,即LCM不控制IO时回复IO的输出。
        BypassEnable(P2BP, 0xff);        // 允许外设自动控制端口   PxBP, 要设置的端口对应位为1,   外设自动控制P0口, 使用外部总线时,如果P2BP=0x00则P2由硬件控制,P2的模式设置不影响总线,如果P2BP=0xff则P2设置会影响总线访问。
        BypassEnable(P3BP, 0xc0);        // 允许外设自动控制端口   PxBP, 要设置的端口对应位为1,   外设自动控制P0口, 使用外部总线时,如果P2BP=0x00则P2由硬件控制,P2的模式设置不影响总线,如果P2BP=0xff则P2设置会影响总线访问。
        BypassEnable(P4BP, 0xa0);        // 允许外设自动控制端口   PxBP, 要设置的端口对应位为1,   外设自动控制P0口, 使用外部总线时,如果P2BP=0x00则P2由硬件控制,P2的模式设置不影响总线,如果P2BP=0xff则P2设置会影响总线访问。

    while(1)
    {
                NOP(10);

    }
}



//========================================================================
// 函数: void LCM_Config(void)
// 描述: LCM配置函数。 LCM DMA发送周期:LCMIFPSCR=0(1分频)时,=(1+E+K+S)T+3Ts,LCMIFPSCR!=0(>=2分频)时, =(1+E+K+S)T+4Ts, T为LCMIF时钟周期, Ts为系统时钟周期, E为数据建立时间,K为数据保持时间,S为DMA间隔时间。
// 参数: 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                (1<<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                (7<<2)        //TFT彩屏接口数据建立时间控制位(bit4~bit2), 0~7, 对应1~8个LCMIF时钟, 即E信号高电平时间.
#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:P37,RW:P36; Setup Time,HOLD Time
        LCMIFSTA= 0x00;        //TFT彩屏接口中断请求标志,软件清0
        LCMIFPSCR = 1-1;        //TFT彩屏接口时钟预分频0~255, LCMIF时钟频率 = SYSclk/(LCMIFPSCR+1), LCD12864速度慢,最快250KHz
        LCMIFCR   = ENLCMIF;
}

//========================================================================
// 函数: void DMA_Config(u8 xdata *wav)
// 描述: LCM DMA配置函数。
// 参数: *wav: 数据首地址.
// 返回: 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.

        #define        LCM_ITVH        1233        //输出1000Hz正弦波@40MHz, 32点正弦波表,LCM耗时17T,则间隔时间=40000/32-17=1233
//        #define        LCM_ITVH        608                //输出2000Hz正弦波@40MHz, 32点正弦波表,LCM耗时17T,则间隔时间=40000/64-17=608
//        #define        LCM_ITVH        233                //输出5000Hz正弦波@40MHz, 32点正弦波表,LCM耗时17T,则间隔时间=40000/160-17=233
//        #define        LCM_ITVH        108                //输出 10KHz正弦波@40MHz, 32点正弦波表,LCM耗时17T,则间隔时间=40000/320-17=108

void DMA_Config(u8 xdata *wav)
{
        u16        LCM_TxAddr;
        LCM_TxAddr   = (u16)wav;                //要发送数据的首地址
        DMA_LCM_TXAH = (u8)(LCM_TxAddr >> 8);        //LCM DMA发送数据首地址
        DMA_LCM_TXAL = (u8)LCM_TxAddr;
        DMA_LCM_AMTH = 0;                                //设置传输总字节数(高8位),        设置传输总字节数 = N+1
        DMA_LCM_AMT= 32-1;                        //设置传输总字节数(低8位).
        DMA_LCM_STA= 0x00;
        DMA_LCM_CFG= DMA_LCMIE | DMA_LCMIP | DMA_LCMPTY;;
        DMA_LCM_ITVH = (u8)((LCM_ITVH-1)/256);        //设置传输间隔时间(高8位),对应N+1个LCMIF时钟(1~65536个LCMIF时钟)
        DMA_LCM_ITVL = (u8)((LCM_ITVH-1)%256);        //设置传输间隔时间(低8位)
        DMA_LCM_CR   = DMA_ENLCM | LCM_TRIGWD;        //启动LCM DMA发送数据
}

//========================================================================
// 函数: void LCMIF_DMA_ISR(interrupt USER_VECTOR//DMA_LCM_VECTORVECTOR
// 描述: LCM DMA中断函数。
// 参数: none.
// 返回: none.
// 版本: V1.0, 2024-8-17
// 备注:
//========================================================================
void LCMIF_DMA_ISR(void) interrupt USER_VECTOR//DMA_LCM_VECTOR
{
        DMA_LCM_CR   = DMA_ENLCM | LCM_TRIGWD;        //启动DAM
        DMA_LCM_STA = 0;
}


页: [1]
查看完整版本: 79-使用TFT彩屏8位数据口接R-2R电阻做DAC通过DMA输出波形,AI8051U实验箱演示程序