272761180 发表于 2024-11-27 18:11:34

使用AI8051U和SPI驱动ST7796的问题

之前用ESP32S3的TFT-ESPI和LVGL驱动过这块屏幕,使用STM32F4、以及配合LVGL也驱动过这块屏幕。

但是这几天学习AI8051U时,我使用AI8051U和普通SPI驱动ST7796一直没有亮屏,按照官方视频和有关代码。


现有一般都是DMA+SPI或者是高速SPI点屏,正常来说使用最基础的方式也能够驱动啊,但屏幕就还只是白色(BL亮),展示不了其他东西。

我的硬件没有PSRAM和SPIFLASH,就只是简单的驱动点屏。源代码上传了,请大佬给指导指导。










DebugLab 发表于 2024-11-27 18:27:59

示波器或逻辑分析仪看看信号正常不

272761180 发表于 2024-11-27 18:56:44

DebugLab 发表于 2024-11-27 18:27
示波器或逻辑分析仪看看信号正常不

业余玩家没有{:5_276:}

272761180 发表于 2024-11-27 21:59:21

又改了一个多小时,还是没办法点亮。是支持不了SPI的ST7796吗?
AI8051U的SPI驱动屏有点难搞,还是要必须配合DMA和PSRAM使用才可以?

神农鼎 发表于 2024-11-27 22:21:52

谁有这个屏,寄2块给我们,我们来帮忙测试下
或者告诉我们在哪买,我们买了来测试下

272761180 发表于 2024-11-27 23:26:58

272761180 发表于 2024-12-1 21:02:44

AI8051U可能驱动不了 SPI的4寸ST7796显示屏。


同样使用试验箱程序包的第76号文件,尝试驱动了2.8寸7789和4寸7796显示屏。

同样的代码,连屏幕分辨率都没改变,只是两者驱动部分改变了(7796的驱动代码在stm可以正常驱动),结果是7789可以驱动,7796无显示。







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

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

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

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

彩色SPI接口TFT LCD240x240的显示程序,通过SPI DMA将3200字节的图片数据送到彩屏,传送时不占用CPU时间。

显示图形,汉字,英文,数字. TFT LCD240X240使用中景园的液晶屏,主控IC型号为ST7789V3。

其中图形显示发送命令使用SPI查询方式(11字节),图片数据使用SPI DMA操作,本例运行于40MHz, 每次SPI DMA传输总时间1.52ms. 整屏刷新55mms.

将要显示的内容放在1024字节的显存中,启动DMA传输即可。

下载时, 选择时钟 40MHz (用户可自行修改频率后重新编译即可).

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

        #define        FOSC        40000000UL

        #include        "AI8051U.h"
        #include         "LCD.h"
        #include        "lcdfont.h"
        #include         "pic.h"

//-----------------LCD端口定义----------------
/*        定义接口        */
                                                        //GND        AI8051U实验箱 V1.1
                                                        //VCC        3~5V
//sbit P_LCD_CLK        =        P3^2;        //D0        SPI or II2 的时钟脚
//sbit P_LCD_SDA        =   P3^3;        //D1        SPI or II2 的数据脚
//sbit P_LCD_RST        =        P4^7;        //RES        复位脚, 低电平复位
//sbit P_LCD_DC        =        P1^1;        //DC        数据或命令脚
//sbit P_LCD_CS        =        P3^5;        //CS        片选脚

sbit P_LCD_CLK        =        P1^7;        //D0        SPI or II2 的时钟脚
sbit P_LCD_SDA        =   P1^5;        //D1        SPI or II2 的数据脚
sbit P_LCD_RST        =        P4^7;        //RES        复位脚, 低电平复位
sbit P_LCD_DC                =        P1^1;        //DC        数据或命令脚
sbit P_LCD_CS                =                P1^4;        //CS        片选脚


// 显示定义
#define USE_HORIZONTAL 3 //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏
#define LCD_W 320
#define LCD_H 240


//-----------------变量定义----------------
bit        B_SPI_DMA_busy;                //SPI DMA忙标志, 1标志SPI-DMA忙,SPI DMA中断中清除此标志,使用SPI DMA前要确认此标志为0
u16        SPI_TxAddr;                        //SPI DMA要发送数据的首地址
u8        xdata DisTmp;        //显示缓冲,将要显示的内容放在显存里,启动DMA即可. 由于LCM DMA有4字节对齐问题,所以这里定位对地址为4的倍数


//N ms延时函数
void delay_ms(u16 ms)        // 1~65535 ms
{
        u16 i;
        do
        {
                i = FOSC / 6000;        //STC32系列
                while(--i)        ;
        }while(--ms);
}


