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;
}
相关推荐
章鱼哥嵌入式开发2 分钟前
# STM32F103 SD卡读写程序
stm32·单片机
stormsha7 分钟前
Linux中su与sudo命令的区别:权限管理的关键差异解析
linux·运维·服务器·鸿蒙系统·ux·batch命令
筏.k1 小时前
grep、wc 与管道符快速上手指南
linux
Johny_Zhao1 小时前
华为MAAS、阿里云PAI、亚马逊AWS SageMaker、微软Azure ML各大模型深度分析对比
linux·人工智能·ai·信息安全·云计算·系统运维
CodeOfCC1 小时前
c语言 封装跨平台线程头文件
linux·c语言·windows
广药门徒1 小时前
定时器时钟来源可以从输入捕获引脚输入
单片机·嵌入式硬件
科文小白狼1 小时前
Linux下VSCode开发环境配置(LSP)
linux·vscode·里氏替换原则·lsp
jugt3 小时前
CentOS 7.9安装Nginx1.24.0时报 checking for LuaJIT 2.x ... not found
linux·运维·centos
多多*4 小时前
LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考
linux·开发语言·redis·python·bootstrap·lua
何双新5 小时前
第21讲、Odoo 18 配置机制详解
linux·python·开源