平台总线(实操)---实现LED灯的资源传递

该博客围绕LED灯驱动开发展开,先明确业务需求为实现主板LED灯开关,分析所需资源。接着阐述编程实现步骤,包括准备工作和device.c、driver.c文件的编程。给出完整程序,展示程序结果。最后介绍自定义资源,将易变数据定义成结构体,提高程序可移植性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、业务需求和分析

业务需求:实现LED灯的开和关(主板上的灯)

业务分析:需要的资源有两种,分别是:描述硬件的资源(寄存器地址资源或者中断资源)和其他用于设备标识的资源(例如,设备名称,设备文件名称,操作寄存器的值等等)

二、编程实现步骤

准备工作:

        1、查看主板原理图,分析LED所在的GPIO口,为GPIOZ(以下操作都以GPIOZ5为例)

2、准备GPIOZ的资源和RCC的资源

起始地址:GPIOZ的起始地址,并且定义在寄存器的头文件中

结束地址:GPIOZ的起始地址 + 寄存器结构体大小 - 1(每个GPIO都有自己的寄存器结构体)

开始编程: 

device.c中:

 1、寄存器资源------可以用struct resource来进行传递
        这里需要两个寄存器资源:

        一个是RCC寄存器的资源,用于打开时钟,

        一个是GPIO寄存器的资源
        我们用结构体描述

struct resource	 led_resource[]={
    [0]={    //GPIOZ的资源
        .start =GPIOZ_Base,                        //GPIOZ寄存器的起始地址
        .end   =GPIOZ_Base+sizeof(GPIO_TypeDef)-1,//GPIOZ寄存器的结束地址
        .name ="GPIOZ",                            //资源的名字
        .flags =IORESOURCE_MEM,                    //寄存器地址资源
    },
    [1]={    //RCC的资源
        .start =RCC_Base,
        .end   =RCC_Base+sizeof(RCC_TypeDef)-1,
        .name ="RCC",
        .flags =IORESOURCE_MEM,	
    },
};

​​​​2、用于设备标识的资源------可以用void  *platform_data; 来传递

       这是在定义自定的资源,因为这些资源会随着硬件的变化而改变

       我们可以将这些易改变的资源全部定义在一个头文件中(platform_data_special.h)

       当我们需要的时候,可以定义一个结构体(Platform_data_spex)来调用需要的资源  

     (自定义资源)看第五点

        platform_data_special.h头文件       

#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__
​struct Platform_data_spex led_special={
    .name  ="drv_led",         //这用于描述设备文件名,也用于描述这个设备
    .minum =0,                 //设备次设备号
    .MODER_SET       = 1<<10,  //查看手册了解以下的参数
    .MODER_RESET     = 3<<10,
    .ODR_SET         = 1<<5,
    .ODR_RESET       = 1<<5,
    .RCC_MP_REGISTER = 0,
    .RCC_OFFSET      = 0x210/4,
};​

 3、在描述设备的结构体中增加用于设备标识的数据地址

struct platform_device  pdev={
    .name  = "stm32mp157a_led",
    .id	   = -1,
    .dev   = {
        .release =drv_led_release,
        .platform_data=&led_special,  //这是新加的,其余的没有改变,别乱来
    },
    .num_resources = ARRAY_SIZE(led_resource),
    .resource      = led_resource,
};

 4,添加头文件

#include "linux/platform_device.h"
#include "stm32mp157xxx.h"
#include "platform_data_special.h"

driver.c中:

1、实例化对象,申请设备号,创建设备节点,映射寄存器地址资源--probe函数中实现 

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参数时,我们算的是从起始地址偏移多少个字节,所以不能用RCC_TypeDef
	unsigned int  *rcc;
	GPIO_TypeDef *gpio;
};
struct stm32mp157axxx *drv_led;

2、这里实现fops结构体的三个函数功能 

        open-close-write

        1,硬件初始化---------------open函数中

int drv_led_open(struct inode *inode, struct file *filp)
{
	int ret=0;
	printk("-------------%s-----------------\n",__FUNCTION__);	
	
	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;
}

        2,实现write函数