//========================================================================
// 函数: voidSPI_Config(u8 SPI_io, u8 SPI_speed)
// 描述: SPI初始化函数。
// 参数: io: 切换到的IO,            SSMOSI MISO SCLK
//                     0: 切换到 P1.4 P1.5 P1.6 P1.7
//                     1: 切换到 P2.4 P2.5 P2.6 P2.7
//                     2: 切换到 P4.0 P4.1 P4.2 P4.3
//                     3: 切换到 P3.5 P3.4 P3.3 P3.2
//       SPI_speed: SPI的速度, 0: fosc/4,1: fosc/8,2: fosc/16,3: fosc/2
// 返回: none.
// 版本: VER1.0
// 日期: 2024-8-13
// 备注:
//========================================================================
voidSPI_Config(u8 SPI_io, u8 SPI_speed)
{
        SPI_io &= 3;

        SPCTL = SPI_speed & 3;        //配置SPI 速度, 这条指令先执行, 顺便Bit7~Bit2清0
        SSIG = 1;        //1: 忽略SS脚,由MSTR位决定主机还是从机                0: SS脚用于决定主机还是从机。
        SPEN = 1;        //1: 允许SPI,                                                                0:禁止SPI,所有SPI管脚均为普通IO
        DORD = 0;        //1:LSB先发,                                                                0:MSB先发
        MSTR = 1;        //1:设为主机                                                                0:设为从机
        CPOL = 1;        //1: 空闲时SCLK为高电平,                                        0:空闲时SCLK为低电平
        CPHA = 1;        //1: 数据在SCLK前沿驱动,后沿采样.                        0: 数据在SCLK前沿采样,后沿驱动.
//        SPR1 = 0;        //SPR1,SPR0   00: fosc/4,   01: fosc/8
//        SPR0 = 0;        //            10: fosc/16,    11: fosc/2
        P_SW1 = (P_SW1 & ~0x0c) | ((SPI_io<<2) & 0x0c);                //切换IO

        HSCLKDIV   = 1;                                        //HSCLKDIV主时钟分频
        SPI_CLKDIV = 1;                                        //SPI_CLKDIV主时钟分频
        SPSTAT = 0x80 + 0x40;                        //清0 SPIF和WCOL标志

        if(SPI_io == 0)
        {
                P1n_standard(0xf0);                        //切换到 P1.4(SS) P1.5(MOSI) P1.6(MISO) P1.7(SCLK), 设置为准双向口
                PullUpEnable(P1PU, 0xf0);        //设置上拉电阻    允许端口内部上拉电阻   PxPU, 要设置的端口对应位为1
                P1n_push_pull(Pin7+Pin5);        //MOSI SCLK设置为推挽输出
                SlewRateHigh(P1SR, Pin7+Pin5);        //MOSI SCLK端口输出设置为高速模式   PxSR, 要设置的端口对应位为1.    高速模式在3.3V供电时速度可以到13.5MHz(27MHz主频,SPI速度2分频)
        }
        else if(SPI_io == 1)
        {
                P2n_standard(0xf0);                        //切换到P2.4(SS) P2.5(MOSI) P2.6(MISO) P2.7(SCLK), 设置为准双向口
                PullUpEnable(P2PU, 0xf0);        //设置上拉电阻    允许端口内部上拉电阻   PxPU, 要设置的端口对应位为1
                P2n_push_pull(Pin7+Pin5);        //MOSI SCLK设置为推挽输出
                SlewRateHigh(P2SR, Pin7+Pin5);        //MOSI SCLK端口输出设置为高速模式   PxSR, 要设置的端口对应位为1.    高速模式在3.3V供电时速度可以到13.5MHz(27MHz主频,SPI速度2分频)
        }
        else if(SPI_io == 2)
        {
                P4n_standard(0x0f);                        //切换到P4.0(SS) P4.1(MOSI) P4.2(MISO) P4.3(SCLK), 设置为准双向口
                PullUpEnable(P4PU, 0x0f);        //设置上拉电阻    允许端口内部上拉电阻   PxPU, 要设置的端口对应位为1
                P4n_push_pull(Pin3+Pin1);        //MOSI SCLK设置为推挽输出
                SlewRateHigh(P4SR, Pin3+Pin1);        //MOSI SCLK端口输出设置为高速模式   PxSR, 要设置的端口对应位为1.    高速模式在3.3V供电时速度可以到13.5MHz(27MHz主频,SPI速度2分频)
        }
        else if(SPI_io == 3)
        {
                P3n_standard(0x3C);                //切换到P3.5(SS) P3.4(MOSI) P3.3(MISO) P3.2(SCLK), 设置为准双向口
                PullUpEnable(P3PU, 0x3c);        //设置上拉电阻    允许端口内部上拉电阻   PxPU, 要设置的端口对应位为1
                P3n_push_pull(Pin4+Pin2);        //MOSI SCLK设置为推挽输出
                SlewRateHigh(P3SR, Pin4+Pin2);        //MOSI SCLK端口输出设置为高速模式   PxSR, 要设置的端口对应位为1.    高速模式在3.3V供电时速度可以到13.5MHz(27MHz主频,SPI速度2分频)
        }
}


//========================================================================
// 描述: 写SPI一个字节函数
// 参数: 无.
// 返回: 无
//========================================================================

void        LCD_SendByte(u8 dat)
{
        SPDAT = dat;        //发送一个字节
        while(SPIF == 0)        ;                        //等待发送完成
        SPSTAT = 0x80 + 0x40;                        //清0 SPIF和WCOL标志
}


/******************************************************************************
      函数说明:LCD写入数据
      入口数据:dat 写入的数据
      返回值:无
******************************************************************************/
void LCD_WR_DATA8(u8 dat)
{
        P_LCD_CS = 0;
        LCD_SendByte(dat);
        P_LCD_CS = 1;
}


/******************************************************************************
      函数说明:LCD写入数据
      入口数据:dat 写入的数据
      返回值:无
******************************************************************************/
void LCD_WR_DATA16(u16 dat)
{
        P_LCD_CS = 0;
        LCD_SendByte((u8)(dat>>8));
        LCD_SendByte((u8)dat);
        P_LCD_CS = 1;
}


/******************************************************************************
      函数说明:LCD写入命令
      入口数据:dat 写入的命令
      返回值:无
******************************************************************************/
void LCD_WR_REG(u8 dat)
{
        P_LCD_DC = 0;//写命令
        P_LCD_CS = 0;
        LCD_SendByte(dat);
        P_LCD_CS = 1;
        P_LCD_DC = 1;//写数据
}


