6.4需要实现的硬件适配代码
移植GUI所需适配的底层硬件函数及示例。
刷屏函数
_flush_cb(struct _gt_disp_drv_s * drv, gt_area_st * area, gt_color_t * color)
参数说明: _gt_disp_drv_s drv此参数不需用户适配
gt_area_st 结构体,area->x为绘图的起始x坐标
area->y为绘图的起始y坐标
area->w为绘图区域的宽度
area->h为绘图区域的高度
color为图片数据数组
例程中调用的为正点原子的区域填充代码,其余方式的实现函数只需要能够在指定区域绘制指定大小的图片均可。
void _flush_cb(struct _gt_disp_drv_s * drv, gt_area_st * area, gt_color_t * color){
LCD_Color_Fill(area->x,area->y,area->w+area->x-1,area->h+area->y-1,color);
}
正点原子该函数实现示例
//在指定区域内填充指定颜色块
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//color:要填充的颜色
void LCD_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color)
{
u16 height,width;
u16 i,j;
if(lcdltdc.pwidth!=0) //如果是RGB屏
{
LTDC_Color_Fill(sx,sy,ex,ey,color);
}else
{
width=ex-sx+1; //得到填充的宽度
height=ey-sy+1; //高度
for(i=0;i<height;i++)
{
LCD_SetCursor(sx,sy+i); //设置光标位置
LCD_WriteRAM_Prepare(); //开始写入GRAM
for(j=0;j<width;j++)LCD->LCD_RAM=color[i*width+j];//写入数据
}
}
}
如果是RGB屏使用DMA方式传输
//在指定区域内填充指定颜色块,DMA2D填充
//此函数仅支持u16,RGB565格式的颜色数组填充.
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
//注意:sx,ex,不能大于lcddev.width-1;sy,ey,不能大于lcddev.height-1!!!
//color:要填充的颜色数组首地址
void LTDC_Color_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color)
{
u32 psx,psy,pex,pey; //以LCD面板为基准的坐标系,不随横竖屏变化而变化
u32 timeout=0;
u16 offline;
u32 addr;
//坐标系转换
if(lcdltdc.dir) //横屏
{
psx=sx;psy=sy;
pex=ex;pey=ey;
}else //竖屏
{
psx=sy;psy=lcdltdc.pheight-ex-1;
pex=ey;pey=lcdltdc.pheight-sx-1;
}
offline=lcdltdc.pwidth-(pex-psx+1);
addr=((u32)ltdc_framebuf[lcdltdc.activelayer]+lcdltdc.pixsize*(lcdltdc.pwidth*psy+psx));
RCC->AHB1ENR|=1<<23; //使能DM2D时钟
DMA2D->CR&=~(1<<0); //先停止DMA2D
DMA2D->CR=0<<16; //存储器到存储器模式
DMA2D->FGPFCCR=LCD_PIXFORMAT; //设置颜色格式
DMA2D->FGOR=0; //前景层行偏移为0
DMA2D->OOR=offline; //设置行偏移
DMA2D->FGMAR=(u32)color; //源地址
DMA2D->OMAR=addr; //输出存储器地址
DMA2D->NLR=(pey-psy+1)|((pex-psx+1)<<16); //设定行数寄存器
DMA2D->CR|=1<<0; //启动DMA2D
while((DMA2D->ISR&(1<<1))==0) //等待传输完成
{
timeout++;
if(timeout>0X1FFFFF)break; //超时退出
}
DMA2D->IFCR|=1<<1; //清除传输完成标志
}
读取触摸函数
void read_cb(struct _gt_indev_drv_s * indev_drv, gt_indev_data_st * data)
参数说明: _gt_indev_drv_s indev_drv此参数不需用户适配
gt_indev_data_st结构体 data->state 有无输入事件发生
data->point.x 触摸点的x坐标
data->point.y 触摸点的y坐标
在此函数中,判断有无触摸事件发生,如果没有发生则返回,如果发生触摸函数则将触摸点的x坐标和y坐标传入data结构体的point.x和point.y,并将state设置为GT_INDEV_STATE_PRESSED 即可。其余的实现方式只需要测试触摸屏幕时,能否读取到正确的触摸状态和对应的x y坐标均可。
void read_cb(struct _gt_indev_drv_s * indev_drv, gt_indev_data_st * data){
if(!tp_dev.scan(1)) {
data->state = GT_INDEV_STATE_RELEASED; //如果没有触摸事件则返回
return ;
}
data->point.x = tp_dev.x[0];
data->point.y = tp_dev.y[0];
data->state = GT_INDEV_STATE_PRESSED;
}
主要需要实现scan函数,正点例程中为了区分不同的触摸设备设计了不同的scan函数。
此处选用其中一个作为示例
//扫描触摸屏(采用查询方式)
//mode:0,正常扫描.
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
u8 GT9147_Scan(u8 mode)
{
u8 buf[4];
u8 i=0;
u8 res=0;
u8 temp;
u8 tempsta;
static u8 t=0; //控制查询间隔,从而降低CPU占用率
t++;
if((t%10)==0||t<10)//空闲时,每进入10次CTP_Scan函数才检测1次,从而节省CPU使用率
{
GT9147_RD_Reg(GT_GSTID_REG,&mode,1); //读取触摸点的状态
if(mode&0X80&&((mode&0XF)<6))
{
temp=0;
GT9147_WR_Reg(GT_GSTID_REG,&temp,1);//清标志
}
if((mode&0XF)&&((mode&0XF)<6))
{
temp=0XFF<<(mode&0XF); //将点的个数转换为1的位数,匹配tp_dev.sta定义
tempsta=tp_dev.sta; //保存当前的tp_dev.sta值
tp_dev.sta=(~temp)|TP_PRES_DOWN|TP_CATH_PRES;
tp_dev.x[4]=tp_dev.x[0]; //保存触点0的数据
tp_dev.y[4]=tp_dev.y[0];
for(i=0;i<5;i++)
{
if(tp_dev.sta&(1<<i)) //触摸有效
{
GT9147_RD_Reg(GT9147_TPX_TBL[i],buf,4); //读取XY坐标值
if(lcddev.id==0X5510) //4.3寸800*480 MCU屏
{
if(tp_dev.touchtype&0X01)//横屏
{
tp_dev.y[i]=((u16)buf[1]<<8)+buf[0];
tp_dev.x[i]=800-(((u16)buf[3]<<8)+buf[2]);
}else
{
tp_dev.x[i]=((u16)buf[1]<<8)+buf[0];
tp_dev.y[i]=((u16)buf[3]<<8)+buf[2];
}
}else if(lcddev.id==0X4342) //4.3寸480*272 RGB屏
{
if(tp_dev.touchtype&0X01)//横屏
{
tp_dev.x[i]=(((u16)buf[1]<<8)+buf[0]);
tp_dev.y[i]=(((u16)buf[3]<<8)+buf[2]);
}else
{
tp_dev.y[i]=((u16)buf[1]<<8)+buf[0];
tp_dev.x[i]=272-(((u16)buf[3]<<8)+buf[2]);
}
}else if(lcddev.id==0X4384) //4.3寸800*480 RGB屏
{
if(tp_dev.touchtype&0X01)//横屏
{
tp_dev.x[i]=(((u16)buf[1]<<8)+buf[0]);
tp_dev.y[i]=(((u16)buf[3]<<8)+buf[2]);
}else
{
tp_dev.y[i]=((u16)buf[1]<<8)+buf[0];
tp_dev.x[i]=480-(((u16)buf[3]<<8)+buf[2]);
}
}
//printf("x[%d]:%d,y[%d]:%d\r\n",i,tp_dev.x[i],i,tp_dev.y[i]);
}
}
res=1;
if(tp_dev.x[0]>lcddev.width||tp_dev.y[0]>lcddev.height)//非法数据(坐标超出了)
{
if((mode&0XF)>1) //有其他点有数据,则复第二个触点的数据到第一个触点.
{
tp_dev.x[0]=tp_dev.x[1];
tp_dev.y[0]=tp_dev.y[1];
t=0; //触发一次,则会最少连续监测10次,从而提高命中率
}else //非法数据,则忽略此次数据(还原原来的)
{
tp_dev.x[0]=tp_dev.x[4];
tp_dev.y[0]=tp_dev.y[4];
mode=0X80;
tp_dev.sta=tempsta; //恢复tp_dev.sta
}
}else t=0; //触发一次,则会最少连续监测10次,从而提高命中率
}
}
if((mode&0X8F)==0X80)//无触摸点按下
{
if(tp_dev.sta&TP_PRES_DOWN) //之前是被按下的
{
tp_dev.sta&=~(1<<7); //标记按键松开
}else //之前就没有被按下
{
tp_dev.x[0]=0xffff;
tp_dev.y[0]=0xffff;
tp_dev.sta&=0XE0; //清除点有效标记
}
}
if(t>240)t=10;//重新从10开始计数
return res;
}
读取按键函数
void read_cb_btn(struct _gt_indev_drv_s * indev_drv, gt_indev_data_st * data)
此函数与读触摸函数类似,需要实现读取按键的函数。如果有按键被按下则将data->state 设置为 GT_INDEV_STATE_PRESSED,同时将btn_id设置为按键键值。
void read_cb_btn(struct _gt_indev_drv_s * indev_drv, gt_indev_data_st * data)
{
uint8_t status = 0;
status = KEY_Scan(1);
if( status ){
data->btn_id = status;
data->state = GT_INDEV_STATE_PRESSED;
}else{
data->state = GT_INDEV_STATE_RELEASED;
}
}
SPI读写函数
uint32_t spi_wr(uint8_t * data_write, uint32t len_write, uint8_t * data_read, uint32_t len_read)
参数说明:data_write 此指针存放着24位的地址值。*(data_write+1)为高八位地址
*(data_write+2)为中八位地址
*(data_write+3)为第八位地址
len_write 参数无需用户适配
data_read 为存放读取数据的指针
len_read 为读取数据的长度
uint32_t spi_wr(uint8_t * data_write, uint32_t len_write, uint8_t * data_read, uint32_t len_read)
{
unsigned long ReadAddr;
ReadAddr = *(data_write+1)<<16; //高八位地址
ReadAddr += *(data_write+2)<<8; //中八位地址
ReadAddr += *(data_write+3); //低八位地址
r_dat_bat(ReadAddr,len_read,data_read);
}
其余实现方式只需要能读取在指定的24位地址读取指定数据的长度并存入data_read中均可。
定时器配置
GUI需要一个1ms一次的心跳脉冲。故而可以通过定时中断来实现。
//定时器3中断服务程序
void TIM3_IRQHandler(void)
{
if(TIM3->SR&0X0001)//溢出中断
{
gt_tick_inc(1);
}
TIM3->SR&=~(1<<0);//清除中断标志位
}
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为48M
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
RCC->APB1ENR|=1<<1; //TIM3时钟使能
TIM3->ARR=arr; //设定计数器自动重装值
TIM3->PSC=psc; //预分频器
TIM3->DIER|=1<<0; //允许更新中断
TIM3->CR1|=0x01; //使能定时器3
MY_NVIC_Init(1,3,TIM3_IRQn,2); //抢占1,子优先级3,组2
}
在定时中断的终端服务函数中加入GUI的心跳脉冲。gt_tick_inc(1)此函数声明在gt_hal_tick.h头文件中。在初始化硬件的时候,将TIM3的定时中断定为1ms触发一次。
定时器的自动重装值和时钟预分频数需要根据各自定时器配置。
TIM3_Int_Init(10-1,9000-1); //1ms定时中断
程序中仅供参考。
SDRAM配置
如果MCU内部RAM无法满足GUI刷屏数组的定义,则需要将gt_port_disp.c中的刷屏数组定义到SRAM或者SDRAM中,地址需要根据项目对SDRAM的使用情况自由调整。
使用 __attribute__ 关键字修饰该数组。
其余配置,如果需要使用GUI的log打印功能,则需要用户自己实现printf函数,使其能在串口格式化打印内容。
-
- GT-HMI Designer用户手册
- 高通字库芯片开发资料
- GTDB-X7ESP AI开发板使用手册
- GTC-480480TFT40XP模块使用手册
- GTC-480272TFT43XP模块使用手册
- GTC-800480TFT50G模块使用手册
- GTC-800480TFT70GP模块使用手册
- GTC-1024600TFT101GP模块使用手册
- GT-GUI LCD 0.96寸液晶模组数据手册
- GT-GUI LCD 1.9寸液晶模组数据手册
- GT-GUI LCD 2.8寸液晶模组数据手册
- GT-GUI LCD 3.5寸液晶模组数据手册
- GT-GUI LCD 7.0寸液晶模组数据手册
- GUI-LCD开发板使用手册
- GT5GL128B标准GUI芯片规格书
- GT5GL64芯片产品规格书
高通字库交流群
GT-HMI交流群