当前位置: 首页 > news >正文

Linux驱动-①电容屏触摸屏②音频③CAN通信

Linux驱动-①电容屏②音频③CAN通信

  • 一,电容屏触摸屏
    • 1.GT9147芯片驱动
  • 二,音频
    • 2.1 I2S总线
    • 2.2 --host=arm-linux-gnueabihf
  • 三,CAN通信
    • 3.1 CAN
    • 3.2 UART/CAN为什么不需要设备驱动,而SPI和I2C需要

一,电容屏触摸屏

1.GT9147芯片驱动

  电容触摸屏内部芯片采用的传输方式为I2C通信,因此总线驱动已经有了,要写的是电容触摸屏GT9147的驱动,包括根据电路连接方式,对设备树进行修改,将其挂载到I2C2节点上,设置好各个属性。触摸方式有单点触摸和多点触摸:
  ①单点触摸,仅支持一个触摸点,比如只能一个手指去触摸,同时放第二个手指,第二个手指触摸信息没反应。检测到 一个触点 后,会更新其寄存器中的坐标数据,通过INT引脚触发中断,通知主机读取数据。上报一个点,就相当于把此轮信息上报完了,就会发一个sync信号,然后继续上报下一个信息。(比如手划过后,会有1,2,3,4,5,点信息,上报完1,给一个sync,再上报2,再给一个sync信号…)

// 单点触控上报(仅X/Y坐标)
input_report_abs(input_dev, ABS_X, x_pos);
input_report_abs(input_dev, ABS_Y, y_pos);
input_sync(input_dev);

  ②多点触摸,可以检测多个手指触摸信息,比如五个手指同时触摸,1号手指触摸1.1,1.2-1.5点,2号手指触摸2.1-2.6点,3号手指触摸3.1-3.3点…,当上报时,会依次先报1号手指1.1,2号手指2.1点,3号手指3.1,4号手指4.1点,5号手指5.1点,信息进行上报,然后发送一个sync信号,继续把*.2点信息上报,发送sync信号。GT9147 会按顺序更新所有触点的寄存器(如触点1在 0x8140~0x8147,触点2在 0x8148~0x814F,依此类推)。所有有效触点的信息会被 一次性读取,然后通过输入子系统上报。

// 遍历所有触点(GT9147 支持最多5点)
for (i = 0; i < max_touches; i++) {
    status = read_reg(0x8140 + i * 8); // 触点状态
    if (status & TOUCH_ACTIVE) {
        x = read_reg(0x8140 + i * 8 + 2); // X坐标
        y = read_reg(0x8140 + i * 8 + 4); // Y坐标

        // 上报单个触点
        input_mt_slot(input_dev, i); // 选择触点槽
        input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);
        input_report_abs(input_dev, ABS_MT_POSITION_X, x);
        input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
    } else {
        // 触点释放
        input_mt_slot(input_dev, i);
        input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
    }
}
input_sync(input_dev); // 所有触点上报完成后发送 SYN_REPORT

  采用的是typeB方式,相当于每次将触摸点变成一个slot,用这个slot来上报触摸点的信息,slot其中的内容包括,第几个点,ID,x轴和y轴的坐标值,完成所有点的上报后,上报一个sync信息,这个作用感觉就是把信息之间给间隔起来,有个分页符。
  **总体流程:**当触摸后,触发了中断,进入中断处理函数中,先设置出了slot即id,第几次按到了,然后设置其state状态,虽然在前面,但是这些数据要先放到内核的缓冲区中,这个函数的上下和最终数据输出并不是一样的顺序,然后上报x y值,然后调用这个sync一下,就是发送数据,然后当触摸屏释放,还会触发一次中断,从而引起 input_mt_slot(dev->input, id); input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false)。
驱动:

/*4.1日内容主要有i2c,中断,input结构,电容屏反馈原理*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/i2c.h>
#include <asm/unaligned.h>
#define LCD_NAME "lcd"

#define GT_CTRL_REG 	        0X8040  /* GT9147控制寄存器         */
#define GT_MODSW_REG 	        0X804D  /* GT9147模式切换寄存器        */
#define GT_9xx_CFGS_REG 	        0X8047  /* GT9147配置起始地址寄存器    */
#define GT_1xx_CFGS_REG 	        0X8050  /* GT1151配置起始地址寄存器    */
#define GT_CHECK_REG 	        0X80FF  /* GT9147校验和寄存器       */
#define GT_PID_REG 		        0X8140  /* GT9147产品ID寄存器       */

#define GT_GSTID_REG 	        0X814E  /* GT9147当前检测到的触摸情况 */
#define GT_TP1_REG 		        0X814F  /* 第一个触摸点数据地址 */
#define GT_TP2_REG 		        0X8157	/* 第二个触摸点数据地址 */
#define GT_TP3_REG 		        0X815F  /* 第三个触摸点数据地址 */
#define GT_TP4_REG 		        0X8167  /* 第四个触摸点数据地址  */
#define GT_TP5_REG 		        0X816F	/* 第五个触摸点数据地址   */
#define MAX_SUPPORT_POINTS      5       /* 最多5点电容触摸 */

const u8 irq_table[] = {IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH};  /* 触发方式 */
struct lcd_dev{
    int irqtype; //中断类型
    int ret_pin,irq_pin;
    struct device dev;
    struct input_dev *input;	/* input结构体 */
    struct i2c_client *client;

};
struct lcd_dev lcd;