/******************************************************************************
      函数说明:设置起始和结束地址
      入口数据:x1,x2 设置列的起始和结束地址
                y1,y2 设置行的起始和结束地址
      返回值:无
******************************************************************************/
void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2)
{
        if(USE_HORIZONTAL == 1)
        {
                y1 += 80;
                y2 += 80;
        }
        else if(USE_HORIZONTAL == 3)
        {
                x1 += 80;
                x2 += 80;
        }

        LCD_WR_REG(0x2a);//列地址设置
        LCD_WR_DATA16(x1);
        LCD_WR_DATA16(x2);
        LCD_WR_REG(0x2b);//行地址设置
        LCD_WR_DATA16(y1);
        LCD_WR_DATA16(y2);
        LCD_WR_REG(0x2c);//储存器写
}


/******************************************************************************
      函数说明:LCD初始化函数
      入口数据:无
      返回值:无
******************************************************************************/
void LCD_Init(u8 LCDdriver)
{
        P_LCD_CLK = 1;//SCLK
        P_LCD_SDA = 1;//MOSI
        P_LCD_RST = 1;//RES
        P_LCD_DC= 1;//DC
        P_LCD_CS= 1;//CS

        P_LCD_RST = 0;
        delay_ms(100);
        P_LCD_RST = 1;
        delay_ms(100);

        if(LCDdriver==0)
        {
                LCD_WR_REG(0x11);         //Sleep out 退出睡眠
                delay_ms(120);                //Delay 120ms
                LCD_WR_REG(0x36);        //显存访问控制
               if(USE_HORIZONTAL == 0)        LCD_WR_DATA8(0x00);
                else if(USE_HORIZONTAL == 1)        LCD_WR_DATA8(0xC0);
                else if(USE_HORIZONTAL == 2)        LCD_WR_DATA8(0x70);
                else LCD_WR_DATA8(0xA0);

                LCD_WR_REG(0x3A);        //接口格式
                LCD_WR_DATA8(0x05);

                LCD_WR_REG(0xB2);
                LCD_WR_DATA8(0x1F);
                LCD_WR_DATA8(0x1F);
                LCD_WR_DATA8(0x00);
                LCD_WR_DATA8(0x33);
                LCD_WR_DATA8(0x33);

                LCD_WR_REG(0xB7);
                LCD_WR_DATA8(0x35);

                LCD_WR_REG(0xBB);
                LCD_WR_DATA8(0x20);   //2b

                LCD_WR_REG(0xC0);
                LCD_WR_DATA8(0x2C);

                LCD_WR_REG(0xC2);
                LCD_WR_DATA8(0x01);

                LCD_WR_REG(0xC3);
                LCD_WR_DATA8(0x01);

                LCD_WR_REG(0xC4);
                LCD_WR_DATA8(0x18);   //VDV, 0x20:0v

                LCD_WR_REG(0xC6);
                LCD_WR_DATA8(0x13);   //0x13:60Hz

                LCD_WR_REG(0xD0);
                LCD_WR_DATA8(0xA4);
                LCD_WR_DATA8(0xA1);

                LCD_WR_REG(0xD6);
                LCD_WR_DATA8(0xA1);   //sleep in后,gate输出为GND

                //---------------ST7789V gamma setting-------------//
                LCD_WR_REG(0xE0);        //Set Gamma
                LCD_WR_DATA8(0xF0);
                LCD_WR_DATA8(0x04);
                LCD_WR_DATA8(0x07);
                LCD_WR_DATA8(0x04);
                LCD_WR_DATA8(0x04);
                LCD_WR_DATA8(0x04);
                LCD_WR_DATA8(0x25);
                LCD_WR_DATA8(0x33);
                LCD_WR_DATA8(0x3C);
                LCD_WR_DATA8(0x36);
                LCD_WR_DATA8(0x14);
                LCD_WR_DATA8(0x12);
                LCD_WR_DATA8(0x29);
                LCD_WR_DATA8(0x30);

                LCD_WR_REG(0xE1);        //Set Gamma
                LCD_WR_DATA8(0xF0);
                LCD_WR_DATA8(0x02);
                LCD_WR_DATA8(0x04);
                LCD_WR_DATA8(0x05);
                LCD_WR_DATA8(0x05);
                LCD_WR_DATA8(0x21);
                LCD_WR_DATA8(0x25);
                LCD_WR_DATA8(0x32);
                LCD_WR_DATA8(0x3B);
                LCD_WR_DATA8(0x38);
                LCD_WR_DATA8(0x12);
                LCD_WR_DATA8(0x14);
                LCD_WR_DATA8(0x27);
                LCD_WR_DATA8(0x31);

                LCD_WR_REG(0xE4);
                LCD_WR_DATA8(0x1D);   //使用240根gate(N+1)*8
                LCD_WR_DATA8(0x00);   //设定gate起点位置
                LCD_WR_DATA8(0x00);   //当gate没有用完时,bit4(TMG)设为0

                LCD_WR_REG(0x20);

                LCD_WR_REG(0x29);        //开启显示
        }
        else
        {
                /****        ST7796S IPS初始化        ****/       
                LCD_WR_REG(0x11);         //Sleep out 退出睡眠
                delay_ms(120);                //Delay 120ms
                LCD_WR_REG(0x36);        //显存访问控制
                LCD_WR_DATA8(0x48);   

                LCD_WR_REG(0x3A);   
                LCD_WR_DATA8(0x55);   //LCD_WR_DATA8(0x66);

                LCD_WR_REG(0xF0);   // Command Set Control
                LCD_WR_DATA8(0xC3);   

                LCD_WR_REG(0xF0);   
                LCD_WR_DATA8(0x96);   

                LCD_WR_REG(0xB4);   
                LCD_WR_DATA8(0x01);   

                LCD_WR_REG(0xB7);   
                LCD_WR_DATA8(0xC6);   

                //LCD_WR_REG(0xB9);   
                //LCD_WR_DATA8(0x02);
                //LCD_WR_DATA8(0xE0);

                LCD_WR_REG(0xC0);   
                LCD_WR_DATA8(0x80);   
                LCD_WR_DATA8(0x45);   

                LCD_WR_REG(0xC1);   
                LCD_WR_DATA8(0x13);   //18//00

                LCD_WR_REG(0xC2);   
                LCD_WR_DATA8(0xA7);   

                LCD_WR_REG(0xC5);   
                LCD_WR_DATA8(0x0A);   

                LCD_WR_REG(0xE8);   
                LCD_WR_DATA8(0x40);
                LCD_WR_DATA8(0x8A);
                LCD_WR_DATA8(0x00);
                LCD_WR_DATA8(0x00);
                LCD_WR_DATA8(0x29);
                LCD_WR_DATA8(0x19);
                LCD_WR_DATA8(0xA5);
                LCD_WR_DATA8(0x33);

                LCD_WR_REG(0xE0);
                LCD_WR_DATA8(0xD0);
                LCD_WR_DATA8(0x08);
                LCD_WR_DATA8(0x0F);
                LCD_WR_DATA8(0x06);
                LCD_WR_DATA8(0x06);
                LCD_WR_DATA8(0x33);
                LCD_WR_DATA8(0x30);
                LCD_WR_DATA8(0x33);
                LCD_WR_DATA8(0x47);
                LCD_WR_DATA8(0x17);
                LCD_WR_DATA8(0x13);
                LCD_WR_DATA8(0x13);
                LCD_WR_DATA8(0x2B);
                LCD_WR_DATA8(0x31);

                LCD_WR_REG(0xE1);
                LCD_WR_DATA8(0xD0);
                LCD_WR_DATA8(0x0A);
                LCD_WR_DATA8(0x11);
                LCD_WR_DATA8(0x0B);
                LCD_WR_DATA8(0x09);
                LCD_WR_DATA8(0x07);
                LCD_WR_DATA8(0x2F);
                LCD_WR_DATA8(0x33);
                LCD_WR_DATA8(0x47);
                LCD_WR_DATA8(0x38);
                LCD_WR_DATA8(0x15);
                LCD_WR_DATA8(0x16);
                LCD_WR_DATA8(0x2C);
                LCD_WR_DATA8(0x32);
               

                LCD_WR_REG(0xF0);   
                LCD_WR_DATA8(0x3C);   

                LCD_WR_REG(0xF0);   
                LCD_WR_DATA8(0x69);   

                delay_ms(120);               

                LCD_WR_REG(0x20);   

                LCD_WR_REG(0x29);
               
                P1M0 |= 0x01;                //BL推挽输出
                P1M1 &= ~0x01;
               
                P10=1;
        }
}

