stm32 触摸屏 XPT2046

引脚功能描述
1

控制字的控制位命令
1

控制字节各位描述
1

单端模式输入配置
1

差分模式输入配置
1

时序
前8个时钟用来通过DIN引脚输入控制字节,接着的12个时钟周期将完成真正的模数转换,剩下的3个多时钟周期将用来完成被转换器忽略的最后字节(DOUT置低)

1

举例
1
2
3

#define TOUCH_READ_TIMES    40 //读取次数
#define TOUCH_ERR_RANGE 20 //误差范围

#define TOUCH_X_CMD 0xD0 //读取X轴
#define TOUCH_Y_CMD 0x90 //读取Y轴
#define TOUCH_Continue_Read 0xFF
#define TOUCH_X_MAX 4000 //X最大值
#define TOUCH_X_MIN 100 //X最小值
#define TOUCH_Y_MAX 4000 //Y最大值
#define TOUCH_Y_MIN 100 //X最小值

#define LCD_CALx_MIN (10) //校准点最小值X
#define LCD_CALx_MAX (tftlcd_data.width - LCD_CALx_MIN) //校准点最大值X
#define LCD_CALy_MIN (10) //校准点最小值Y
#define LCD_CALy_MAX (tftlcd_data.height - LCD_CALy_MIN) //校准点最大值Y

#define LCD_CAL_X   (LCD_CALx_MAX - LCD_CALx_MIN) //方框的宽度
#define LCD_CAL_Y   (LCD_CALy_MAX - LCD_CALy_MIN) //方框的高度

#define TOUCH_CAL_OK        'Y' //校准OK标志
#define TOUCH_CAL_ADDR  200  //校准参数在at24c02的保存地址

typedef struct
{
    u8 flag; 
    short xoffset;
    short yoffset; 
    float xFactor;
    float yFactor;
} calibrate_t;

calibrate_t calibrate = {0};

u16 touchX;
u16 touchY;

void TOUCH_init()
{
    GPIO_InitTypeDef gpioa = 
    {
        GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7,
        GPIO_Speed_50MHz,
        GPIO_Mode_AF_PP
    };

    GPIO_InitTypeDef gpiod6 = 
    {
        GPIO_Pin_6,
        GPIO_Speed_50MHz,
        GPIO_Mode_Out_PP
    };
    GPIO_InitTypeDef gpiod7 = 
    {
        GPIO_Pin_7,
        GPIO_Speed_50MHz,
        GPIO_Mode_IPU
    };

    SPI_InitTypeDef spi = 
    {
        SPI_Direction_2Lines_FullDuplex,
        SPI_Mode_Master, //0x0104
        SPI_DataSize_8b,
        SPI_CPOL_High,
        SPI_CPHA_2Edge,
        SPI_NSS_Soft,
        SPI_BaudRatePrescaler_256,
        SPI_FirstBit_MSB,
        7
    };

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD, ENABLE);

    GPIO_Init(GPIOA, &gpioa);
    GPIO_Init(GPIOD, &gpiod6);
    GPIO_Init(GPIOD, &gpiod7);

    SPI_Init(SPI1, &spi); //初始化SPI

    SPI_Cmd(SPI1, ENABLE); //使能SPI
}

u16 TOUCH_read(u16 cmd)
{
    u8 i = 0, j = 0;
    u16 tmp;
    u16 value[TOUCH_READ_TIMES] = {0};
    u32 total_value = 0;

    SPI1_SetSpeed(SPI_BaudRatePrescaler_32); //设置SPI速度

    for(i = 0; i < TOUCH_READ_TIMES; i++) //读取次数
    {
        TOUCH_CS = 0;

        SPI1_read_write(cmd);

        value[i] = SPI1_read_write(TOUCH_Continue_Read) << 8; //详见时序
        value[i] |= SPI1_read_write(TOUCH_Continue_Read);
        value[i] >>= 3;

        TOUCH_CS = 1;
    }

    for (i = 0; i < TOUCH_READ_TIMES; i++) //排序
    {
        for (j = i + 1; j < TOUCH_READ_TIMES; j++)
        {
            if (value[i] < value[j])
            {
                tmp = value[i];
                value[i] = value[j];
                value[j] = tmp;
            }
        }
    }

    for (i = 1; i < TOUCH_READ_TIMES - 1; i++) //去掉一个最大值,一个最小值
    {
        total_value += value[i];
    }

    total_value /= (TOUCH_READ_TIMES - 2); //求平均值

    return total_value;
}