static int lcd_read_regs(struct lcd_dev *dev,u16 reg,u8 *buf ,int len)
{
	int ret;
    u8 reg_adress[2]={0};
    struct i2c_client *client = lcd.client;//传递数据
	struct i2c_msg msg[2];
    reg_adress[0] = reg >> 8;
    reg_adress[1] = reg & 0xff;

	msg[0].addr  = client->addr;//设备地址
	msg[0].flags = 0;//写操作
	msg[0].len   = 2;
	msg[0].buf   = reg_adress;

	msg[1].addr  = client->addr;//设备地址
	msg[1].flags = I2C_M_RD;//读
	msg[1].len   = len;//这样就能连续读数据
	msg[1].buf   = buf;

	ret = i2c_transfer(client->adapter, msg, 2);
	if (ret < 0) {
		printk(KERN_ERR "error %d\n", ret);
        ret = -EREMOTEIO;
	}else ret = 0;
	return ret;
}
static void lcd_write_regs(struct lcd_dev *dev,u16 reg,u8 *buf,u8 len)
{
    u8 b[6];
    struct i2c_client *client = lcd.client;//传递数据
	struct i2c_msg msg[1];
    b[0] = reg >> 8;//寄存器地址
    b[1] = reg & 0xff;
    memcpy(&b[2],buf,len);//每次写入一个数据

	msg[0].addr  = client->addr;//设备地址
	msg[0].flags = 0;//写操作
	msg[0].len   = len+2;//长度为2
	msg[0].buf   = b;//b中放的先是寄存器地址,后面是数据
	i2c_transfer(client->adapter, msg, 1);
}
// static void lcd_write_reg(struct lcd_dev *dev,u16 reg,u8 data)
// {
//     lcd_write_regs(&lcd,reg,&data,1);
// }
// static int gt9147_read_firmware(struct i2c_client *client, struct lcd_dev *dev)
// {
// 	int ret = 0, version = 0;
// 	u16 id = 0;
// 	u8 data[7]={0};
// 	char id_str[5];
// 	ret = lcd_read_regs(dev, GT_PID_REG, data, 6);
// 	if (ret) {
// 		dev_err(&client->dev, "Unable to read PID.\n");
// 		return ret;
// 	}
// 	memcpy(id_str, data, 4);
// 	id_str[4] = 0;
//     if (kstrtou16(id_str, 10, &id))
//         id = 0x1001;
// 	version = get_unaligned_le16(&data[4]);
// 	dev_info(&client->dev, "ID %d, version: %04x\n", id, version);
// 	switch (id) {    /* 由于不同的芯片配置寄存器地址不一样需要判断一下  */
//     case 1151:
//     case 1158:
//     case 5663:
//     case 5688:    /* 读取固件里面的配置信息  */
//         ret = lcd_read_regs(dev, GT_1xx_CFGS_REG, data, 7);  
// 		break;
//     default:
//         ret = lcd_read_regs(dev, GT_9xx_CFGS_REG, data, 7);
// 		break;
//     }
// 	if (ret) {
// 		dev_err(&client->dev, "Unable to read Firmware.\n");
// 		return ret;
// 	}
// 	// dev->max_x = (data[2] << 8) + data[1];
// 	// dev->max_y = (data[4] << 8) + data[3];
// 	dev->irqtype = data[6] & 0x3;
// 	printk("irq type  = %d\r\n", dev->irqtype);
// 	return 0;
// }
static irqreturn_t lcd_irq_handler(int irq,void *dev_id)
{
    int touch_num = 0;
    int input_x, input_y;
    int id = 0;
    int ret = 0;
    u8 data;
    u8 touch_data[5];
    struct lcd_dev *dev = (struct lcd_dev *)dev_id;    
    
    ret = lcd_read_regs(dev,GT_GSTID_REG,&data,1);
    if(data!=0)
    {
        touch_num = data & 0x0f;
    }else 
    {
        goto irq_fail;
    }
    if(touch_num)
    {
        lcd_read_regs(dev,GT_TP1_REG,touch_data,5);
        id = touch_data[0] & 0x0f;
        if(id == 0)
        {
            input_x  = touch_data[1] | (touch_data[2] << 8);
            input_y  = touch_data[3] | (touch_data[4] << 8);

            input_mt_slot(dev->input, id);
		    input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, true);
		    input_report_abs(dev->input, ABS_MT_POSITION_X, input_x);
		    input_report_abs(dev->input, ABS_MT_POSITION_Y, input_y);            
        }
    }else if(touch_num ==0)
    {
        input_mt_slot(dev->input, id);
        input_mt_report_slot_state(dev->input, MT_TOOL_FINGER, false);
    }
    input_mt_report_pointer_emulation(dev->input, true);
    input_sync(dev->input);
    data = 0x00;                /* 向0X814E寄存器写0 */
    lcd_write_regs(dev, GT_GSTID_REG, &data, 1);
