我们之前的程序都是对需要的gpio寄存器地址进行映射,从而实现对gpio口的配置,在Linux内核中可以调用某些API接口实现对gpio口的设置,这就是gpio子系统。
可以将设备树下的led节点改为:
stm32mp1_led {
compatible= "stm32mp1-led";
status= "okay";
reg= <0X50000A28 0X04 /* RCC_MP_AHB4ENSETR */
0X500020000X04 /* GPIOI_MODER */
0X500020040X04 /* GPIOI_OTYPER */
0X500020080X04 /* GPIOI_OSPEEDR */
0X5000200C0X04 /* GPIOI_PUPDR */
0X500020180X04 >; /* GPIOI_BSRR */
};
改为,其中改了个名字,compatible改为”name,led”否则申请不到该设备,reg原来为映射地址,此处改为led-gpio,在gpio子系统中直接设置io信息。
xhy_led{
compatible= "xhy,led";
status= "okay";
led-gpio= <&gpioa 14 GPIO_ACTIVE_LOW>;
};
然后重新编译.dtb文件:
make ARCH=armCROSS_COMPILE=arm-none-linux-gnueabihf- dtbs
然后将dts/stm32mp157a-dk1.dtb复制到tftp文件夹中:
cp arch/arm/boot/dts/stm32mp157a-dk1.dtb/home/helloxhy/tftp/ -rf
板子重启后可以在超级终端上面查询到修改后的设备树信息:
ls /proc/device-tree/
接下来写驱动程序:
首先加入子系统需要的头文件:
#include <linux/of_gpio.h>
然后把地址映射的指针所有信息删除:
/*
static void __iomem*MPU_AHB4_PERIPH_RCC_PI;
static void __iomem *GPIOI_MODER_PI;
static void __iomem *GPIOI_OTYPER_PI;
static void __iomem *GPIOI_OSPEEDR_PI;
static void __iomem *GPIOI_PUPDR_PI;
static void __iomem *GPIOI_BSRR_PI;
*/
接下来在设备结构体中添加节点的gpio编号:
struct newchrled_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 在设备结构体中添加设备节点 */
int led_gpio; /* led 所使用的 GPIO 编号 */
};
然后在led_write中修改对GPIOI_BSRR_PI的操作,改为对设备节点的操作,直接调用API接口gpio_set_value:
gpio_set_value(dev->led_gpio, 1); //关闭LED
gpio_set_value(dev->led_gpio, 0); //开启LED
接下来对led_init进行修改,不需要做寄存器映射和寄存器操作了,直接在设备树节点上面的信息进行获取然后调用API进行操作就可以了。
首先把映射和寄存器操作全部删除:
MPU_AHB4_PERIPH_RCC_PI= of_iomap(newchrled.nd, 0);
GPIOI_MODER_PI= of_iomap(newchrled.nd, 1);
GPIOI_OTYPER_PI= of_iomap(newchrled.nd, 2);
GPIOI_OSPEEDR_PI= of_iomap(newchrled.nd, 3);
GPIOI_PUPDR_PI= of_iomap(newchrled.nd, 4);
GPIOI_BSRR_PI= of_iomap(newchrled.nd, 5);
//2、时钟配置打开对应寄存器的时钟,查阅手册第866页,GPIOA为寄存器的第0位
val= readl(MPU_AHB4_PERIPH_RCC_PI);
val&= ~(0X1 << 0); // 清除以前的设置
val|= (0X1 << 0); // 设置新值
writel(val,MPU_AHB4_PERIPH_RCC_PI);
/*2、设置GPIOI_MODER_PI寄存器,查阅手册第1072页13.4.1,PA14设置第28、29两个字节。
00:输入模式
01:输出模式
10:复用模式
11:模拟模式
*//*
val= readl(GPIOI_MODER_PI);
val&= ~(0X3 << 28); // bit0:1清零
val|= (0X1 << 28); // bit0:1设置01
writel(val,GPIOI_MODER_PI);
/*3、设置GPIOI_OTYPER_PI寄存器,查阅手册第1072页13.4.2,PA14设置第14字节。
0:推览输出
1:上拉模式
*//*
val= readl(GPIOI_OTYPER_PI);
val&= ~(0X1 << 14); /* bit0清零,设置为上拉*/
/*writel(val,GPIOI_OTYPER_PI);
/*4、设置GPIOI_ OSPEEDR _PI寄存器,查阅手册第1073页13.4.3,PA14设置第28、29两个字节
设置IO口速度:
00ow speed
01:Medium speed
10:High speed
11:Very high speed
*//*
val= readl(GPIOI_OSPEEDR_PI);
val&= ~(0X3 << 28); /* bit0:1 清零 */
/*val|= (0x2 << 28); /* bit0:1 设置为10*/
/*writel(val,GPIOI_OSPEEDR_PI);
/*5、设置GPIOI_ PUPDR寄存器,查阅手册第1073页13.4.4,PA14设置第28、29两个字节。
配置I/O上拉或下拉
00:禁止上拉、下拉
01:上拉
10:下拉
11:保留
*/
/*val= readl(GPIOI_PUPDR_PI);
val&= ~(0X3 << 28); /* bit0:1 清零*/
/*val|= (0x1 << 28); /*bit0:1 设置为01*/
/*writel(val,GPIOI_PUPDR_PI);
/*6、设置GPIOI_ BSRR寄存器,查阅手册第1074页13.4.7,PA14设置第14、30两个字节。
用来设置IO的开关
*/
val= readl(GPIOI_BSRR_PI);
//val|= (0x1 << 14);
//writel(val,GPIOI_BSRR_PI);
接下来对设备数进行读取,依然把获取的信息放在.nd中:
newchrled.nd =of_find_node_by_path("/xhy_led");
然后获取设备树的compatible信息"xhy,led":
proper= of_find_property(newchrled.nd, "compatible", NULL);
获取status 属性内容"okay":
ret =of_property_read_string(newchrled.nd, "status", &str);
原来的程序时获取reg信息,此时已经改为led-gpio,所以此处获取gpio属性<&gpioa 14 GPIO_ACTIVE_LOW>:
newchrled.led_gpio =of_get_named_gpio(newchrled.nd, "led-gpio", 0);
获取到led-gpiod 编号和信息,下一步需要调用API使申请到的信息起作用,此处发现一个问题,compatible参数如果不为name,name2格式的话会报错:
ret = gpio_request(newchrled.led_gpio,"LED-GPIO");
然后设备便起作用了,接下来进行操作就可以了:
gpio_direction_output(newchrled.led_gpio,1);
另外需要把led_init和led_exit解除映射改为清除gpio:
iounmap(MPU_AHB4_PERIPH_RCC_PI);
iounmap(GPIOI_MODER_PI);
iounmap(GPIOI_OTYPER_PI);
iounmap(GPIOI_OSPEEDR_PI);
iounmap(GPIOI_PUPDR_PI);
iounmap(GPIOI_BSRR_PI);*/
改为:
gpio_free(newchrled.led_gpio);
编译以及测试
depmod
modprobeled
./ledApp/dev/newled 0
./ledApp/dev/newled 1