mtt661 发表于 2025-9-27 23:20:48

用AI8051,直接驱动480*272分辨率,RGB接口屏,

屏幕是标准40pin的RGB接口屏,频率22.1184Mhz,用DE模式驱动,本来单纯想学学这种屏幕的时序,没想到显示个汉字啥的,还能接受,因为是用24位RGB,所以用的32bit模式,8位対long的兼容不好,程序写的不好,希望大家指正。
为了显示汉字,数字之类的,实在不知道怎么写这个函数,因为每次都要全屏刷新像素,就用了最粗暴的方式,建立整屏缓存,然后把需要显示的汉字或者数字数组,替换掉全屏缓存内对应的字节。

#include <AI8051U.H>

#include "intrins.h"


// 引脚定义
sbit CLK   = P5^7;

sbit DE    = P4^0;


//sbit DISP= P1^5;

sbit LED    = P5^6;

#define port_r   P1
#define port_g   P0
#define port_b   P2

// 屏幕参数
#define H_ACTIVE      480   // 水平有效像素
#define V_ACTIVE      272   // 垂直有效行数
#define H_BLANK       45      // 水平消隐期(前廊+同步+后廊)
#define V_BLANK       14      // 垂直消隐期(前廊+同步+后廊)

#define H_TOTAL (H_ACTIVE + H_BLANK)
#define V_TOTAL (V_ACTIVE + V_BLANK)

#define FOSC               22118400UL
#define BRT         (65536 - FOSC / 115200 / 4)

#define bai               0xffffff
#define hong               0xff0000
#define lv                        0x00ff00
#define huang               0xffff00
#define hei               0x000000
#define qianhuang         0xffffbe
#define lan               0x0000ff


unsigned char xdata full={0};

