当前位置: 首页 > news >正文

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);  // 打开 LED1sleep(1);  // 等待 1 秒ioctl(fd, LED_OFF);    // 关闭 LED1sleep(1);  // 等待 1 秒}return 0;
}

http://www.lryc.cn/news/543347.html

相关文章:

  • Java 常见的面试题(设计模式)
  • 机器学习3-聚类
  • html中的css
  • 36. Spring Boot 2.1.3.RELEASE 中实现监控信息可视化并添加邮件报警功能
  • Linux: 已占用接口
  • Vscode的通义灵码占用空间过大问题【.lingma】
  • 鸿蒙Next如何自定义标签页
  • 知识拓展:Python 接口实现方式对比:Protocol vs @implementer
  • 开源程序wordpress在海外品牌推广中的重要作用
  • 【Python爬虫(89)】爬虫“反水”:助力数字版权保护的逆向之旅
  • k8s面试题总结(五)
  • 文章精读篇——用于遥感小样本语义分割的可学习Prompt
  • Spring Boot2.0之十 使用自定义注解、Json序列化器实现自动转换字典类型字段
  • 从电子管到量子计算:计算机技术的未来趋势
  • 将CUBE或3DL LUT转换为PNG图像
  • python文件的基本操作,文件读写
  • 华为认证考试证书下载步骤(纸质+电子版)
  • 正式页面开发-登录注册页面
  • nss刷题5(misc)
  • 深入Linux序列:进程的终止与等待
  • 蓝桥杯之日期问题2
  • 【STL】7.STL常用算法(1)
  • uniapp 本地数据库多端适配实例(根据运行环境自动选择适配器)
  • 百度觉醒,李彦宏渴望光荣
  • 【算法工程】大模型局限性新发现之解决能连github但无法clone项目的问题
  • SOME/IP-SD -- 协议英文原文讲解3
  • 软件测试八股文,软件测试常见面试合集【附答案】
  • 数据结构秘籍(一)线性数据结构
  • TFChat:腾讯大模型知识引擎(DeepSeek R1)+飞书机器人实现AI智能助手
  • 使用消息队列怎样防止消息重复?