一.IIC必知基本知识
1. 两根线SCL SDA,同步、串行、半双工
2. 多从机设备,每一个器件都可以既当主机,又当从机,每个器件都有一个7bit的地址标识
3.具有冲突检测和仲裁机制,当多主机同时启用总线时,可以防止错误产生
4.四大信号:起始信号、终止信号、应答信号和发送信号(SCL高电平SDA信号不可变化,为接收器接收;SCL低电平SDA可以变化,为发送器发送信号)
5. 发送器不同于串口(可以发5-8位数据位),iic必须每次发一个字节
6. 发送器每次发送完一个字节数据之后,接收器必须发送一位的应答位来回答发送器
7. 典型IIC时序:

接下来的8,9步被封装在iic_transfer、i2c_smbus_xfer等API函数中
8. IIC读流程:
主机发送起始信号(标志通信的开始) -> 发送I2C设备以及写(请求写入数据)-> 从机发送应答ACK信号 -> 重新发送开始信号(可有可没有) -> 主机发送要读取数据的寄存器地址 -> ACK ->主
机重新发送开始信号(标志接下来的操作是读取数据而不是写)->发送I2C设备以及读(请求读取数据)-> ACK ->从设备将数据字节发送给主机->主机发送ACK信号->主机发送停止信号
9. IIC写流程:
主机发送开始信号 -> 主机发送I2C设备地址及0写(表示要写数据) -> 从机发送应答ACK信号 ->
重新发送开始信号(可有可没有) -> 主机发送要写入数据的寄存器地址 -> 从机发送ACK信号 -> 主机发送要写入寄存器的地址 -> ACK信号 -> 停止信号
总结:
-
写操作:
- 主设备发送寄存器地址和要写入的数据。
- 从设备接收到寄存器地址后,准备接收数据。
-
读操作:
- 主设备首先发送寄存器地址以指定要读取的寄存器。
- 接着,主设备通过发送重复开始信号(Repeated START),切换到读取模式,并发送设备地址及读操作位(1)。
- 从设备根据指定的寄存器地址将数据发送给主设备。
10.总线速度 五种模式
二.具体操作,驱动设备分离的思想,主控制器由I.MX的iic控制器,从设备这里设置为ap3216
I.MX
的
I2C
适配器驱动采用了
DMA
方式
I.MX适配器驱动(SOC.I2C控制器,主) + I.MX设备驱动(AP3216,从,外接设备)
①NXP官方已经撰写完成,包括中断号获取、linux虚拟内存映射获取I2C1控制器实际物理地址、初始化等工作
②设备树修改IO口,复用IO口,并创建相应的设备信息(ap3216),包括compatible和reg器件地址 →AP3216驱动编写
→2.1 驱动注册:将ap3216注册到核心层,使内核开始监听匹配的I2C设备
2.2 设备与驱动匹配:内核在I2C 总线上扫描设备并通过
ap3216c_id
和
ap3216c_of_match
进行设备与驱动的匹配。当匹配成功时,内核会调用驱动程序中的
probe
函数。
2.3 初始化设备:
ap3216c_probe():当
probe
函数被调用时,它会初始化 AP3216C 设备。具体步骤包括:
- 获取并解析设备树中的配置信息(如果使用设备树)。
- 为设备结构体(如
ap3216c_dev
)分配内存并初始化相关成员变量,包括private_data
,用于存储i2c_client
结构体。 - 注册字符设备,以便通过
/dev
接口与用户空间进行交互。 - 初始化设备所需的资源,如 GPIO、时钟等。
2.4 设备操作:在应用程序中,可以通过标准的字符设备接口(如 open
, read
, write
)访问 AP3216C 传感器的数据。这些接口调用会触发驱动中的相应操作函数,例如:ap3216c_read_reg():读取单个寄存器的数据。
2.5 数据处理:
- 驱动会周期性地读取 AP3216C 的传感器数据(如 IR、ALS、PS)。这些数据通过合适的接口传递给用户空间的应用程序。
- 数据处理的流程通常是通过定时器或中断触发的,例如每隔一段时间(如 0.3 秒)通过
QTimer
定时器调用相应的槽函数来读取数据并更新显示。
2.6 驱动卸载:当驱动需要被卸载的时候,会释放所有分配的资源
整个执行流程从驱动的注册开始,到设备的匹配与初始化,进而通过字符设备接口进行数据交互,最后在需要时卸载驱动并释放资源。这一流程确保了 AP3216C 传感器的正常工作,并通过内核驱动为用户空间提供稳定的访问接口。
③QT:在 Qt 中,定时器(
QTimer
)与槽函数之间的连接是通过信号和槽的机制来实现的。具体来说,
QTimer
对象会发出一个
timeout()
信号,当定时器达到设定的时间间隔时,Qt 会自动触发这个信号,调用与之关联的槽函数。
connect连接槽函数以及信号:connect(ap3216c_tim, &QTimer::timeout, this, &MainWindow::ap3216_timeout);
三.LCD驱动 GT911
裸机:寄存器设置,配置LCD现存 VSYNC等 -> LCD IO口初始化 -> LCD像素时钟设置
驱动:IIC驱动+中断(监测到触摸信息后会触发中断,在中断函数里面读取触摸点信息)+input子系统触摸屏上报
驱动:IIC驱动+中断(监测到触摸信息后会触发中断,在中断函数里面读取触摸点信息)+input子系统触摸屏上报
LCD驱动 platform ——只需要修改设备树(电气属性、背光信息等)
多点触摸屏驱动编写与测试:
多点电容触摸的两大协议:TypeA(上报原始数据)以及
TypeB采用!(slot更新触摸点信息并硬件追踪上报)
1.
驱动主框架是
IIC
设备,会用到中断,在中断处理函数里面上报触摸点信息,要用到
input
子系统框架
2.
设备树
IO
修改,
IIC
节点添加:,修改屏幕参数
INT- GPIO1_IO09 GPIO
RST-SNVS_TAMPER9 GPIO
I2C_SDA-UART5_RXD
I2C_SCL-UART5_RXD
1.
主体
I2C
框架准备好
2.
复位引脚和中断引脚,包括中断
3. input
子系统框架 这里通过触发ABS_MT事件上报
4.
初始化
ft5426
5.
在中断服务函数里面读取触摸坐标值,然后上报给系统
可以作为难点:注意:采用request_thread_irq(比request_irq多一个threaded函数)实现中断线程化
那么为什么要中断线程化呢?我们都知道硬件中断具有最高优先级,不论什么时候
只要硬件中断发生,那么内核都会终止当前正在执行的操作,转而去执行中断处理程序
(
不考虑
关闭中断和中断优先级的情况
)
,如果中断非常频繁的话那么内核将会频繁的执行中断处理程序,
导致任务得不到及时的处理。中断线程化以后中断将作为内核线程运行,而且也可以被赋予不
同的优先级,任务的优先级可能比中断线程的优先级高,这样做的目的就是保证高优先级的任
务能被优先处理。大家可能会疑问,前面不是说可以将比较耗时的中断放到下半部
(bottom half)
处理吗?虽然下半部可以被延迟处理,但是依旧先于线程执行,中断线程化可以让这些比较耗
时的下半部与进程进行公平竞争。
要注意,并不是所有的中断都可以被线程化,重要的中断就不能这么操作。对于触摸屏而
言只要手指放到屏幕上,它可能就会一直产生中断
(
视具体芯片而定,
FT5426
是这样的
)
,中断
处理程序里面需要通过
I2C
读取触摸信息并上报给内核,
I2C
的速度最大只有
400KHz
,算是低
速外设。不断的产生中断、读取触摸信息、上报信息会导致处理器在触摸中断上花费大量的时
间,但是触摸相对来说不是那么重要的事件,因此可以将触摸中断线程化。
-> 思考:
在嵌入式软件开发中,线程化的需求通常由以下几种情况引发:
1. 并发任务处理
- 需求:当系统需要同时处理多个任务或服务时,例如同时处理传感器数据采集、用户界面更新和通信任务。
- 例子:同时读取传感器数据、处理用户输入、并进行通信时,将这些任务分配到不同的线程中可以提高响应性和效率。
2. 实时性要求
- 需求:对于有实时性要求的任务,需要确保任务能够及时响应外部事件。
- 例子:实时控制系统,如电机控制或传感器数据处理,可能需要单独的线程来确保响应时间符合要求。
3. 资源管理
- 需求:当需要对共享资源进行保护,避免数据竞争或冲突时。
- 例子:多个线程需要访问同一个硬件资源或数据结构时,使用互斥锁(mutex)来保护资源的访问,防止数据不一致性。
4. 异步操作
- 需求:当需要执行一些耗时操作而不阻塞主线程。
- 例子:在处理网络请求或文件读写时,使用线程来执行这些操作,从而避免主线程被阻塞,保持界面的响应性。
5. 提高系统吞吐量
- 需求:提高系统的处理能力,通过并行化任务来提高吞吐量。
- 例子:在图像处理、数据分析等高负载应用中,将不同的处理步骤分配到不同的线程中以提高整体处理速度。
6. 任务分离与组织
- 需求:将复杂的任务分解成更简单的子任务,以提高代码的组织性和可维护性。
- 例子:将用户界面处理、数据采集和数据处理分开到不同的线程中,使每个线程专注于一个任务,提高代码的清晰度。
7. 响应外部事件
- 需求:处理外部事件时需要保证系统能够实时响应。
- 例子:处理中断或接收来自外部设备的数据,通常会使用线程来处理这些事件,以确保系统能够及时响应。
总结
线程化通常用于提高系统的并发处理能力、确保实时性、保护共享资源、执行异步操作、提高系统吞吐量、分离任务以及响应外部事件。根据具体的需求和系统设计,可以选择合适的线程管理方式来实现这些目标。
补充:input子系统——管理比如按键输入、键盘、鼠标、触摸屏等的子系统

上报输入事件:
input
设备都是具有输入功能的,但是具体是什么样的输入值 Linux
内核是不知道的,我们需要获取到具体的输入值,或者说是输入事件,然后将输入事件上报给 Linux 内核。不同的事件,其上报事件的 API 函数不同。