unsigned char xdata shan={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x08,0x10,0x18,0x00, /* 0 */
       0x0f,0xf8,0x18,0x00,0x0c,0x30,0x18,0x00,0x0c,0x20,0x18,0x60,0x0c,0x67,0xff,0xf0,
       0x0c,0x40,0x18,0x00,0x04,0x40,0x18,0x00,0x04,0x82,0x18,0xc0,0x04,0x83,0x10,0xc0,
       0x04,0x83,0x10,0x80,0x04,0x41,0x91,0x00,0x04,0x21,0x91,0x00,0x04,0x11,0x12,0x00,
       0x04,0x10,0x10,0x10,0x04,0x1f,0xff,0xf8,0x04,0x18,0x34,0x00,0x04,0x18,0x34,0x00,
       0x07,0xf0,0x24,0x00,0x04,0xe0,0x62,0x00,0x0c,0x00,0x43,0x00,0x0c,0x00,0xc1,0x00,
       0x0c,0x01,0x81,0xc0,0x0c,0x03,0x00,0xe0,0x0c,0x06,0x00,0x78,0x0c,0x18,0x00,0x30,
       0x08,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
unsigned char xdata xi={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20, /* 1 */
       0x1f,0xff,0xff,0xf0,0x00,0x0c,0x30,0x00,0x00,0x0c,0x30,0x00,0x00,0x0c,0x30,0x00,
       0x00,0x0c,0x30,0x00,0x04,0x0c,0x30,0x40,0x07,0xff,0xff,0xf0,0x06,0x0c,0x30,0x60,
       0x06,0x0c,0x30,0x60,0x06,0x0c,0x30,0x60,0x06,0x0c,0x30,0x60,0x06,0x08,0x30,0x60,
       0x06,0x08,0x30,0x60,0x06,0x18,0x30,0x60,0x06,0x10,0x31,0x60,0x06,0x30,0x1f,0xe0,
       0x06,0x20,0x00,0x60,0x06,0x40,0x00,0x60,0x06,0x80,0x00,0x60,0x06,0x00,0x00,0x60,
       0x07,0xff,0xff,0xe0,0x06,0x00,0x00,0x60,0x06,0x00,0x00,0x60,0x04,0x00,0x00,0x00,
       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
unsigned char xdata sheng={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x80,0x00, /* 2 */
       0x00,0x41,0x00,0x00,0x00,0x71,0x03,0x80,0x00,0xc1,0x00,0xc0,0x01,0x81,0x0c,0x70,
       0x01,0x81,0x1c,0x30,0x03,0x01,0x30,0x30,0x06,0x02,0xe0,0x10,0x08,0x01,0xc0,0x00,
       0x10,0x03,0x00,0x00,0x00,0x0e,0x01,0x00,0x00,0x3f,0xff,0x80,0x00,0x70,0x03,0x00,
       0x00,0xe0,0x03,0x00,0x07,0x20,0x03,0x00,0x08,0x3f,0xff,0x00,0x00,0x20,0x03,0x00,
       0x00,0x20,0x03,0x00,0x00,0x20,0x03,0x00,0x00,0x3f,0xff,0x00,0x00,0x20,0x03,0x00,
       0x00,0x20,0x03,0x00,0x00,0x3f,0xff,0x00,0x00,0x20,0x03,0x00,0x00,0x20,0x03,0x00,
       0x00,0x20,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};


unsigned char xdata shuzi1632[] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xc0,0x0e,0x60,0x1c,0x70, /* 0 */
       0x38,0x38,0x38,0x38,0x38,0x18,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x70,0x1c,
       0x70,0x1c,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x30,0x18,0x38,0x38,0x38,0x38,0x1c,0x70,
       0x0e,0x60,0x07,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x01,0xc0,0x0f,0xc0, /* 1 */
       0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,
       0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,0x01,0xc0,
       0x01,0xe0,0x0f,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xe0,0x0c,0x78,0x18,0x38, /* 2 */
       0x30,0x1c,0x30,0x1c,0x38,0x1c,0x38,0x1c,0x00,0x1c,0x00,0x38,0x00,0x30,0x00,0x70,
       0x00,0xe0,0x01,0xc0,0x03,0x80,0x07,0x00,0x0e,0x00,0x0c,0x0c,0x18,0x0c,0x30,0x1c,
       0x3f,0xfc,0x3f,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xc0,0x18,0xf0,0x38,0x70, /* 3 */
       0x38,0x38,0x38,0x38,0x38,0x38,0x00,0x38,0x00,0x70,0x00,0xe0,0x03,0xc0,0x00,0x70,
       0x00,0x38,0x00,0x18,0x00,0x1c,0x00,0x1c,0x38,0x1c,0x38,0x1c,0x38,0x1c,0x38,0x38,
       0x18,0x70,0x0f,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x70,0x00,0xf0, /* 4 */
       0x00,0xf0,0x01,0xf0,0x03,0x70,0x03,0x70,0x06,0x70,0x0e,0x70,0x0c,0x70,0x18,0x70,
       0x18,0x70,0x30,0x70,0x60,0x70,0x7f,0xfe,0x00,0x70,0x00,0x70,0x00,0x70,0x00,0x70,
       0x00,0x70,0x03,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xfc,0x1f,0xfc,0x18,0x00, /* 5 */
       0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x18,0x00,0x1b,0xe0,0x1e,0x78,0x1c,0x38,
       0x00,0x1c,0x00,0x1c,0x00,0x1c,0x00,0x1c,0x38,0x1c,0x38,0x1c,0x30,0x38,0x30,0x38,
       0x1c,0x70,0x07,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xe0,0x0e,0x38,0x1c,0x38, /* 6 */
       0x18,0x38,0x38,0x00,0x30,0x00,0x30,0x00,0x70,0x00,0x77,0xe0,0x7e,0x70,0x78,0x38,
       0x78,0x1c,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x38,0x1c,0x38,0x18,0x1c,0x38,
       0x1e,0x70,0x07,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0xfc,0x1f,0xfc,0x38,0x18, /* 7 */
       0x30,0x30,0x30,0x30,0x00,0x60,0x00,0x60,0x00,0xc0,0x00,0xc0,0x01,0x80,0x01,0x80,
       0x01,0x80,0x03,0x00,0x03,0x00,0x03,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x07,0x00,
       0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xe0,0x1c,0x70,0x38,0x38, /* 8 */
       0x70,0x1c,0x70,0x1c,0x70,0x1c,0x78,0x1c,0x3c,0x38,0x1e,0x30,0x0f,0xe0,0x0f,0xe0,
       0x18,0xf0,0x38,0x78,0x70,0x3c,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x38,0x38,
       0x1c,0x70,0x0f,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xc0,0x1c,0x70,0x38,0x30, /* 9 */
       0x38,0x38,0x70,0x18,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x70,0x1c,0x70,0x3c,0x38,0x3c,
       0x3c,0xfc,0x0f,0xdc,0x00,0x1c,0x00,0x38,0x00,0x38,0x00,0x38,0x38,0x70,0x38,0x70,
       0x38,0xe0,0x1f,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

void Delayms(unsigned int x)      //@22.1184MHz
{
      unsigned long edata i;
      while(x--)
      {
      _nop_();
      i = 5528UL;
      while (i) i--;
      }
}

void UartInit()                //定时器1(模式0)做串口1波特率发生器
{
      SCON = 0x50;                //8位数据,可变波特率
      AUXR |= 0x40;                //定时器时钟1T模式
      AUXR &= 0xFE;                //串口1选择定时器1为波特率发生器
      TMOD &= 0x0F;                //设置定时器模式
      TL1 =BRT;                        //设置定时初始值
      TH1 = BRT >> 8;                        //设置定时初始值
      ET1 = 0;                        //禁止定时器中断
      TR1 = 1;                        //定时器1开始计时


    ES = 1;      //打开串口中断
    EA = 1;      //打开总中断

}

void UartSend(unsigned char dat)      //单片机发送一个字节
{

    SBUF = dat;
      while(TI==0);
      TI=0;
}

void CheckCustomCmd(unsigned char dat)                   //检测命令序列:"@STCISP#"
{
      unsigned char bStage;
      
    switch (bStage++)
    {
    default:
      L_Check1st:
    case 0:                                     //检测命令头
      bStage = (dat == '@');
      break;
    case 1:
      if (dat != 'S') goto L_Check1st;
      break;
    case 2:
      if (dat != 'T') goto L_Check1st;
      break;
    case 3:
      if (dat != 'C') goto L_Check1st;
      break;
    case 4:
      if (dat != 'I') goto L_Check1st;
      break;
    case 5:
      if (dat != 'S') goto L_Check1st;
      break;
    case 6:
      if (dat != 'P') goto L_Check1st;
      break;
    case 7:
      if (dat != '#') goto L_Check1st;
      Delayms(5);                           //检测到正确的命令序列后复位到系统区,此处的延时可省去
      IAP_CONTR = 0x60;                     //复位到系统区
      break;
    }
}

void UartSendStr(unsigned char *p)//单片机发送字符串
{
    while (*p)
    {
      UartSend(*p++);
    }
}

void Delayus(unsigned int x)      //@20.000MHz
{
      unsigned long edata i;
      while(x--)
{
      _nop_();
      _nop_();
      _nop_();
      i = 3UL;
      while (i) i--;
}

}


void Full_RGB_Display(unsigned long color)

{
    unsigned int line, pixel;

      for(line = 0; line < V_ACTIVE; line++)            // 逐行显示      272
      {
            DE = 1;// 行有效期开始
            

            for(pixel = 0; pixel < H_ACTIVE; pixel++)            // 生成一行像素时钟      480
            {


//                              Delayus(100);


                              port_r=color>>16;
                              port_g=color>>8;
                              port_b=color;


                CLK = 1;

                CLK = 0;


            }
            
            DE = 0;// 行有效期结束
            
         
            for(pixel = 0; pixel < H_BLANK; pixel++) // 行消隐期
            {
                CLK = 1;

                CLK = 0;

            }
      }
      
      // 帧消隐期
      for(line = 0; line < V_BLANK; line++)
      {
            for(pixel = 0; pixel < H_TOTAL; pixel++)
            {
                CLK = 1;

                CLK = 0;

            }
      }
}

void Simple_RGB_Display(unsigned long x,unsigned long y)      //竖向条文
{
    unsigned int line, pixel;
      unsigned char i;
      for(line = 0; line < V_ACTIVE; line++)            // 逐行显示      272
      {
            DE = 1;// 行有效期开始
            

            for(pixel = 0; pixel < 10; pixel++)            // 生成一行像素时钟      480
            {



                              for(i = 0; i < 24; i++)
                              {
                              port_r=x>>16;
                              port_g=x>>8;
                              port_b=x;

                CLK = 1;

                CLK = 0;

                              }
                              for(i = 0; i < 24; i++)
                              {
                              port_r=y>>16;
                              port_g=y>>8;
                              port_b=y;

                CLK = 1;

                CLK = 0;

                              }


            }
            
            DE = 0;// 行有效期结束
            
         
            for(pixel = 0; pixel < H_BLANK; pixel++) // 行消隐期
            {
                CLK = 1;

                CLK = 0;


            }
      }
      
      // 帧消隐期
      for(line = 0; line < V_BLANK; line++)
      {
            for(pixel = 0; pixel < H_TOTAL; pixel++)
            {
                CLK = 1;

                CLK = 0;

            }
      }
}

void pic480272(unsigned long x,unsigned long y)      //图片
{
    unsigned int line, pixel;

      unsigned char a,b,i;

      for(line = 0; line < V_ACTIVE; line++)            // 逐行显示      272
      {
            DE = 1;// 行有效期开始
            

            for(pixel = 0; pixel < 60; pixel++)            // 生成一行像素时钟      480
            {
                              a=full;


                              for(i=0;i<8;i++)
                              {
                                        b=a&0x80;
                                        if(b==0)
                                        {
                                        port_r=y>>16;
                                        port_g=y>>8;
                                        port_b=y;

                                        CLK = 1;

                                        CLK = 0;
                                        }
                                        else
                                        {
                                        port_r=x>>16;
                                        port_g=x>>8;
                                        port_b=x;

                                        CLK = 1;

                                        CLK = 0;
                                        }
                              a=a<<1;
                              }






            }
            
            DE = 0;// 行有效期结束
            
         
            for(pixel = 0; pixel < H_BLANK; pixel++) // 行消隐期
            {
                CLK = 1;

                CLK = 0;

            }
      }
      
      // 帧消隐期
      for(line = 0; line < V_BLANK; line++)
      {
            for(pixel = 0; pixel < H_TOTAL; pixel++)
            {
                CLK = 1;

                CLK = 0;

            }
      }
}




//void display_3232 (unsigned char x,unsigned char y,unsigned char *p,unsigned long bfcolor, unsigned long bhcolor)      //x的范围0-272   y的范围0-60
void display_3232 (unsigned char y,unsigned char x,unsigned char *p)
{
      unsigned char i,j;

      for(i=0;i<32;i++)                //一行480/8个字节
      {
                for(j=0;j<4;j++)      //32*32汉字,汉字一行32/4个字节
                {
                        full[(y*60+x)+(j+i*60)]=p;

                }

      

      }

//      pic480272(bfcolor,bhcolor);

}

void displayNum_1632 (unsigned char y,unsigned char x,unsigned char a)
{
      unsigned char i,j;
      unsigned char num;

      num=a/100;

      for(i=0;i<32;i++)                //一行480/8个字节
      {
                for(j=0;j<2;j++)      //16*32数字,数字一行16/82个字节
                {
                        full[(y*60+x)+(j+i*60)]=shuzi1632;
                }

      }
      x=x+2;
      num=(a % 100) / 10;
      for(i=0;i<32;i++)                //一行480/8个字节
      {
                for(j=0;j<2;j++)      //16*32数字,数字一行16/82个字节
                {
                        full[(y*60+x)+(j+i*60)]=shuzi1632;
                }

      }
      x=x+2;
      num=a%10;
      for(i=0;i<32;i++)                //一行480/8个字节
      {
                for(j=0;j<2;j++)      //16*32数字,数字一行16/82个字节
                {
                        full[(y*60+x)+(j+i*60)]=shuzi1632;
                }

      }
//      pic480272(bfcolor,bhcolor);

}


// 主函数
void main(void)
{

      unsigned char a;
    // P0P1P2口推挽输出
    P0M0 = 0xff;         //不推挽输出也行
      P0M1 = 0x00;
    P1M0 = 0xff;
    P1M1 = 0x00;
    P2M0 = 0xff;
      P2M1 = 0x00;
    P3M0 = 0x00;
      P3M1 = 0xc0;
    P4M0 = 0x00;
      P4M1 = 0x00;
    P5M0 = 0x00;
      P5M1 = 0x00;

      UartInit();

      Delayms(5);
//    DISP = 1;// 打开显示直接接3.3v的时候,可以不要这句



   
    while(1)
    {
      // 主循环空闲,所有工作由中断处理
      // 可以在这里添加其他功能
Full_RGB_Display(hong);
      Delayms(300);                //整屏显示后,大概可以维持800ms,等待时间再长图像就失真了


Full_RGB_Display(huang);      //刷一次屏大概170ms
      Delayms(300);
Full_RGB_Display(lan);
      Delayms(300);

for(a=0;a<25;a++)
{
      display_3232 (10,10,shan);      
      display_3232 (10,10+4,xi);
      display_3232 (10,10+4+4,sheng);

      displayNum_1632(10,10+4+4+4,a);
      pic480272(huang,hei);
}










      
      LED=~LED;


    }
}

void UartIsr() interrupt 4      //串口1中断函数
{

//      unsigned char i=0;
    if (RI)      //接收
    {
      RI = 0;
      CheckCustomCmd(SBUF);      //检测串口为指定命令函数

    }
      
//    if (TI)
//    {
//      TI = 0;

//    }      
      
}现在有个问题:为啥22MHz左右的频率可以正常显示,30Mhz以上频率就花屏,感觉clk翻转是不是太快了,我在clk=1和clk=0中加入延时,还是不行。


955

国芯人工智能 发表于 2025-9-27 23:23:00

你这段代码和描述已经非常贴近底层驱动开发了,尤其是在使用国产AI8051芯片(可能是基于增强型8051内核的国产MCU)直接驱动RGB接口的480x272分辨率液晶屏,这种做法在资源受限的MCU平台上具有一定的挑战性。以下是对你的代码和开发思路的专业性分析与优化建议。

一、整体架构分析

你使用的是DE模式驱动RGB接口的TFT液晶屏,这是一种较为常见的驱动方式,适用于没有VSYNC/HSYNC信号的液晶屏,通过DE(Data Enable)信号来判断当前是否处于有效显示区域。

你定义了以下关键参数:
水平分辨率:480
垂直分辨率:272
水平总周期:HACTIVE + HBLANK = 480 + 45 = 525
垂直总周期:VACTIVE + VBLANK = 272 + 14 = 286
主频:22.1184 MHz
RGB接口为24位,使用32位模式兼容

你采用的是一种全屏缓存+像素级替换的方式进行字符显示,这在资源有限的8位MCU上是一种比较常见但效率较低的方式,适用于静态或低帧率显示。

二、关于RGB接口与8051的适配问题

你提到“8位对long的兼容不好”,这可能是指你在进行像素数据拼接时,使用的是unsigned long类型来表示RGB数据(例如RGB888),但在8位MCU上操作32位数据效率较低,容易造成性能瓶颈。

建议:
使用结构体或联合体来处理RGB数据,提高可读性和可移植性;
避免频繁的32位运算,可以将RGB颜色值预处理为数组或常量;
若使用的是RGB565格式(16位),可考虑将颜色数据压缩为16位,降低内存和带宽压力。

三、关于全屏缓存与字符显示的优化

你提到“建立整屏缓存,替换对应字节”的方式,这种方式虽然简单直接,但在8位MCU上会带来以下问题:

1. 内存占用大:480x272x3 = 391,680字节(RGB888),对于片内RAM有限的MCU来说,必须使用外部SRAM或显存;
2. 刷新效率低:每次刷新都要遍历整个缓存,效率低下;
3. 字体渲染方式原始:缺乏字体缓存、区域更新等机制。

建议优化方案:

1. 使用帧缓存压缩(如RGB565)
RGB565格式占用内存为480x272x2 = 261,120字节,节省约1/3内存;
可以通过宏定义或函数实现RGB888到RGB565的转换。

2. 实现字符绘制函数
你提到“不知道怎么写这个函数”,其实可以按如下方式实现一个简单的字符绘制函数:

```c
void DrawChar(int x, int y, char c, unsigned short color) {
    const unsigned char font = GetFont(c); // 获取字符点阵数据
    for (int row = 0; row < FONTHEIGHT; row++) {
      for (int col = 0; col < FONTWIDTH; col++) {
            if (font & (1

[本文内容由国芯人工智能辅助生成,仅供参考]

mtt661 发表于 2025-9-27 23:34:35

第一次传视频,为啥我传的视频这么大{:fadai:}
页: [1]
查看完整版本: 用AI8051,直接驱动480*272分辨率,RGB接口屏,