目录

STM32MP15-FSMP1A单片机移植Linux系统platform总线驱动

之前在该单片机下移植的Linux驱动是学习过程中,对Linux内核驱动的引导学习,接下来才是比较正常的驱动开发。

在Linux内核中,对于驱动的处理,一般会通过总线进行设备信息和设备驱动的匹配,来达到自动检测外设连接系统以及驱动外设的效果,设备信息由内核设备树写入,内核驱动则对设备树信息进行相关的注册、注销等操作,实现效果:设备信息包含可控制的gpio或i2c,驱动对gpio进行高低电平的控制或进行i2c通信获取从机数据。

上图是Linux内核驱动使用内核自带总线驱动进行处理时的处理方式,其中由I2C、PWM等总线驱动,但是实际使用时很多外设驱动不在自带的总线驱动中,因此引入platform虚拟总线驱动以实现相关功能。

这里可以看出platform虚拟总线和Linux内核总线的处理方式基本一致。

Linux设备树文件-->stm32mp157a-fsmp1a.dts

新增设备信息

复制代码
/dts-v1/;
#include "stm32mp157.dtsi"
#include "stm32mp15xa.dtsi"
#include "stm32mp15-pinctrl.dtsi"
#include "stm32mp15xxac-pinctrl.dtsi"
#include "stm32mp15xx-fsmp1x.dtsi" 
/ {
	model = "HQYJ STM32MP157 FSMP1A Discovery Board";
	compatible = "st,stm32mp157a-fsmp1a", "st,stm32mp157";

	aliases {
		serial0 = &uart4;
		serial5 = &usart3;
	};

	chosen {
		stdout-path = "serial0:115200n8";
	};

	reserved-memory {
			gpu_reserved: gpu@d4000000 {
				  reg = <0xd4000000 0x4000000>;
				  no-map;
			  };
		
			optee_memory: optee@0xde000000 {
				  reg = <0xde000000 0x02000000>;
				  no-map;
			  };
	};

	//测试platform设备驱动
	myplatform {
		compatible = "johnson,myplatform_test";
		led1_gpio = <&gpioe 10 0>;
	};
};

以上代码为测试使用设备树信息,其中myplatform为设备名,compatible为设备信息,led1_gpio为该设备控制的gpio为PE10

驱动源码

复制代码
#include <linux/init.h>       // 包含内核初始化相关的头文件
#include <linux/module.h>     // 包含内核模块相关的头文件
#include <linux/of.h>         // 包含设备树操作相关的头文件
#include <linux/gpio.h>       // 包含 GPIO 操作相关的头文件
#include <linux/of_gpio.h>    // 包含设备树 GPIO 相关的头文件
#include <linux/fs.h>         // 包含文件操作相关的头文件
#include <linux/uaccess.h>    // 包含用户空间访问内核空间相关的头文件
#include <linux/device.h>     // 包含设备相关的头文件
#include <linux/platform_device.h> // 包含 platform 设备相关的头文件
#include "platform_test.h"    // 包含自定义头文件

//字符设备
static int major;  // 定义主设备号
//class类
static struct class *led_class;  // 定义类指针
//device节点
static struct device *led_device;  // 定义设备指针
//编写gpio_desc设备
struct gpio_desc *gdesc;

static int led_open(struct inode *inode, struct file *file)
{
    printk("led_open\n");
    return 0;
}

static int led_close(struct inode *inode, struct file *file)
{
    printk("led_close\n");
    return 0;
}

static int led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    printk("led_read\n");
    return 0;
}

static int led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    printk("led_write\n");
    return 0;
}

static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    switch(cmd)
    {
        case LED_ON:
            gpiod_set_value(gdesc, 1);
            break;
        case LED_OFF:
            gpiod_set_value(gdesc, 0);
            break;
        default:
        break;
    }
    printk("led_ioctl\n");
    return 0;
}

//定义file_operations结构体
struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .release = led_close,
    .read = led_read,
    .write = led_write,
    .unlocked_ioctl = led_ioctl,
};