/******************************************************************************
      函数说明:在指定区域填充颜色
      入口数据:xsta,ysta   起始坐标
                xend,yend   终止坐标
                                                                color       要填充的颜色
      返回值:无
******************************************************************************/
void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)
{
        u16 i,j;
        LCD_Address_Set(xsta,ysta,xend-1,yend-1);//设置显示范围
        for(i=ysta;i<yend;i++)
        {
                for(j=xsta;j<xend;j++)
                {
                        LCD_WR_DATA16(color);
                }
        }
}

/******************************************************************************
      函数说明:在指定位置画点
      入口数据:x,y 画点坐标
                color 点的颜色
      返回值:无
******************************************************************************/
void LCD_DrawPoint(u16 x,u16 y,u16 color)
{
        LCD_Address_Set(x,y,x,y);//设置光标位置
        LCD_WR_DATA16(color);
}

/******************************************************************************
      函数说明:显示单个12x12汉字
      入口数据:x,y显示坐标
                *s 要显示的汉字
                fc 字的颜色
                bc 字的背景色
                sizey 字号
                mode:0非叠加模式1叠加模式
      返回值:无
******************************************************************************/
void LCD_ShowChinese12x12(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
        u8 i,j,m=0;
        u16 k;
        u16 HZnum;//汉字数目
        u16 TypefaceNum;//一个字符所占字节大小
        u16 x0=x;
        TypefaceNum=(sizey/8+((sizey%8)?1:0))*sizey;

        HZnum=sizeof(tfont12)/sizeof(typFNT_GB12);        //统计汉字数目
        for(k=0;k<HZnum;k++)
        {
                if((tfont12.Index==*(s))&&(tfont12.Index==*(s+1)))
                {
                        LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
                        for(i=0;i<TypefaceNum;i++)
                        {
                                for(j=0;j<8;j++)
                                {
                                        if(!mode)//非叠加方式
                                        {
                                                if(tfont12.Msk&(0x01<<j))                LCD_WR_DATA16(fc);
                                                else LCD_WR_DATA16(bc);
                                                m++;
                                                if(m%sizey==0)
                                                {
                                                        m=0;
                                                        break;
                                                }
                                        }
                                        else//叠加方式
                                        {
                                                if(tfont12.Msk&(0x01<<j))        LCD_DrawPoint(x,y,fc);//画一个点
                                                x++;
                                                if((x-x0)==sizey)
                                                {
                                                        x=x0;
                                                        y++;
                                                        break;
                                                }
                                        }
                                }
                        }
                }
                continue;//查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
        }
}

