前言:
介绍之前的三问:
什么是平台总线?为什么要使用平台总线?如何使用平台总线?
1、总线:
但凡有总线,就一定有信息的传输。USB总线 IIC总线 SPI总线等 用于设备间信息的传输,并且在设备间有物理连线的。属于物理总线。
平台总线也是总线的一种,但它是Linux内核虚拟出来的,抽象出来的总线,实际上不存在样的总线。
2、为什么要使用总线:
首先说一下总线的优点:减少重复代码,提高程序的移植性,设备挂载在平台总线上,方便管理。
设备树与平台总线类似都可将设备与驱动分离,虽然设备树可以代替平台总线来描述硬件设备,但它们的角色和作用是不同的。设备树主要用于描述硬件设备的层次结构、寄存器配置和中断信息等,以便驱动程序能够正确地与硬件设备进行通信。而平台总线则提供了一种物理接口和通信协议,使得不同的硬件设备能够在同一个系统中进行互联和通信。学习平台总线还是有必要的。
3、如何使用平台总线:
a,编写两个C源文件(driver.c和device.c)
device.c中写的是硬件资源,包括寄存器的地址,中断号,时钟等等。
driver.c中写的是驱动相同的部分,包括申请设备号,创建设备节点等等。
b,将driver.c和device.c注册到平台总线中
c,使用平台总线将driver.c和device.c联系在一起---设置匹配的字符串
一、编写平台总线部分内容介绍:
1、首先是硬件描述相关的结构体:--------device.c
硬件设备资源的结构体可以调用这样的结构体
struct platform_device {
const char * name;
//暗号--用于匹配的名字-----并且在我们加载完device.c后它会在sys/bus/.....生成此名字的文件
int id; //设备id,通常是-1(表示只有一个实例),它是用于,当有两个相同name的设备进行变化的
struct device dev;//父类,总线match时,实际match的是dev,所有设备共性的部分
u32 num_resources;//资源的数量
struct resource * resource;//硬件资源
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */ //强制匹配的驱动程序名称
/* MFD cell pointer */ //多功能设备 单元指针
struct mfd_cell *mfd_cell;
/* arch specific additions */ //arch中特定的附加物
struct pdev_archdata archdata;
};
2、描述硬件资源的结构体 ------一般用来表示两种资源,地址资源和中断资源--------device.c
struct resource {
resource_size_t start; //如果是地址资源---资源的开始地址;如果是中断资源---中断通道号
resource_size_t end;//如果是地址资源---资源的最后一个字节的地址;如果是中断资源---中断通道号
const char *name;//资源的名字
unsigned long flags;//资源的种类 IORESOURCE_MEM(地址资源)IORESOURCE_IRQ(中断资源)
};
3、linux内核中,描述驱动部分的结构体----------driver.c
3>linux内核中,描述驱动部分的结构体----------driver.c
struct platform_driver {
int (*probe)(struct platform_device *);
//当device.c和driver.c匹配成功,就会执行probe中的内容----//也就是模块加载函数总的内容
int (*remove)(struct platform_device *);
//当卸载device.c和driver.c中任意一个后执行的内容-----//也就是模块卸载函数中的内容
void (*shutdown)(struct platform_device *);
//当设备收到shutdown命令的时候执行的函数
int (*suspend)(struct platform_device *, pm_message_t state);
//当设备收到suspend命令的时候执行的函数---休眠
int (*resume)(struct platform_device *);
//当设备收到resume命令的时候执行的函数---唤醒
struct device_driver driver; //父类
const struct platform_device_id *id_table;//与device.c中的名字相匹配----暗号
bool prevent_deferred_probe;//设置为私有
};
4、注销注册函数:
device.c:
int platform_device_register(struct platform_device *pdev)//注册到平台总线中
void platform_device_unregister(struct platform_device *pdev) //从平台总线注销
drivers.c:
int platform_driver_register(struct platform_driver *drv)//注册到平台总线中
void platform_driver_unregister(struct platform_driver *drv) //从平台总线注销
二、平台总线的编写步骤:
建立:drv_device.c文件,drv_driver.c和一个makefile文件
1.1基本框架:
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "stm32mp157xxx.h"
#include "platform_data_special.h"
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "stm32mp157xxx.h"
#include "platform_data_special.h"
struct resource resource_1={//用于存放资源
};
//自定义资源地址
struct Platform_data_spex pdata={
};
void led_close(struct device *dev)
{
}
struct platform_device pdev[]={
};
static int __init led_device_init(void)
{
//加载驱动时候注册平台
platform_device_unregister(&pdev[i-1]);//在平台总线中移除一个硬件设备
return ret;
}
void __exit led_device_exit(void)
{
int i;
printk("-----------------%s------------------\n",__FUNCTION__);
platform_device_unregister(&pdev[i]);//平台总线中移除一个硬件设备
}
module_init(led_device_init);
module_exit(led_device_exit);
MODULE_LICENSE("GPL");
driver.c的文件编写。
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "linux/mod_devicetable.h"
#include "linux/slab.h"
#include "platform_data_special.h"
#include "linux/fs.h"
#include "linux/device.h"
#include "asm/io.h"
#include "stm32mp157xxx.h"
#include "linux/uaccess.h"
//先自定义一个结构体用于驱动,必要信息
struct stm32mp157axxx{
unsigned int major;//表示动态申请主设备号
struct class *cls;//类
struct device *dev;//设备节点
};
const struct file_operations fops={//设备相关的接口
};
int led_probe(struct platform_device *pdev){}//创建平台总线调用的函数
const struct platform_device_id led_id_table[]={}//匹配存放的字符串
int led_remove(struct platform_device *pdev){}//卸载调用函数
创建平台总线的结构体
struct platform_driver pdrv={
.probe = led_probe,//在驱动driver.c和device.c匹配成功后,会调用这个函数
.remove = led_remove,//卸载时候调用
.driver ={
.name = "led_drv",
},
.id_table = led_id_table,
}
int __init drv_driver_init(void)
{
int ret=0;
printk("-----------------%s------------------\n",__FUNCTION__);
注册
return ret;
}
void __exit drv_driver_exit(void)
{
printk("-----------------%s------------------\n",__FUNCTION__);
卸载
}
module_init(drv_driver_init);
module_exit(drv_driver_exit);
MODULE_LICENSE("GPL");
说实话上面的这些我自己看了都头疼,不知道写了写啥,不会总结,算了直接拿最简单的led灯的驱动来说话。后续自己看起来也好受点。
三、以驱动stm32mp157的 led灯为示例:
1、首先看了一下开发板灯的信息。
灯在原理图中的位置,有两处。
主板的灯
扩展板的灯
2、所以我们需要开的灯在GPIOZ,GPIOE,GPIOF上。所以找手册上
直接在数据手册上搜GPIOZ所以知道我们需要开的总线是AHB5和AHB4
3、且由上面查找知道GPIOZ,GPIOE,GPIOF的地址是0x54004000,0x50006000,0x50007000
4、我们点灯需要操作的寄存器是:MODE寄存器,pin_x号引脚,PZ7所以.MODER_SET =0x1<<14(.MODER_SET 下面的头文件会定于,自己编写定义的)
5、输出ODR寄存器:
6、在编写之前需要将寄存器的结构体编写出来,方便后续操作寄存器设置灯的状态。
7开灯需要用到的寄存器有两个GPIO和RCC的寄存器。
所以编写一个STM32MP157XXX.h的头文件方便调用,可以查看手册找到具体的内容介绍
#ifndef __STM32MP157XXX__
#define __STM32MP157XXX__
#define GPIOZ_Base 0x54004000
#define GPIOE_Base 0x50006000
#define GPIOF_Base 0x50007000
#define RCC_Base 0x50000000
#define __IO volatile
#define uint32_t unsigned int
#define uint16_t unsigned short
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint16_t BSRRL; /*!< GPIO port bit set/reset low register, Address offset: 0x18 */
__IO uint16_t BSRRH; /*!< GPIO port bit set/reset high register, Address offset: 0x1A */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
__IO uint32_t BRR; /*!< GPIO port bit reset register, Address offset: 0x28 */
__IO uint32_t BUF[1]; /*!< NO register, Address offset: 0x2C */
__IO uint32_t SECCFGR; /*!< GPIO secure configuration register, Address offset: 0x30 */
} GPIO_TypeDef;
typedef struct {
__IO unsigned int TZCR; // 0x000
__IO unsigned int res1[2]; // 0x004-0x008
__IO unsigned int OCENSETR; // 0x00C
__IO unsigned int OCENCLRR; // 0x010
__IO unsigned int res2[1]; // 0x014
__IO unsigned int HSICFGR; // 0x018
__IO unsigned int CSICFGR; // 0x01C
__IO unsigned int MPCKSELR; // 0x020
__IO unsigned int ASSCKSELR; // 0x024
__IO unsigned int PCK12SELR; // 0x028
__IO unsigned int MPCKDIVR; // 0x02C
__IO unsigned int AXIDIVR; // 0x030
__IO unsigned int res3[2];
__IO unsigned int APB4DIVR; // 0x03C
__IO unsigned int APB5DIVR; // 0x040
__IO unsigned int RTCDIVR; // 0x044
__IO unsigned int MSSCKSELR; // 0x048
__IO unsigned int res4[13];
__IO unsigned int PLL1CR; // 0x080
__IO unsigned int PLL1CFGR1; // 0x084
__IO unsigned int PLL1CFGR2; // 0x088
__IO unsigned int PLL1FRACR; // 0x08C
__IO unsigned int PLL1CSGR; // 0x090
__IO unsigned int PLL2CR; // 0x094
__IO unsigned int PLL2CFGR1; // 0x098
__IO unsigned int PLL2CFGR2; // 0x09C
__IO unsigned int PLL2FRACR; // 0x0A0
__IO unsigned int PLL2CSGR; // 0x0A4
__IO unsigned int res5[6];
__IO unsigned int I2C46CKSELR; // 0x0C0
__IO unsigned int SPI6CKSELR; // 0x0C4
__IO unsigned int UART1CKSELR; // 0x0C8
__IO unsigned int RNG1CKSELR; // 0x0CC
__IO unsigned int CPERCKSELR; // 0x0D0
__IO unsigned int STGENCKSELR; // 0x0D4
__IO unsigned int DDRITFCR; // 0x0D8
__IO unsigned int res6[9];
__IO unsigned int MP_BOOTCR; // 0x100
__IO unsigned int MP_SREQSETR; // 0x104
__IO unsigned int MP_SREQCLRR; // 0x108
__IO unsigned int MP_GCR; // 0x10C
__IO unsigned int MP_APRSTCR; // 0x110
__IO unsigned int MP_APRSTSR; // 0x114
__IO unsigned int res7[10];
__IO unsigned int BDCR; // 0x140
__IO unsigned int RDLSICR; // 0x144
__IO unsigned int res8[14];
__IO unsigned int APB4RSTSETR; // 0x180
__IO unsigned int APB4RSTCLRR; // 0x184
__IO unsigned int APB5RSTSETR; // 0x188
__IO unsigned int APB5RSTCLRR; // 0x18C
__IO unsigned int AHB5RSTSETR; // 0x190
__IO unsigned int AHB5RSTCLRR; // 0x194
__IO unsigned int AHB6RSTSETR; // 0x198
__IO unsigned int AHB6RSTCLRR; // 0x19C
__IO unsigned int TZAHB6RSTSELR;// 0x1A0
__IO unsigned int TZAHB6RSTCLRR;// 0x1A4
__IO unsigned int res9[22];
__IO unsigned int MP_APB4ENSETR;// 0x200
__IO unsigned int MP_APB4ENCLRR;// 0x204
__IO unsigned int MP_APB5ENSETR;// 0x208
__IO unsigned int MP_APB5ENCLRR;// 0x20C
__IO unsigned int MP_AHB5ENSETR;// 0x210
__IO unsigned int MP_AHB5ENCLRR;// 0x214
__IO unsigned int MP_AHB6ENSETR;// 0x218
__IO unsigned int MP_AHB6ENCLRR;// 0x21C
__IO unsigned int MP_TZAHB6ENSELR;// 0x220
__IO unsigned int MP_TZAHB6ENCLRR;// 0x224
__IO unsigned int res10[22];
__IO unsigned int MC_APB4ENSETR; // 0x280
__IO unsigned int MC_APB4ENCLRR; // 0x284
__IO unsigned int MC_APB5ENSETR; // 0x288
__IO unsigned int MC_APB5ENCLRR; // 0x28C
__IO unsigned int MC_AHB5ENSETR; // 0x290
__IO unsigned int MC_AHB5ENCLRR; // 0x294
__IO unsigned int MC_AHB6ENSETR; // 0x298
__IO unsigned int MC_AHB6ENCLRR; // 0x29C
__IO unsigned int res11[24];
__IO unsigned int MP_APB4LPENSETR; // 0x300
__IO unsigned int MP_APB4LPENCLRR; // 0x304
__IO unsigned int MP_APB5LPENSETR; // 0x308
__IO unsigned int MP_APB5LPENCLRR; // 0x30C
__IO unsigned int MP_AHB5LPENSETR; // 0x310
__IO unsigned int MP_AHB5LPENCLRR; // 0x314
__IO unsigned int MP_AHB6LPENSETR; // 0x318
__IO unsigned int MP_AHB6LPENCLRR; // 0x31C
__IO unsigned int MP_TZAHB6LPENSETR; // 0x320
__IO unsigned int MP_TZAHB6LPENCLRR; // 0x324
__IO unsigned int res12[22];
__IO unsigned int MC_APB4LPENSETR; // 0x380
__IO unsigned int MC_APB4LPENCLRR; // 0x384
__IO unsigned int MC_APB5LPENSETR; // 0x388
__IO unsigned int MC_APB5LPENCLRR; // 0x38C
__IO unsigned int MC_AHB5LPENSETR; // 0x390
__IO unsigned int MC_AHB5LPENCLRR; // 0x394
__IO unsigned int MC_AHB6LPENSETR; // 0x398
__IO unsigned int MC_AHB6LPENCLRR; // 0x39C
__IO unsigned int res13[24];
__IO unsigned int BR_RSTSCLRR; // 0x400
__IO unsigned int MP_GRSTCSETR; // 0x404
__IO unsigned int MP_RSTSR; // 0x408
__IO unsigned int MP_IWDGFZSETR; // 0x40C
__IO unsigned int MP_IWDGFZCLRR; // 0x410
__IO unsigned int MP_CIER; // 0x414
__IO unsigned int MP_CIFR; // 0x418
__IO unsigned int PWRLPDLYCR; // 0x41C
__IO unsigned int MP_RSTSS; // 0x420
__IO unsigned int res14[247];
__IO unsigned int MCO1CFGR; // 0x800
__IO unsigned int MCO2CFGR; // 0x804
__IO unsigned int OCRDYR; // 0x808
__IO unsigned int DBGCFGR; // 0x80C
__IO unsigned int res15[4];
__IO unsigned int RCK3SELR; // 0x820
__IO unsigned int RCK4SELR; // 0x824
__IO unsigned int TIMG1PRER; // 0x828
__IO unsigned int TIMG2PRER; // 0x82C
__IO unsigned int MCUDIVR; // 0x830
__IO unsigned int APB1DIVR; // 0x834
__IO unsigned int APB2DIVR; // 0x838
__IO unsigned int APB3DIVR; // 0x83C
__IO unsigned int res16[16];
__IO unsigned int PLL3CR; // 0x880
__IO unsigned int PLL3CFGR1; // 0x884
__IO unsigned int PLL3CFGR2; // 0x888
__IO unsigned int PLL3FRACR; // 0x88C
__IO unsigned int PLL3CSGR; // 0x890
__IO unsigned int PLL4CR; // 0x894
__IO unsigned int PLL4CFGR1; // 0x898
__IO unsigned int PLL4CFGR2; // 0x89C
__IO unsigned int PLL4FRACR; // 0x8A0
__IO unsigned int PLL4CSGR; // 0x8A4
__IO unsigned int res17[6];
__IO unsigned int I2C12CKSELR; // 0x8C0
__IO unsigned int I2C35CKSELR; // 0x8C4
__IO unsigned int SAI1CKSELR; // 0x8C8
__IO unsigned int SAI2CKSELR; // 0x8CC
__IO unsigned int SAI3CKSELR; // 0x8D0
__IO unsigned int SAI4CKSELR; // 0x8D4
__IO unsigned int SPI2S1CKSELR; // 0x8D8
__IO unsigned int SPI2S23CKSELR; // 0x8DC
__IO unsigned int SPI45CKSELR; // 0x8E0
__IO unsigned int UART6CKSELR; // 0x8E4
__IO unsigned int UART24CKSELR; // 0x8E8
__IO unsigned int UART35CKSELR; // 0x8EC
__IO unsigned int UART78CKSELR; // 0x8F0
__IO unsigned int SDMMC12CKSELR; // 0x8F4
__IO unsigned int SDMMC3CKSELR; // 0x8F8
__IO unsigned int ETHCKSELR; // 0x8FC
__IO unsigned int QSPICKSELR; // 0x900
__IO unsigned int FMCCKSELR; // 0x904
__IO unsigned int res18[1];
__IO unsigned int FDCANCKSELR; // 0x90C
__IO unsigned int res19[1];
__IO unsigned int SPDIFCKSELR; // 0x914
__IO unsigned int CECCKSELR; // 0x918
__IO unsigned int USBCKSELR; // 0x91C
__IO unsigned int RNG2CKSELR; // 0x920
__IO unsigned int DSICKSELR; // 0x924
__IO unsigned int ADCCKSELR; // 0x928
__IO unsigned int LPTIM45CKSELR; // 0x92C
__IO unsigned int LPTIM23CKSELR; // 0x930
__IO unsigned int LPTIM1CKSELR; // 0x934
__IO unsigned int res20[18];
__IO unsigned int APB1RSTSETR; // 0x980
__IO unsigned int APB1RSTCLRR; // 0x984
__IO unsigned int APB2RSTSETR; // 0x988
__IO unsigned int APB2RSTCLRR; // 0x98C
__IO unsigned int APB3RSTSETR; // 0x990
__IO unsigned int APB3RSTCLRR; // 0x994
__IO unsigned int AHB2RSTSETR; // 0x998
__IO unsigned int AHB2RSTCLRR; // 0x99C
__IO unsigned int AHB3RSTSETR; // 0x9A0
__IO unsigned int AHB3RSTCLRR; // 0x9A4
__IO unsigned int AHB4RSTSETR; // 0x9A8
__IO unsigned int AHB4RSTCLRR; // 0x9AC
__IO unsigned int res21[20];
__IO unsigned int MP_APB1ENSETR; // 0xA00
__IO unsigned int MP_APB1ENCLRR; // 0xA04
__IO unsigned int MP_APB2ENSETR; // 0xA08
__IO unsigned int MP_APB2ENCLRR; // 0xA0C
__IO unsigned int MP_APB3ENSETR; // 0xA10
__IO unsigned int MP_APB3ENCLRR; // 0xA14
__IO unsigned int MP_AHB2ENSETR; // 0xA18
__IO unsigned int MP_AHB2ENCLRR; // 0xA1C
__IO unsigned int MP_AHB3ENSETR; // 0xA20
__IO unsigned int MP_AHB3ENCLRR; // 0xA24
__IO unsigned int MP_AHB4ENSETR; // 0xA28
__IO unsigned int MP_AHB4ENCLRR; // 0xA2C
__IO unsigned int res22[2];
__IO unsigned int MP_MLAHBENSETR; // 0xA38
__IO unsigned int MP_MLAHBENCLRR; // 0xA3C
__IO unsigned int res23[16];
__IO unsigned int MC_APB1ENSETR; // 0xA80
__IO unsigned int MC_APB1ENCLRR; // 0xA84
__IO unsigned int MC_APB2ENSETR; // 0xA88
__IO unsigned int MC_APB2ENCLRR; // 0xA8C
__IO unsigned int MC_APB3ENSETR; // 0xA90
__IO unsigned int MC_APB3ENCLRR; // 0xA94
__IO unsigned int MC_AHB2ENSETR; // 0xA98
__IO unsigned int MC_AHB2ENCLRR; // 0xA9C
__IO unsigned int MC_AHB3ENSETR; // 0xAA0
__IO unsigned int MC_AHB3ENCLRR; // 0xAA4
__IO unsigned int MC_AHB4ENSETR; // 0xAA8
__IO unsigned int MC_AHB4ENCLRR; // 0xAAC
__IO unsigned int MC_AXIMENSETR; // 0xAB0
__IO unsigned int MC_AXIMENCLRR; // 0xAB4
__IO unsigned int MC_MLAHBENSETR; // 0xAB8
__IO unsigned int MC_MLAHBENCLRR; // 0xABC
__IO unsigned int res24[16];
__IO unsigned int MP_APB1LPENSETR; // 0xB00
__IO unsigned int MP_APB1LPENCLRR; // 0xB04
__IO unsigned int MP_APB2LPENSETR; // 0xB08
__IO unsigned int MP_APB2LPENCLRR; // 0xB0C
__IO unsigned int MP_APB3LPENSETR; // 0xB10
__IO unsigned int MP_APB3LPENCLRR; // 0xB14
__IO unsigned int MP_AHB2LPENSETR; // 0xB18
__IO unsigned int MP_AHB2LPENCLRR; // 0xB1C
__IO unsigned int MP_AHB3LPENSETR; // 0xB20
__IO unsigned int MP_AHB3LPENCLRR; // 0xB24
__IO unsigned int MP_AHB4LPENSETR; // 0xB28
__IO unsigned int MP_AHB4LPENCLRR; // 0xB2C
__IO unsigned int MP_AXIMLPENSETR; // 0xB30
__IO unsigned int MP_AXIMLPENCLRR; // 0xB34
__IO unsigned int MP_MLAHBLPENSETR; // 0xB38
__IO unsigned int MP_MLAHBLPENCLRR; // 0xB3C
__IO unsigned int res25[16];
__IO unsigned int MC_APB1LPENSETR; // 0xB80
__IO unsigned int MC_APB1LPENCLRR; // 0xB84
__IO unsigned int MC_APB2LPENSETR; // 0xB88
__IO unsigned int MC_APB2LPENCLRR; // 0xB8C
__IO unsigned int MC_APB3LPENSETR; // 0xB90
__IO unsigned int MC_APB3LPENCLRR; // 0xB94
__IO unsigned int MC_AHB2LPENSETR; // 0xB98
__IO unsigned int MC_AHB2LPENCLRR; // 0xB9C
__IO unsigned int MC_AHB3LPENSETR; // 0xBA0
__IO unsigned int MC_AHB3LPENCLRR; // 0xBA4
__IO unsigned int MC_AHB4LPENSETR; // 0xBA8
__IO unsigned int MC_AHB4LPENCLRR; // 0xBAC
__IO unsigned int MC_AXIMLPENSETR; // 0xBB0
__IO unsigned int MC_AXIMLPENCLRR; // 0xBB4
__IO unsigned int MC_MLAHBLPENSETR; // 0xBB8
__IO unsigned int MC_MLAHBLPENCLRR; // 0xBBC
__IO unsigned int res26[16];
__IO unsigned int MC_RSTSCLRR; // 0xC00
__IO unsigned int res27[4];
__IO unsigned int MC_CIER; // 0xC14
__IO unsigned int MC_CIFR; // 0xC18
__IO unsigned int res28[246];
__IO unsigned int VERR; // 0xFF4
__IO unsigned int IDR; // 0xFF8
__IO unsigned int SIDR; // 0xFFC
}RCC_TypeDef;
#endif //__STM32MP157XXX__
5、这里编写一份我们自己需要用到的平台总线自定义资源的头文件
#ifndef __PLATFORM_DATA_SPECIAL__
#define __PLATFORM_DATA_SPECIAL__
struct Platform_data_spex{
char *name; // 设备�?
unsigned int minum;// 次设备号
unsigned int MODER_SET;
unsigned int MODER_RESET;
unsigned int OTYPER_SET;
unsigned int OTYPER_RESET;
unsigned int OSPEEDR_SET;
unsigned int OSPEEDR_RESET;
unsigned int PUPDR_SET;
unsigned int PUPDR_RESET;
unsigned int IDR_SET;
unsigned int IDR_RESET;
unsigned int ODR_SET;
unsigned int ODR_RESET;
unsigned int BSRRL_SET;
unsigned int BSRRL_RESET;
unsigned int BSRRH_SET;
unsigned int BSRRH_RESET;
unsigned int LCKR_SET;
unsigned int LCKR_RESET;
unsigned int BRR_SET;
unsigned int BRR_RESET;
unsigned int SECCFGR_SET;
unsigned int SECCFGR_RESET;
unsigned int RCC_MP_REGISTER; //操作寄存器的哪一个位
unsigned int RCC_OFFSET;
};
#endif //__PLATFORM_DATA_SPECIAL__
四、开始编写设备与驱动文件
1、device设备文件,具体看注释
Linux源码部分解析:
#include "linux/init.h"
#include "linux/module.h"
struct resource led_resource[][2]={//地址资源
[0]={//PZ 5
[0]={//GPIO
.start =GPIOZ_Base,//起始地址
.end =GPIOZ_Base+sizeof(GPIO_TypeDef)-1,//结束地址
.name ="gpioz",//资源名字
.flags =IORESOURCE_MEM,//资源类型,地址资源
},
[1]={//RCC资源
.start =RCC_Base,//RCC基地址
.end =RCC_Base+sizeof(RCC_TypeDef)-1,//RCC结束地址
.name ="rcc",
.flags =IORESOURCE_MEM,//地址资源
},
},
[1]={//PZ 6
[0]={//GPIO
.start =GPIOZ_Base,
.end =GPIOZ_Base+sizeof(GPIO_TypeDef)-1,
.name ="gpioz",
.flags =IORESOURCE_MEM,
},
[1]={//RCC��Դ
.start =RCC_Base,
.end =RCC_Base+sizeof(RCC_TypeDef)-1,
.name ="rcc",
.flags =IORESOURCE_MEM,
},
},
[2]={//PZ 7
[0]={//GPIO
.start =GPIOZ_Base,
.end =GPIOZ_Base+sizeof(GPIO_TypeDef)-1,
.name ="gpioz",
.flags =IORESOURCE_MEM,
},
[1]={//RCC��Դ
.start =RCC_Base,
.end =RCC_Base+sizeof(RCC_TypeDef)-1,
.name ="rcc",
.flags =IORESOURCE_MEM,
},
},
[3]={//PE8
[0]={//GPIO
.start =GPIOE_Base,
.end =GPIOE_Base+sizeof(GPIO_TypeDef)-1,
.name ="gpioe",
.flags =IORESOURCE_MEM,
},
[1]={//RCC��Դ
.start =RCC_Base,
.end =RCC_Base+sizeof(RCC_TypeDef)-1,
.name ="rcc",
.flags =IORESOURCE_MEM,
},
},
[4]={//PE10
[0]={//GPIO
.start =GPIOE_Base,
.end =GPIOE_Base+sizeof(GPIO_TypeDef)-1,
.name ="gpioe",
.flags =IORESOURCE_MEM,
},
[1]={//RCC��Դ
.start =RCC_Base,
.end =RCC_Base+sizeof(RCC_TypeDef)-1,
.name ="rcc",
.flags =IORESOURCE_MEM,
},
},
[5]={//PF10
[0]={//GPIO
.start =GPIOF_Base,
.end =GPIOF_Base+sizeof(GPIO_TypeDef)-1,
.name ="gpiof",
.flags =IORESOURCE_MEM,
},
[1]={//RCC��Դ
.start =RCC_Base,
.end =RCC_Base+sizeof(RCC_TypeDef)-1,
.name ="rcc",
.flags =IORESOURCE_MEM,
},
},
};
struct Platform_data_spex led_special[]={//自定义资源
[0]={//pz5
.name ="drv_led0",//设备名字名字
.minum =0,//次设备号,此处我们用次设备号用于排序
.MODER_SET =0x1<<10,//位移操作,之前分析寄存器的时候有所要用哪些
.MODER_RESET =0x3<<10,
.ODR_SET =0x1<<5,
.ODR_RESET =0x1<<5,
.RCC_MP_REGISTER=0,
.RCC_OFFSET = 0x210/4,//偏移量,寄存器大小
},
[1]={//PZ6
.name ="drv_led1",
.minum =1,
.MODER_SET =0x1<<12,
.MODER_RESET =0x3<<12,
.ODR_SET =0x1<<6,
.ODR_RESET =0x1<<6,
.RCC_MP_REGISTER=0,
.RCC_OFFSET = 0x210/4,
},
[2]={//PZ7
.name ="drv_led2",
.minum =2,
.MODER_SET =0x1<<14,
.MODER_RESET =0x3<<14,
.ODR_SET =0x1<<7,
.ODR_RESET =0x1<<7,
.RCC_MP_REGISTER=0,
.RCC_OFFSET = 0x210/4,
},
[3]={//PE8
.name ="drv_led3",
.minum =3,
.MODER_SET =0x1<<16,
.MODER_RESET =0x3<<16,
.ODR_SET =0x1<<8,
.ODR_RESET =0x1<<8,
.RCC_MP_REGISTER=4,
.RCC_OFFSET = 0xA28/4,
},
[4]={//PE10
.name ="drv_led4",
.minum =4,
.MODER_SET =0x1<<20,
.MODER_RESET =0x3<<20,
.ODR_SET =0x1<<10,
.ODR_RESET =0x1<<10,
.RCC_MP_REGISTER=4,
.RCC_OFFSET = 0xA28/4,
},
[5]={//PF10
.name ="drv_led5",
.minum =5,
.MODER_SET =0x1<<20,
.MODER_RESET =0x3<<20,
.ODR_SET =0x1<<10,
.ODR_RESET =0x1<<10,
.RCC_MP_REGISTER=5,
.RCC_OFFSET = 0xA28/4,
},
};
void led_close(struct device *dev)
{
//必须要调用的,卸载时候会调用
}
struct platform_device pdev[]={ //注册的结构体
[0]={
.name = "stm32mp157_led0",//匹配的名字
.id = -1, //id号一般为-1
.dev ={//设备号结构体
.release = led_close, //卸载时调用
.platform_data =&led_special[0],//获取的自定义资源
},
.num_resources =sizeof(led_resource[0])/sizeof(led_resource[0][0]),//资源个数
.resource = led_resource[0],//资源
},
[1]={
.name = "stm32mp157_led1",
.id = -1,
.dev ={
.release = led_close,
.platform_data =&led_special[1],
},
.num_resources =sizeof(led_resource[1])/sizeof(led_resource[1][0]),
.resource = led_resource[1],
},
[2]={
.name = "stm32mp157_led2",
.id = -1,
.dev ={
.release = led_close,
.platform_data =&led_special[2],
},
.num_resources =sizeof(led_resource[2])/sizeof(led_resource[2][0]),
.resource = led_resource[2],
},
[3]={
.name = "stm32mp157_led3",
.id = -1,
.dev ={
.release = led_close,
.platform_data =&led_special[3],
},
.num_resources =sizeof(led_resource[3])/sizeof(led_resource[3][0]),
.resource = led_resource[3],
},
[4]={
.name = "stm32mp157_led4",
.id = -1,
.dev ={
.release = led_close,
.platform_data =&led_special[4],
},
.num_resources =sizeof(led_resource[4])/sizeof(led_resource[4][0]),
.resource = led_resource[4],
},
[5]={
.name = "stm32mp157_led5",
.id = -1,
.dev ={
.release = led_close,
.platform_data =&led_special[5],
},
.num_resources =sizeof(led_resource[5])/sizeof(led_resource[5][0]),
.resource = led_resource[5],
},
};
static int __init led_device_init(void)//入口函数
{
int ret =0;//判断返回值
int i;//循环注册的变量
for(i=0;i<ARRAY_SIZE(pdev);i++){//ARRAY_SIZES是一个宏用于求数据的大小,pdev是资源获取的机构体
ret =platform_device_register(&pdev[i]);//注册的个数
if(ret<0){//注册失败
printk("dev_led_init:platform_device_register is error\n");
goto platform_device_register_err;//跳转到下面错误的操作
}
}
return ret;
platform_device_register_err://存在错误删除上述操作,退出
for(;i>0;i--){
platform_device_unregister(&pdev[i-1]);
}
return ret;
}
void __exit led_device_exit(void)
{
int i;
printk("-----------------%s------------------\n",__FUNCTION__);
for(i=0;i<ARRAY_SIZE(pdev);i++){
platform_device_unregister(&pdev[i]);//卸载时注销
}
}
module_init(led_device_init);//申明
module_exit(led_device_exit);
MODULE_LICENSE("GPL");//协议
2、drive设备文件,具体看注释
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "linux/mod_devicetable.h"
#include "linux/slab.h"
#include "platform_data_special.h"
#include "linux/fs.h"
#include "linux/device.h"
#include "asm/io.h"
#include "stm32mp157xxx.h"
#include "linux/uaccess.h"
//注册设备号等基本信息结构体
struct stm32mp157axxx{
unsigned int major;//设备号号
struct class *cls;//类,创建需要用到节点类
struct device *dev;//设备节点
struct resource *led_resource[2];//LED的地址资源
struct Platform_data_spex *led_special;//自定义资源
unsigned int *rcc;//RCC标识
GPIO_TypeDef *gpio;//GPIO标识
};
struct stm32mp157axxx **led_drvs;//2级指针全局变量用于存放各类驱动的一级指针
int led_open(struct inode *inode, struct file *filp)//开灯函数,在入口函数后调用
{
int ret = 0;
struct stm32mp157axxx *led_drv;
unsigned int minor;//次设备号,用于排序
printk("-------------%s-----------------\n",__FUNCTION__);
minor = iminor(inode);//获取节点次设备号函数,Linux驱动的函数,可以直接使用
led_drv = led_drvs[minor];//以次设备号做下表放在全局变量中
if(!(*(led_drv->rcc+led_drv->led_special->RCC_OFFSET)&(1<<led_drv->led_special->RCC_MP_REGISTER))){ //判断时钟是否已经开启,有些时钟是启动时候已经开启了,所以需要判断
*(led_drv->rcc+led_drv->led_special->RCC_OFFSET) |=(1<<led_drv->led_special->RCC_MP_REGISTER);//未开启,直接开启,上面有概述时钟
}
led_drv->gpio->MODER &=~(led_drv->led_special->MODER_RESET);//模式先清理0再置1
led_drv->gpio->MODER |=led_drv->led_special->MODER_SET;
// 设置ODR寄存器关灯
led_drv->gpio->ODR &=~(led_drv->led_special->ODR_RESET);
return ret;
}
int led_close(struct inode *inode, struct file *filp)
{
//卸载时会自动调用
int ret=0;
printk("-------------%s-----------------\n",__FUNCTION__);
return ret;
}
//write写入函数,写入设备信息
ssize_t led_write(struct file *filp, const char __user *buf, size_t size, loff_t *flag)
{
int ret=0;
int on;
struct stm32mp157axxx *led_drv;
unsigned int minor;
printk("-------------%s-----------------\n",__FUNCTION__);
minor=iminor(filp->f_inode);//获取节点信息
led_drv =led_drvs[minor];
ret=copy_from_user(&on,buf,size);//接受引用层信息,应用层和内核是无法直传递信息的需要此函数转换一下
if(ret<0){//判断转换是否成功
printk("drv_led_write: copy_from_user is error\n");
return ret;
}
if(1==on){//判断应用层输入的值是多少,1开灯,0关灯
led_drv->gpio->ODR |=(led_drv->led_special->ODR_SET);
}else{
led_drv->gpio->ODR &=~(led_drv->led_special->ODR_RESET);
}
return ret;
}
const struct file_operations fops={//接口函数结构体
.open = led_open,//调用内核的接口函数,重新写入
.release= led_close,
.write = led_write,
};
int led_probe(struct platform_device *pdev)
{
int ret=0;
struct stm32mp157axxx *led_drv;
printk("-------------%s-----------------\n",__FUNCTION__);
led_drv=kzalloc(sizeof(struct stm32mp157axxx),GFP_KERNEL);//开辟空间设备结构体存储空间
if(IS_ERR(led_drv)){//判断开辟空间是否成功,IS_ERR函数,内核自带的,需要与PTR函数配合
ret=PTR_ERR(led_drv);
printk("led_probe: led_drv kzalloc is error\n");
return ret;
}
//led_drv->led_resource[],
led_drv->led_resource[0]=platform_get_resource(pdev,IORESOURCE_MEM, 0);//获取地址资源,后面的0和1表示获取同类型资源的序号,IORESOURCE_MEM表示地址资源
led_drv->led_resource[1]=platform_get_resource(pdev,IORESOURCE_MEM, 1);
led_drv->led_special =pdev->dev.platform_data;//获取自定义资源
led_drv->major = register_chrdev(0, led_drv->led_special->name, &fops);//注册设备号,0表示自动注册。
if(led_drv->major<0){//注册设备号失败
printk("drv_led_init: led_drv register_chrdev is error\n");
ret =led_drv->major;
goto register_chrdev_err;
}
led_drv->cls = class_create(THIS_MODULE,led_drv->led_special->name);//创建节点类
if(IS_ERR(led_drv->cls)){//判断节点类是否创建成功
printk("drv_led_init: led_drv class_create is error\n");
ret=PTR_ERR(led_drv->cls);
goto class_create_err;//创建失败跳转到释放设备号处
}
// 创建设备节点
led_drv->dev=device_create(led_drv->cls,NULL,MKDEV(led_drv->major, led_drv->led_special->minum),NULL,led_drv->led_special
->name);
//判断节点创建是否成功
if(IS_ERR(led_drv->dev)){
printk("drv_led_init: led_drv->dev device_create is error\n");
ret=PTR_ERR(led_drv->dev);
goto device_create_err;//释放节点类
}
//将虚拟空间映射到STM32MP157的RCC实际地址
led_drv->rcc=ioremap(led_drv->led_resource[1]->start,led_drv->led_resource[1]->end - led_drv->led_resource[1]->start+1);
if(IS_ERR(led_drv->rcc)){
printk("drv_led_init: drv_led->rcc ioremap is error\n");
ret=PTR_ERR(led_drv->rcc);//释放设备节点
goto rcc_ioremap_err;
}
//将虚拟空间映射到STM32MP157的GPIO实际地址
led_drv->gpio=ioremap(led_drv->led_resource[0]->start,led_drv->led_resource[0]->end - led_drv->led_resource[0]->start+1);
if(IS_ERR(led_drv->gpio)){
printk("drv_led_init: led_drv->gpio ioremap is error\n");
ret=PTR_ERR(led_drv->gpio);
goto gpio_ioremap_err;
}
led_drvs[led_drv->led_special->minum]=led_drv;
return ret;
gpio_ioremap_err:
iounmap(led_drv->rcc);
rcc_ioremap_err:
device_destroy(led_drv->cls,MKDEV(led_drv->major, led_drv->led_special->minum));
device_create_err:
class_destroy(led_drv->cls);
class_create_err:
unregister_chrdev(led_drv->major,led_drv->led_special->name);
register_chrdev_err:
kfree(led_drv);
return ret;
}
int led_remove(struct platform_device *pdev)//卸载时候自动调用需要将之前创建的空间之类的,设备号之类的东西全部释放掉,映射的东西也要接触映射
{
int ret =0;
struct stm32mp157axxx *led_drv;
struct Platform_data_spex *led_special;//获取的自定义空间
led_special=pdev->dev.platform_data;
led_drv=led_drvs[led_special->minum];
iounmap(led_drv->gpio);//解除映射GPIO
iounmap(led_drv->rcc);
device_destroy(led_drv->cls,MKDEV(led_drv->major, led_drv->led_special->minum));
class_destroy(led_drv->cls);
unregister_chrdev(led_drv->major,led_drv->led_special->name);
kfree(led_drv);
return ret;
}
const struct platform_device_id led_id_table[]={//字符设备匹配的名字
{"stm32mp157_led0"},
{"stm32mp157_led1"},
{"stm32mp157_led2"},
{"stm32mp157_led3"},
{"stm32mp157_led4"},
{"stm32mp157_led5"},
};
struct platform_driver pdrv={
.probe = led_probe,//注册账号会自动调用的
.remove = led_remove,//卸载时候自动调用的函数
.driver ={
.name = "led_drv",//设备的名字
},
.id_table = led_id_table,//匹配的名字
};
static int __init led_driver_init(void)//入口函数
{
int ret =0;
printk("-----------------%s------------------\n",__FUNCTION__);
led_drvs=kzalloc(sizeof(struct stm32mp157axxx *)*10, GFP_KERNEL);//注册
if(IS_ERR(led_drvs)){
ret=PTR_ERR(led_drvs);
printk("led_driver_init:kzalloc is error\n");
return ret;
}
platform_driver_register(&pdrv);
return ret;
}
void __exit led_driver_exit(void)
{
printk("-----------------%s------------------\n",__FUNCTION__);
platform_driver_unregister(&pdrv);//卸载
kfree(led_drvs);
}
module_init(led_driver_init);//声明
module_exit(led_driver_exit);
MODULE_LICENSE("GPL"); //协议
3.简单写一下应用层的代码:
#include "stdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
int fd0,fd1,fd2,fd3,fd4,fd5;
int on;
int i;
fd0=open("/dev/drv_led0",O_RDWR);
if(fd0<0){
perror("open");
exit(1);
}
fd1=open("/dev/drv_led1",O_RDWR);
if(fd1<0){
perror("open");
exit(1);
}
fd2=open("/dev/drv_led2",O_RDWR);
if(fd2<0){
perror("open");
exit(1);
}
fd3=open("/dev/drv_led3",O_RDWR);
if(fd3<0){
perror("open");
exit(1);
}
fd4=open("/dev/drv_led4",O_RDWR);
if(fd4<0){
perror("open");
exit(1);
}
fd5=open("/dev/drv_led5",O_RDWR);
if(fd5<0){
perror("open");
exit(1);
}
while(1){
printf("please input a data:");
scanf("%d",&on);
write(fd0,&on,sizeof(on));
write(fd1,&on,sizeof(on));
write(fd2,&on,sizeof(on));
write(fd3,&on,sizeof(on));
write(fd4,&on,sizeof(on));
write(fd5,&on,sizeof(on));
}
close(fd0);
close(fd1);
close(fd2);
close(fd3);
close(fd4);
close(fd5);
return 0;
}
五测试:
makefile函数:
#编译的地址 Kernel_Dir = /home/pm/fs-mp157/kernel/linux-stm32mp-5.4.31-r0/linux-5.4.31 #当前代码地址用PWD获取 Cue_Dir = ${shell pwd} Myapp = app_test all : make -C $(Kernel_Dir) M=$(Cue_Dir) modules $(CC) $(Myapp).c -o $(Myapp) clean: make -C $(Kernel_Dir) M=$(Cue_Dir) clean rm $(Myapp) install://将编译的.ko文件放到Linux开发板中 cp ./*.ko $(Myapp) /opt/rootfs/drv_module obj-m =led_device.o obj-m +=led_drivers.o
make的时候出现这个莫慌,是交叉编译器没有导入的原因,不是makefile写错了。
输入:source /opt/st/stm32mp1/3.1-openstlinux-5.4-dunfell-mp1-20-06-24/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi再内核文件目录下就ok了
你看这不就解决了吗。
测试加载一下:
加载结果:
实验结果:
看这不就点亮了了嘛,简简单单,加油咯。