一、设备树
早期的linux是没有设备树概念的,早期这些板级信息文件 都是.c 或.h 文件,都会被硬编码进 Linux 内核中,导致内核的非常大,一年出的arm架构芯片有上千种,再做成电路板更多。
Linux的设备树一般是dts源文件编译成dtb文件,再又linux内核去读取他。
设备树上面记录了片内外设和片外外设的各种信息。
Linux驱动开发中,会间接的去驱动外设。通过读取设备树上的节点,来获取所对应的信息再进行操作。
如图:(树枝上就是对应的相应节点)
(1)设备树语法
语法上面跟C语言差不多,但是开头用一个/表示记录的信息从这里开始。
通过括号扩起来,上面的例如chosen,memory就是所对应的节点信息。
&代表节点上所追加的信息
(2)设备树节点
spi4 {
compatible = "spi-gpio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi4>;
/* pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>; */
status = "okay";
gpio-sck = <&gpio5 11 0>;
gpio-mosi = <&gpio5 10 0>;
/* cs-gpios = <&gpio5 7 0>; */
num-chipselects = <1>;
#address-cells = <1>;
#size-cells = <0>;
gpio_spi: gpio_spi@0 {
compatible = "fairchild,74hc595";
gpio-controller;
#gpio-cells = <2>;
reg = <0>;
registers-number = <1>;
registers-default = /bits/ 8 <0x57>;
spi-max-frequency = <100000>;
};
};
spi上面的节点,节点下面套了一个gpio_spi的子节点。
这个节点记录了芯片spi4的信息,
compatible为属性(这个随便写也行)
pinctrl-names为设备的名字,他的名字叫默认”default”
pinctrl-0 记录他的电气属性信息
status 记录状态,如果okay就是使能,disable就是不使能。
gpio-sck = <&gpio5 11 0>; 记录了sck线在哪个引脚,这个gpio-sck是可以随便起的。前面的不行。
gpio-mosi = <&gpio5 10 0>; 同上
num-chipselects = <1>;
#address-cells = <1>;
#size-cells = <0>;
上面三条就不解释了,看名字就知道是干嘛的。
然后这个节点下面追加了一个子节点
compatible = “fairchild,74hc595”;
gpio-controller;
#gpio-cells = <2>;
reg = <0>;
registers-number = <1>;
registers-default = /bits/ 8 <0x57>;
spi-max-frequency = <100000>;
子节点记录了属性信息,跟上面一样的就不说了。
节点下面是可以套节点的,有时候你这个功能不止给你一个外设使用,有多个外设给使用。
(3)Pinctrl
看这个名字就知道这个是引脚的属性信息,上面pinctrl-0 = <&pinctrl_spi4>;
&后面就是追加的信息。
这个追加的引脚信息,MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10在这里所对应
#define MX6UL_PAD_BOOT_MODE0__GPIO5_IO10 0x0014 0x02A0 0x0000 0x5 0x0
第一个就是这个引脚的设置复用寄存器的偏移,第二个就是引脚电气属性寄存器的偏移,第三个就是输入的寄存器偏移,第四个就是设置复用的模式的值,第五个就设置输入的值。
他的电气属性设置值放在了设备树的这:
MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1
0x70a1 这个就是配置引脚电气属性的值,寄存器手册我就懒得翻了
二、 并发与竞争
并发就是同个时间下,大家都要访问这个内存地址。大家一起来那谁先来就得排个队呀,两个数据都要访问或者改写这个内存段,得要有先来后到,不然这段数据读起来大家都不准。
这个函数太多太复杂,就不写啦。懒!!!
三、定时器
Linux里面启动内核后,有个系统时钟,这个系统时钟会记录使能后的时间,到达时间后可以触发,但不是硬件中断!!!这个使能后溢出后会从新置0并且自动关闭。这个系统时间用的时钟源是谁提供的呢?说实话我也不知道,因为我没研究他内核的结构。不同芯片系统时钟提供的时钟源可能不一样,这个一般是芯片厂商帮我们写好的。
(1)函数使用
init_timer(&pinctl_led.timer); //取这个结构体的信息,这个函数有点类似注册系统时钟。
void init_timer_key(struct timer_list *timer, unsigned int flags,
const char *name, struct lock_class_key *key);
函数原型第一个是timer_list的类型的结构体,使用init_timer函数使用,别人已经帮我们写好了后面的,我们只需要写结构体里面的信息就行了。
timer_list类型的结构体里面只需要编写function,expires,data即可。
Function就是他时间到了后所触发的函数,类似于中断
Expires就是设置他定时器的时间,多久溢出,一般就jiffies+msecs_to_jiffies()
就可以设置多少毫秒溢出了。Jiffies在32位和64位溢出时间是完全不一样的。
在64位可以不用考虑溢出问题,因为他溢出得等上万天,32位好像溢出就只需要几天时间,Jiffies就是内核启动后所记录的时间。
设置配置信息之后,就只需要添加定时器就行了,也类似于使能
可以使用timer_add函数,也可以使用mod_timer函数。
要注销定时器的话使用extern int del_timer(struct timer_list * timer);
函数。