先上效果
VID_20240629_191036
VID_20240701_195036
使用100mhz的SPI频率,在不使用DMA情况下,大约10帧左右,加上DMA后提高到接近40帧.
使用480*320*2大小的缓冲区,如果单纯刷屏可以减小到HAL库SPI发送函数最大长度.因为缓冲区过大所以分几次发送的.
CUBEMX生成代码
一共需要使用3个外设,定时器可以不用
硬件I2C+SPI+串口
首先使能RCC,SPI,USART,I2C
电容触摸
电容触摸芯片GT911,I2C频率最大400khz,STM32开到1000k也无所谓,没有使能DMA和中断,使用外部中断获取触摸信号,读寄存器
对配置好的I2C封装一下,这里沿用了以前的代码,因为GT911有16位寄存器,所以再写一个16位读写函数.
void i2c_write(uint8_t devaddr,uint8_t addr,uint8_t pdata,int len){
HAL_I2C_Mem_Write(&I2C,devaddr,addr,I2C_MEMADD_SIZE_8BIT,&pdata,len,1000);
}
uint8_t i2c_read(uint8_t devaddr,uint8_t addr)
{ uint8_t data;
HAL_I2C_Mem_Read(&I2C,devaddr,addr,I2C_MEMADD_SIZE_8BIT,&data,1,1000);
return data;
}
void i2c_write16(uint8_t devaddr,uint16_t addr,uint8_t pdata,int len){
HAL_I2C_Mem_Write(&I2C,devaddr,addr,I2C_MEMADD_SIZE_16BIT,&pdata,len,1000);
}
uint8_t i2c_read16(uint8_t devaddr,uint16_t addr)
{ uint8_t data;
HAL_I2C_Mem_Read(&I2C,devaddr,addr,I2C_MEMADD_SIZE_16BIT,&data,1,1000);
return data;
}
void i2c_read16n(uint8_t devaddr,uint16_t addr,uint8_t* data,uint8_t n)
{
HAL_I2C_Mem_Read(&I2C,devaddr,addr,I2C_MEMADD_SIZE_16BIT,data,n,1000);
}
void i2c_write16n(uint8_t devaddr,uint16_t addr,uint8_t* data,uint8_t n)
{
HAL_I2C_Mem_Write(&I2C,devaddr,addr,I2C_MEMADD_SIZE_16BIT,data,n,1000);
}
GT911初始化
GT911的寄存器表大概就用这几个就好了
#define USE_GT911
#define hi2cx GT911
//#define GT_WRITECFG //写配置表
#define GT_ADDR 0X28
#define GT_CTRL_REG 0X8040 //GT911控制寄存器
#define GT_CFGS_REG 0X8047 //GT911配置起始地址寄存器
#define GT_CHECK_REG 0X80FF //GT911校验和寄存器
#define GT_PID_REG 0X8140 //GT911产品ID寄存器
#define GT_GSTID_REG 0X814E //GT911当前检测到的触摸情况
GT911的初始化很简单,不用配置什么,上电就能读取数据,因为我没找到芯片id到底是多少所以没有读id,同时千万别写控制寄存器0x8040,写了就裂开,找厂家要配置表.这里选择了要不要写配置表
void GT911_INIT(){
HAL_Delay(100);
uint8_t data;
#ifdef GT_WRITECFG
i2c_write16(GT_ADDR,GT_CTRL_REG,2,1);
i2c_write16n(GT_ADDR,0x8047,(uint8_t *)datacfg,186);
i2c_write16(GT_ADDR,GT_CTRL_REG,0,1);
#endif
touch=0;
HAL_Delay(100);
i2c_write16(GT_ADDR,GT_GSTID_REG,0x00,1);
}
随后就是判断是否被触摸以及触摸点数.
通过电容触摸屏GT911、GT928、GT9147的使用_gt911 3.5寸配置文件-CSDN博客得知,当触摸发生时,寄存器0x814E 最高位变成1,同时低四位表示有几个点
例如10000011:触摸按下,有3个点按下,这里我用了两次判断,先判断高位在判断低四位是否不为0,因为触摸结束的时候没有及时清除814E寄存器,第8位还为1,会误入读取坐标,加一个判断能够在触摸结束马上得知.
通过上文得知,坐标寄存器都是挨着的,只用按顺序读就好了氛围高8位和低8位
坐标=高八位*255+低八位
读完以后存储到LIST里面,方便读取.
void GET_NUM(void){
#ifdef TOUCHOK
touch=0;
uint8_t data;
data=i2c_read16(GT_ADDR,GT_GSTID_REG);
if((data&0x80)!=0){
if((data&0X0f)!=0){
touch=data&0x0F;
read_point(LIST,touch);
}
i2c_write16(GT_ADDR,GT_GSTID_REG,0x00,1);
}
return ;
#endif
printf("TOUCH ERROR PLEASE TRY LATER\r\n");
}
void read_point(POINT LIST[],uint8_t NUM){
uint16_t x,y;
i2c_read16n(GT_ADDR,0x8150,datatemp,8*NUM);
for(uint8_t i=0;i<NUM;i++){
x=(datatemp[i*8]+(datatemp[i*8+1]<<8));
y=(datatemp[i*8+2]+(datatemp[i*8+3]<<8));
LIST[i].xPos=x;
LIST[i].yPos=y;
LIST[i].id=i+1;
printf("TOUCH %d X->%d Y->%d\r\n",i+1,x,y);
}
}
测试,数据也是比较准的
SPI初始化
200mhz 2分频 ,h7就是暴力,直接硬干都有10多帧,8位传输,仅发送,加入DMA,开启SPI DMA中断
其他配置都差不多不用改
封装一下发送函数
void SPI_ReadWriteByte(uint8_t TxData)
{
HAL_SPI_Transmit(&SPI,&TxData,1,10); //·µ»ØÊÕµ½µÄÊý¾Ý
}
void SPI_SetSpeed(uint32_t SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//ÅжÏÓÐЧÐÔ
__HAL_SPI_DISABLE(&SPI); //¹Ø±ÕSPI
SPI.Instance->CFG1&=~(0X7<<28); //λ30-28ÇåÁ㣬ÓÃÀ´ÉèÖò¨ÌØÂÊ
SPI.Instance->CFG1|=SPI_BaudRatePrescaler;//ÉèÖÃSPIËÙ¶È
__HAL_SPI_ENABLE(&SPI); //ʹÄÜSPI
}
引脚,这里使用cubemx的宏定义,方便移植,命名好就能直接使用
DMA
这里是最难绷的,前前后后试了好久,不知道为什么一直发送不成功,返回不是HAL_ERROR就是HAL_BUSY,直到这几天突然看到片文章STM32H7:解决DMA传输无效的问题-CSDN博客
牛莫得,H7默认的程序执行范围起始位置DMA是无法访问的,解决方法也很简单
将起始区域修改为可以的就好了
LCD代码
这里使用了安富莱的LCD代码
lcd.h注释不知道为什么乱码,内容挺好理解的
#ifndef __LCD_H
#define __LCD_H
#include "stdlib.h"
#include "GPIO.h"
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
extern uint16_t D_Color;
extern uint16_t B_Color;
/******************************************************************************
½Ó¿Ú¶¨Ò壬Çë¸ù¾Ý½ÓÏßÐ޸IJ¢ÐÞ¸ÄÏàÓ¦IO³õʼ»¯--Çý¶¯°å×ÓºÍIOÒª¶ÔÓ¦
// #define LCD_SDA //PD7 //Êý¾ÝÊäÈëÏß
// #define LCD_SCL //PB3 //ʱÖÓÏß
// #define LCD_CS //PB4 //Ƭѡ
// #define LCD_RESET //PG11 //Êý¾ÝÊä³ö/¸´Î»
// #define LCD_RS //PD6 //ÃüÁî/Êý¾ÝÇл»
// #define LCD_BLK //PG14 //±³¹â¿ØÖÆ
*******************************************************************************/
//Òº¾§¿ØÖÆ¿ÚÖÃ1²Ù×÷Óï¾äºê¶¨Òå
#define LCD_SDA_SET HAL_GPIO_WritePin(LCD_MOSI_GPIO_Port,LCD_MOSI_Pin,GPIO_PIN_SET) //PB15ÖÃ1 LCD_SDI£º PB15 //Êý¾ÝÊäÈëÏß
#define LCD_SCL_SET HAL_GPIO_WritePin(LCD_CLK_GPIO_Port,LCD_CLK_Pin,GPIO_PIN_SET) //PB13ÖÃ1 LCD_SCL£º PB13 //ʱÖÓÏß
#define LCD_CS_SET HAL_GPIO_WritePin(LCD_CS_GPIO_Port,LCD_CS_Pin,GPIO_PIN_SET) //PB12ÖÃ1 LCD_CS£º PB12 //Ƭѡ
#define LCD_RST_SET HAL_GPIO_WritePin(LCD_RESET_GPIO_Port,LCD_RESET_Pin,GPIO_PIN_SET) //PB14ÖÃ1 LCD_SDO £ºPB14 //Êý¾ÝÊä³ö/¸´Î»
#define LCD_RS_SET HAL_GPIO_WritePin(LCD_RS_GPIO_Port,LCD_RS_Pin,GPIO_PIN_SET) //PB1ÖÃ1 LCD_RS£º PB1 //ÃüÁî/Êý¾ÝÇл»
#define LCD_BLK_SET HAL_GPIO_WritePin(LCD_BL_GPIO_Port,LCD_BL_Pin,GPIO_PIN_SET) //PB0ÖÃ1 LCD_BLK £ºPB0 //±³¹â¿ØÖÆ
//Òº¾§¿ØÖÆ¿ÚÖÃ0²Ù×÷Óï¾äºê¶¨Òå
#define LCD_SDA_CLR HAL_GPIO_WritePin(LCD_MOSI_GPIO_Port,LCD_MOSI_Pin