// 实现write接口
ssize_t drv_led_write(struct file *filp, const char __user *buf, size_t size, loff_t *flag)
{
	int ret = 0;
	int on;
	printk("-------------%s-----------------\n",__FUNCTION__);	

	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;
}

 fops结构体

const struct file_operations fops={
	.open   =drv_led_open,
	.release=drv_led_close,
	.write  =drv_led_write,
};

3、编写probe函数 

    // 实例化对象
    drv_led =kzalloc(sizeof(struct stm32mp157a_led), GFP_KERNEL);
    if(IS_ERR(drv_led)){
        printk("probe:drv_led kzalloc is error\n");
        ret=PTR_ERR(drv_led);
        return  ret;
    }

    // 申请设备号
    drv_led->major=register_chrdev(0,led_special->name,&fops);
    if(drv_led->major<0){
        ret=drv_led->major;
        printk("probe:drv_led register_chrdev is error\n");
        goto kzalloc_err;
    }

    // 创建类
    drv_led->cls=class_create(THIS_MODULE, led_special->name);
    if(IS_ERR(drv_led->cls)){
        printk("probe:drv_led class_create is error\n");
        ret=PTR_ERR(drv_led->cls);
        goto register_chrdev_err;
    }

    // 创建设备节点
    drv_led->dev=device_create(drv_led->cls,NULL,MKDEV(drv_led->major,led_special-            >minum),NULL,led_special->name);
    if(IS_ERR(drv_led->dev)){
        printk("probe:drv_led device_create is error\n");
        ret=PTR_ERR(drv_led->dev);
        goto class_create_err;
    }	

    // 映射寄存器地址
    drv_led->GPIO=ioremap(led_reg_resource[0]->start,led_reg_resource[0]->end -                 led_reg_resource[0]->start + 1);
    if(IS_ERR(drv_led->GPIO)){
        printk("probe:drv_led(GPIO) ioremap is error\n");
        ret=PTR_ERR(drv_led->GPIO);
        goto device_create_err;
    }		

    drv_led->RCC=ioremap(led_reg_resource[1]->start,led_reg_resource[1]->end - led_reg_resource[1]->start + 1);
    if(IS_ERR(drv_led->RCC)){
        printk("probe:drv_led(RCC) ioremap is error\n");
        ret=PTR_ERR(drv_led->RCC);
        goto ioremap_gpio_err;
    }
			
    return ret;
    ioremap_gpio_err:
    iounmap(drv_led->GPIO);
    device_create_err:
    device_destroy(drv_led->cls,MKDEV(drv_led->major,led_special->minum));
    class_create_err:
    class_destroy(drv_led->cls);
    register_chrdev_err:
    unregister_chrdev(drv_led->major,led_special->name);
    kzalloc_err:
    kfree(drv_led);
    return ret;

4、编写remove函数 

int drv_led_remove(struct platform_device *pdev)
{
	int ret=0;
	printk("-------------%s-----------------\n",__FUNCTION__);
	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;
}

5、 匹配

//这个名字必须和device.c中的platform_device中的name名字一致
const struct platform_device_id led_id_table[]={
	{"stm32mp157xxx_led"},
};

6、定义 pdev结构体

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,   //用于和硬件设备匹配的名字数组
};

7、入口函数-出口函数-声明 

8、添加头文件 

    #include "linux/slab.h"
    #include "linux/fs.h"
    #include "linux/device.h"
    #include "linux/io.h"

三、完整程序

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 fd;
	int on;
	fd = open("/dev/drv_led0",O_RDWR);
	if(fd < 0){
		perror("open");
		exit(1);
	}
	while(1){
		printf("please input a data:");
		scanf("%d",&on);
		write(fd,&on,sizeof(on));
	}
	close(fd);
	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={
	.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,
};

struct resource led_resource[]={//具体的地址资源或者中断资源
	[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,
		},

};

void	drv_led_release(struct device *dev)
{
	//必须实现,可以不干事,在卸载这个驱动时,会被调用,如果找不到这个函数,系统会提示错误
}