/******************************************************************************
      函数说明:显示单个16x16汉字
      入口数据:x,y显示坐标
                *s 要显示的汉字
                fc 字的颜色
                bc 字的背景色
                sizey 字号
                mode:0非叠加模式1叠加模式
      返回值:无
******************************************************************************/
void LCD_ShowChinese16x16(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
        u8 i,j,m=0;
        u16 k;
        u16 HZnum;//汉字数目
        u16 TypefaceNum;//一个字符所占字节大小
        u16 x0=x;
TypefaceNum=(sizey/8+((sizey%8)?1:0))*sizey;
        HZnum=sizeof(tfont16)/sizeof(typFNT_GB16);        //统计汉字数目
        for(k=0;k<HZnum;k++)
        {
                if ((tfont16.Index==*(s))&&(tfont16.Index==*(s+1)))
                {
                        LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
                        for(i=0;i<TypefaceNum;i++)
                        {
                                for(j=0;j<8;j++)
                                {
                                        if(!mode)//非叠加方式
                                        {
                                                if(tfont16.Msk&(0x01<<j))        LCD_WR_DATA16(fc);
                                                else LCD_WR_DATA16(bc);
                                                m++;
                                                if(m%sizey==0)
                                                {
                                                        m=0;
                                                        break;
                                                }
                                        }
                                        else//叠加方式
                                        {
                                                if(tfont16.Msk&(0x01<<j))        LCD_DrawPoint(x,y,fc);//画一个点
                                                x++;
                                                if((x-x0)==sizey)
                                                {
                                                        x=x0;
                                                        y++;
                                                        break;
                                                }
                                        }
                                }
                        }
                }
                continue;//查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
        }
}


/******************************************************************************
      函数说明:显示单个24x24汉字
      入口数据:x,y显示坐标
                *s 要显示的汉字
                fc 字的颜色
                bc 字的背景色
                sizey 字号
                mode:0非叠加模式1叠加模式
      返回值:无
******************************************************************************/
void LCD_ShowChinese24x24(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
        u8 i,j,m=0;
        u16 k;
        u16 HZnum;//汉字数目
        u16 TypefaceNum;//一个字符所占字节大小
        u16 x0=x;
        TypefaceNum=(sizey/8+((sizey%8)?1:0))*sizey;
        HZnum=sizeof(tfont24)/sizeof(typFNT_GB24);        //统计汉字数目
        for(k=0;k<HZnum;k++)
        {
                if ((tfont24.Index==*(s))&&(tfont24.Index==*(s+1)))
                {
                        LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
                        for(i=0;i<TypefaceNum;i++)
                        {
                                for(j=0;j<8;j++)
                                {
                                        if(!mode)//非叠加方式
                                        {
                                                if(tfont24.Msk&(0x01<<j))        LCD_WR_DATA16(fc);
                                                else LCD_WR_DATA16(bc);
                                                m++;
                                                if(m%sizey==0)
                                                {
                                                        m=0;
                                                        break;
                                                }
                                        }
                                        else//叠加方式
                                        {
                                                if(tfont24.Msk&(0x01<<j))        LCD_DrawPoint(x,y,fc);//画一个点
                                                x++;
                                                if((x-x0)==sizey)
                                                {
                                                        x=x0;
                                                        y++;
                                                        break;
                                                }
                                        }
                                }
                        }
                }
                continue;//查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
        }
}

/******************************************************************************
      函数说明:显示单个32x32汉字
      入口数据:x,y显示坐标
                *s 要显示的汉字
                fc 字的颜色
                bc 字的背景色
                sizey 字号
                mode:0非叠加模式1叠加模式
      返回值:无
******************************************************************************/
void LCD_ShowChinese32x32(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
        u8 i,j,m=0;
        u16 k;
        u16 HZnum;//汉字数目
        u16 TypefaceNum;//一个字符所占字节大小
        u16 x0=x;
        TypefaceNum=(sizey/8+((sizey%8)?1:0))*sizey;
        HZnum=sizeof(tfont32)/sizeof(typFNT_GB32);        //统计汉字数目
        for(k=0;k<HZnum;k++)
        {
                if ((tfont32.Index==*(s))&&(tfont32.Index==*(s+1)))
                {
                        LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
                        for(i=0;i<TypefaceNum;i++)
                        {
                                for(j=0;j<8;j++)
                                {
                                        if(!mode)//非叠加方式
                                        {
                                                if(tfont32.Msk&(0x01<<j))        LCD_WR_DATA16(fc);
                                                else LCD_WR_DATA16(bc);
                                                m++;
                                                if(m%sizey==0)
                                                {
                                                        m=0;
                                                        break;
                                                }
                                        }
                                        else//叠加方式
                                        {
                                                if(tfont32.Msk&(0x01<<j))        LCD_DrawPoint(x,y,fc);//画一个点
                                                x++;
                                                if((x-x0)==sizey)
                                                {
                                                        x=x0;
                                                        y++;
                                                        break;
                                                }
                                        }
                                }
                        }
                }
                continue;//查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
        }
}