irq_fail:    
    return IRQ_HANDLED;      // 中断已处理
}
static int lcd_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    u8 data;
    //u8  *reslu = NULL;
    lcd.client = client;
    /*1.中断以及引脚*/
    lcd.irq_pin = of_get_named_gpio(client->dev.of_node,"interrupt-gpios", 0);
    lcd.ret_pin = of_get_named_gpio(client->dev.of_node,"reset-gpios", 0);
    if(!lcd.irq_pin||! lcd.ret_pin)
    {
        printk("get gpio error !\r\n");
    }
    ret = devm_gpio_request_one(&client->dev,lcd.irq_pin,GPIOF_OUT_INIT_HIGH,"lcd_irq");
    if(ret<0)
    {
        printk("gpio request error !\r\n");
    }
    /*复位*/
    ret = devm_gpio_request_one(&client->dev,lcd.ret_pin, GPIOF_OUT_INIT_HIGH,"ret_pin");
    if(ret<0)
    {
        printk("ret  request error !\r\n");
    }
    gpio_set_value(lcd.ret_pin,0);
    msleep(10);
    gpio_set_value(lcd.ret_pin,1);
    msleep(10);
    gpio_set_value(lcd.irq_pin,0);
    msleep(50);
    gpio_direction_input(lcd.irq_pin);  
    /*2.初始化lcd*/
    data = 0x02;
    lcd_write_regs(&lcd,GT_CTRL_REG,&data,1);
    mdelay(100);

    // lcd_read_regs(&lcd,GT_CTRL_REG,reslu,1);
    // printk("read data = %d\r\n",reslu[0]);

    data = 0;
    lcd_write_regs(&lcd,GT_CTRL_REG, &data,1);
    mdelay(100); 


    /*3.input初始化及注册*/
    // ret = gt9147_read_firmware(client, &lcd);
    // if(ret!=0)
    // {
    //     printk("firmware error\r\n");
    // }
    lcd.input = devm_input_allocate_device(&client->dev);
    lcd.input->name = client->name;
    lcd.input->id.bustype = BUS_I2C;
    lcd.input->dev.parent = &client->dev;//还是不太懂,为什么要加parent
    __set_bit(EV_KEY,lcd.input->evbit);
    __set_bit(EV_ABS,lcd.input->evbit);
    __set_bit(BTN_TOUCH,lcd.input->keybit);
    input_set_abs_params(lcd.input,ABS_X,0,800,0,0);
    input_set_abs_params(lcd.input,ABS_Y,0,480,0,0);
    input_set_abs_params(lcd.input,ABS_MT_POSITION_X,0,800,0,0);
    input_set_abs_params(lcd.input,ABS_MT_POSITION_Y,0,480,0,0);
    ret =  input_mt_init_slots(lcd.input, MAX_SUPPORT_POINTS, 0);
    if(ret<0)
    {
        printk("input_mt_init_slots error !\r\n");
    
    }
    ret = input_register_device(lcd.input); 

    /*client->irq表示这是i2c引起的中断*/
    ret = devm_request_threaded_irq(&client->dev,client->irq,NULL,lcd_irq_handler,
                                   IRQ_TYPE_EDGE_FALLING| IRQF_ONESHOT ,client->name, &lcd);
    if(ret<0)
    {
        printk("irq request error !\r\n");
    } 
    printk("probe probe probe  !\r\n");  
    return ret;
}

static int lcd_remove(struct i2c_client *client)
{
    input_unregister_device(lcd.input);
    return 0;
}
static const struct i2c_device_id lcd_id[] = {
	{ "www", 0 },
	{ }
};
const struct of_device_id	lcd_of_match[]={
    {.compatible = "wyt,lcd"},
    { /*sentinel*/ },
};
static struct i2c_driver lcd_driver = {
	.probe		= lcd_probe,
	.remove		= lcd_remove,
	.driver		= {
        .owner = THIS_MODULE,
		.name	= "lcd666",//1.无设备树,匹配名字
        .of_match_table = lcd_of_match,//2.有设备树,直接利用设备树中compatible属性
	},
    .id_table = lcd_id,
};
static int __init lcd_init(void)
{ 
    int ret = 0;
    ret = i2c_add_driver(&lcd_driver);
    return ret;
}
static void  __exit lcd_exit(void)
{
    i2c_del_driver(&lcd_driver);  
}


/*驱动入口和出口*/
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyt");


二,音频

2.1 I2S总线

  同步串行通信,全双工,用于控制音频,三根基本线SCK,WS,SD,可选MCLK,主模式下,主设备提供时钟,从设备同步接收。SCLK:位时钟,频率=采样率 × 位数 × 通道数。WS:字选择(Word Select)/左右声道时钟,0=左声道,1=右声道。SD:串行数据(Serial Data),传输 PCM 样本。MCLK:主时钟,提供系统参考时钟。
在这里插入图片描述

2.2 --host=arm-linux-gnueabihf

–host:指定编译生成的代码在哪个平台上运行(目标平台)。
arm:目标 CPU 架构为 ARM(如树莓派、嵌入式开发板等)。
linux:目标操作系统为 Linux。
gnueabihf ABI 类型:GNU 嵌入式应用二进制接口,支持硬件浮点(Hard-Float)加速。