struct platform_device pdev={//描述这个硬件设备的信息
	.name  ="stm32mp157xxx_led",//匹配的名字,这个硬件设备和谁去匹配
	.id    =-1,//一般为-1
	.dev   ={//自定义资源在这个里面定义,暂时我还没用
		.release  =drv_led_release,//在卸载时会报错,所以需要实现
		.platform_data =&special_data,//将定义好的资源传给driver.c
	},
	.num_resources  =sizeof(led_resource)/sizeof(led_resource[0]),//有多少个struct resource这个类型的结构体数据,用总的数据长度/一个数据的长度
	.resource       =led_resource,//要传递的资源的首地址
};

// 2,入口函数----在加载,insmod的时候调用的函数
static int __init dev_led_init(void)
{
	printk("-------------%s-----------------\n",__FUNCTION__);
	return platform_device_register(&pdev);//在平台总线中注册一个硬件设备
}
// 3,出口函数---在卸载这个模块,rmmod的时候调用
static void __exit dev_led_exit (void)
{
	printk("-------------%s-----------------\n",__FUNCTION__);
	platform_device_unregister(&pdev);//在平台总线中移除一个硬件设备
}
// 声明
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_led;
int drv_led_open(struct inode *inode, struct file *filp)
{
	int ret=0;
	printk("-------------%s-----------------\n",__FUNCTION__);	
	
	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;
	printk("-------------%s-----------------\n",__FUNCTION__);	

	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;
	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;
	}			
	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;
	printk("-------------%s-----------------\n",__FUNCTION__);
	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_led"},//这个名字必须和device.c中的platform_device中的name名字一致
};

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)
{
	printk("-------------%s-----------------\n",__FUNCTION__);
	return 	platform_driver_register(&pdrv); //注册一个驱动到平台总线中
}

// 3,出口函数---在卸载这个模块,rmmod的时候调用
static void __exit drv_led_exit (void)
{
	printk("-------------%s-----------------\n",__FUNCTION__);
	platform_driver_unregister(&pdrv);//从平台总线中,移除这个驱动
}

// 声明
module_init(drv_led_init);//声明入口函数是哪一个函数
module_exit(drv_led_exit);//声明哪一个函数是出口函数
MODULE_LICENSE("GPL");

四、程序结果

五、自定义资源

        一开始我们代码像下面这样,拿到一个资源之后,我们要对这个资源进行操作,比如用GPIOZ口开灯,它的时钟可能是AHB5,但如果用GPIOF,那么时钟又需要修改成AHB4;再比如GPIOZ的引脚,这个时候我用的是GPIOZ的PIN_5,那我想要用PIN_4的时候又需要修改大量的代码;以及它的ODR输出寄存器,这些易改变的数据我们都需要将它们定义成一个结构体,到时候只需要修改这个结构体中这些数据的值即可而,device.c文件和driver.c文件都不需要改动.(这样的程序可移植性高!)

1、一开始的程序(易改变的数据,以GPIOZ 5为例)

 2、全部定义成一个结构体(Platform_data_spex),放进一个头文件specil中,然后再调用

3、在device.c中调用头文件里所需要的数据,定义一个结构体

//这是在定义自定的资源,因为这些资源会随着硬件的变化而改变
struct Platform_data_spex special_data={
	.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,
};

当我要设置模式的时候,用MODER_SET代替0x1<<10编译到driver.c中,

当我要设置ODR的时候,ODR_SET = 0x1<<5,

比如:

drv_led->gpio->MODER  |= (0x1<<10);

drv_led->gpio->MODER  |= drv_led->led_pdata->MODER_SET;

drv_led->gpio->ODR       &= ~(0x1<<5);

drv_led->gpio->ODR       &= ~(drv_led->led_pdata->ODR_RESET);

4、查看数据手册(datasheet),查找每个寄存器所需的移位 

以GPIOZ 5为例:

    ODR输出寄存器也是这样:    

RCC_MP_REGISTER (操作寄存器的位)和OFFSET(偏移量)

5、修改替换后的程序 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值