/******************************************************************************
      函数说明:显示单个字符
      入口数据:x,y显示坐标
                num 要显示的字符
                fc 字的颜色
                bc 字的背景色
                sizey 字号
                mode:0非叠加模式1叠加模式
      返回值:无
******************************************************************************/
void LCD_ShowChar(u16 x,u16 y,u8 num,u16 fc,u16 bc,u8 sizey,u8 mode)
{
        u8 temp,sizex,t,m=0;
        u16 i,TypefaceNum;//一个字符所占字节大小
        u16 x0=x;
        sizex=sizey/2;
        TypefaceNum=(sizex/8+((sizex%8)?1:0))*sizey;
        num=num-' ';    //得到偏移后的值
        LCD_Address_Set(x,y,x+sizex-1,y+sizey-1);//设置光标位置
        for(i=0;i<TypefaceNum;i++)
        {
                if(sizey==12)temp=ascii_1206;                     //调用6x12字体
                else if(sizey==16)temp=ascii_1608;               //调用8x16字体
                else if(sizey==24)temp=ascii_2412;               //调用12x24字体
                else if(sizey==32)temp=ascii_3216;               //调用16x32字体
                else return;
                for(t=0;t<8;t++)
                {
                        if(!mode)//非叠加模式
                        {
                                if(temp&(0x01<<t))        LCD_WR_DATA16(fc);
                                else LCD_WR_DATA16(bc);
                                m++;
                                if(m%sizex==0)
                                {
                                        m=0;
                                        break;
                                }
                        }
                        else//叠加模式
                        {
                                if(temp&(0x01<<t))LCD_DrawPoint(x,y,fc);//画一个点
                                x++;
                                if((x-x0)==sizex)
                                {
                                        x=x0;
                                        y++;
                                        break;
                                }
                        }
                }
        }
}


