打印
[N32G43x]

【N32G43XCL-STB_V1.0】+SPI DMA驱动LCD及疑问

[复制链接]
1302|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
今天,我们来搞下SPI DMA来驱动240X240的ST7789的一个小LCD~LCD电路:


我们设定硬件连接引脚:

//              GND   电源地
//              VCC   3.3v电源
//              SCL   PA5(SCLK)
//              SDA   PA7(MOSI)
//              RES   PA1
//              DC    PA2
//              BLK   PA3

其中仅用到SPI1的SCLK 及MOSI ,即仅发送模式。
下面配置SPI:
void SPI1_Init(void)
{
        GPIO_InitType GPIO_InitStructure;
        SPI_InitType SPI_InitStructure;


  RCC_ConfigPclk2(RCC_HCLK_DIV2);
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_SPI1, ENABLE);//使能SPI1
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA|RCC_APB2_PERIPH_AFIO, ENABLE);//使能GPIOA

  
  GPIO_InitStruct(&GPIO_InitStructure);
  
        GPIO_InitStructure.Pin =GPIO_PIN_5|GPIO_PIN_7;
        GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽输出
  GPIO_InitStructure.GPIO_Alternate = GPIO_AF0_SPI1;
        GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure); //初始化GPIOA
        GPIO_SetBits(GPIOA,GPIO_PIN_5|GPIO_PIN_7);

        SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;//只发送模式
        SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;//设置SPI工作模式:主机模式
        SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;//设置SPI数据大小:8位帧结构
        SPI_InitStructure.CLKPOL        = SPI_CLKPOL_HIGH;//串行同步时钟空闲时SCLK位高电平
        SPI_InitStructure.CLKPHA        = SPI_CLKPHA_SECOND_EDGE;//串行同步时钟空第二个时钟沿捕获
        SPI_InitStructure.NSS           = SPI_NSS_SOFT;//NSS信号由软件管理
        SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_2;//波特率预分频值:波特率预分频值为2
        SPI_InitStructure.FirstBit      = SPI_FB_MSB;//数据传输高位先行
        SPI_InitStructure.CRCPoly = 7;//CRC值计算的多项式
        SPI_Init(SPI1,&SPI_InitStructure);//初始化SPI
        SPI_Enable(SPI1, ENABLE);//使能SPI
}
参看产品手册SPI1挂在APB2总线  最高54M,也是蛋疼的,这个设计~具体还没想明白,一旦波特率定义小了,SPI输出就不正常,这里用最大的2分频~
下面进行DMA配置,这了我们配置2种模式:
1、正常的数据地址递增,外设地址不变   这种模式发送大量数据刷屏
2、正常数据地址固定,外设地址固定      这种情况适合单一颜色刷屏
DMA_InitType DMA_InitStructure;

u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度          单次最大65535   
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
void MYDMA_Config(DMA_ChannelType* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
        RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA, ENABLE);        //使能DMA传输
       
  DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
        DMA1_MEM_LEN=cndtr;
        DMA_InitStructure.PeriphAddr      = cpar;  //DMA外设ADC基地址
        DMA_InitStructure.MemAddr         = cmar;  //DMA内存基地址
        DMA_InitStructure.Direction       = DMA_DIR_PERIPH_DST;  //数据传输方向,从内存读取发送到外设
        DMA_InitStructure.BufSize         = cndtr;  //DMA通道的DMA缓存的大小
        DMA_InitStructure.PeriphInc       = DMA_PERIPH_INC_DISABLE;  //外设地址寄存器不变
        DMA_InitStructure.DMA_MemoryInc   = DMA_MEM_INC_ENABLE;  //内存地址寄存器递增
        DMA_InitStructure.PeriphDataSize  = DMA_PERIPH_DATA_SIZE_BYTE;  //数据宽度为8位
        DMA_InitStructure.MemDataSize     = DMA_MemoryDataSize_Byte; //数据宽度为8位
        DMA_InitStructure.CircularMode    = DMA_MODE_NORMAL;  //工作在正常缓存模式
        DMA_InitStructure.Priority        = DMA_PRIORITY_MEDIUM; //DMA通道 x拥有中优先级
        DMA_InitStructure.Mem2Mem         = DMA_M2M_DISABLE;  //DMA通道x没有设置为内存到内存传输
        DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
                 
}