u8 TOUCH_readXY(u16 *x, u16 *y)
{
    u16 valueX1, valueY1, valueX2, valueY2;

    valueX1 = TOUCH_read(TOUCH_X_CMD); //读取触摸值
    valueY1 = TOUCH_read(TOUCH_Y_CMD);
    valueX2 = TOUCH_read(TOUCH_X_CMD);
    valueY2 = TOUCH_read(TOUCH_Y_CMD);

    *x = valueX1 > valueX2 ? (valueX1 - valueX2) : (valueX2 - valueX1); 
    *y = valueY1 > valueY2 ? (valueY1 - valueY2) : (valueY2 - valueY1);
    if((*x > TOUCH_ERR_RANGE) || (*y > TOUCH_ERR_RANGE)) //判断容错范围
    {
        return 0xFF;
    }

    *x = (valueX1 + valueX2) / 2;
    *y = (valueY1 + valueY2) / 2;

    if((*x < TOUCH_X_MIN || *x > TOUCH_X_MAX) || //判断边界范围
        (*y < TOUCH_Y_MIN || *y > TOUCH_Y_MAX))
    {
        return 0xFF;
    }

    return 0;
}

void TOUCH_start_calibrate(u16 x, u16 y, u16 *valueX,u16 *valueY) //开始校准
{
    u8 i = 0;

    LCD_Clear(BACK_COLOR); //清屏
    LCD_DrowSign(x, y, BACK_COLOR); //画十字

    while(1)
    {
        if(TOUCH_readXY(valueX, valueY) != 0xFF)
        {
            i++;
            if(i > 10)
            {
                LCD_DrowSign(x, y, BACK_COLOR);
                break;
            }
        }
    }
}

void TOUCH_calibrate()
{
    u16 px[2], py[2], valueX[4], valueY[4];
    float xFactor = 0, yFactor = 0;

    TOUCH_start_calibrate(LCD_CALx_MIN, LCD_CALy_MIN, &valueX[0], &valueY[0]); //第一次校准
    delay_ms(500);

    TOUCH_start_calibrate(LCD_CALx_MIN, LCD_CALy_MAX, &valueX[1], &valueY[1]);
    delay_ms(500);

    TOUCH_start_calibrate(LCD_CALx_MAX, LCD_CALy_MIN, &valueX[2], &valueY[2]);
    delay_ms(500);

    TOUCH_start_calibrate(LCD_CALx_MAX, LCD_CALy_MAX, &valueX[3], &valueY[3]);
    delay_ms(500);

    //整合成对角的两点
    px[0] = (valueX[0] + valueX[1]) / 2;
    py[0] = (valueY[0] + valueY[2]) / 2;
    px[1] = (valueX[3] + valueX[2]) / 2;
    py[1] = (valueY[3] + valueY[1]) / 2;

    //求出比例因子
    xFactor = (float)LCD_CAL_X / (px[1] - px[0]);
    yFactor = (float)LCD_CAL_Y / (py[1] - py[0]); 

    //求出偏移量
    calibrate.xoffset = (short)LCD_CALx_MAX - ((float)px[1] * xFactor);
    calibrate.yoffset = (short)LCD_CALy_MAX - ((float)py[1] * yFactor);

    calibrate.xFactor = xFactor ;
    calibrate.yFactor = yFactor ;

    printf("xoffset %d\n", calibrate.xoffset);
    printf("yoffset %d\n", calibrate.yoffset);
    printf("xFactor %f\n", calibrate.xFactor);
    printf("yFactor %f\n", calibrate.yFactor);

    //保存校准数据到at24c02
    calibrate.flag = TOUCH_CAL_OK;
    at24c02Write_buf((u8*)&calibrate, TOUCH_CAL_ADDR, sizeof(calibrate));   
}