/******************************************************************************
      函数说明:显示汉字串
      入口数据:x,y显示坐标
                *s 要显示的汉字串
                fc 字的颜色
                bc 字的背景色
                sizey 字号 可选 16 24 32
                mode:0非叠加模式1叠加模式
      返回值:无
******************************************************************************/
void LCD_ShowChinese(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
{
        while(*s!=0)
        {
                       if(sizey==12) LCD_ShowChinese12x12(x,y,s,fc,bc,sizey,mode);
                else if(sizey==16) LCD_ShowChinese16x16(x,y,s,fc,bc,sizey,mode);
                else if(sizey==24) LCD_ShowChinese24x24(x,y,s,fc,bc,sizey,mode);
                else if(sizey==32) LCD_ShowChinese32x32(x,y,s,fc,bc,sizey,mode);
                else return;
                s+=2;
                x+=sizey;
        }
}

/******************************************************************************
      函数说明:显示字符串
      入口数据:x,y显示坐标
                *p 要显示的字符串
                fc 字的颜色
                bc 字的背景色
                sizey 字号
                mode:0非叠加模式1叠加模式
      返回值:无
******************************************************************************/
void LCD_ShowString(u16 x,u16 y,const u8 *p,u16 fc,u16 bc,u8 sizey,u8 mode)
{
        while(*p!='\0')
        {
                LCD_ShowChar(x,y,*p,fc,bc,sizey,mode);
                x+=sizey/2;
                p++;
        }
}


/******************************************************************************
      函数说明:显示数字
      入口数据:m底数,n指数
      返回值:无
******************************************************************************/
u32 mypow(u8 m,u8 n)
{
        u32 result=1;
        while(n--)result*=m;
        return result;
}


/******************************************************************************
      函数说明:显示整数变量
      入口数据:x,y显示坐标
                num 要显示整数变量
                len 要显示的位数
                fc 字的颜色
                bc 字的背景色
                sizey 字号
      返回值:无
******************************************************************************/
void LCD_ShowIntNum(u16 x,u16 y,u16 num,u8 len,u16 fc,u16 bc,u8 sizey)
{
        u8 t,temp;
        u8 enshow=0;
        u8 sizex=sizey/2;
        for(t=0;t<len;t++)
        {
                temp = (num/mypow(10, (u8)(len-t-1)))%10;
                if(enshow==0&&t<(len-1))
                {
                        if(temp==0)
                        {
                                LCD_ShowChar(x+t*sizex,y,' ',fc,bc,sizey,0);
                                continue;
                        }else enshow=1;

                }
               LCD_ShowChar(x+t*sizex,y, (u8)(temp+48),fc,bc,sizey,0);
        }
}


/******************************************************************************
      函数说明:显示两位小数变量
      入口数据:x,y显示坐标
                num 要显示小数变量
                len 要显示的位数
                fc 字的颜色
                bc 字的背景色
                sizey 字号
      返回值:无
******************************************************************************/
void LCD_ShowFloatNum1(u16 x,u16 y,float num,u8 len,u16 fc,u16 bc,u8 sizey)
{
        u8 t,temp,sizex;
        u16 num1;
        sizex=sizey/2;
        num1=num*100;
        for(t=0;t<len;t++)
        {
                temp=(num1/mypow(10, (u8)(len-t-1)))%10;
                if(t==(len-2))
                {
                        LCD_ShowChar(x+(len-2)*sizex,y,'.',fc,bc,sizey,0);
                        t++;
                        len+=1;
                }
               LCD_ShowChar(x+t*sizex,y,(u8)(temp+48),fc,bc,sizey,0);
        }
}


/******************************************************************************
      函数说明:显示图片
      入口数据:x,y起点坐标
                length 图片长度
                width图片宽度
                pic[]图片数组
      返回值:无
******************************************************************************/
void LCD_ShowPicture(u16 x,u16 y,u16 length,u16 width, u8 xdata *pic)
{
        LCD_Address_Set(x,y,x+length-1,y+width-1);
        SPI_DMA_TRIG(pic);        //触发SPI DAM发送一个图片
        while(B_SPI_DMA_busy);        //等待图片发送完毕
}


//DMA_SPI_CR         SPI_DMA控制寄存器
#define                DMA_ENSPI                (1<<7)        // SPI DMA功能使能控制位,    bit7, 0:禁止SPI DMA功能,1:允许SPI DMA功能。
#define                SPI_TRIG_M                (1<<6)        // SPI DMA主机模式触发控制位,bit6, 0:写0无效,          1:写1开始SPI DMA主机模式操作。
#define                SPI_TRIG_S                (0<<5)        // SPI DMA从机模式触发控制位,bit5, 0:写0无效,          1:写1开始SPI DMA从机模式操作。
#define                SPI_CLRFIFO                1                // 清除SPI DMA接收FIFO控制位,bit0, 0:写0无效,          1:写1复位FIFO指针。


//DMA_SPI_CFG         SPI_DMA配置寄存器
#define                DMA_SPIIE        (1<<7)        // SPI DMA中断使能控制位,bit7, 0:禁止SPI DMA中断,   1:允许中断。
#define                SPI_ACT_TX        (1<<6)        // SPI DMA发送数据控制位,bit6, 0:禁止SPI DMA发送数据,主机只发时钟不发数据,从机也不发. 1:允许发送。
#define                SPI_ACT_RX        (0<<5)        // SPI DMA接收数据控制位,bit5, 0:禁止SPI DMA接收数据,主机只发时钟不收数据,从机也不收. 1:允许接收。
#define                DMA_SPIIP        (0<<2)        // SPI DMA中断优先级控制位,bit3~bit2, (最低)0~3(最高).
#define                DMA_SPIPTY        0                // SPI DMA数据总线访问优先级控制位,bit1~bit0, (最低)0~3(最高).

//DMA_SPI_CFG2         SPI_DMA配置寄存器2
#define                SPI_WRPSS        (0<<2)        // SPI DMA过程中使能SS脚控制位,bit2, 0: SPI DMA传输过程不自动控制SS脚。1:自动拉低SS脚。
#define                SPI_SSS          3                // SPI DMA过程中自动控制SS脚选择位,bit1~bit0, 0: P1.4,1:P2.4,2: P4.0,3:P3.5。

//DMA_SPI_STA         SPI_DMA状态寄存器
#define                SPI_TXOVW        (1<<2)        // SPI DMA数据覆盖标志位,bit2, 软件清0.
#define                SPI_RXLOSS        (1<<1)        // SPI DMA接收数据丢弃标志位,bit1, 软件清0.
#define                DMA_SPIIF        1                // SPI DMA中断请求标志位,bit0, 软件清0.

//HSSPI_CFG高速SPI配置寄存器
#define                SS_HOLD                (0<<4)        //高速模式时SS控制信号的HOLD时间, 0~15, 默认3. 在DMA中会增加N个系统时钟,当SPI速度为系统时钟/2时执行DMA,SS_HOLD、SS_SETUP和SS_DACT都必须设置大于2的值.
#define                SS_SETUP                3        //高速模式时SS控制信号的SETUP时间,0~15, 默认3. 在DMA中不影响时间,       当SPI速度为系统时钟/2时执行DMA,SS_HOLD、SS_SETUP和SS_DACT都必须设置大于2的值.

//HSSPI_CFG2高速SPI配置寄存器2
#define                SPI_IOSW        (1<<6)        //bit6:交换MOSI和MISO脚位,0:不交换,1:交换
#define                HSSPIEN                (0<<5)        //bit5:高速SPI使能位,0:关闭高速模式,1:使能高速模式
#define                FIFOEN                (1<<4)        //bit4:高速SPI的FIFO模式使能位,0:关闭FIFO模式,1:使能FIFO模式,使能FIFO模式在DMA中减少13个系统时间。
#define                SS_DACT                        3        //bit3~0:高速模式时SS控制信号的DEACTIVE时间,0~15, 默认3, 不影响DMA时间.当SPI速度为系统时钟/2时执行DMA,SS_HOLD、SS_SETUP和SS_DACT都必须设置大于2的值.


void        SPI_DMA_TRIG(u8 xdata *TxBuf)
{
                                //@40MHz, Fosc/4, 200字节258us,100字节130us,50字节66us,N个字节耗时 N*1.280+2 us, 51T一个字节,其中状态机19T, 传输耗时32T.
                                //@40MHz, Fosc/2, 200字节177us,100字节 89.5us,50字节46us,N个字节耗时 N*0.875+2 us, 35T一个字节,其中状态机19T, 传输耗时16T.
                                //@40MHz, Fosc/2, SPI DMA传输一个字节, FIFO=1, HOLD=0,耗时16+3=19T(0.475us), HOLD=3,耗时16+6=22T(0.55us).
                                //@40MHz, Fosc/4, SPI DMA传输一个字节, FIFO=1, HOLD=0,耗时32+3=35T(0.875us), HOLD=3,耗时32+6=38T(0.95us).
        HSSPI_CFG= SS_HOLD | SS_SETUP;        //SS_HOLD会增加N个系统时钟, SS_SETUP没有增加时钟。驱动OLED 40MHz时SS_HOLD可以设置为0,
        HSSPI_CFG2 = SPI_IOSW | FIFOEN | SS_DACT;        //FIFOEN允许FIFO会减小13个时钟.

        P_LCD_DC= 1;        //写数据
        P_LCD_CS= 0;        //片选
        B_SPI_DMA_busy = 1;        //标志SPI-DMA忙,SPI DMA中断中清除此标志,使用SPI DMA前要确认此标志为0

        SPI_TxAddr   = (u16)TxBuf;                //要发送数据的首地址
        DMA_SPI_TXAH = (u8)(SPI_TxAddr >> 8);                //发送地址寄存器高字节
        DMA_SPI_TXAL = (u8)SPI_TxAddr;                                //发送地址寄存器低字节
        DMA_SPI_AMTH = (u8)((3200-1)/256);                //设置传输总字节数(高8位),        设置传输总字节数 = N+1
        DMA_SPI_AMT= (u8)(3200-1);                        //设置传输总字节数(低8位).
        DMA_SPI_ITVH = 0;
        DMA_SPI_ITVL = 0;
        DMA_SPI_STA= 0x00;
        DMA_SPI_CFG= DMA_SPIIE | SPI_ACT_TX | SPI_ACT_RX | DMA_SPIIP | DMA_SPIPTY;
        DMA_SPI_CFG2 = SPI_WRPSS | SPI_SSS;
        DMA_SPI_CR   = DMA_ENSPI | SPI_TRIG_M | SPI_TRIG_S | SPI_CLRFIFO;        //启动SPI DMA发送命令
}


//========================================================================
// 函数: void SPI_DMA_ISR (void) interrupt DMA_SPI_VECTOR
// 描述:SPI_DMA中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2024-1-5
//========================================================================
void SPI_DMA_ISR (void) interrupt DMA_SPI_VECTOR
{
        DMA_SPI_CR = 0;                        //关闭SPI DMA
        B_SPI_DMA_busy = 0;                //清除SPI-DMA忙标志,SPI DMA中断中清除此标志,使用SPI DMA前要确认此标志为0
        SPSTAT = 0x80 + 0x40;        //清0 SPIF和WCOL标志
        HSSPI_CFG2 = SPI_IOSW | SS_DACT;        //使用SPI查询或中断方式时,要禁止FIFO
        P_LCD_CS = 1;
        DMA_SPI_STA = 0;                //清除中断标志
}




void main(void)
{
        u16 i;
        u8        j;
        float t=0;

        EAXFR = 1;        //允许访问扩展寄存器
        WTST= 0;
        CKCON = 0;

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

        //==================== SPI初始化 ==================================
        SPI_Config(0, 3);        //(SPI_io, SPI_speed), 参数:         SPI_io: 切换IO(SS MOSI MISO SCLK), 0: 切换到P1.4 P1.5 P1.6 P1.7,1: 切换到P2.4 P2.5 P2.6 P2.7, 2: 切换到P4.0 P4.1 P4.2 P4.3,3: 切换到P3.5 P3.4 P3.3 P3.2,
                                                //                                                                SPI_speed: SPI的速度, 0: fosc/4,1: fosc/8,2: fosc/16,3: fosc/2
//        HSSPI_CFG2 = 0x40;        //交换MOSI MISO, P3.3是MOSI

        P1n_standard(Pin1);                        //SPI引脚设置为准双向口, SPI和控制信号
        PullUpEnable(P1PU, Pin1);        // 允许端口内部上拉电阻   PxPU, 要设置的端口对应位为1
        P3n_standard(0x2c);                        //SPI引脚设置为准双向口, SPI和控制信号
        PullUpEnable(P3PU, 0x2c);        // 允许端口内部上拉电阻   PxPU, 要设置的端口对应位为1
        P4n_standard(Pin7);                        //SPI引脚设置为准双向口, SPI和控制信号
        PullUpEnable(P4PU, Pin7);        // 允许端口内部上拉电阻   PxPU, 要设置的端口对应位为1
        //=================================================================

        LCD_Init(0);//LCD初始化
        LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
        EA = 1;

        while(1)
        {
//                LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
                LCD_ShowChinese(0,0,"深圳国芯人工智能",RED,WHITE,24,0);
//                LCD_ShowString(0,40,"LCD_W:",RED,WHITE,16,0);
//                LCD_ShowIntNum(48,40,LCD_W,3,RED,WHITE,16);
//                LCD_ShowString(80,40,"LCD_H:",RED,WHITE,16,0);
//                LCD_ShowIntNum(128,40,LCD_H,3,RED,WHITE,16);
//                LCD_ShowString(80,40,"LCD_H:",RED,WHITE,16,0);
//                LCD_ShowString(0,70,"Increaseing Nun:",RED,WHITE,16,0);
                LCD_ShowFloatNum1(128,70,t,4,RED,WHITE,16);
                t+=0.11;
                delay_ms(3000);        // 1~65535 ms

//                for(i=0; i<3200; i++)        DisTmp = Image_1;        //将图片装载到显存
//                for(j=0; j<6; j++)                //6行图片, 整屏36个图片 @40MHz FIFOEN=1, SS_HOLD=0时55ms @2T
//                {
//                        for(i=0; i<6; i++)        //一行6个图片
//                        {
//                                LCD_ShowPicture(40*i, j*40, 40, 40, DisTmp);        //触发SPI DMA显示一个图片, 3200字节 1.52ms @40MHz
//                        }
//                }
//                delay_ms(3000);        // 1~65535 ms

//                for(i=0; i<3200; i++)        DisTmp = Image_2;        //将图片装载到显存
//                for(j=0; j<6; j++)                //6行图片, 整屏36个图片 @40MHz FIFOEN=1, SS_HOLD=0时55ms @2T
//                {
//                        for(i=0; i<6; i++)        //一行6个图片
//                        {
//                                LCD_ShowPicture(40*i, j*40, 40, 40, DisTmp);        //触发SPI DMA显示一个图片, 3200字节 1.52ms @40MHz
//                        }
//                }
//                delay_ms(3000);
        }
}












为什么呢???

272761180 发表于 2024-12-1 21:08:48

明天抽空用STM驱动试试,再排除下是不是硬件问题

haiyang201 发表于 2024-12-2 13:41:27

272761180 发表于 2024-12-1 21:02
AI8051U可能驱动不了 SPI的4寸ST7796显示屏。




频率搞小点,延时搞长点试试

durongze 发表于 2025-1-11 11:47:49

楼主你试过楼上这位大佬说的方法了吗? 会不会和频率和延时有关呢?我之前搞过其他,就在这里出的问题。
页: [1] 2
查看完整版本: 使用AI8051U和SPI驱动ST7796的问题