请选择 进入手机版 | 继续访问电脑版

你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

Stm32mp157A-DK1评估Linux LED驱动-6设备树platform设备

[复制链接]
寒门过河卒 发布时间:2021-7-2 11:16
之前编辑了一个platform设备,我们需要写一个设备程序,之前使用设备树发现设备程序中的内容我们都可以用设备树来实现,因此我们可以通过platform总线驱动设备树中的硬件设备,这样我们就可以只写一个驱动程序就可以了。
使用platform总线和设备树中的节点之间有一个pinctrl节点来关联,这里需要改动的文件有两个:stm32mp15-pinctrl.dtsi和stm32mp157a-dk1.dts两个。
stm32mp15-pinctrl.dtsi中添加pinctrl节点:
led_pins_xhy:gpioled-0 {
pins {
       pinmux= <STM32_PINMUX('A', 14, GPIO)>; //设置 PA14 复用为 GPIO 功能
drive-push-pull;  //推挽输出
bias-pull-up;        //内部上拉
output-high;         //输出高电平
slew-rate =<0>;   //速度为0 档
};
};
stm32mp157a-dk1.dts中xhy_led加入 pinctrl-names和pinctrl-0节点信息。
xhy_led {
              compatible= "xhy,led";
              status= "okay";
              led-gpio= <&gpioa 14 GPIO_ACTIVE_LOW>;
              pinctrl-names= "default";
              pinctrl-0= <&led_pins_xhy>;
      };  
然后重新编译.dtb文件:
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs
然后将dts/stm32mp157a-dk1.dtb复制到tftp文件夹中:
cp arch/arm/boot/dts/stm32mp157a-dk1.dtb /home/helloxhy/tftp/ -rf
驱动编写
这只需要编写一个led.c文件就可以了,由于这次即使用了platform总线有使用了设备树,属于之前几种编写方式的集合,所以我们从新编写led.c文件:
首先写入头文件:
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
然后定义设备结构体:
struct leddev_dev{
/*   用于注册设备   */
       dev_tdevid;                        
       structcdev cdev;                  
       structclass *class;         
       structdevice *device;   
        /*   用于从设备树获取信息   */
       structdevice_node *node;   
       intgpio_led;                        
};
struct leddev_dev leddev;
之后定义驱动结构体,由于使用了platform总线所以release由设备驱动定义:
static struct file_operations led_fops = {
       .owner= THIS_MODULE,
       .open= led_open,
       .read =led_read,
       .write= led_write,
};
其中.open和.read为空函数,原因是.read没有调用,这里只写没有读IO口,.open没有需要执行的信息。
所以只需要写.write程序就可以了,其中只调用了两个函数
copy_from_user(databuf, buf, cnt)获取需要执行的信息
gpio_set_value(leddev.gpio_led, 0)对IO口进行操作
static ssize_t led_write(struct file *filp,const char __user *buf, size_t cnt, loff_t *offt)
{
       intretvalue;
       unsignedchar databuf[1];
       unsignedchar ledstat;
2 C  D4 d- I7 A2 {! k/ X2 k
       retvalue= copy_from_user(databuf, buf, cnt);
       if(retvalue< 0) {
              printk("kernelwrite failed!\r\n");
              return-EFAULT;
       }

/ C# g2 y9 T; m  u
       ledstat= databuf[0];
       if(ledstat == LEDON) {
              lgpio_set_value(leddev.gpio_led,0);
       }else if (ledstat == LEDOFF) {
              gpio_set_value(leddev.gpio_led,1);
       }
       return0;
}
接下来需要对platform总线进行操作,驱动的入口和出口函数分别是对platform模块的加载和卸载:
static int __init leddriver_init(void)
{
       returnplatform_driver_register(&led_driver); //加载led_driver
}
static void __exit leddriver_exit(void)
{
       platform_driver_unregister(&led_driver);//卸载led_driver
}
由此可见需要定义platform模块,名称为led_driver,其中需要定义的由四个:
1设备树的名称,便于关联设备树。2、驱动的名称。
3、flatform驱动初始化。4、flatform驱动清除:
static struct platform_driver led_driver ={
       .driver            = {
              .name     = "xhy-led",   //            2、驱动的名称。
              .of_match_table     = led_of_match,           // 1设备树的名称,便于关联设备树
       },
       .probe           = led_probe,         //3、flatform驱动初始化
       .remove         = led_remove,       //4、flatform驱动清除
};
接下来需要定义led_of_match其中.compatible是设备树中定义的内容:
static const struct of_device_idled_of_match[] = {
       {.compatible = " xhy,led " },
       {  }
};
MODULE_DEVICE_TABLE(of, led_of_match);
最后需要编写flatform驱动初始化和flatform驱动清除两个函数:
flatform驱动初始化包括gpio的初始化和设备注册。
gpio初始化使用函数:of_get_named_gpio、gpio_request和gpio_direction_output,分别为从设备树中获取gpio,从flatform总线申请gpio,以及配置。
leddev.gpio_led = of_get_named_gpio(nd,"led-gpio", 0);
//"led-gpio"在设备树中的内容为led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>;
ret = gpio_request(leddev.gpio_led,"LED0");
gpio_direction_output(leddev.gpio_led,1);
接下来是设备注册,直接使用新字符设备中的设备注册:
/* 1、设置设备号 */
ret =alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
       if(ret< 0) {
              pr_err("%sCouldn't alloc_chrdev_region, ret=%d\r\n", LEDDEV_NAME, ret);
              gotofree_gpio;
       }

* L6 G& u# E. D
       /*2、初始化cdev  */
       leddev.cdev.owner= THIS_MODULE;
       cdev_init(&leddev.cdev,&led_fops);
" N2 @8 m# d" F9 _9 ^
       /*3、添加一个cdev */
       ret= cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
       if(ret< 0)
              gotodel_unregister;
8 i7 C8 l' J4 U- _! T! s* I% Z: w
       /*4、创建类      */
       leddev.class= class_create(THIS_MODULE, LEDDEV_NAME);
       if(IS_ERR(leddev.class)) {
              gotodel_cdev;
       }

: m& {+ \6 N, A/ |
       /*5、创建设备 */
       leddev.device= device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
       if(IS_ERR(leddev.device)) {
              gotodestroy_class;
       }
最后是platform驱动的remove函数,主要是led关闭和设备的注销:
       gpio_set_value(leddev.gpio_led,1);   /* 卸载驱动的时候关闭LED*/
       gpio_free(leddev.gpio_led);  /* 注销GPIO */
       cdev_del(&leddev.cdev);                           /*  删除cdev */
       unregister_chrdev_region(leddev.devid,LEDDEV_CNT); /* 注销设备号 */
       device_destroy(leddev.class,leddev.devid);       /* 注销设备 */
       class_destroy(leddev.class);/* 注销类 */
编译测试:
Ubuntu编译:
make
arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp
cp led.ko ledApp /home/helloxhy/nfs/lib/modules/5.4.56/ -rf
板子上测试:
depmod
modprobe led
./ledApp /dev/newled 0
./ledApp /dev/newled 1
pinctrl.rar (29.13 KB, 下载次数: 0)
收藏 评论0 发布时间:2021-7-2 11:16

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版