1 、uart
通信模式:串行异步全双工
波特率:每秒钟传输的bit数
传输距离:
TTL电平:0.5m以内采用(短距离,因为一般是5v左右表示高电平,所以不适合长距离)
RS232:0.5m~15m(最大30m左右,有的也说50m,根据具体情况吧),3v-12v表示高电平1,速率一般为20kbps
RS485:速率可达10Mbps,标准情况最大距离1200m,最大也可达3km。
数据格式:最开始电平拉低,维持一段时间,表示起始位,要开始发送数据。然后发送一个字节数据,发送完之后发校验位,再发送停止位,停止位可以是1位或者1.5位等,要继续发送数据就循环这个过程。
发送的数据
起始位 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 校验位 | 停止位 |
内部实现:数据线发送到fifo队列里,发送的时候从队列取数据,通过移位寄存器发出去。接收数据时,数据放到fifo队列,再通过移位寄存器取数据。
如何知道有数据? : 可以通过轮询或者中断实现。
2、串口编程实验
tty:终端,分为虚拟终端和真实终端,真实终端比如硬件设备,串口也是一个终端,控制台就是一个虚拟终端。如果不想区分是不是虚拟终端,可以用tty代表当前终端。
在此引入了 行规程 ,他会对数据进行处理。
在终端,键盘输入一个字符时,会发送到串口驱动程序,驱动程序发给行规程,行规程发现只是一个普通的字符,就回环,把数据发送给驱动程序,驱动程序再发给pc终端显示。
当输入回车或者删除键时,行规程判断是特殊的字符,比如回车就会把数据发送给app程序,程序就开始运行。它里面有一个buffer,存放输入的数据,如果是删除,他就会把输入的字符删掉,然后把buff的数据发给驱动程序。
3、iic中的重要结构体
APP 通过 I2C Controller 与 I2C Device 传输数据。一个芯片里可能有多个 I2C Controller,
对于使用者,只要确定是第几个 I2C Controller 即可。
i2c_client:里面存储设备地址,还有adapter结构体指针。
i2c_adapter:表示是第几个I2C Controller,其中有i2c_algorithm结构体
i2c_algorithm,其中有i2c的msg结构体,这个msg结构体表示传输的i2c数据的信息,包含设备地址,数据,读或写,长度等信息。
4、编写驱动程序
当我们open,write文件时,内核会去进行系统调用,调用内核的open,write。
当我们open,write设备时,内核会去调用对应驱动程序的open,write。
所以我们去写驱动程序时,要提供open,write等函数。
流程:
写驱动程序时,提供设备号,file_operation结构体,里面包含驱动程序的open等函数,要把它告诉内核,所以要注册驱动程序。
如何注册呢?需要提供入口函数,安装时会去调用它
想卸载驱动怎么办呢?所以需要提供出口函数。
open,write时,系统会根据设备号打开对应的驱动程序。然后再进行相应的调用。
驱动程序如何访问硬件呢?需要将寄存器地址映射为虚拟地址,通过这个虚拟地址来进行访问。
5、GPIO
高速模式和低速怎么选择呢?
当我们使用高速模式时,速率快,但波形也会更抖,对其他外设的干扰就比较大,当我们想去做电磁兼容测试时,可能就通不过,所以高速和低速要去根据我们的外界设备来进行选择
6、为什么要将物理地址映射为虚拟地址,比如ioremap
程序运行时,它的物理地址是通过pid来进行分配的,cpu看到的地址是MMU映射后的虚拟地址。
而且写代码的人水平有高有低,不能让一窍不通的人直接去访问硬件
地址的映射是由MMU完成的,
mmu有两个作用:地址映射和权限保护(cpu发出的地址要结果mmu的审核才能访问硬件,cpu能不能访问硬件由mmu决定)
7、app读取按键操作
1、查询
2、休眠-唤醒
3、poll方式(定时)
4、异步通知方式
8、中断、异常
中断不能嵌套,中断处理要越快越好
中断控制器可以告诉cpu有需要处理的事情
中断属于一种异常,异常(比如指令错误,数据访问有问题)是不可屏蔽的
中断设置:设置中断源,使能中断
cpu每执行完一条指令,都会检测有无中断产生,这是由硬件决定的
处理流程:
对于不同的异常,cpu跳转到不同的地址(这些只是跳转指令,这些地址一般排在一块,称为异常向量),然后执行不同的处理程序,
比如发生中断异常,就会跳到中断处理的代码,这些代码会执行:保存现场、处理中断(分辨中断源,调用对应的处理函数)、恢复现场这几个过程。
如何保存现场呢?
例子:在执行a=a+b时,cpu会分别从寄存器里读取a和b的值,放入cpu里的寄存器,进行加法操作后数据保存到a寄存器中。
如果此时发生中断,其他程序也要用到这些寄存器,那不就是乱套了?
所以会把寄存器相应的值保存到栈中,恢复现场时,根据这个栈去恢复相应寄存器的值即可。
函数A里面运行函数B的话,A的值就会保存到B的栈中,B才再去执行自己的程序。
(注:虽然进程里的资源线程是共享的,但是线程也有自己的栈)
中断处理的改进
如果某个中断很耗时怎么办呢?我们可以把中断分为上半部和下半部,上半部执行重要的处理,把耗时的放到下半部去执行。 也就是硬件中断(不可被打断)和软件中断
在中断处理时,preempt_count会++,上半部会关中断,这时候不可被打断,当上半部执行完, count--,然后开中断,此时才会被打断。如果此时被打断,当另外一个中断的上半部执行完时,会判断count是否为零,如果大于0,中断会马上处理完成,这时候去恢复所有中断的下半部,这些下半部都会得到执行。
但是这些执行很慢,系统会变得卡顿怎么办?此时程序员们想到了,把中断放入线程中去执行,这样系统的程序和中断都会得到调度,就不会那么卡了。
内核会给你创建一个worker线程(内核线程),线程里会有worker queue队列,想让worker执行时,往队列放数据即可。
但是这些线程只会在一个核上运行,效率较低。所以用到了thread_irq,当你传入函数时,会单独创建一个线程,这些单独的线程可以在不同的核上执行。
9、stm32中断系统
由 中断向量表、中断优先级、EXIT、NVIC、中断函数组成
中断优先级:
分为抢占式优先级和响应优先级
配置:
GPIO-AFIO
/打开AFIO时钟(供中断使用)
EXIT: line(中断线) ->trigger(上升沿还是下降沿触发)
/创建结构体
/选择中断线
/开启中断
/指定中断线模式(中断模式/事件模式)
/指定触发有效边沿(上升/下降沿触发)
/EXTl_Init函数将结构体放入寄存器位置
NVIC: channle-> 抢占优先级->响应优先级
//配置NVIC优先级分组(每个程序只有一个)
//定义结构体
/指定中断通道开启或关闭
/指定通道是使能或失能
/指定通道的抢占优先级
/指定通道的响应优先级
/NVIC_Init
中断函数
//配置对应的中断处理函数
//如果有多个中断线,就判断标志位是哪一根线的中断
//中断结束后清除标志位