----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4
开发板
eMMC
:16GB
LPDDR3
:4GB
显示屏 :15.6
英寸HDMI
接口显示屏
u-boot
:2023.04
linux
:6.3
----------------------------------------------------------------------------------------------------------------------------
一、电路原理图
我所使用的NanoPC-T4
开发板可以外接一个散热风扇,下面我们来介绍一下散热风扇硬件相关的内容。
1.1 电路原理图
下图是我们使用的NanoPC-T4开发板AP6356的接线图:
VCC_12V0_SYS
为系统电压,电压级别为12V。风扇支持通过GPIO
和PWM
控制;
上图中:
- 当
GPIO4_C6/PWM1
输出高电平时,三极管Q1
导通,N MOS
管Q1
截止,BM03B-GHS-TBT
2号引脚没有电压输入,风扇停止转动; - 当
GPIO4_C6/PWM1
输出低电平时,三极管Q1
截止,N MOS
管Q1
导通,BM03B-GHS-TBT
2号引脚输入12V
电压,风扇转动;
至于GPIO2_A6_FAN_TACH
引脚是用来测量转速的。
1.2 散热风扇接口引脚定
连接器型号: JST GH
系列连接器,3Pin,BM03B-GHS-TBT
:
Pin# | Assignment | Description |
---|---|---|
1 | GND | 0V |
2 | 12V | 12V输出,由GPIO4_C6/PWM1控制 |
3 | GPIO2_A6_FAN_TACH | 用来测量转速 |
二、GPIO
控制
这一节我们将介绍如何通过控制GPIO
口来实现控制散热风扇的运行和停止。
2.1 GPIO
子系统简介
既然是控制GPIO
口,那自然少不了GPIO
子系统。我们在文章《linux
驱动移植-GPIO
子系统》中介绍过GPIO
子系统相关的内容,但是有一块内容却遗漏掉了,那就是有关通过sysfs
来将控制GPIO
。
linux
内核对GPIO
资源进行了抽象,抽象出来的概念就是gpiolib
;gpiolib
汇总了GPIO
的通用操作,根据GPIO
的特性:
- 对上
gpiolib
提供的一套统一通用的操作GPIO
的软件接口,屏蔽了不同芯片的具体实现; - 对下
gpiolib
提供了针对不同芯片操作的一套framework
,针对不同芯片,只需要实现gpio controller driver
,然后使用gpiolib
提供的注册函数,将其挂接到gpiolib
上,这样就完成了这一套东西;
此外,为了方便应用层控制GPIO
口,GPIO
子系统提供了通过sysfs
控制GPIO
就的方式,应用层通过sysfs
操作GPIO
的前提是内核中已经向GPIO
子系统注册GPIO
资源,并且在/sys/class/
目录下可以看到gpio
类。
2.2 内核配置
这里我们需要进行如下配置:
General setup --->
[*] Configure standard kernel features (expert users) // CONFIG_EXPERT
Device Drivers --->
-*- GPIO Support ---> // CONFIG_GPIOLIB
[*] /sys/class/gpio/... (sysfs interface) // CONFIG_GPIO_SYSFS
其中:
CONFIG_GPIO_SYSFS
:决定sysfs
是否支持GPIO
子系统,也就是能否在/sys/class/
目录下看到gpio
类;CONFIG_GPIOLIB
:决定是否将gpiolib
编译进内核,如果选择否则在内核和驱动中不能使用GPIO
子系统相关的函数接口;
CONFIG_GPIOLIB
一般都是选择y
,因为其它驱动会用到GPIO
子系统;CONFIG_GPIO_SYSFS
根据自己的需求来进行选择,如果不需要通过/sys/class/gpio
目录下的文件来操作GPIO
口,就不需要开启。
2.2.1 配置电源域
一般IO
电源的电压有1.8v
,3.3v
,2.5v
,5.0v
等,有些IO
同时支持多种电压,io-domain
就是配置IO
电源域的寄存器,依据真实的硬件电压范围来配置对应的电压寄存器,否则无法正常工作。
在RK3399
的datasheet
中我们搜索GRF_IO_VSEL
寄存器,位于GRF
偏移地址offset
(0x0e640
):
支持两种电压配置:
- 寄存器配置成1,一般对应的电压范围是1.62v ~ 1.98v,
typical
电压 1.8v; - 寄存器配置成0,一般对应的电压范围是3.00v ~ 3.60v,
typical
电压 3.3v。
在 Documentation/devicetree/bindings/power/rockchip-io-domain.yaml文中有关于RK3399 IO
电源域的配置描述描述:
rk3399:
if:
properties:
compatible:
contains:
const: rockchip,rk3399-io-voltage-domain
then:
properties:
audio-supply:
description: The supply connected to APIO5_VDD.
bt656-supply:
description: The supply connected to APIO2_VDD.
gpio1830-supply:
description: The supply connected to APIO4_VDD.
sdmmc-supply:
description: The supply connected to SDMMC0_VDD.
rk3399-pmu:
if:
properties:
compatible:
contains:
const: rockchip,rk3399-pmu-io-voltage-domain
then:
properties:
pmu1830-supply:
description: The supply connected to PMUIO2_VDD.
通过查看rockchip-io-domain.yaml
文中文档, 我们知道了RK3399
的电源域需要配置包含bt565
,audio
,sdmmc
,gpio1830,以及PMUGRF
下面的pmu1830
这几个supply
,后面的The supplyconnected to ***_VDD
表示在硬件原理图上对应的名称。
我们在rockchip-io-domain.yml
中找到了gpio1830-supply
对应的硬件原理图上表示为APIO4_VDD
。所以通过搜索APIO4_VDD
得到NanoPC-T4
开发板 硬件原理图上的APIO4_VDD
电源VCC_3V0
是由rk808下
的15号引脚VLDO8
输出的;
得到了配置的名称和供电源头,在设备树里面找到对应的regulator: vcc_3v0
,就可以在arch/arm64/boot/dts/rockchip/rk3399-evb.dts
中追加配置;
&io_domains {
bt656-supply = <&vcc_1v8>;
audio-supply = <&vcca1v8_codec>;
sdmmc-supply = <&vcc_sdio>;
gpio1830-supply = <&vcc_3v0>;
status = "okay";
};
&pmu_io_domains {
pmu1830-supply = <&vcc_3v0>;
status = "okay";
};
注意:这里为了方便我直接把所有可能用到的电源域都配置上了。
2.2.2 编译内核
在linux
内核根目录下执行如下命令进行编译内核:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# make -j8
u-boot-2023.04
路径下的mkimage
工具拷贝过来,然后在命令行使用mkimage
工具编译即可:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp ../u-boot-2023.04/tools/mkimage ./
root@zhengyang:/work/sambashare/rk3399/linux-6.3# ./mkimage -f kernel.its kernel.itb
2.2.3 通过tftp
烧录内核
给开发板上电,同时连接上网线,进入uboot
命令行。我们将内核拷贝到tftp
文件目录:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp kernel.itb /work/tftpboot/
接着给开发板上电。通过uboot
命令行将kernel.itb
下到内存地址0x10000000
处:
=> tftp 0x10000000 kernel.itb
通过mmc write
命令将内核镜像烧录到eMMC
第0x8000
个扇区处:
=> mmc erase 0x8000 0xA000
=> mmc write 0x10000000 0x8000 0xA000
2.3 /sys/class/gpio
开发板上电后,查看/sys/class/gpio
目录;
root@rk3399:~# ls /sys/class/gpio
export gpiochip0 gpiochip32 gpiochip96
gpio156 gpiochip128 gpiochip64 unexport
gpiochipXX
:每个文件夹对应一个端口,名字的末尾是端口中第一个GPIO
口在内核中的编号;
每个gpiochipXX
文件夹下面都有base
、label
、ngpio
文件;
root@rk3399:~# ll /sys/class/gpio/gpiochip128
lrwxrwxrwx 1 root root 0 Mar 15 2023 /sys/class/gpio/gpiochip128 -> ../../devices/platform/pinctrl/ff790000.gpio/gpio/gpiochip128/
root@rk3399:~# ls /sys/class/gpio/gpiochip128
base device label ngpio power subsystem uevent
其中:
base
:保存的是GPIO
控制器的第一个GPIO
编号;label
:保存GPIO
控制器标签;ngpio
:GPIO
控制器包含的GPIO
数量;
这些数据是和内核中用来表示GPIO
控制器的struct gpio_chip
结构体对应的,以gpiochip32
为例;
root@rk3399:~# cat /sys/class/gpio/gpiochip128/base
128
root@rk3399:~# cat /sys/class/gpio/gpiochip128/label
gpio4
root@rk3399:~# cat /sys/class/gpio/gpiochip128/ngpio
32
GPIO
控制器名字是gpio4
,包含32
个GPIO
口(GPIO4_A0~A7
、GPIO4_B0~B7
、GPIO4_C0~B7
、GPIO4_D0~D7
),第一个GPIO
口在内核的编号是128,所以该端口包含128-159
号GPIO
口。
2.4 操作GPIO
口
GPIO
资源对上层应用是以文件的形式呈现的,应用操作GPIO
口就是读写相应的文件。GPIO
的操作接口包括direction
和value
等;
direction
控制GPIO
方向;value
控制GPIO
输出或获得GPIO
输入;
操作GPIO
口步骤;
- 通过数据手册和
gpiochipXX
文件夹查询到GPIO
在GPIO
子系统的编号; - 向
export
文件写入GPIO
口编号,导出GPIO
口; - 通过读写
GPIO
口导出的文件,操作GPIO
口; - 向
unexport
文件写入GPIO
口编号,撤销GPIO
口的导出;
更多内容可以参考:Documentation/admin-guide/gpio/sysfs.rst;
2.4.1 查找GPIO
口
当前控制风扇通过控制GPIO
高低电平来实现,查找GPIO
口编号;
root@rk3399:~# cat /sys/kernel/debug/gpio
gpiochip0: GPIOs 0-31, parent: platform/ff720000.gpio, gpio0:
gpio-4 ( |bt_default_wake_host) in lo IRQ
gpio-9 ( |bt_default_reset ) out lo
gpio-10 ( |reset ) out hi ACTIVE LOW
gpiochip1: GPIOs 32-63, parent: platform/ff730000.gpio, gpio1:
gpiochip2: GPIOs 64-95, parent: platform/ff780000.gpio, gpio2:
gpio-83 ( |bt_default_rts ) in hi
gpio-90 ( |bt_default_wake ) in hi
gpiochip3: GPIOs 96-127, parent: platform/ff788000.gpio, gpio3:
gpio-111 ( |snps,reset ) out hi ACTIVE LOW
gpiochip4: GPIOs 128-159, parent: platform/ff790000.gpio, gpio4:
gpio-154 ( |vbus-typec ) out hi
gpio-156 ( |Headphone detection ) in lo IRQ
GPIO4_C6/PWM1
在GPIO
子系统的编号为150。
2.4.2 导出GPIO
口
导出GPIO
口:
root@rk3399:~# cd /sys/class/gpio
root@rk3399:/sys/class/gpio# echo 150 > export
查看GPIO
文件夹下会多出gpio150
文件夹:
root@rk3399:/sys/class/gpio# ls
export gpio156 gpiochip128 gpiochip64 unexport
gpio150 gpiochip0 gpiochip32 gpiochip96
2.4.3 操作GPIO
口
进入文件夹gpio150
;
root@rk3399:/sys/class/gpio# cd gpio150
root@rk3399:/sys/class/gpio/gpio150# ll
-rw-r--r-- 1 root root 4096 Sep 20 20:34 active_low
lrwxrwxrwx 1 root root 0 Sep 20 20:34 device -> ../../../gpiochip4/
-rw-r--r-- 1 root root 4096 Sep 20 20:34 direction
-rw-r--r-- 1 root root 4096 Sep 20 20:34 edge
drwxr-xr-x 2 root root 0 Sep 20 20:34 power/
lrwxrwxrwx 1 root root 0 Sep 20 20:34 subsystem -> ../../../../../../../class/gpio/
-rw-r--r-- 1 root root 4096 Sep 20 20:32 uevent
-rw-r--r-- 1 root root 4096 Sep 20 20:34 value
设置为输出:
root@rk3399:/sys/class/gpio/gpio150# echo out > direction
direction
接受的参数可以是:in
、out
、high
、low
。其中参数high
/ low
在设置方向为输出的同时将value
设置为相应的1/0
。
修改GPIO
口状态,输出高电平,此时散热风扇会工作
root@rk3399:/sys/class/gpio/gpio150# echo 1 > value
输出低电平,此时散热风扇会停止工作
root@rk3399:/sys/class/gpio/gpio150# echo 0 > value
查看输出值:
root@rk3399:/sys/class/gpio/gpio150# cat value
0
盱眙:需要先配置电源域:不然的话配置的输出不会生效。
2.4.4 取消导出
root@rk3399:/sys/class/gpio/gpio150# cd /
root@rk3399:/# echo 150 > /sys/class/gpio/unexport
三、PWM
控制
PWM
全称是脉宽调制技术,它通过在一个固定周期内改变信号的占空比来模拟不同的电平或功率输出,因此PWM
有两个很重要的参数:
- 频率:指每秒钟发生的
PWM
周期的数量,通常以赫兹(Hz
)为单位表示。较高的频率意味着更快的周期重复率,可以提供更精细的控制和响应速度; - 占空比:是指在每个
PWM
周期中,信号处于高电平状态(通常为逻辑高)的时间与整个周期的比例。它范围从0到1,也可以表示为百分比。例如,50%的占空比意味着信号在周期的一半时间内处于高电平状态;
通过改变PWM
信号的频率和占空比,可以实现不同的控制效果。例如,在电机控制中,可以通过调整PWM
信号的频率和占空比来控制电机的转速和方向。
3.1 配置设备节点
3.1.1 配置风扇驱动
风扇驱动位于drivers/hwmon/pwm-fan.c
,设备节点的配置参考文档 Documentation/devicetree/bindings/hwmon/pwm-fan.txt
。
在arch/arm64/boot/dts/rockchip/rk3399-evb.dts
中追加配置;
fan: pwm-fan {
compatible = "pwm-fan";
/*
* With 20KHz PWM and an EVERCOOL EC4007H12SA fan, these levels
* work out to 0, ~1200, ~3000, and 5000RPM respectively.
*/
cooling-levels = <0 12 18 255>;
#cooling-cells = <2>;
fan-supply = <&vcc12v0_sys>;
pwms = <&pwm1 0 50000 PWM_POLARITY_INVERTED>;
};
vcc12v0_sys: vcc12v0-sys {
compatible = "regulator-fixed";
regulator-always-on;
regulator-boot-on;
regulator-max-microvolt = <12000000>;
regulator-min-microvolt = <12000000>;
regulator-name = "vcc12v0_sys";
};
节点属性pwms
用于控制风扇的PWM
,这里指定为pwm
的phandle
,后面有三个参数:
- 参数1:表示
index
(per-chip index of the PWM to request
),一般是 0,因为Rockchip PWM
每个
chip
只有一个; - 参数 2:表示
PWM
输出波形的时间周期,单位是ns
;上面我们配置的 50000就是表示想要得到的
PWM 输出周期是20KHz
;
参数3:表示极性,为可选参数;上面例子中的配置为负极性。
属性cooling-levels
:PWM
占空比的取值范围是0到255,对应着热量冷却状态。这些值通常被映射到特定的占空比,用于控制风扇或泵等冷却设备的速度。
属性fan-supply
:提供给风扇电源的电压调节器所使用的phandle
;
3.1.2 配置PWM
驱动
PWM
驱动位于drivers/pwm/pwm-rockchip.c
,设备节点的配置参考文档 :
Documentation/devicetree/bindings/pwm/pwm.yaml
:Documentation/devicetree/bindings/pwm/pwm.txt
:
在arch/arm64/boot/dts/rockchip/rk3399-evb.dts
中追加配置;
&pwm1 {
status = "okay";
};
pwm1
定义在arch/arm64/boot/dts/rockchip/rk3399.dtsi
文件;
pwm1 {
pwm1_pin: pwm1-pin {
rockchip,pins =
<4 RK_PC6 1 &pcfg_pull_none>;
};
pwm1_pin_pull_down: pwm1-pin-pull-down {
rockchip,pins =
<4 RK_PC6 1 &pcfg_pull_down>;
};
};
由于控制引脚接GPIO4_C6/PWM1
,这里配置GPIO4_C6
引脚功能复用为pwm_1
;
3.2 配置内核
配置内核:
Device Drivers --->
[*] Pulse-Width Modulation (PWM) Support --->
<*> Rockchip PWM support // CONFIG_PWM_ROCKCHIP
<*> Hardware Monitoring support ---> // CONFIG_HWMON
<*> PWM fan // CONFIG_SENSORS_PWM_FAN
配置完内核之后记得保存配置:
存档:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# mv rk3399_defconfig ./arch/arm64/configs/
重新配置内核(如果不想重新编译内核,可以存档一份到.config
):
root@zhengyang:/work/sambashare/rk3399/linux-6.3# make rk3399_defconfig
3.2.1 编译内核
在linux
内核根目录下执行如下命令进行编译内核:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# make -j8
u-boot-2023.04
路径下的mkimage
工具拷贝过来,然后在命令行使用mkimage
工具编译即可:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp ../u-boot-2023.04/tools/mkimage ./
root@zhengyang:/work/sambashare/rk3399/linux-6.3# ./mkimage -f kernel.its kernel.itb
3.2.2 通过tftp
烧录内核
给开发板上电,同时连接上网线,进入uboot
命令行。我们将内核拷贝到tftp
文件目录:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp kernel.itb /work/tftpboot/
接着给开发板上电。通过uboot
命令行将kernel.itb
下到内存地址0x10000000
处:
=> tftp 0x10000000 kernel.itb
通过mmc write
命令将内核镜像烧录到eMMC
第0x8000
个扇区处:
=> mmc erase 0x8000 0xA000
=> mmc write 0x10000000 0x8000 0xA000
3.3 控制PWM
占空比
PWM
提供了用户层的接口,在 /sys/class/pwm/
节点下面,PWM
驱动加载成功后,会在/sys/class/pwm/
目录下产生pwmchip0
目录;
root@rk3399:~# cd /sys/class/pwm/pwmchip0/
root@rk3399:/sys/class/pwm/pwmchip0# ll
lrwxrwxrwx 1 root root 0 Sep 20 23:45 device -> ../../../ff420000.pwm/
--w------- 1 root root 4096 Sep 20 23:45 export
-r--r--r-- 1 root root 4096 Sep 20 23:45 npwm
drwxr-xr-x 2 root root 0 Sep 20 23:45 power/
lrwxrwxrwx 1 root root 0 Mar 15 2023 subsystem -> ../../../../../class/pwm/
-rw-r--r-- 1 root root 4096 Mar 15 2023 uevent
--w------- 1 root root 4096 Sep 20 23:45 unexport
向export
文件写入0,就是打开pwm
定时器0,会产生一个pwm0
目录,相反的往unexport
写入0就会关闭pwm
定时器了,同时pwm0
目录会被删除。
root@rk3399:/sys/class/pwm/pwmchip0# echo 0 > export
root@rk3399:/sys/class/pwm/pwmchip0# ll pwm0/
-r--r--r-- 1 root root 4096 Sep 20 23:49 capture
-rw-r--r-- 1 root root 4096 Sep 20 23:49 duty_cycle
-rw-r--r-- 1 root root 4096 Sep 20 23:49 enable
-rw-r--r-- 1 root root 4096 Sep 20 23:49 period
-rw-r--r-- 1 root root 4096 Sep 20 23:49 polarity
drwxr-xr-x 2 root root 0 Sep 20 23:49 power/
-rw-r--r-- 1 root root 4096 Sep 20 23:49 uevent
/sys/class/pwm/pwmchip0/pwm0
目录下有以下几个文件:
enable
:写入1使能pwm
,写入0关闭pwm
;polarity
:有normal
或inversed
两个参数选择,表示输出引脚电平翻转;duty_cycle
:在normal
模式下,表示一个周期内高电平持续的时间(单位:纳秒),在reversed
模式下,表示一个周期中低电平持续的时间(单位:纳秒);period
:表示pwm
波的周期(单位:纳秒);
设置pwm0
输出频率100KHz
,占空比50%
, 极性为正极性:
root@rk3399:/sys/class/pwm/pwmchip0# cd pwm0
root@rk3399:/sys/class/pwm/pwmchip0/pwm0# echo 100000 > period
root@rk3399:/sys/class/pwm/pwmchip0/pwm0# echo 50000 > duty_cycle
root@rk3399:/sys/class/pwm/pwmchip0/pwm0# echo normal > polarity
root@rk3399:/sys/class/pwm/pwmchip0/pwm0# cat enable
0
root@rk3399:/sys/class/pwm/pwmchip0/pwm0# echo 1 > enable
通过修改占空比达到控制风扇转动,占空比的有效操作大致在0-255左右,数据值越大,转速越慢;
root@rk3399:/sys/class/pwm/pwmchip0# echo 100 > /sys/devices/platform/pwm-fan/hwmon/hwmon1/pwm1
参考文章
[1] rk3399 linux
风扇调试
[2] PWM
控制
[4] 控制rk3399
的某个GPIO
**[5] Rockchip_Developer_Guide_Linux_IO_DOMAIN_CN.pdf
**