三,CAN通信

3.1 CAN

在这里插入图片描述
  CAN使用消息ID区分不同的数据帧,不是设备地址,相当于给每个ID设置一种类型,比如0X100表示发动机转速,0x200表示刹车,CAN设备有自己的过滤器,比如单元1想往单元二发消息,单元二只接受ID为0x200的数据,其他的就会过滤掉。其次是优先级,当多个单元同时工作,谁发出去的ID号越小,优先级越高。
  CAN 协议提供了 5 种帧格式来传输数据:数据帧、遥控帧、错误帧、过载帧和帧间隔。数据帧:①、帧起始,表示数据帧开始的段。②、仲裁段,表示该帧优先级的段。③、控制段,表示数据的字节数及保留位的段。④、数据段,数据的内容,一帧可发送 0~8 个字节的数据。⑤、CRC 段,检查帧的传输错误的段。⑥、ACK 段,表示确认正常接收的段。⑦、帧结束,表示数据帧结束的段。

在这里插入图片描述
  异步串行通信(异步是因为没有时钟线),差分信号CAN_H/CAN_L,,CAN 总线电平分为显性电平和隐性电平两种。显性电平表示逻辑“0”,此时 CAN_H 电平比 CAN_L 高,分别为 3.5V 和 1.5V,电位差为 2V。隐形电平表示逻辑“1”,此时 CAN_H 和 CAN_L 电压都为 2.5V 左右,电位差为 0V。

3.2 UART/CAN为什么不需要设备驱动,而SPI和I2C需要

  RS485,422,232最后都要归属到UART驱动,CAN通信采用单独的驱动框架,这两种驱动框架,无论什么设备,都直接用这个驱动就行,不需要额外再写设备的驱动,因为UART/CAN设备简单,直接读取数据就行,设置一下波特率,传输速率就行了,I2C和SPI不一样,设备复杂,不同设备有不同的寄存器访问方式,需要设置设备驱动。

http://www.dtcms.com/a/113657.html

相关文章:

  • client-go如何监听自定义资源
  • 2011-2019年各省地方财政资源勘探电力信息等事务支出数据
  • Jetpack Compose 自定义标题栏终极指南:从基础到高级实战
  • 蓝桥杯2024年第十五届省赛真题-宝石组合
  • BGP路由协议之特殊配置
  • Linux内核slab分配器
  • Linux 系统安装与优化全攻略:打造高效开发环境
  • Airflow量化入门系列:第四章 A股数据处理与存储优化
  • 浅谈StarRocks 常见问题解析
  • (5)模拟后——Leonardo的可视化操作
  • 探秘叁仟智盒设备:智慧城市的智能枢纽
  • Django4.0 快速集成jwt
  • ASP.NET Core Web API 参数传递方式
  • NLP简介及其发展历史
  • docker stack常用命令
  • C#结构体(Struct)深度解析:轻量数据容器与游戏开发应用 (Day 20)
  • pinia-plugin-persist、vuex
  • Spring Boot项目连接MySQL数据库及CRUD操作示例
  • Java Timer:老派但好用的“定时任务小闹钟“
  • 【Linux】进程间通信、匿名管道、进程池
  • 将OpenFOAM中的lduMatrix数据转换为CSC稀疏矩阵格式
  • 混合编程的架构
  • Java EE期末总结(第三章)
  • Leedcode刷题 | 回溯算法小总结01
  • kali——masscan
  • Matlab轴承故障信号仿真与故障分析
  • spring-cloud-alibaba-nacos-config使用说明
  • 《K230 从熟悉到...》无线网络
  • LINUX 4 tar -zcvf -jcvf -Jcvf -tf -uf
  • Transformer+BO-SVM多变量时间序列预测(Matlab)