void MYDMA_Config1(DMA_ChannelType* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
        RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA, ENABLE);        //使能DMA传输
       
  DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
        DMA1_MEM_LEN=cndtr;
        DMA_InitStructure.PeriphAddr      = cpar;  //DMA外设ADC基地址
        DMA_InitStructure.MemAddr         = cmar;  //DMA内存基地址
        DMA_InitStructure.Direction       = DMA_DIR_PERIPH_DST;  //数据传输方向,从内存读取发送到外设
        DMA_InitStructure.BufSize         = cndtr;  //DMA通道的DMA缓存的大小
        DMA_InitStructure.PeriphInc       = DMA_PERIPH_INC_DISABLE;  //外设地址寄存器不变
        DMA_InitStructure.DMA_MemoryInc   = DMA_MEM_INC_DISABLE;  //内存地址寄存器不变
        DMA_InitStructure.PeriphDataSize  = DMA_PERIPH_DATA_SIZE_HALFWORD;  //数据宽度为16位
        DMA_InitStructure.MemDataSize     = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
        DMA_InitStructure.CircularMode    = DMA_MODE_NORMAL;  //工作在正常缓存模式
        DMA_InitStructure.Priority        = DMA_PRIORITY_MEDIUM; //DMA通道 x拥有中优先级
        DMA_InitStructure.Mem2Mem         = DMA_M2M_DISABLE;  //DMA通道x没有设置为内存到内存传输
        DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
}

//开启一次DMA传输
void MYDMA_Enable(DMA_ChannelType*DMA_CHx)
{
        DMA_EnableChannel(DMA_CHx, DISABLE );
  /*从新设置发送数量*/
        DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);
  /* Enable DMA ChannelX */
  DMA_RequestRemap(DMA_REMAP_SPI1_TX,DMA, DMA_CHx, ENABLE);
        DMA_EnableChannel(DMA_CHx, ENABLE);
}       
下面我们就对屏幕进行操作了:
屏幕基本操作相关:
#include "lcd_init.h"
#include "delay.h"
#include "spi.h"

void LCD_GPIO_Init(void)
{
        GPIO_InitType  GPIO_InitStructure;
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);         //使能A端口时钟
  
  GPIO_InitStruct(&GPIO_InitStructure);
  
        GPIO_InitStructure.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;         
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
        GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
        GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);          //初始化GPIOA
        GPIO_SetBits(GPIOA,GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
}

void delay(int t)
{
        while(t--);
}
/******************************************************************************
      函数说明:LCD串行数据写入函数
      入口数据:dat  要写入的串行数据
      返回值:  无
******************************************************************************/
void LCD_Writ_Bus(u8 dat)
{       
  while(SPI_I2S_GetStatus(SPI1, SPI_I2S_TE_FLAG) == RESET);//检查接收标志位
        SPI_I2S_TransmitData(SPI1,dat);
        delay(1);
}

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


/******************************************************************************
      函数说明:LCD写入数据
      入口数据:dat 写入的数据
      返回值:  无
******************************************************************************/
void LCD_WR_DATA(u16 dat)
{
        LCD_Writ_Bus(dat>>8);
        LCD_Writ_Bus(dat);
}


/******************************************************************************
      函数说明:LCD写入命令
      入口数据:dat 写入的命令
      返回值:  无
******************************************************************************/
void LCD_WR_REG(u8 dat)
{
        LCD_DC_Clr();//写命令
        LCD_Writ_Bus(dat);
        LCD_DC_Set();//写数据
}


