一、业务需求和分析
业务需求:利用平台总线的方式实现多个LED灯的开和关(PZ5、6、7和PE8、10和PF10)
业务分析:
1、只有一个driver.c和一个device.c (不要写多个device.c)
2、将driver.c封装成一个通用的led控制文件,也就是就算一百个灯,driver.c也不需要修改,只要在device.c中增加相应的GPIO资源。
二、 编程实现步骤
device.c中的修改:
1、首先,我们这里有六个设备,led0到led5,有六个灯,就需要提供六个灯的寄存器地址资源和描述设备的其他资源
a:将前面的传递寄存器资源的数组(struct resource led_resource[]),定义成一个二维数组,二维数值中的每一个一维数组都表示一个设备的寄存器地址资源。
struct resource led_resource[][2]={//具体的地址资源或者中断资源
[0]={//PZ5
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOZ_Base,
.end = GPIOZ_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioz",
.flags = IORESOURCE_MEM,
},
},
[1]={//PZ6
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOZ_Base,
.end = GPIOZ_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioz",
.flags = IORESOURCE_MEM,
},
},
[2]={//PZ7
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOZ_Base,
.end = GPIOZ_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioz",
.flags = IORESOURCE_MEM,
},
},
[3]={//PE8
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOE_Base,
.end = GPIOE_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioe",
.flags = IORESOURCE_MEM,
},
},
[4]={//PE10
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOE_Base,
.end = GPIOE_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioe",
.flags = IORESOURCE_MEM,
},
},
[5]={//PF10
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOF_Base,
.end = GPIOF_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpiof",
.flags = IORESOURCE_MEM,
},
},
};
b:将前面的传递其他描述设备资源的变量(struct Platform_data_spex led_special)定义成一个一维数组,一维数值中的每一个元素都表示一个设备的其他描述设备的资源。
struct Platform_data_spex special_data[]={
[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,
},
};
2、前面是描述设备的资源,接下来,需要用结构体来描述这两个设备,因为有两个设备,自然也需要两个struct platform_device结构体,同样的我们可以定义一个这个类型的结构体数组来描述这两个资源,一个元素,描述一个资源
struct platform_device pdev[]={//描述这个硬件设备的信息
[0]={
.name = "stm32mp157xxx_led0",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[0],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[0])/sizeof(led_resource[0][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[0],//要传递的资源的首地址
},
[1]={
.name = "stm32mp157xxx_led1",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[1],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[1])/sizeof(led_resource[1][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[1],//要传递的资源的首地址
},
[2]={
.name = "stm32mp157xxx_led2",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[2],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[2])/sizeof(led_resource[2][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[2],//要传递的资源的首地址
},
[3]={
.name = "stm32mp157xxx_led3",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[3],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[3])/sizeof(led_resource[3][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[3],//要传递的资源的首地址
},
[4]={
.name = "stm32mp157xxx_led4",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[4],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[4])/sizeof(led_resource[4][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[4],//要传递的资源的首地址
},
[5]={
.name = "stm32mp157xxx_led5",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[5],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[5])/sizeof(led_resource[5][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[5],//要传递的资源的首地址
},
};
3、因为有两个设备所以需要将两个设备都注册到平台总线中,所以需要注册两次
// 2,入口函数----在加载,insmod的时候调用的函数
static int __init dev_led_init(void)
{
int ret = 0,i;
printk("-------------%s-----------------\n",__FUNCTION__);
for(i = 0; i < ARRAY_SIZE(pdev); i++){
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;
}
4、在出口函数中,注销设备
// 3,出口函数---在卸载这个模块,rmmod的时候调用
static void __exit dev_led_exit (void)
{
int i;
printk("-------------%s-----------------\n",__FUNCTION__);
for(i=0;i<ARRAY_SIZE(pdev);i++){
platform_device_unregister(&pdev[i]);//平台总线中移除一个硬件设备
}
}
driver.c中的修改:
1、首先你注册多个设备和driver.c匹配,你的driver.c就应该能够兼容多个设备
const struct platform_device_id led_id_table[]={
{"stm32mp157xxx_led0"},//这个名字必须和device.c中的platform_device中的name名字一致
{"stm32mp157xxx_led1"},
{"stm32mp157xxx_led2"},
{"stm32mp157xxx_led3"},
{"stm32mp157xxx_led4"},
{"stm32mp157xxx_led5"},
};
2、这样改好了以后,可以试一下看能不能调用多次probe,每注册一次pdev会调用一次,这里六个灯就是六次
3、虽然能调用两次probe,但是每次调用probe都是用的drv_led这个指针,当你调用了led0的资源后,再调用led1的资源,然后你再想操作led0,你还能找到led0的资源吗?不能吧,在第二次匹配的时候,led0的资源已经被覆盖了,所以我们就要思考,怎么能把多个灯的资源全部都保存下来?
灯的资源我们现在是用struct stm32mp157a_led *drv_led;来保存的,可不可以这样,每次获取到资源,注册完设备号设备节点,映射地址后,我将drv_led保存下来,当我要操作某个灯的时候再将它调出来。用什么保持呢?因为你每个设备资源是一个指针(struct stm32mp157a_led *drv_led),我们可不可以用一个数组将它们保存起来,并且是用一个指针数组。并且用每一个设备的次设备号作为数组下标。
具体如何实现,步骤如下:
a、定义一个二级指针(相当于指针数组),全局的,用来保存每个设备的资源,设备号,设备文件等等
struct stm32mp157axxx **drv_leds;
b、给这个二级指针申请空间,相当于给数组申请空间----------在入口函数中申请
// 2,入口函数----在加载,insmod的时候调用的函数 static int __init drv_led_init(void) { int ret=0; printk("-------------%s-----------------\n",__FUNCTION__); drv_leds=kzalloc(sizeof(struct stm32mp157axxx *)*10, GFP_KERNEL); if(IS_ERR(drv_leds)){ ret=PTR_ERR(drv_leds); printk("drv_led_init:kzalloc is error\n"); return ret; } platform_driver_register(&pdrv); //注册一个驱动到平台总线中 return ret; }
c、在出口函数中去释放空间
// 3,出口函数---在卸载这个模块,rmmod的时候调用 static void __exit drv_led_exit (void) { printk("-------------%s-----------------\n",__FUNCTION__); platform_driver_unregister(&pdrv);//从平台总线中,移除这个驱动 kfree(drv_leds); }
d、利用开辟的空间去保存设备的参数----------probe函数
1、在probe中定义一个局部的drv_led变量,以前那个全局的删除
struct stm32mp157axxx *drv_led;
2、其他的不要变,在probe函数的最后,将drv_led保存在drv_leds中
drv_leds[drv_led->led_pdata->minum]=drv_led;
e、在卸载设备的时候,调用remove函数,卸载设备,我们要找到对应的需要卸载的设备
1、在remove中定义一个局部的drv_led变量,
struct stm32mp157axxx *drv_led;
2、根据struct platform_device *pdev中的dev中的platform_data拿到次设备号,找到次设备号。定义一个特殊资源指针,拿到特殊资源
//定义一个指针,用于接收这个设备所对应的自定义的数据 struct Platform_data_spex *led_special; //从pdev中拿到自定义数据,指针指向自定义数据 led_special=pdev->dev.platform_data;
3、将drv_leds中次设备号所对应数组下标的元素给drv_led
//从自定义数据中拿到次设备号,再将次设备号作为数组下标,找到对应的设备操作对象 drv_led=drv_leds[led_special->minum];
f、在应用层调用open的时候,我们要找到对应的设备,操作
1、在open中定义一个局部的drv_led变量,
struct stm32mp157axxx *drv_led;
2、根据 struct file *filp找到次设备号,将drv_leds中次设备号所对应数组下标的元素给drv_led
unsigned int minor; minor=iminor(inode); drv_led =drv_leds[minor];
g、在应用层调用write的时候,我们要找到对应的设备,操作
1、在write中定义一个局部的drv_led变量,
struct stm32mp157axxx *drv_led;
2、根据 struct file *filp找到次设备号,将drv_leds中次设备号所对应数组下标的元素给drv_led
minor=iminor(filp->f_inode); //根据filp中的完整的设备号,获得次设备号 drv_led =drv_leds[minor]; //根据次设备号,找到操作的设备对象
三、完整程序
app.c文件
#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;
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/xyn/fs_mp157/kernel/linux-stm32mp-5.4.31-r0/linux-5.4.31
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:
cp ./*.ko $(Myapp) /opt/rootfs/drv_module/
obj-m =platform_led_driver.o
obj-m +=platform_led_device.o
special文件
#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__
device.c
// 1,头文件
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "stm32mp157xxx.h"
#include "platform_data_special.h"
//这是在定义自定的资源,因为这些资源会随着硬件的变化而改变
struct Platform_data_spex special_data[]={
[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,
},
};
struct resource led_resource[][2]={//具体的地址资源或者中断资源
[0]={//PZ5
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOZ_Base,
.end = GPIOZ_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioz",
.flags = IORESOURCE_MEM,
},
},
[1]={//PZ6
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOZ_Base,
.end = GPIOZ_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioz",
.flags = IORESOURCE_MEM,
},
},
[2]={//PZ7
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOZ_Base,
.end = GPIOZ_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioz",
.flags = IORESOURCE_MEM,
},
},
[3]={//PE8
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOE_Base,
.end = GPIOE_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioe",
.flags = IORESOURCE_MEM,
},
},
[4]={//PE10
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOE_Base,
.end = GPIOE_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpioe",
.flags = IORESOURCE_MEM,
},
},
[5]={//PF10
[0]={//RCC的地址资源
.start = RCC_Base,
.end = RCC_Base+sizeof(RCC_TypeDef)-1,
.name = "rcc",
.flags = IORESOURCE_MEM,
},
[1]={//GPIO的地址资源
.start = GPIOF_Base,
.end = GPIOF_Base+sizeof(GPIO_TypeDef)-1,
.name = "gpiof",
.flags = IORESOURCE_MEM,
},
},
};
void drv_led_release(struct device *dev)
{
//必须实现,可以不干事,在卸载这个驱动时,会被调用,如果找不到这个函数,系统会提示错误
}
struct platform_device pdev[]={//描述这个硬件设备的信息
[0]={
.name = "stm32mp157xxx_led0",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[0],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[0])/sizeof(led_resource[0][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[0],//要传递的资源的首地址
},
[1]={
.name = "stm32mp157xxx_led1",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[1],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[1])/sizeof(led_resource[1][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[1],//要传递的资源的首地址
},
[2]={
.name = "stm32mp157xxx_led2",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[2],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[2])/sizeof(led_resource[2][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[2],//要传递的资源的首地址
},
[3]={
.name = "stm32mp157xxx_led3",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[3],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[3])/sizeof(led_resource[3][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[3],//要传递的资源的首地址
},
[4]={
.name = "stm32mp157xxx_led4",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[4],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[4])/sizeof(led_resource[4][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[4],//要传递的资源的首地址
},
[5]={
.name = "stm32mp157xxx_led5",//匹配的名字,这个硬件设备和谁去匹配
.id = -1,//一般为-1
.dev = {//自定义资源在这个里面定义,暂时我还没用
.release = drv_led_release,//在卸载时会报错,所以需要实现
.platform_data = &special_data[5],//将定义好的资源传给driver.c
},
.num_resources = sizeof(led_resource[5])/sizeof(led_resource[5][0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
.resource = led_resource[5],//要传递的资源的首地址
},
};
// 2,入口函数----在加载,insmod的时候调用的函数
static int __init dev_led_init(void)
{
int ret = 0,i;
printk("-------------%s-----------------\n",__FUNCTION__);
for(i = 0; i < ARRAY_SIZE(pdev); i++){
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;
}
// 3,出口函数---在卸载这个模块,rmmod的时候调用
static void __exit dev_led_exit (void)
{
int i;
printk("-------------%s-----------------\n",__FUNCTION__);
for(i=0;i<ARRAY_SIZE(pdev);i++){
platform_device_unregister(&pdev[i]);//平台总线中移除一个硬件设备
}
}
// 声明
module_init(dev_led_init);//声明入口函数是哪一个函数
module_exit(dev_led_exit);//声明哪一个函数是出口函数
MODULE_LICENSE("GPL");
driver.c
// 1,头文件
#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 *devi;
struct resource *led_resource[2];
struct Platform_data_spex *led_pdata;
unsigned int *rcc;
GPIO_TypeDef *gpio;
};
struct stm32mp157axxx **drv_leds;
int drv_led_open(struct inode *inode, struct file *filp)
{
int ret = 0;
struct stm32mp157axxx *drv_led;
unsigned int minor;
printk("-------------%s-----------------\n",__FUNCTION__);
minor = iminor(inode);
drv_led = drv_leds[minor];
if(!(*(drv_led->rcc+drv_led->led_pdata->RCC_OFFSET)&(1<<drv_led->led_pdata->RCC_MP_REGISTER))){ //判断时钟是否是默认开启
*(drv_led->rcc+drv_led->led_pdata->RCC_OFFSET) |=(1<<drv_led->led_pdata->RCC_MP_REGISTER);//如果时钟未开始,打开时钟
}
// 设置GPIOZ的第5个引脚的模式
drv_led->gpio->MODER &= ~(drv_led->led_pdata->MODER_RESET);
drv_led->gpio->MODER |= drv_led->led_pdata->MODER_SET;
// 默认关灯
drv_led->gpio->ODR &= ~(drv_led->led_pdata->ODR_RESET);
return ret;
}
int drv_led_close(struct inode *inode, struct file *filp)
{
int ret=0;
printk("-------------%s-----------------\n",__FUNCTION__);
return ret;
}
// 实现write接口
ssize_t drv_led_write(struct file *filp, const char __user *buf, size_t size, loff_t *flag)
{
int ret = 0;
int on;
struct stm32mp157axxx *drv_led;
unsigned int minor; //定义一个变量,用于接收次设备号
printk("-------------%s-----------------\n",__FUNCTION__);
minor = iminor(filp->f_inode);//根据filp中的完整的设备号,获得次设备号
drv_led = drv_leds[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){
drv_led->gpio->ODR |=(drv_led->led_pdata->ODR_SET);
}else{
drv_led->gpio->ODR &=~(drv_led->led_pdata->ODR_RESET);
}
return ret;
}
const struct file_operations fops={
.open =drv_led_open,
.release=drv_led_close,
.write =drv_led_write,
};
int drv_led_probe(struct platform_device *pdev)
{
int ret = 0;
struct stm32mp157axxx *drv_led;
printk("-------------%s-----------------\n",__FUNCTION__);
drv_led = kzalloc(sizeof(struct stm32mp157axxx),GFP_KERNEL);
if(IS_ERR(drv_led)){
ret=PTR_ERR(drv_led);
printk("drv_led_probe: drv_led kzalloc is error\n");
return ret;
}
drv_led->led_resource[0]=platform_get_resource(pdev,IORESOURCE_MEM,0);
printk("led_resource[0]->start=0x%x,led_resource[0]->end=0x%x\n",drv_led->led_resource[0]->start,drv_led->led_resource[0]->end);
drv_led->led_resource[1]=platform_get_resource(pdev,IORESOURCE_MEM,1);
printk("led_resource[1]->start=0x%x,led_resource[1]->end=0x%x\n",drv_led->led_resource[1]->start,drv_led->led_resource[1]->end);
drv_led->led_pdata =pdev->dev.platform_data;
printk("drv_led->led_pdata->name=%s\n",drv_led->led_pdata->name);
printk("drv_led->led_pdata->minum=%d\n",drv_led->led_pdata->minum);
printk("drv_led->led_pdata->MODER_SET=%d\n",drv_led->led_pdata->MODER_SET);
printk("drv_led->led_pdata->MODER_RESET=%d\n",drv_led->led_pdata->MODER_RESET);
printk("drv_led->led_pdata->ODR_SET=%d\n",drv_led->led_pdata->ODR_SET);
printk("drv_led->led_pdata->ODR_RESET=%d\n",drv_led->led_pdata->ODR_RESET);
printk("drv_led->led_pdata->RCC_MP_REGISTER=%d\n",drv_led->led_pdata->RCC_MP_REGISTER);
printk("drv_led->led_pdata->RCC_OFFSET=%d\n",drv_led->led_pdata->RCC_OFFSET);
//1,申请设备号-----动态分配
drv_led->major=register_chrdev(0,drv_led->led_pdata->name,&fops);
if(drv_led->major<0){
printk("drv_led_init: drv_led register_chrdev is error\n");
ret =drv_led->major;
goto register_chrdev_err;
}
// 2,创建设备文件
// 1,创建类
drv_led->cls = class_create(THIS_MODULE,drv_led->led_pdata->name);
//判断drv_led->cls 创建类是否成功
if(IS_ERR(drv_led->cls)){//判断drv_led->cls这个类创建是否成功,如果指向空,IS_ERR(drv_led->cls)的结果就为真
printk("drv_led_init: drv_led class_create is error\n");
ret=PTR_ERR(drv_led->cls);//获得错误码
goto class_create_err;
}
// 2,创建设备文件
drv_led->devi=device_create(drv_led->cls,NULL,MKDEV(drv_led->major, drv_led->led_pdata->minum),NULL,drv_led->led_pdata->name);
//判断drv_led->devi 创建类是否成功
if(IS_ERR(drv_led->devi)){//判断drv_led->devi这个类创建是否成功,如果指向空,IS_ERR(drv_led->devi)的结果就为真
printk("drv_led_init: drv_led device_create is error\n");
ret=PTR_ERR(drv_led->devi);//获得错误码
goto device_create_err;
}
drv_led->rcc=ioremap(drv_led->led_resource[0]->start,drv_led->led_resource[0]->end-drv_led->led_resource[0]->start+1);
if(IS_ERR(drv_led->rcc)){
printk("drv_led_init: drv_led->rcc ioremap is error\n");
ret=PTR_ERR(drv_led->rcc);//获得错误码
goto rcc_ioremap_err;
}
drv_led->gpio=ioremap(drv_led->led_resource[1]->start,drv_led->led_resource[1]->end-drv_led->led_resource[1]->start+1);
if(IS_ERR(drv_led->gpio)){
printk("drv_led_init: drv_led->gpio ioremap is error\n");
ret=PTR_ERR(drv_led->gpio);//获得错误码
goto gpio_ioremap_err;
}
drv_leds[drv_led->led_pdata->minum]=drv_led;
return ret;
gpio_ioremap_err:
iounmap(drv_led->rcc);
rcc_ioremap_err:
device_destroy(drv_led->cls,MKDEV(drv_led->major, drv_led->led_pdata->minum));//删除设备文件
device_create_err:
class_destroy(drv_led->cls);//删除类
class_create_err:
unregister_chrdev(drv_led->major,drv_led->led_pdata->name);//删除设备号
register_chrdev_err:
kfree(drv_led);//当申请设备号失败,释放指针空间
return ret;
}
int drv_led_remove(struct platform_device *pdev)
{
int ret=0;
struct stm32mp157axxx *drv_led;
struct Platform_data_spex *led_special; //定义一个指针,用于接收这个设备所对应的自定义的数据
printk("-------------%s-----------------\n",__FUNCTION__);
led_special=pdev->dev.platform_data;//从pdev中拿到自定义数据,指针指向自定义数据
drv_led=drv_leds[led_special->minum];//从自定义数据中拿到次设备号,再将次设备号作为数组下标,找到对应的设备操作对象
iounmap(drv_led->gpio);
iounmap(drv_led->rcc);
device_destroy(drv_led->cls,MKDEV(drv_led->major, drv_led->led_pdata->minum));//删除设备文件
class_destroy(drv_led->cls);//删除类
unregister_chrdev(drv_led->major,drv_led->led_pdata->name);//删除设备号
kfree(drv_led);
return ret;
}
const struct platform_device_id led_id_table[]={
{"stm32mp157xxx_led0"},//这个名字必须和device.c中的platform_device中的name名字一致
{"stm32mp157xxx_led1"},
{"stm32mp157xxx_led2"},
{"stm32mp157xxx_led3"},
{"stm32mp157xxx_led4"},
{"stm32mp157xxx_led5"},
};
struct platform_driver pdrv={ //具体的驱动
.probe =drv_led_probe,//在驱动driver.c和device.c匹配成功后,会调用这个函数
.remove =drv_led_remove,//在移除driver.c和device.c中的任何一个会调用的函数
.driver ={
.name ="drv_led",//描述这个驱动的名字
},
.id_table =led_id_table,//用于和硬件设备匹配的名字数组
};
// 2,入口函数----在加载,insmod的时候调用的函数
static int __init drv_led_init(void)
{
int ret=0;
printk("-------------%s-----------------\n",__FUNCTION__);
drv_leds=kzalloc(sizeof(struct stm32mp157axxx *)*10, GFP_KERNEL);
if(IS_ERR(drv_leds)){
ret=PTR_ERR(drv_leds);
printk("drv_led_init:kzalloc is error\n");
return ret;
}
platform_driver_register(&pdrv); //注册一个驱动到平台总线中
return ret;
}
// 3,出口函数---在卸载这个模块,rmmod的时候调用
static void __exit drv_led_exit (void)
{
printk("-------------%s-----------------\n",__FUNCTION__);
platform_driver_unregister(&pdrv);//从平台总线中,移除这个驱动
kfree(drv_leds);
}
// 声明
module_init(drv_led_init);//声明入口函数是哪一个函数
module_exit(drv_led_exit);//声明哪一个函数是出口函数
MODULE_LICENSE("GPL");
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__
四、程序结果
注意!!!
1、两个资源的接收指针必须放在结构体中
struct stm32mp157a_led{
unsigned int major;
struct class *cls;
struct device *dev;
GPIO_TypeDef *GPIO;
unsigned int *RCC;
struct resource *led_reg_resource[10];
struct Platform_data_spex *led_special;
};
2、在probe中定义了struct stm32mp157a_led * drv_led;必须先分配空间再去使用