今天,我们来搞下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上完全验证通过的。
|
共1人点赞
|
屏幕的驱动芯片对于spi速率有要求,不能太低。