/******************************************************************************
      函数说明:设置起始和结束地址
      入口数据:x1,x2 设置列的起始和结束地址
                y1,y2 设置行的起始和结束地址
      返回值:  无
******************************************************************************/
void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2)
{
        if(USE_HORIZONTAL==0)
        {
                LCD_WR_REG(0x2a);//列地址设置
                LCD_WR_DATA(x1);
                LCD_WR_DATA(x2);
                LCD_WR_REG(0x2b);//行地址设置
                LCD_WR_DATA(y1);
                LCD_WR_DATA(y2);
                LCD_WR_REG(0x2c);//储存器写
        }
        else if(USE_HORIZONTAL==1)
        {
                LCD_WR_REG(0x2a);//列地址设置
                LCD_WR_DATA(x1);
                LCD_WR_DATA(x2);
                LCD_WR_REG(0x2b);//行地址设置
                LCD_WR_DATA(y1+80);
                LCD_WR_DATA(y2+80);
                LCD_WR_REG(0x2c);//储存器写
        }
        else if(USE_HORIZONTAL==2)
        {
                LCD_WR_REG(0x2a);//列地址设置
                LCD_WR_DATA(x1);
                LCD_WR_DATA(x2);
                LCD_WR_REG(0x2b);//行地址设置
                LCD_WR_DATA(y1);
                LCD_WR_DATA(y2);
                LCD_WR_REG(0x2c);//储存器写
        }
        else
        {
                LCD_WR_REG(0x2a);//列地址设置
                LCD_WR_DATA(x1+80);
                LCD_WR_DATA(x2+80);
                LCD_WR_REG(0x2b);//行地址设置
                LCD_WR_DATA(y1);
                LCD_WR_DATA(y2);
                LCD_WR_REG(0x2c);//储存器写
        }
}

void LCD_Init(void)
{
        SPI1_Init();
        LCD_GPIO_Init();//初始化GPIO
       
        LCD_RES_Clr();//复位
        delay_ms(100);
        LCD_RES_Set();
        delay_ms(100);
       
        LCD_BLK_Set();//打开背光
  delay_ms(100);
       
        //************* Start Initial Sequence **********//
        LCD_WR_REG(0x11); //Sleep out
        delay_ms(120);              //Delay 120ms
        //************* Start Initial Sequence **********//
        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(0x0C);
        LCD_WR_DATA8(0x0C);
        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(0x19);

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

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

        LCD_WR_REG(0xC3);
        LCD_WR_DATA8(0x12);   

        LCD_WR_REG(0xC4);
        LCD_WR_DATA8(0x20);  

        LCD_WR_REG(0xC6);
        LCD_WR_DATA8(0x0F);   

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

        LCD_WR_REG(0xE0);
        LCD_WR_DATA8(0xD0);
        LCD_WR_DATA8(0x04);
        LCD_WR_DATA8(0x0D);
        LCD_WR_DATA8(0x11);
        LCD_WR_DATA8(0x13);
        LCD_WR_DATA8(0x2B);
        LCD_WR_DATA8(0x3F);
        LCD_WR_DATA8(0x54);
        LCD_WR_DATA8(0x4C);
        LCD_WR_DATA8(0x18);
        LCD_WR_DATA8(0x0D);
        LCD_WR_DATA8(0x0B);
        LCD_WR_DATA8(0x1F);
        LCD_WR_DATA8(0x23);

        LCD_WR_REG(0xE1);
        LCD_WR_DATA8(0xD0);
        LCD_WR_DATA8(0x04);
        LCD_WR_DATA8(0x0C);
        LCD_WR_DATA8(0x11);
        LCD_WR_DATA8(0x13);
        LCD_WR_DATA8(0x2C);
        LCD_WR_DATA8(0x3F);
        LCD_WR_DATA8(0x44);
        LCD_WR_DATA8(0x51);
        LCD_WR_DATA8(0x2F);
        LCD_WR_DATA8(0x1F);
        LCD_WR_DATA8(0x1F);
        LCD_WR_DATA8(0x20);
        LCD_WR_DATA8(0x23);
        LCD_WR_REG(0x21);

        LCD_WR_REG(0x29);
}
屏幕显示字符图片等相关,这里用到DMA发送大量数据:
#include "lcd.h"
#include "lcd_init.h"
#include "lcdfont.h"
#include "delay.h"
#include "spi.h"
#include "dma.h"

