Linux 驱动之 Pinctrl 子系统
Pinctrl 概述
关于 pinctrl 主要可以归结为两类设置,其中一类是功能选择,即一组 gpio 是用于 iic 还是 uart 还是就作为普通 gpio 来用,另一类是 gpio 的特性配置,即上拉、下拉、驱动能力和速率的配置。而 pinctrl 主要负责这两类配置的管理工作。总结起来 pinctrl 主要完成以下三种功能:
引脚枚举与命名 (Enumerating and naming)
引脚复用 (Multiplexing):比如用作 GPIO、I2C 或其他功能
引脚配置 (Configuration):比如上拉、下来、open drain、驱动强度等
dts 配置
以全志平台的写法如下(虚构的):
123456789101112131415uart0_pins: uart0-pins { pins = "PA4", "PA5"; function = "uart0";};uart0_sleep_pins: uart0-pins { pins = "PA4", "PA5"; function = "gpio";};...&uar ...
Linux 驱动之 GPIO 子系统
GPIO 概述
以前学习 stm32 的时候看到手册里有关于 gpio 的电路图,如下:
保护二极体:
IO 引脚上下两边两个二极体用于防止引脚外部过高、过低的电压输入。当引脚电压高于 VDD 时,上方的二极体导通;当引脚电压低于 VSS 时,下方的二极体导通,防止不正常电压引入晶片导致晶片烧毁
P-MOS 管和 N-MOS 管:
由 P-MOS 管和 N-MOS 管组成的单元电路使得 GPIO 具有“推挽输出”和“开漏输出”的模式
TTL 肖特基触发器:
信号经过触发器后,模拟信号转化为 0 和 1 的数字信号。但是,当 GPIO 引脚作为 ADC 采集电压的输入通道时,用其“模拟输入”功能,此时信号不再经过触发器进行 TTL 电平转换。ADC 外设要采集到的原始的模拟信号
STM32 的 GPIO 支持 4 种输入模式(浮空输入、上拉输入、下拉输入、模拟输入)和 4 种输出模式(开漏输出、开漏复用输出、推挽输出、推挽复用输出)
浮空输入模式:
上拉输入模式:
下拉输入模式:
模拟输入模式:
开漏输出模式:
开漏输出模式下,通过 ...
Linux 驱动之 SPI 子系统
Linux SPI 驱动框架
对于 SPI 的驱动框架与 I2C 是大致一致的,也分为两层,控制器驱动程序层叫 spi_controller ,主要提供 transfer 函数,进行 spi 协议的收发。
另一层是设备驱动层,基于 spi_bus_type,在 driver 里则使用 spi_read、spi_writer 等函数,最终也会调用到 controller->transfer 函数进行发送接收。
数据结构
整体数据结构如下:原图
spi_controller 结构体用于描述一个 spi 控制器,可以类比 iic 的 i2c_adapt。
spi_device 结构体用于描述一个 spi 设备,可以类比于 i2c_client 结构体。
spi_device 结构里有个 mode 很重要下面是其详细描述,SPI 模式主要有两个特征:
CPOL:这是时钟极性:
0:初始时钟状态为低电平,第一个时钟边沿是上升沿
1:初始时钟状态为高电平,第一个时钟边沿是下降沿
CPHA:这是时钟相位,选择在哪个边沿采样数据:
0:数据在下降沿锁存,输出在上升沿改变 ...
Linux 驱动之 PWM
PWM 概述
Ton:信号高电平持续时间
Toff:信号底电平持续时间
Period: 完整 pwm 周期
Duty cycle:pwm 信号周期内保持为 ON 的时间百分比
Linux 下 PWM 驱动
数据结构
pwm 的数据结构如下图所示:原图
pwm_chip 结构体用于抽象 soc 的 pwm 控制器
pwm_device 用来抽象设备
pwm_state 表示 pwm 设备的周期、占空比、极性等信息
pwm ops 结构体提供一系列回调函数。这些回调函数的操作对象是具体的 pwm device
实现
pwm 驱动分为 consumer 和 provider。其中 consumer 提供接口如下:
123static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) //配置 pwm device 的频率、占空比。static inline int pwm_enable(struct pwm_device *pwm) ...
Linux 驱动之 IIC 子系统
IIC 协议
写操作
流程如下:
主芯片要发出一个 start 信号
然后发出一个设备地址(用来确定是往哪一个芯片写数据),方向(读/写,0 表示写,1 表示读)
从设备回应(用来确定这个设备是否存在),然后就可以传输数据
主设备发送一个字节数据给从设备,并等待回应
每传输一字节数据,接收方要有一个回应信号(确定数据是否接受完成),然后再传输下一个数据
数据发送完之后,主芯片就会发送一个停止信号
下图:白色背景表示"主→从",灰色背景表示"从→主"
读操作
流程如下:
主芯片要发出一个 start 信号
然后发出一个设备地址(用来确定是往哪一个芯片写数据),方向(读/写,0 表示写,1 表示读)
从设备回应(用来确定这个设备是否存在),然后就可以传输数据
从设备发送一个字节数据给主设备,并等待回应
每传输一字节数据,接收方要有一个回应信号(确定数据是否接受完成),然后再传输下一个数据
数据发送完之后,主芯片就会发送一个停止信号
下图:白色背景表示"主→从",灰色背景表示"从→主"
IIC 信号
IIC 协议中数据传输的单位是字节,也就是 8 位。但是要用到 9 个 ...
Linux 驱动之设备树
dtb 数据解析
内核启动阶段获得 dtb 位置指针
以 arm64 为例,内核启动如下:
1234567891011__HEAD_head:#ifdef CONFIG_EFI add x13, x18, #0x16 b stext#else b stext // branch to kernel start, magic .long 0 // reserved#endif...
在开启 UEFI 支持时,add x13, x18, #0x16 这个 code 实际上是为了满足 EFI 格式的”MZ”头。如果使用 UEFI 来启动 kernel, 会识别出来并走 UEFI 启动的流程,如果是普通的启动过程如使用 uboot 的 booti 进行引导,那么第一条指令就是一条 dummy 指令,第二条就跳转到 stext 运行了。
x0 寄存器保存的是 dtb 里 blob 块的物理地址,x0 寄存器内容由 uboot 设置,uboot 将 dtb 地址传递给内核,跳转到 stext,如下:
123456789101112131415161718192021222324E ...
Linux 驱动之文件系统
seq_file
概述
seq_file 只是在普通文件 read 中加入了内核缓冲的功能,从而实现顺序多次遍历读取大数据量的简单接口,seq_file 一般只提供只读接口。
123456struct seq_operations { void * (*start) (struct seq_file *m, loff_t *pos); void (*stop) (struct seq_file *m, void *v); void * (*next) (struct seq_file *m, void *v, loff_t *pos); int (*show) (struct seq_file *m, void *v);};
核心功能
seq_file 接口可通过 <linux/seq_file.h> 获得。 seq_file 包含三个方面:
An iterator interface which lets a virtual file implementation step through the objects it is presenting
Some ut ...
Linux 驱动之设备驱动模型
kobject
概述
Kobjects 有一个 name 和一个引用计数,还具有父指针(允许将对象排列成层次结构)
ktype 是嵌入在 kobject 结构体中的对象。 每个 kobject 结构都需要一个相应的 ktype。 ktype 控制 kobject 在创建和销毁时的行为
kset 是一组 kobjects(kset 本身包含一个 kobject), 这些 kobject 可以拥有相同的 ktype,kset 也是 sysfs 中的一个子目录。Ksets 可以支持 kobjects 的“热插拔”(uevent 事件)
kobject 的数据结构和实现
下图展示了 kobject、ktype 和 kset 三者数据结构之间的关系:原图
sysfs_ops:
show 是回调函数,在读取具有该 kobj_type 的所有属性时调用他。缓冲区长度始终是 PAGE_SIZE,在成功时返回写入缓冲区的数据大小,失败返回错误码。写入的时候调用 store 函数,其参数 buf 最大为 PAGE_SIZE,成功返回写入数据大小,失败返回错误码
uevent_ops: ...
foc 相关算法
MTPA
为什么要用 MTPA
当电机采用 id=0 的控制策略,这种控制方法忽略了磁阻转矩的作用,电磁转钜方程如下:
τe=32p[ke⋅iq+(Ld−Lq)⋅id⋅iq]\tau _e = \frac{3}{2}p[k_e \cdot i_q + (L_d - L_q)\cdot i_d \cdot i_q]
τe=23p[ke⋅iq+(Ld−Lq)⋅id⋅iq]
转矩分为永磁转矩 Tr 和磁阻转矩 Tm,而 id=0 只剩下 Tr。这会导致电流的利用率不高,系统的效率降低。所以 id=0 的控制比较适用于隐极式电机(Ld=Lq),而对于凸极式电机并不最优,所以需要重新考虑控制策略。
推导过程
电动机电压方程:
Ud=rId−LqIqωeUq=rIq+kEωe+LdIdωe\begin{aligned}
U_d = rI_d - L_qI_q\omega_e \\
U_q = rI_q + k_E\omega_e + L_dI_d\omega_e
\end{aligned}
Ud=rId−LqIqωeUq=rIq+kEωe+LdIdωe ...
FOC 原理
物理学基础概念
左手定则
将左手的食指,中指和拇指伸直,使其在空间内相互垂直。食指方向代表磁场的方向(从 N 级到 S 级),中指代表电流的方向(从正极到负极),那拇指所指的方向就是受力的方向。可用于判断安培力(运动导体所受到的力)和洛伦兹力(运动电荷所受的力)。
右手定则
伸开右手,使拇指与其余四个手指垂直,并且都与手掌在同一平面内;让磁感线垂直于手心进入,并使拇指指向导线运动方向,这时四指所指的方向就是感应电流的方向。用于判断导体在做切割磁场时产生的电流方向。
安培定则(右手螺旋定则)
电直导线中的安培定则(安培定则一):用右手握住通电直导线,让大拇指指向直导线中电流方向,那么四指指向就是通电导线周围磁场的方向;
通电螺线管中的安培定则(安培定则二):用右手握住通电螺线管,让四指指向电流的方向,那么大拇指所指的那一端是通电螺线管的 N 极。
安培力定则
安培力: 以电流强度为 I 的长度为 L 的直导线,置于磁感应强度为 B 的均匀外磁场中,则导线受到的安培力的大小为:
F=IBLsinαF = IBLsin\alpha
F=IBLsinα
式中α为导线中的电流 ...
