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

之前编辑了一个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;

       retvalue= copy_from_user(databuf, buf, cnt);
       if(retvalue< 0) {
              printk("kernelwrite failed!\r\n");
              return-EFAULT;
       }

       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;
       }

       /*2、初始化cdev  */
       leddev.cdev.owner= THIS_MODULE;
       cdev_init(&leddev.cdev,&led_fops);

       /*3、添加一个cdev */
       ret= cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
       if(ret< 0)
              gotodel_unregister;

       /*4、创建类      */
       leddev.class= class_create(THIS_MODULE, LEDDEV_NAME);
       if(IS_ERR(leddev.class)) {
              gotodel_cdev;
       }

       /*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

 
测试的过程中遇见了几个BUG,分享出来:
1、 err -2 

如图发现gpio相关的很多函数查找不到,原因是没有进行申明,在程序接为需要申明下:
module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xhy");
MODULE_INFO(intree, "Y");
2、 err -22
无法加载
执行modprobe led的时候没有打印信息,之后对dev进行查询发现没有加载驱动,执行rmmod led打印leddriver_exit,然后再执行modprobe led报错err  -22。 

查询资料发现其实Linux内核时调用了pinctrl的gpio接口的,打开/home/helloxhy/mylinux/drivers/pinctrl/stm32/pinctrl-stm32.c文件,搜索gpio_set发现由13处,其中第870行代码为
static const struct pinmux_ops stm32_pmx_ops = {
    .get_functions_count    = stm32_pmx_get_funcs_cnt,
    .get_function_name      = stm32_pmx_get_func_name,
    .get_function_groups    = stm32_pmx_get_func_groups,
    .set_mux        = stm32_pmx_set_mux,
    .gpio_set_direction       = stm32_pmx_gpio_set_direction,
    .strict                    = true,
};,
这是个结构体,把其中的true改为false,然后重新编译内核并使用新的内核重启:
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- uImage dtbs LOADADDR=0XC2000040 -j16
cp arch/arm/boot/uImage /home/helloxhy/tftp/ -rf
然后重新执行: 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值