/******************************************************************************
      函数说明:在指定区域填充颜色
      入口数据:xsta,ysta   起始坐标
                xend,yend   终止坐标
                                                                color       要填充的颜色
      返回值:  无
******************************************************************************/
void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)
{         
        u16 color1[1],t=1;
        u32 num,num1;
        color1[0]=color;
        num=(xend-xsta)*(yend-ysta);
        LCD_Address_Set(xsta,ysta,xend-1,yend-1);//设置显示范围
        //SPI1->CTRL1|=1<<11;//设置SPI16位传输模式
  SPI_Enable(SPI1, DISABLE);//使能SPI
  SPI_ConfigDataLen(SPI1, SPI_DATA_SIZE_16BITS);
        SPI_Enable(SPI1, ENABLE);//使能SPI
        while(t)
        {
                if(num>65534)
                {
                        num-=65534;
                        num1=65534;
                }
                else
                {
                        t=0;
                        num1=num;
                }
                MYDMA_Config1(DMA_CH3,(u32)&SPI1->DAT,(u32)color1,num1);
    //MYDMA_Config1(DMA_CH3,(u32)SPI_MASTER_DR_Base,(u32)color1,num1);
                SPI_I2S_EnableDma(SPI1,SPI_I2S_DMA_TX,ENABLE);
    //SPI_Enable(SPI1, ENABLE);
                MYDMA_Enable(DMA_CH3);
                while(1)
                {
                        if(DMA_GetFlagStatus(DMA_FLAG_TC3,DMA)!=RESET)//等待通道4传输完成
                        {
                                DMA_ClearFlag(DMA_FLAG_TC3,DMA);//清除通道3传输完成标志
                                break;
                        }
                }
  }

//        while(1)
//        {
////DMA单次最大65535
//    num1=240*240;//57600
//                MYDMA_Config1(DMA_CH3,(u32)&SPI1->DAT,(u32)color1,num1);
//    //MYDMA_Config1(DMA_CH3,(u32)SPI_MASTER_DR_Base,(u32)color1,num1);
//                SPI_I2S_EnableDma(SPI1,SPI_I2S_DMA_TX,ENABLE);
//    //SPI_Enable(SPI1, ENABLE);
//                MYDMA_Enable(DMA_CH3);
//                while(1)
//                {
//                        if(DMA_GetFlagStatus(DMA_FLAG_TC3,DMA)!=RESET)//等待通道3传输完成
//                        {
//                                DMA_ClearFlag(DMA_FLAG_TC3,DMA);//清除通道3传输完成标志
//                                break;
//                        }
//                }
//  }
//        SPI1->CTRL1=~SPI1->CTRL1;
//        SPI1->CTRL1|=1<<11;
//        SPI1->CTRL1=~SPI1->CTRL1;//设置SPI8位传输模式
//  SPI1->CTRL1&=~(1<<11);
  SPI_Enable(SPI1, DISABLE);//使能SPI
  SPI_ConfigDataLen(SPI1, SPI_DATA_SIZE_8BITS);
        SPI_Enable(SPI1, ENABLE);//使能SPI
}

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


/******************************************************************************
      函数说明:画线
      入口数据:x1,y1   起始坐标
                x2,y2   终止坐标
                color   线的颜色
      返回值:  无
******************************************************************************/
void LCD_DrawLine(u16 x1,u16 y1,u16 x2,u16 y2,u16 color)
{
        u16 t;
        int xerr=0,yerr=0,delta_x,delta_y,distance;
        int incx,incy,uRow,uCol;
        delta_x=x2-x1; //计算坐标增量
        delta_y=y2-y1;
        uRow=x1;//画线起点坐标
        uCol=y1;
        if(delta_x>0)incx=1; //设置单步方向
        else if (delta_x==0)incx=0;//垂直线
        else {incx=-1;delta_x=-delta_x;}
        if(delta_y>0)incy=1;
        else if (delta_y==0)incy=0;//水平线
        else {incy=-1;delta_y=-delta_x;}
        if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
        else distance=delta_y;
        for(t=0;t<distance+1;t++)
        {
                LCD_DrawPoint(uRow,uCol,color);//画点
                xerr+=delta_x;
                yerr+=delta_y;
                if(xerr>distance)
                {
                        xerr-=distance;
                        uRow+=incx;
                }
                if(yerr>distance)
                {
                        yerr-=distance;
                        uCol+=incy;
                }
        }
}


/******************************************************************************
      函数说明:画矩形
      入口数据:x1,y1   起始坐标
                x2,y2   终止坐标
                color   矩形的颜色
      返回值:  无
******************************************************************************/
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2,u16 color)
{
        LCD_DrawLine(x1,y1,x2,y1,color);
        LCD_DrawLine(x1,y1,x1,y2,color);
        LCD_DrawLine(x1,y2,x2,y2,color);
        LCD_DrawLine(x2,y1,x2,y2,color);
}