u8 TOUCH_scan() //查看是否触摸
{
    u16 valueX;
    u16 valueY; 

    if(TOUCH_readXY(&valueX, &valueY) == 0xFF)
    {
        return 0xFF;
    }

    //根据物理坐标,计算彩屏坐标
    touchX = valueX * calibrate.xFactor + calibrate.xoffset;
    touchY = valueY * calibrate.yFactor + calibrate.yoffset;

    if((touchX > tftlcd_data.width) || (touchY > tftlcd_data.height))
    {
        return 0xFF;
    }

    return 0;
}

int main(void)
{
    I2C_init();
    TOUCH_init();

    at24c02Read_buf((u8*)&calibrate, TOUCH_CAL_ADDR, sizeof(calibrate));
    if(calibrate.flag != TOUCH_CAL_OK) //判断是否已经校准
    {
        TOUCH_calibrate();
    }
    LCD_ShowString(tftlcd_data.width-40,0,tftlcd_data.width,tftlcd_data.height,16,"clear");

    while(1)
    {
        if(TOUCH_scan() == 0)
        {
            LCD_Fill(touchX-1, touchY-1, touchX+2, touchY+2, FRONT_COLOR); //画粗线

            if((touchX > tftlcd_data.width-40) && (touchY < 20))
            {
                LCD_Fill(0, 0, tftlcd_data.width,tftlcd_data.height, BACK_COLOR); //清屏
                LCD_ShowString(tftlcd_data.width-40,0,tftlcd_data.width,tftlcd_data.height,16,"clear");
            }
        }
    }
}
  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
xpt2046触摸屏是一种常见的电容式触摸屏,可以通过SPI接口与单片机或嵌入式系统进行通信。在Linux系统中,可以通过编写驱动程序实现对xpt2046触摸屏的控制和数据读取。 以下是一个简单的xpt2046触摸屏Linux驱动的示例代码: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/spi/spi.h> #define XPT2046_CMD_X 0xD0 #define XPT2046_CMD_Y 0x90 #define XPT2046_MAX_X 4095 #define XPT2046_MAX_Y 4095 static struct spi_device *xpt2046_spi_dev; static int xpt2046_read_adc(u8 cmd) { u8 tx_buf[3] = {cmd, 0, 0}; u8 rx_buf[3] = {0, 0, 0}; struct spi_transfer transfer = { .tx_buf = tx_buf, .rx_buf = rx_buf, .len = 3, }; spi_sync_transfer(xpt2046_spi_dev, &transfer, 1); return ((rx_buf[1] << 8) | rx_buf[2]) >> 3; } static int xpt2046_read_touch(int *x, int *y) { int x_raw, y_raw; x_raw = xpt2046_read_adc(XPT2046_CMD_X); y_raw = xpt2046_read_adc(XPT2046_CMD_Y); *x = (x_raw * XPT2046_MAX_X) / 4096; *y = (y_raw * XPT2046_MAX_Y) / 4096; return 0; } static int xpt2046_probe(struct spi_device *spi_dev) { xpt2046_spi_dev = spi_dev; return 0; } static int xpt2046_remove(struct spi_device *spi_dev) { xpt2046_spi_dev = NULL; return 0; } static struct spi_driver xpt2046_spi_driver = { .driver = { .name = "xpt2046", .owner = THIS_MODULE, }, .probe = xpt2046_probe, .remove = xpt2046_remove, }; static int __init xpt2046_init(void) { return spi_register_driver(&xpt2046_spi_driver); } static void __exit xpt2046_exit(void) { spi_unregister_driver(&xpt2046_spi_driver); } module_init(xpt2046_init); module_exit(xpt2046_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("XPT2046 Touchscreen Driver"); ``` 此示例代码实现了一个简单的xpt2046触摸屏Linux驱动,可以通过SPI接口读取触摸坐标。在驱动程序中,首先定义了xpt2046触摸屏的命令码和最大坐标值。然后通过spi_sync_transfer()函数实现了SPI数据传输,读取x和y坐标。最后定义了驱动程序的初始化和退出函数,并注册到Linux内核中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值