前期已经实现了LCD液晶屏的显示,这次实现电阻触摸屏的使用。电阻触摸屏的原理再次不在赘述,可以去看原子教程的原理讲解,其中校准函数等都参考了原子的代码。校准完成之后需要将校准系数等保存到AT24C02中的前13个字节,上篇我们已经是现实了硬件I2C来将数据写入AT24C02并读取,我们可以直接拿来使用。
首先,先介绍一下HC32F4A0的硬件SPI。HC32F4A0共有6个硬件SPI,支持高速全双工通讯,数据格式有多种选择,并且可以设置3线或4线SPI,波特率可以根据实际调整。本篇使用到的是SPI6,4线全双工模式,数据格式为MSB先行,波特率根据实际情况调整为PLCK1/64,PCLK为120MHz。下面是波特率设置图。
本篇使用到的SPI6对应的管脚是SCK->PE4、MOSI->PE5、MISO->PE5、CS->TCA9539的P06、Pen_IRQ->TCA9539的P04。根据数据手册43页的表2-1引脚功能表可知,通讯复用功能组为FG2,再根据51页的表2-2 Fun32-63表(如下图)可知,SPI6_SCK、SPI6_MOSI、SPI6_MISO的复用号分别为Func46、Func47、Func48。
用到的电阻触摸屏是用XPT2046这个芯片,这是一个12位4通道的ADC芯片。下面是XPT2046电路图、通讯时序图。
通过发送相应的命令字,即可从xpt2046中读回数据,从时序图中可知,读回来的是一个12位的数据。在程序中的实现方式是读回两个8位数据,再将数据合并。以下是实现代码:
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] TP_Read_AD
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param CMD: [输入/出]
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2022-11-09
**************************************************************/
uint16_t TP_Read_AD( uint8_t CMD )
{
uint16_t Num = 0;
T_CS_LOW(); //选中触摸屏IC
xpt2046_spi_send_byte( CMD ); //发送命令字
Num = 0x0;
Num = xpt2046_spi_send_byte( 0x00 ) << 8;
Num |= xpt2046_spi_send_byte( 0x00 );
Num >>= 4;
T_CS_HIGH(); //释放片选
Num &= 0x0FFF;
return( Num );
}
上述代码中使用到的xpt2046_spi_send_byte()函数是驱动触摸屏的关键函数,用的是查询法读SPI,先等待发送寄存器为空,发送字节数据,查询接收寄存器,再返回数据,其代码如下:
uint16_t xpt2046_spi_send_byte( uint8_t byte )
{
/* 等待发送寄存器为空 */
while( RESET == SPI_GetStatus( T_SPIx, SPI_FLAG_TX_BUF_EMPTY ) );
/* 发送一个字节数据 */
SPI_WriteData( T_SPIx, byte );
/* 等待接收寄存器不为空 */
while( RESET == SPI_GetStatus( T_SPIx, SPI_FLAG_RX_BUF_FULL ) );
/* 返回收到的数据 */
return SPI_ReadData( T_SPIx );
}
了解了相关电路与关键代码,开始配置SPI,以下是SPI的配置,包括管脚配置和SPI配置,其中管脚配置需要注意,除了配置复用功能外,其他如管脚的上下拉、输入输出等都不要配置,否则SPI会读不到数据,卡在while( RESET == SPI_GetStatus( T_SPIx, SPI_FLAG_RX_BUF_FULL ) ),保持默认就好。其余参数配置都做了相关注释。
/**************************************************************
* @Name bsp_XPT2046_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
void bsp_XPT2046_Init( void )
{
bsp_XPT2046_GPIO_Init();
bsp_XPT2046_SPI_Init();
delay_ms( 100 );
TP_Read_XY( &tp_dev.x[0], &tp_dev.y[0] ); //第一次读取初始化
if( TP_Get_Adjdata() )
{
return; //已经校准
}
else //未校准?
{
//#ifdef SSD1963
// SSD_LCD_Clear( WHITE ); //清屏
//#else
// LCD_Clear( WHITE ); //清屏
//#endif
TP_Adjust(); //屏幕校准
}
TP_Get_Adjdata();
}
/**************************************************************
* @Name bsp_XPT2046_GPIO_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
static void bsp_XPT2046_GPIO_Init( void )
{
// stc_gpio_init_t GPIO_InitStruct = { 0 };
LL_PERIPH_WE( LL_PERIPH_GPIO );
/* Config T_SCK_PIN */
// GPIO_StructInit( &GPIO_InitStruct );
// GPIO_InitStruct.u16PinDrv = PIN_HIGH_DRV;
// GPIO_InitStruct.u16PinDir = PIN_DIR_OUT; /* --方向 输入或输出--- */
// GPIO_InitStruct.u16PullUp = PIN_PU_ON;
// GPIO_InitStruct.u16PinOutputType = PIN_OUT_TYPE_NMOS; /* --正常CMOS电平输出或NMOS开漏输出--- */
// GPIO_Init( T_SCK_PORT, T_SCK_PIN, &GPIO_InitStruct );
/* Config T_MISO_PIN */
// GPIO_StructInit( &GPIO_InitStruct );
// GPIO_InitStruct.u16PinDir = PIN_DIR_IN; /* --方向 输入或输出--- */
// GPIO_InitStruct.u16PullUp = PIN_PU_ON;
// GPIO_Init( T_MISO_PORT, T_MISO_PIN, &GPIO_InitStruct );
/* Config T_MOSI_PIN */
// GPIO_StructInit( &GPIO_InitStruct );
// GPIO_InitStruct.u16PinDrv = PIN_HIGH_DRV;
// GPIO_InitStruct.u16PinDir = PIN_DIR_OUT; /* --方向 输入或输出--- */
// GPIO_InitStruct.u16PullUp = PIN_PU_ON;
// GPIO_InitStruct.u16PinOutputType = PIN_OUT_TYPE_NMOS; /* --正常CMOS电平输出或NMOS开漏输出--- */
// GPIO_Init( T_MOSI_PORT, T_MOSI_PIN, &GPIO_InitStruct );
/* Config T_CS_PIN */
TCA9539_ConfigPin( T_CS_PORT, T_CS_PIN, TCA9539_DIR_OUT );
/* Config T_PEN_IRQ_PIN */
TCA9539_ConfigPin( T_PEN_IRQ_PORT, T_PEN_IRQ_PIN, TCA9539_DIR_IN );
/* ---复用设置--- */
GPIO_SetFunc( T_SCK_PORT, T_SCK_PIN, GPIO_FUNC_46 );
GPIO_SetFunc( T_MOSI_PORT, T_MOSI_PIN, GPIO_FUNC_47 );
GPIO_SetFunc( T_MISO_PORT, T_MISO_PIN, GPIO_FUNC_48 );
LL_PERIPH_WP( LL_PERIPH_GPIO );
}
/**************************************************************
* @Name bsp_XPT2046_SPI_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
static void bsp_XPT2046_SPI_Init( void )
{
stc_spi_init_t spi_init_struct;
stc_spi_delay_t stcSpiDelayCfg;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO );
T_CS_HIGH(); /* ---拉高片选--- */
SPI_StructInit( &spi_init_struct );
SPI_DelayStructInit( &stcSpiDelayCfg );
FCG_Fcg1PeriphClockCmd( FCG1_PERIPH_SPI6, ENABLE ); /* ---失能SPI6时钟--- */
SPI_DeInit( T_SPIx ); /* ---反初始化SPI6--- */
spi_init_struct.u32BaudRatePrescaler = SPI_BR_CLK_DIV64; /* ---波特率 PCLK1 / 64--- */
spi_init_struct.u32DataBits = SPI_DATA_SIZE_8BIT; /* ---8位数据长度--- */
spi_init_struct.u32FirstBit = SPI_FIRST_MSB; /* ---高位先行--- */
spi_init_struct.u32FrameLevel = SPI_1_FRAME; /* ---帧数--- */
spi_init_struct.u32MasterSlave = SPI_MASTER; /* ---主机模式--- */
spi_init_struct.u32ModeFaultDetect = SPI_MD_FAULT_DETECT_DISABLE;
spi_init_struct.u32Parity = SPI_PARITY_INVD; /* ---禁用校验--- */
spi_init_struct.u32SpiMode = SPI_MD_0; /* ---模式0-- */
spi_init_struct.u32SuspendMode = SPI_COM_SUSP_FUNC_OFF;
spi_init_struct.u32TransMode = SPI_FULL_DUPLEX; /* ---全双工--- */
spi_init_struct.u32WireMode = SPI_4_WIRE; /* ---四线制--- */
SPI_Init( T_SPIx, &spi_init_struct );
stcSpiDelayCfg.u32IntervalDelay = SPI_INTERVAL_TIME_8SCK; /* ---数据延时--- */
stcSpiDelayCfg.u32ReleaseDelay = SPI_RELEASE_TIME_8SCK;
stcSpiDelayCfg.u32SetupDelay = SPI_SETUP_TIME_1SCK;
SPI_DelayTimeConfig( T_SPIx, &stcSpiDelayCfg );
SPI_Cmd( T_SPIx, ENABLE ); /* ---失能SPI6--- */
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO );
}
以下是读电阻触摸屏AD值、校准等相关代码,参考了原子的。
/**************************************************************
* @Name TP_Read_AD
* @brief
* @param CMD: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
uint16_t TP_Read_AD( uint8_t CMD )
{
uint16_t Num = 0;
T_CS_LOW(); //选中触摸屏IC
xpt2046_spi_send_byte( CMD ); //发送命令字
Num = 0x0;
Num = xpt2046_spi_send_byte( 0x00 ) << 8;
Num |= xpt2046_spi_send_byte( 0x00 );
Num >>= 4;
T_CS_HIGH(); //释放片选
Num &= 0x0FFF;
return( Num );
}
/**************************************************************
* @Name TP_Read_XOY
* @brief 读取一个坐标值(x或者y)
* @param xy: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
#define READ_TIMES 5 //读取次数
#define LOST_VAL 2 //丢弃值
uint16_t TP_Read_XOY( uint8_t xy )
{
uint16_t i, j;
uint16_t buf[READ_TIMES];
uint16_t sum = 0;
uint16_t temp;
for( i = 0; i < READ_TIMES; i++ )
buf[i] = TP_Read_AD( xy );
for( i = 0; i < READ_TIMES - 1; i++ ) //排序
{
for( j = i + 1; j < READ_TIMES; j++ )
{
if( buf[i] > buf[j] ) //升序排列
{
temp = buf[i];
buf[i] = buf[j];
buf[j] = temp;
}
}
}
sum = 0;
for( i = LOST_VAL; i < READ_TIMES - LOST_VAL; i++ )sum += buf[i];
temp = sum / ( READ_TIMES - 2 * LOST_VAL );
return temp;
}
/**************************************************************
* @Name TP_Read_XY
* @brief 读取x,y坐标
* @param x: [输入/出]
** y: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
uint8_t TP_Read_XY( uint16_t *x, uint16_t *y )
{
uint16_t xtemp, ytemp;
xtemp = TP_Read_XOY( CMD_RDX );
ytemp = TP_Read_XOY( CMD_RDY );
if( xtemp < 100 || ytemp < 100 )
return 0;//读数失败
*x = xtemp;
*y = ytemp;
return 1;//读数成功
}
/**************************************************************
* @Name TP_Read_XY2
* @brief
* @param x: [输入/出]
** y: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
#define ERR_RANGE 50 //误差范围
uint8_t TP_Read_XY2( uint16_t *x, uint16_t *y )
{
uint16_t x1, y1;
uint16_t x2, y2;
uint8_t flag;
flag = TP_Read_XY( &x1, &y1 );
if( flag == 0 )
return( 0 );
flag = TP_Read_XY( &x2, &y2 );
if( flag == 0 )
return( 0 );
if( ( ( x2 <= x1 && x1 < x2 + ERR_RANGE ) || ( x1 <= x2 && x2 < x1 + ERR_RANGE ) ) //前后两次采样在+-50内
&& ( ( y2 <= y1 && y1 < y2 + ERR_RANGE ) || ( y1 <= y2 && y2 < y1 + ERR_RANGE ) ) )
{
*x = ( x1 + x2 ) / 2;
*y = ( y1 + y2 ) / 2;
return 1;
} else return 0;
}
//与LCD部分有关的函数
//画一个触摸点
//用来校准用的
//x,y:坐标
//color:颜色
void TP_Drow_Touch_Point( uint16_t x, uint16_t y, uint16_t color )
{
#ifdef SSD1963
LCD_DrawLine( x - 12, y, x + 13, y, color ); //横线
LCD_DrawLine( x, y - 12, x, y + 13, color ); //竖线
SSD_LCD_Fast_DrawPoint( x + 1, y + 1, color );
SSD_LCD_Fast_DrawPoint( x - 1, y + 1, color );
SSD_LCD_Fast_DrawPoint( x + 1, y - 1, color );
SSD_LCD_Fast_DrawPoint( x - 1, y - 1, color );
LCD_Draw_Circle( x, y, 6, color ); //画中心圈
#else
LCD_DrawLine( x - 12, y, x + 13, y, color ); //横线
LCD_DrawLine( x, y - 12, x, y + 13, color ); //竖线
LCD_DrawPoint( x + 1, y + 1, color );
LCD_DrawPoint( x - 1, y + 1, color );
LCD_DrawPoint( x + 1, y - 1, color );
LCD_DrawPoint( x - 1, y - 1, color );
LCD_Draw_Circle( x, y, 6, color ); //画中心圈
#endif
}
//画一个大点(2*2的点)
//x,y:坐标
//color:颜色
void TP_Draw_Big_Point( uint16_t x, uint16_t y, uint16_t color )
{
#ifdef SSD1963
SSD_LCD_Fast_DrawPoint( x, y, color ); //中心点
SSD_LCD_Fast_DrawPoint( x + 1, y, color );
SSD_LCD_Fast_DrawPoint( x, y + 1, color );
SSD_LCD_Fast_DrawPoint( x + 1, y + 1, color );
#else
LCD_DrawPoint( x, y, color ); //中心点
LCD_DrawPoint( x + 1, y, color );
LCD_DrawPoint( x, y + 1, color );
LCD_DrawPoint( x + 1, y + 1, color );
#endif
}
/**************************************************************
* @Name TP_Scan
* @brief
* @param tp: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
uint8_t TP_Scan( uint8_t tp )
{
uint8_t KeyStat = 0x01;
TCA9539_ReadPin( T_PEN_IRQ_PORT, T_PEN_IRQ_PIN, &KeyStat );
if( KeyStat == RESET ) //有按键按下
{
if( tp )
{
TP_Read_XY2( &tp_dev.x[0], &tp_dev.y[0] ); //读取物理坐标
}
else if( TP_Read_XY2( &tp_dev.x[0], &tp_dev.y[0] ) ) //读取屏幕坐标
{
tp_dev.x[0] = tp_dev.xfac * tp_dev.x[0] + tp_dev.xoff; //将结果转换为屏幕坐标
tp_dev.y[0] = tp_dev.yfac * tp_dev.y[0] + tp_dev.yoff;
}
if( ( tp_dev.sta & TP_PRES_DOWN ) == 0 ) //之前没有被按下
{
tp_dev.sta = TP_PRES_DOWN | TP_CATH_PRES; //按键按下
tp_dev.x[4] = tp_dev.x[0]; //记录第一次按下时的坐标
tp_dev.y[4] = tp_dev.y[0];
}
}
else
{
if( tp_dev.sta & TP_PRES_DOWN ) //之前是被按下的
{
tp_dev.sta &= ~( 1 << 7 ); //标记按键松开
}
else//之前就没有被按下
{
tp_dev.x[4] = 0;
tp_dev.y[4] = 0;
tp_dev.x[0] = 0xffff;
tp_dev.y[0] = 0xffff;
}
}
return tp_dev.sta & TP_PRES_DOWN; //返回当前的触屏状态
}
#define SAVE_ADDR_BASE 0
//保存校准参数
void TP_Save_Adjdata( void )
{
uint8_t Reg;
uint8_t tempbuf[8];
int32_t temp;
//保存校正结果!
temp = tp_dev.xfac * 100000000; //保存x校正因素
tempbuf[0] = ( temp & 0xFF000000 ) >> 24;
tempbuf[1] = ( temp & 0x00FF0000 ) >> 16;
tempbuf[2] = ( temp & 0x0000FF00 ) >> 8;
tempbuf[3] = ( temp & 0x000000FF );
Reg = SAVE_ADDR_BASE;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 4U );
delay_ms( 20 );
temp = tp_dev.yfac * 100000000; //保存y校正因素
tempbuf[0] = ( temp & 0xFF000000 ) >> 24;
tempbuf[1] = ( temp & 0x00FF0000 ) >> 16;
tempbuf[2] = ( temp & 0x0000FF00 ) >> 8;
tempbuf[3] = ( temp & 0x000000FF );
Reg = SAVE_ADDR_BASE + 4;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 4U );
delay_ms( 20 );
//保存x偏移量
tempbuf[0] = ( tp_dev.xoff & 0xFF00 ) >> 8;
tempbuf[1] = tp_dev.xoff & 0x00FF;
Reg = SAVE_ADDR_BASE + 8;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 2U );
delay_ms( 20 );
//保存y偏移量
tempbuf[0] = ( tp_dev.yoff & 0xFF00 ) >> 8;
tempbuf[1] = tp_dev.yoff & 0x00FF;
Reg = SAVE_ADDR_BASE + 10;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 2U );
delay_ms( 20 );
//保存触屏类型
tempbuf[0] = tp_dev.touchtype;
Reg = SAVE_ADDR_BASE + 12;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );
delay_ms( 20 );
tempbuf[0] = 0X0A; //标记校准过了
Reg = SAVE_ADDR_BASE + 13;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );
}
//得到保存在EEPROM里面的校准值
//返回值:1,成功获取数据
// 0,获取失败,要重新校准
uint8_t TP_Get_Adjdata( void )
{
uint8_t Reg, tempbuf[4];
int32_t tempfac;
Reg = SAVE_ADDR_BASE + 13;
// tempbuf[0] = 0x00;
// AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );//读取标记字,看是否校准过!
if( tempbuf[0] == 0x0A ) //触摸屏已经校准过了
{
Reg = SAVE_ADDR_BASE;
//tempfac = FM24CL16_ReadLenByte( SAVE_ADDR_BASE, 4 );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 4U );
tempfac = tempbuf[0] << 24 | tempbuf[1] << 16 | tempbuf[2] << 8 | tempbuf[3];
tp_dev.xfac = ( float )tempfac / 100000000; //得到x校准参数
Reg = SAVE_ADDR_BASE + 4;
//tempfac = FM24CL16_ReadLenByte( SAVE_ADDR_BASE + 4, 4 );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 4U );
tempfac = tempbuf[0] << 24 | tempbuf[1] << 16 | tempbuf[2] << 8 | tempbuf[3];
tp_dev.yfac = ( float )tempfac / 100000000; //得到y校准参数
//得到x偏移量
//tp_dev.xoff = FM24CL16_ReadLenByte( SAVE_ADDR_BASE + 8, 2 );
Reg = SAVE_ADDR_BASE + 8;
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 2U );
tempfac = tempbuf[0] << 8 | tempbuf[1];
tp_dev.xoff = tempfac;
//得到y偏移量
//tp_dev.yoff = FM24CL16_ReadLenByte( SAVE_ADDR_BASE + 10, 2 );
Reg = SAVE_ADDR_BASE + 10;
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 2U );
tempfac = tempbuf[0] << 8 | tempbuf[1];
tp_dev.yoff = tempfac;
Reg = SAVE_ADDR_BASE + 12;
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );
tempfac = tempbuf[0];
tp_dev.touchtype = tempfac; //读取触屏类型标记
if( tp_dev.touchtype ) //X,Y方向与屏幕相反
{
CMD_RDX = 0X90;
CMD_RDY = 0XD0;
} else //X,Y方向与屏幕相同
{
CMD_RDX = 0XD0;
CMD_RDY = 0X90;
}
return 1;
}
return 0;
}
//提示校准结果(各个参数)
void TP_Adj_Info_Show( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t fac )
{
LCD_ShowString( 40, 160, lcddev.width, lcddev.height, 16, ( uint8_t * )"x1:", RED, GBLUE );
LCD_ShowString( 40 + 80, 160, lcddev.width, lcddev.height, 16, ( uint8_t * )"y1:", RED, GBLUE );
LCD_ShowString( 40, 180, lcddev.width, lcddev.height, 16, ( uint8_t * )"x2:", RED, GBLUE );
LCD_ShowString( 40 + 80, 180, lcddev.width, lcddev.height, 16, ( uint8_t * )"y2:", RED, GBLUE );
LCD_ShowString( 40, 200, lcddev.width, lcddev.height, 16, ( uint8_t * )"x3:", RED, GBLUE );
LCD_ShowString( 40 + 80, 200, lcddev.width, lcddev.height, 16, ( uint8_t * )"y3:", RED, GBLUE );
LCD_ShowString( 40, 220, lcddev.width, lcddev.height, 16, ( uint8_t * )"x4:", RED, GBLUE );
LCD_ShowString( 40 + 80, 220, lcddev.width, lcddev.height, 16, ( uint8_t * )"y4:", RED, GBLUE );
LCD_ShowString( 40, 240, lcddev.width, lcddev.height, 16, ( uint8_t * )"fac is:", RED, GBLUE );
LCD_ShowNum( 40 + 24, 160, x0, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24 + 80, 160, y0, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24, 180, x1, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24 + 80, 180, y1, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24, 200, x2, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24 + 80, 200, y2, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24, 220, x3, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24 + 80, 220, y3, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 56, 240, fac, 3, 16, RED, GBLUE ); //显示数值,该数值必须在95~105范围之内.
}
//触摸屏校准代码
//得到四个校准参数
uint8_t* const TP_REMIND_MSG_TBL = ( uint8_t * )"Please use the stylus click the cross on the screen.The cross will always move until the screen adjustment is completed.";
void TP_Adjust( void )
{
uint16_t pos_temp[4][2];//坐标缓存值
uint8_t cnt = 0;
uint16_t d1, d2;
uint32_t tem1, tem2;
double fac;
uint16_t outtime = 0;
cnt = 0;
#ifdef SSD1963
SSD_LCD_Clear( WHITE ); //清屏
#else
LCD_Clear( WHITE );
#endif
#ifdef SSD1963
SSD_LCD_ShowString( 40, 40, 160, 100, 16, ( uint8_t* )TP_REMIND_MSG_TBL, RED, GBLUE ); //显示提示信息
#else
LCD_ShowString( 40, 40, 160, 100, 16, ( uint8_t* )TP_REMIND_MSG_TBL, RED, GBLUE ); //显示提示信息
#endif
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
tp_dev.sta = 0; //消除触发信号
tp_dev.xfac = 0; //xfac用来标记是否校准过,所以校准之前必须清掉!以免错误
while( 1 ) //如果连续10秒钟没有按下,则自动退出
{
tp_dev.scan( 1 ); //扫描物理坐标
if( ( tp_dev.sta & 0xc0 ) == TP_CATH_PRES ) //按键按下了一次(此时按键松开了.)
{
outtime = 0;
tp_dev.sta &= ~( 1 << 6 ); //标记按键已经被处理过了.
pos_temp[cnt][0] = tp_dev.x[0];
pos_temp[cnt][1] = tp_dev.y[0];
cnt++;
switch( cnt )
{
case 1:
TP_Drow_Touch_Point( 20, 20, WHITE ); //清除点1
TP_Drow_Touch_Point( lcddev.width - 20, 20, RED ); //画点2
break;
case 2:
TP_Drow_Touch_Point( lcddev.width - 20, 20, WHITE ); //清除点2
TP_Drow_Touch_Point( 20, lcddev.height - 20, RED ); //画点3
break;
case 3:
TP_Drow_Touch_Point( 20, lcddev.height - 20, WHITE ); //清除点3
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, RED ); //画点4
break;
case 4: //全部四个点已经得到
//对边相等
tem1 = abs( pos_temp[0][0] - pos_temp[1][0] ); //x1-x2
tem2 = abs( pos_temp[0][1] - pos_temp[1][1] ); //y1-y2
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt( tem1 + tem2 ); //得到1,2的距离
tem1 = abs( pos_temp[2][0] - pos_temp[3][0] ); //x3-x4
tem2 = abs( pos_temp[2][1] - pos_temp[3][1] ); //y3-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt( tem1 + tem2 ); //得到3,4的距离
fac = ( float )d1 / d2;
if( fac < 0.95 || fac > 1.05 || d1 == 0 || d2 == 0 ) //不合格
{
cnt = 0;
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, WHITE ); //清除点4
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
TP_Adj_Info_Show( pos_temp[0][0], pos_temp[0][1], pos_temp[1][0], pos_temp[1][1], pos_temp[2][0], pos_temp[2][1], pos_temp[3][0], pos_temp[3][1], fac * 100 ); //显示数据
continue;
}
tem1 = abs( pos_temp[0][0] - pos_temp[2][0] ); //x1-x3
tem2 = abs( pos_temp[0][1] - pos_temp[2][1] ); //y1-y3
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt( tem1 + tem2 ); //得到1,3的距离
tem1 = abs( pos_temp[1][0] - pos_temp[3][0] ); //x2-x4
tem2 = abs( pos_temp[1][1] - pos_temp[3][1] ); //y2-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt( tem1 + tem2 ); //得到2,4的距离
fac = ( float )d1 / d2;
if( fac < 0.95 || fac > 1.05 ) //不合格
{
cnt = 0;
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, WHITE ); //清除点4
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
TP_Adj_Info_Show( pos_temp[0][0], pos_temp[0][1], pos_temp[1][0], pos_temp[1][1], pos_temp[2][0], pos_temp[2][1], pos_temp[3][0], pos_temp[3][1], fac * 100 ); //显示数据
continue;
}//正确了
//对角线相等
tem1 = abs( pos_temp[1][0] - pos_temp[2][0] ); //x1-x3
tem2 = abs( pos_temp[1][1] - pos_temp[2][1] ); //y1-y3
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt( tem1 + tem2 ); //得到1,4的距离
tem1 = abs( pos_temp[0][0] - pos_temp[3][0] ); //x2-x4
tem2 = abs( pos_temp[0][1] - pos_temp[3][1] ); //y2-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt( tem1 + tem2 ); //得到2,3的距离
fac = ( float )d1 / d2;
if( fac < 0.95 || fac > 1.05 ) //不合格
{
cnt = 0;
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, WHITE ); //清除点4
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
TP_Adj_Info_Show( pos_temp[0][0], pos_temp[0][1], pos_temp[1][0], pos_temp[1][1], pos_temp[2][0], pos_temp[2][1], pos_temp[3][0], pos_temp[3][1], fac * 100 ); //显示数据
continue;
}//正确了
//计算结果
tp_dev.xfac = ( float )( lcddev.width - 40 ) / ( pos_temp[1][0] - pos_temp[0][0] ); //得到xfac
tp_dev.xoff = ( lcddev.width - tp_dev.xfac * ( pos_temp[1][0] + pos_temp[0][0] ) ) / 2; //得到xoff
tp_dev.yfac = ( float )( lcddev.height - 40 ) / ( pos_temp[2][1] - pos_temp[0][1] ); //得到yfac
tp_dev.yoff = ( lcddev.height - tp_dev.yfac * ( pos_temp[2][1] + pos_temp[0][1] ) ) / 2; //得到yoff
if( fabs( tp_dev.xfac ) > 2 || fabs( tp_dev.yfac ) > 2 ) //触屏和预设的相反了.
{
cnt = 0;
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, WHITE ); //清除点4
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
LCD_ShowString( 40, 26, lcddev.width, lcddev.height, 16, ( uint8_t * )"TP Need readjust!", RED, GBLUE );
tp_dev.touchtype = !tp_dev.touchtype; //修改触屏类型.
if( tp_dev.touchtype ) //X,Y方向与屏幕相反
{
CMD_RDX = 0X90;
CMD_RDY = 0XD0;
} else //X,Y方向与屏幕相同
{
CMD_RDX = 0XD0;
CMD_RDY = 0X90;
}
continue;
}
LCD_Clear( WHITE ); //清屏
LCD_ShowString( 35, 110, lcddev.width, lcddev.height, 16, ( uint8_t * )"Touch Screen Adjust OK!", GREEN, GBLUE ); //校正完成
delay_ms( 1000 );
TP_Save_Adjdata();
LCD_Clear( WHITE ); //清屏
return;//校正完成
}
}
delay_ms( 10 );
outtime++;
if( outtime > 1000 )
{
TP_Get_Adjdata();
break;
}
}
}
以下是main.c中的代码:
/**
************************************* Copyright ******************************
*
* (C) Copyright 2022,Zachary ,HPU, China.
* All Rights Reserved
*
* FileName : main.c
* Version : v1.0
* Author : Zachary
* Date : 2023-02-13
* Description: This is a template project of HC32F4A0
---Zachary,in Zhejiang Hanpu.
******************************************************************************
*/
/* Includes --------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "bsp_Gpio.h"
#include "bsp_TCA9539.h"
#include "bsp_NT35510.h"
#include "bsp_XPT2046.h"
#include "bsp_AT24Cxx.h"
/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
stc_clock_freq_t sysclk;
uint8_t Reg = 0x20;
uint8_t ReadBuf[8];
uint8_t WriteBuf[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
char DispBuf1[50], DispBuf2[50];
/* --- Functions ---------------------------------------------------------*/
static void SysInit( void );
static void bsp_SetSysClk( void );
static void LCD_Test( void );
/*------------------------------------------------------------------------*/
void Load_Drow_Dialog( void )
{
LCD_Clear( WHITE );//清屏
LCD_ShowString( lcddev.width - 24, 0, lcddev.width, lcddev.height, 16, ( uint8_t * )"RST", BLACK, WHITE ); //显示清屏区域
}
//电阻触摸屏测试函数
void rtp_test( void )
{
Load_Drow_Dialog();
while( 1 )
{
tp_dev.scan( 0 );
if( tp_dev.sta & TP_PRES_DOWN ) //触摸屏被按下
{
if( tp_dev.x[0] < lcddev.width && tp_dev.y[0] < lcddev.height )
{
if( tp_dev.x[0] > ( lcddev.width - 24 ) && tp_dev.y[0] < 16 )
Load_Drow_Dialog();//清除
else
TP_Draw_Big_Point( tp_dev.x[0], tp_dev.y[0], RED ); //画图
}
}
else
delay_ms( 1 ); //没有按键按下的时候
}
}
/**************************************************************
* @Name main
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-22
**************************************************************/
int32_t main( void )
{
SysInit();
sprintf( DispBuf1, "WirteBuf:%d,%d,%d,%d,%d,%d,%d,%d", WriteBuf[0], WriteBuf[1], \
WriteBuf[2], WriteBuf[3], WriteBuf[4], WriteBuf[5], WriteBuf[6], WriteBuf[7] );
LCD_ShowString( 10, 500, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf1, WHITE, BLACK );
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 0x01, WriteBuf, 0x08 );
delay_ms( 10 );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 0x01, ReadBuf, 0x08 );
sprintf( DispBuf2, "ReadBuf:%d,%d,%d,%d,%d,%d,%d,%d", ReadBuf[0], ReadBuf[1], \
ReadBuf[2], ReadBuf[3], ReadBuf[4], ReadBuf[5], ReadBuf[6], ReadBuf[7] );
LCD_ShowString( 10, 550, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf2, WHITE, BLACK );
delay_ms( 1000 );
rtp_test();
while( 1 )
{
LED10_TOGGLE();
LED11_TOGGLE();
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_SET );
delay_ms( 500 );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_RESET );
delay_ms( 500 );
}
}
/**************************************************************
* @Name SysInit
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void SysInit( void )
{
bsp_SetSysClk();
CLK_GetClockFreq( &sysclk ); /* ---检查时钟是否正确--- */
delay_init( 240 ); /* ---系统滴答定时器初始化--- */
bsp_Gpio_Init(); /* ---初始化Gpio--- */
bsp_TCA9539_Init();
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_DIR_OUT );
bsp_NT35510_Init();
LCD_Test();
bsp_XPT2046_Init();
}
/**************************************************************
* @Name LCD_Test
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-04
**************************************************************/
static void LCD_Test( void )
{
LCD_Clear( RED );
delay_ms( 500 );
LCD_Clear( GREEN );
delay_ms( 500 );
LCD_Clear( BLACK );
delay_ms( 500 );
LCD_Clear( BLUE );
delay_ms( 500 );
LCD_Clear( YELLOW );
delay_ms( 500 );
LCD_Clear( MAGENTA );
delay_ms( 500 );
LCD_Draw_Circle( 200, 200, 100, BLACK );
LCD_ShowString( 300, 300, lcddev.width, lcddev.height, 24, ( uint8_t * )"hello world", BLACK, MAGENTA );
}
/**************************************************************
* @Name SetSysClk
* @brief 参考自官方工程
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void bsp_SetSysClk( void )
{
stc_clock_xtal_init_t stcXtalInit;
stc_clock_pll_init_t stcPLLHInit;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU | \
LL_PERIPH_EFM | LL_PERIPH_SRAM ); /* ---解保护相关外设--- */
/* PCLK0, HCLK Max 240MHz */
/* PCLK1, PCLK4 Max 120MHz */
/* PCLK2, PCLK3 Max 60MHz */
/* EX BUS Max 120MHz */
CLK_SetClockDiv( CLK_BUS_CLK_ALL, \
( CLK_PCLK0_DIV1 | CLK_PCLK1_DIV2 | CLK_PCLK2_DIV4 | \
CLK_PCLK3_DIV4 | CLK_PCLK4_DIV2 | CLK_EXCLK_DIV2 | \
CLK_HCLK_DIV1 ) ); /* ---外设总线分频--- */
CLK_XtalStructInit( &stcXtalInit );
/* Config Xtal and enable Xtal */
stcXtalInit.u8Mode = CLK_XTAL_MD_OSC; /* ---选择外部晶振--- */
stcXtalInit.u8Drv = CLK_XTAL_DRV_ULOW; /* ---晶振驱动能力 有源晶振驱动能力强--- */
stcXtalInit.u8State = CLK_XTAL_ON;
stcXtalInit.u8StableTime = CLK_XTAL_STB_2MS; /* ---晶振稳定等待周期--- */
CLK_XtalInit( &stcXtalInit );
CLK_PLLStructInit( &stcPLLHInit ); /* ---锁相环寄存器初始化--- */
/* VCO = (8/1)*120 = 960MHz*/
stcPLLHInit.u8PLLState = CLK_PLL_ON;
stcPLLHInit.PLLCFGR = 0UL;
stcPLLHInit.PLLCFGR_f.PLLM = 1UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLN = 120UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLP = 4UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLQ = 4UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLR = 4UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLSRC = CLK_PLL_SRC_XTAL;
CLK_PLLInit( &stcPLLHInit );
/* Highspeed SRAM set to 0 Read/Write wait cycle */
SRAM_SetWaitCycle( SRAM_SRAMH, SRAM_WAIT_CYCLE0, SRAM_WAIT_CYCLE0 );
/* SRAM1_2_3_4_backup set to 1 Read/Write wait cycle */
SRAM_SetWaitCycle( ( SRAM_SRAM123 | SRAM_SRAM4 | SRAM_SRAMB ), SRAM_WAIT_CYCLE1, SRAM_WAIT_CYCLE1 );
/* 0-wait @ 40MHz */
EFM_SetWaitCycle( EFM_WAIT_CYCLE5 );
/* 4 cycles for 200 ~ 250MHz */
GPIO_SetReadWaitCycle( GPIO_RD_WAIT4 );
CLK_SetSysClockSrc( CLK_SYSCLK_SRC_PLL );
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU | \
LL_PERIPH_EFM | LL_PERIPH_SRAM ); /* ---保护相关外设--- */
}
以下是视频验证:
b285d44851a6a40271447aef5ef9f492
以下是代码附件:
|