/******************************************************************************
      函数说明:画圆
      入口数据:x0,y0   圆心坐标
                r       半径
                color   圆的颜色
      返回值:  无
******************************************************************************/
void Draw_Circle(u16 x0,u16 y0,u8 r,u16 color)
{
        int a,b;
        a=0;b=r;          
        while(a<=b)
        {
                LCD_DrawPoint(x0-b,y0-a,color);             //3           
                LCD_DrawPoint(x0+b,y0-a,color);             //0           
                LCD_DrawPoint(x0-a,y0+b,color);             //1               
                LCD_DrawPoint(x0-a,y0-b,color);             //2            
                LCD_DrawPoint(x0+b,y0+a,color);             //4               
                LCD_DrawPoint(x0+a,y0-b,color);             //5
                LCD_DrawPoint(x0+a,y0+b,color);             //6
                LCD_DrawPoint(x0-b,y0+a,color);             //7
                a++;
                if((a*a+b*b)>(r*r))//判断要画的点是否过远
                {
                        b--;
                }
        }
}

/******************************************************************************
      函数说明:显示汉字串
      入口数据: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==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;
        }
}

/******************************************************************************
      函数说明:显示单个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;
        u16 k;
        u16 HZnum;//汉字数目
        u16 TypefaceNum;//一个字符所占字节大小
        u16 x0=x;
        TypefaceNum=sizey/8*sizey;//此算法只适用于字宽等于字高,且字高是8的倍数的字,
                                  //也建议用户使用这样大小的字,否则显示容易出问题!
        HZnum=sizeof(tfont16)/sizeof(typFNT_GB16);        //统计汉字数目
        for(k=0;k<HZnum;k++)
        {
                if ((tfont16[k].Index[0]==*(s))&&(tfont16[k].Index[1]==*(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[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
                                                else LCD_WR_DATA(bc);
                                        }
                                        else//叠加方式
                                        {
                                                if(tfont16[k].Msk[i]&(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;
        u16 k;
        u16 HZnum;//汉字数目
        u16 TypefaceNum;//一个字符所占字节大小
        u16 x0=x;
        TypefaceNum=sizey/8*sizey;//此算法只适用于字宽等于字高,且字高是8的倍数的字,
                                  //也建议用户使用这样大小的字,否则显示容易出问题!
        HZnum=sizeof(tfont24)/sizeof(typFNT_GB24);        //统计汉字数目
        for(k=0;k<HZnum;k++)
        {
                if ((tfont24[k].Index[0]==*(s))&&(tfont24[k].Index[1]==*(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[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
                                                else LCD_WR_DATA(bc);
                                        }
                                        else//叠加方式
                                        {
                                                if(tfont24[k].Msk[i]&(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;
        u16 k;
        u16 HZnum;//汉字数目
        u16 TypefaceNum;//一个字符所占字节大小
        u16 x0=x;
        TypefaceNum=sizey/8*sizey;//此算法只适用于字宽等于字高,且字高是8的倍数的字,
                                  //也建议用户使用这样大小的字,否则显示容易出问题!
        HZnum=sizeof(tfont32)/sizeof(typFNT_GB32);        //统计汉字数目
        for(k=0;k<HZnum;k++)
        {
                if ((tfont32[k].Index[0]==*(s))&&(tfont32[k].Index[1]==*(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[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
                                                else LCD_WR_DATA(bc);
                                        }
                                        else//叠加方式
                                        {
                                                if(tfont32[k].Msk[i]&(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;
        u16 i,TypefaceNum;//一个字符所占字节大小
        u16 x0=x;
        sizex=sizey/2;
        TypefaceNum=sizex/8*sizey;
        num=num-' ';    //得到偏移后的值
        LCD_Address_Set(x,y,x+sizex-1,y+sizey-1);  //设置光标位置
        for(i=0;i<TypefaceNum;i++)
        {
                if(sizey==16)temp=ascii_1608[num][i];                       //调用8x16字体
                else if(sizey==32)temp=ascii_3216[num][i];                 //调用16x32字体
                else return;
                for(t=0;t<8;t++)
                {
                        if(!mode)//非叠加模式
                        {
                                if(temp&(0x01<<t))LCD_WR_DATA(fc);
                                else LCD_WR_DATA(bc);
                        }
                        else//叠加模式
                        {
                                if(temp&(0x01<<t))LCD_DrawPoint(x,y,fc);//画一个点
                                x++;
                                if((x-x0)==sizex)
                                {
                                        x=x0;
                                        y++;
                                        break;
                                }
                        }
                }
        }                      
}


/******************************************************************************
      函数说明:显示字符串
      入口数据: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,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,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,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,temp+48,fc,bc,sizey,0);
        }
}


/******************************************************************************
      函数说明:显示图片
      入口数据:x,y起点坐标
                length 图片长度
                width  图片宽度
                pic[]  图片数组   
      返回值:  无
******************************************************************************/
void LCD_ShowPicture(u16 x,u16 y,u16 length,u16 width,const u8 pic[])
{
        u8 t=1;
        u32 num=length*width*2,num1;
        LCD_Address_Set(x,y,x+length-1,y+width-1);
        while(t)
        {
          if(num>65534)
                {
                        num-=65534;
                        num1=65534;
                }
                else
                {
                        t=0;
                        num1=num;
                }
                MYDMA_Config(DMA_CH3,(u32)&SPI1->DAT,(u32)pic,num1);
    //MYDMA_Config(DMA_CH3,(u32)SPI_MASTER_DR_Base,(u32)pic,num1);
                SPI_I2S_EnableDma(SPI1,SPI_I2S_DMA_TX,ENABLE);
    //SPI_Enable(SPI1, ENABLE);
                MYDMA_Enable(DMA_CH3);
                while(1)
                {
                        if(DMA_GetFlagStatus(DMA_FLAG_TC3,DMA)!=RESET)//等待通道3传输完成
                        {
                                DMA_ClearFlag(DMA_FLAG_TC3,DMA);//清除通道3传输完成标志
                                break;
                        }
                }
                pic+=65534;
        }
}