//编写platform设备驱动
static int mydriver_probe(struct platform_device *pdev)
{
    printk("mydriver_probe\n");

    //注册字符设备
    major = register_chrdev(0, "leds_control", &fops);    
    if(major < 0)
    {
        printk("register_chrdev failed\n");
        return -ENODEV;
    }

    //创建类
    led_class = class_create(THIS_MODULE, "leds_control");
    if (IS_ERR(led_class))
    {
        printk("class_create failed\n");
        unregister_chrdev(major, "leds_control");
        return -ENODEV;
    }

    //创建设备节点
    led_device = device_create(led_class, NULL, MKDEV(major, 0), NULL, "leds_control");
    if (IS_ERR(led_device))
    {
        printk("device_create failed\n");
        class_destroy(led_class);
        unregister_chrdev(major, "leds_control");
        return -ENODEV;
    }   
    
    gdesc = gpiod_get_from_of_node(pdev->dev.of_node, "led1_gpio", 0, GPIOD_OUT_HIGH, NULL);
    if (IS_ERR(gdesc))
    {
        printk("gpiod_get_from_of_node failed\n");
        class_destroy(led_class);
        device_destroy(led_class, MKDEV(major, 0));
        unregister_chrdev(major, "leds_control");
        return PTR_ERR(gdesc);
    }
    printk("gpiod_get_from_of_node success\n");
    return 0;
}

static int mydriver_remove(struct platform_device *pdev)
{
    printk("mydriver_remove\n");
    gpiod_set_value(gdesc, 0);
    gpiod_put(gdesc);

    device_destroy(led_class, MKDEV(major, 0));
    class_destroy(led_class);
    unregister_chrdev(major, "leds_control");
    
    return 0;
}

static const struct of_device_id myplatform_test_match_table[] = {
    { .compatible = "johnson,myplatform_test", },
    { /* sentinel */ }
};


struct platform_driver mydriver = {
    .probe = mydriver_probe,
    .remove = mydriver_remove,
    .driver = {
        .name = "myplatform",
        .of_match_table = myplatform_test_match_table,
    },
};

/* static __init int mydriver_init(void)
{
    platform_driver_register(&mydriver);
    return 0;
}

static __exit void mydriver_exit(void)
{
    platform_driver_unregister(&mydriver);
}

module_init(mydriver_init);
module_exit(mydriver_exit); */

//一键注册宏定义
module_platform_driver(mydriver);
MODULE_LICENSE("GPL");

驱动头文件

复制代码
#ifndef __PLATFROM_H__
#define __PLATFROM_H__

#define LED_ON _IO('l', 1)
#define LED_OFF _IO('l', 0)

#endif

应用程序-->实现1s控制LED灯亮灭

复制代码
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
#include<sys/ioctl.h>
#include "platform_test.h"            // 包含自定义头文件

int main(int argc,const char * argv[])
{
    int fd;
    fd = open("/dev/leds_control", O_RDWR);  // 打开设备文件
    if(fd < 0)  // 如果打开设备文件失败
    {
        perror("open");  // 打印错误信息
        return -1;  // 返回错误码
    }
    printf("open success\n");  // 打印打开设备文件成功的消息

    while(1)
    {
        ioctl(fd, LED_ON);  // 打开 LED1
        sleep(1);  // 等待 1 秒
        ioctl(fd, LED_OFF);    // 关闭 LED1
        sleep(1);  // 等待 1 秒

    }

    return 0;
}
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
清风~徐~来1 小时前
【Linux】环境变量
linux·运维·chrome
YdaMooc3 小时前
STM32-FreeRTOS的详细配置
stm32·单片机·嵌入式硬件
云山工作室3 小时前
基于STM32的智能门禁系统
stm32·单片机·毕业设计·毕设
Willliam_william4 小时前
QEMU学习之路(8)— ARM32通过u-boot 启动Linux
linux·学习·elasticsearch
无处在4 小时前
STM32 四足机器人常见问题汇总
stm32·嵌入式硬件·机器人
橘猫0.o5 小时前
【Linux 并发与竞争实验】
linux·运维·服务器·驱动开发
南梦也要学习6 小时前
STM32江科大-----PWR电源控制
stm32·单片机·嵌入式硬件
时光の尘6 小时前
FreeRTOS菜鸟入门(五)·空闲任务与阻塞延时的实现
c语言·stm32·嵌入式硬件·mcu·物联网·freertos
洁✘7 小时前
shell编程正则表达式与文本处理器
linux·运维·正则表达式
深夜面包7 小时前
Ubuntu 安装与配置 Docker
linux·ubuntu·docker