其中LCD_Fill 为单一颜色刷屏,用到DMA配置1,其余均用DMA默认配置。
我们在MAIN函数里面调用:
int main(void)
{
  u8 t,i,j;
        u8 len;       
        u16 times=0;  
  float m=0;
  /*SystemInit() function has been called by startup file startup_n32g43x.s*/
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
        delay_init(108);                //延时初始化
        DBG_UART_Init(115200);        //串口初始化波特率为115200
        LED_Init();                                  //初始化与LED连接的硬件接口  
  
  LCD_Init();//LCD初始化
        LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
  LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
  LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
//  while(1){}
  while(1)
        {
//                LCD_ShowPicture(0,0,240,240,gImage_2);
//                delay_ms(1000);
                LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
                LCD_ShowChinese(0,0,"国民技术论坛",RED,WHITE,32,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,m,4,RED,WHITE,16);
                m+=0.11;
                for(j=0;j<3;j++)
                {
                        for(i=0;i<6;i++)
                        {
                                LCD_ShowPicture(40*i,120+j*40,40,40,gImage_1);
                        }
                }
                delay_ms(5000);
                LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
//    delay_ms(200);
//    LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
        }
显示如下:

总体而言,是实现了过程,但是对于SPI速度不能调慢,慢了就不正常,我也是没弄明白~还有就是LCD_FILL这个函数莫名的奇怪。
必须调用2次,才正常。看了代码,也没看成那边有问题~这个代码可是在STM32上完全验证通过的。

使用特权

评论回复
评论
shenyebuwu 2024-9-18 10:18 回复TA
屏幕的驱动芯片对于spi速率有要求,不能太低。 
沙发
kyzhd| | 2022-1-21 14:13 | 只看该作者
可以咨询一下国民的技术。

使用特权

评论回复
板凳
沧桑小草| | 2022-1-24 11:30 | 只看该作者
可以将源工程也附上,让大家看看找找原因

使用特权

评论回复
地板
mutable| | 2022-2-8 17:19 | 只看该作者
看注释,还以为是I2C,原来是SPI

使用特权

评论回复
5
RAYINGPX| | 2024-5-31 17:08 | 只看该作者
利害!顶

使用特权

评论回复
6
结合国际经验| | 2024-8-31 20:11 | 只看该作者
开发设计和调试

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

111

主题

627

帖子

2

粉丝