Linux初级驱动开发(实操)---STM32MP157点灯

该博客围绕STM32驱动开发,以点灯业务为需求,详细介绍实现步骤。包括内核模块框架搭建,如编写Makefile、应用层程序调用测试、编译和加载内核模块;还阐述完整驱动组成,从准备工作到编程实现,最后给出示例代码。

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

一、业务需求与分析

业务需求:用驱动设备点灯输入1:开6个灯         输入0:关6个灯

业务分析:

二、实现步骤

1、内核模块框架

(1) 内核模块组成(基本框架)

    //1、头文件
	#include <linux/init.h>
	#include <linux/module.h>

	//2、模块加载函数------模块加载后先执行这个函数
	static int __init hello_drv_init(void)
	{
		printk("--------^_^ %s----------\n",__FUNCTION__);
		return 0;
	}

	//3、模块卸载函数----卸载驱动程序时,就会去调用这个出口函数
	static void __exit hello_drv_exit(void)
	{
		printk("--------^_^ %s----------\n",__FUNCTION__);
	}

	//4、模块声明和认证
	module_init(hello_drv_init);
	module_exit(hello_drv_exit);
	MODULE_LICENSE("GPL");

(2) 编写Makefile文件(vim makefile)  

        -C是表示进入$(KERNELDIR)目录执行makefile
         而M不是makefile的选项,是内核根目录下的Makefile中使用的变量(M是makefile脚本中的一个变量(variable))
        让该Makefile在构造modules目标之前返回到模块源代码目录并在当前目录生成obj-m指定的xxx.o目标模块
        modules 是一个参数,就是告诉内核在编译时,把此驱动程序编成单独模块,不编译进内核

//写一个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:
    //将当前目录中的源码和内核源码一起编译,并将当前目录中的文件编译成.ko文件
	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 =drv_led_v1.o

(3)编写应用层程序调用测试 

//写一个应用程序测试(app_test.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_led",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;
}

(4)编译

        先导入交叉译器

        source opt/st/stm32mp1/3.1-openstlinux-5.4-dunfell-mp1-20-06-24/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi        

        make(直接编译)

(5)加载内核模块

1》在rootfs中新建一个文件夹,用来装编译好的内核源码
        xyn@ubuntu:/opt/rootfs/drv_module$ sudo mkdir /opt/rootfs/drv_modules
 2》将编译好的内核模块拷贝到 /opt/rootfs/drv_modules(我已经在makefile中写好了,直接使用make install就可以完成)
        cp *.ko /opt/rootfs/drv_module
 3》打开开发板,加载内核模块

       先在uboot中设置以下内容
        env set -f ethaddr 00:80:E1:42:60:17  //可以没有
        env set serverip 192.168.90.115(虚拟机的IP)
        env set gatewayip 192.168.90.1 (网关)
        env set ipaddr 192.168.90.195   (没被占用的IP)
        env save                                      (保存)

        静态IP启动:if pxe get; then pxe boot; fi

        自动登录设置:env set bootargs root=/dev/nfs nfsroot=192.168.90.115:/opt/rootfs ip=192.168.90.195 rootwait rw earlyprintk console=ttySTM0,115200
        [root@fsmp1a ]# cd drv_modules/   //进入模块文件夹
        [root@fsmp1a drv_modules]# ls
        drv_hello.ko
        [root@fsmp1a drv_modules]# insmod drv_hello.ko  //加载内核模块
        [  426.479925] drv_hello: loading out-of-tree module taints kernel.
            /* 忽视就行,有强迫症的往下看 内核被污染的原因是加载了树外模块,也就是加载自己写的驱动,不在内核源码树中 */
        [  426.484679] drv_hello: module verification failed: signature and/or required key missing - tainting kernel
            /* 在Makefile文件中加入CONFIG_MODULE_SIG=n这条语句---关闭模块数字签名 */
        [  426.494642] -----------------drv_hello_init-----------------
        [root@fsmp1a drv_modules]# lsmod                         //查询系统中加载的模块
        drv_hello 16384 0 - Live 0xbf000000 (OE)

        [root@fsmp1a drv_module]# ./app_test
        [  216.634553] -------------drv_led_open-----------------
        please input a data:1    //输入1开灯
        [  220.636897] -------------drv_led_write-----------------
        please input a data:0    //输入0关灯
        [  222.104010] -------------drv_led_write-----------------

        [root@fsmp1a drv_modules]# rmmod drv_hello.ko  //移除已加载的模块
        [  448.752884] -----------------drv_hello_exit-----------------

2、一个完整驱动的组成 (如何编写内核模块程序) 

A、准备工作        

(1)  查看原理图,找到需要开灯的引脚为PE10、PE8、PF10   

(2)打开datasheet手册查看引脚所在的总线(AHB4和AHB5)

(3)查看RCC寄存器,发现GPIOE、F、Z 在对应的外设时钟使能位置1时,时钟打开 

B、开始编程

头文件

        关于用了哪个头文件,选中函数,在Source Insight左下角就会显示

#include "linux/init.h"
#include "linux/module.h"
#include "linux/fs.h"
#include "linux/slab.h"
#include "linux/device.h"
#include "asm/io.h"
#include "stm32mp157xxx.h"
#include "linux/uaccess.h"
(1) 定义一个描述设备的结构体
//定义一个描述设备的结构体
struct stm32mp157axxx{
	unsigned int major;   //设备的主版本号
	struct class *cls;    //设备所属的类
	struct device *devi;  //设备对象
	RCC_TypeDef  *rcc;    //RCC寄存器
	GPIO_TypeDef *gpioz;  //GPIOZ寄存器
	GPIO_TypeDef *gpioe;
	GPIO_TypeDef *gpiof;
};
(2)定义一个指针,指向led设备,用这个指针描述led设备
struct stm32mp157axxx *drv_led;
(3)drv_led_open函数
int drv_led_open(struct inode *inode, struct file *filp)
{
	int ret=0;
	printk("-------------%s-----------------\n",__FUNCTION__);	
	if(!(drv_led->rcc->MP_AHB5ENSETR&(1<<0))){ //判断时钟是否是默认开启
		 drv_led->rcc->MP_AHB5ENSETR |= (1<<0); //如果时钟未开始,打开时钟
	} 
	if(!(drv_led->rcc->MP_AHB4ENSETR&(1<<4))){ //判断时钟是否是默认开启
		 drv_led->rcc->MP_AHB4ENSETR |= (1<<4); //如果时钟未开始,打开时钟
	}
	if(!(drv_led->rcc->MP_AHB4ENSETR&(1<<5))){ //判断时钟是否是默认开启
		 drv_led->rcc->MP_AHB4ENSETR |= (1<<5); //如果时钟未开始,打开时钟
	}	
	// 设置GPIOZ的第5,6,7个引脚的模式
    drv_led->gpioe->MODER    &= ~((0x3<<16)|(0x3<<20)); //2个e引脚
	drv_led->gpioe->MODER    |= (0x1<<16)|(0x1<<20);
	 
	 //配置8 10的GPIOE    10的GPIOF
	drv_led->gpioz->MODER    &= ~((0x3<<10)|(0x3<<12)|(0x3<<14));
	drv_led->gpioz->MODER    |= (0x1<<10)|(0x1<<12)|(0x1<<14);

	drv_led->gpioe->MODER    &= ~((0x3<<16)|(0x3<<20));
	drv_led->gpioe->MODER    |= (0x1<<16)|(0x1<<20);

	drv_led->gpiof->MODER    &= ~(0x3<<20);
	drv_led->gpiof->MODER    |= (0x1<<20);

	// 默认关灯
	drv_led->gpioz->ODR      &= ~((0x1<<5)|(0x1<<6)|(0x1<<7));
	drv_led->gpioe->ODR      &= ~((0x1<<8)|(0x1<<10));
	drv_led->gpiof->ODR      &= ~(0x1<<10);
	
	return ret;
}
(4)drv_led_close函数
int drv_led_close(struct inode *inode, struct file *filp)
{
	int ret=0;
	printk("-------------%s-----------------\n",__FUNCTION__);	
	return ret;
}
(5)drv_led_write函数

        往驱动中写入数据,比如我在按下按键,灯亮,在这里是打出“1”灯亮,打出“0”灯灭

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->gpioz->ODR     |= ((0x1<<5)|(0x1<<6)|(0x1<<7));	
		drv_led->gpioe->ODR     |= ((0x1<<8)|(0x1<<10));
		drv_led->gpiof->ODR     |= (0x1<<10);
	}else{
		drv_led->gpioz->ODR     &= ~((0x1<<5)|(0x1<<6)|(0x1<<7));
		drv_led->gpioe->ODR     &= ~((0x1<<8)|(0x1<<10));
		drv_led->gpiof->ODR     &= ~(0x1<<10);
	}
	return ret;
}
(6)内核中操作函数的结构体

        用了多少驱动操作函数都要在这里替换,然后在结构体之前实现函数的功能

const struct file_operations fops={
	.open    = drv_led_open,
	.release = drv_led_close,
	.write   = drv_led_write,
};
入口函数static int __init drv_led_init(void)
    int ret = 0;
	printk("-------------%s-----------------\n",__FUNCTION__);
	//首先给led这个对象分配空间(在内核中GFP_KERNEL分配)
	drv_led = kzalloc(sizeof(struct stm32mp157axxx),GFP_KERNEL);
	//判断分配空间是否成功
	if(IS_ERR(drv_led)){ //判断drv_led这个指针分配空间是否成功,如果指向空,IS_ERR(drv_led)的结果就为真
		printk("drv_led_init: drv_led kzalloc is error\n");
		ret=PTR_ERR(drv_led); //获得错误码
		return ret;           //返回错误码
	}
(1)申请设备号
drv_led->major = register_chrdev(0,"drv_led",&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)创建设备文件
        a、创建类
drv_led->cls = class_create(THIS_MODULE,"drv_led");
	//判断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;
	}	
        b、创建设备文件
drv_led->devi = device_create(drv_led->cls,NULL,MKDEV(drv_led->major, 2),NULL,"drv_led");
	//判断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(RCC_Base,sizeof(RCC_TypeDef));
	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->gpioz = ioremap(GPIOZ_Base,sizeof(GPIO_TypeDef));
	if(IS_ERR(drv_led->gpioz)){
		printk("drv_led_init: drv_led->gpioz ioremap is error\n");
		ret = PTR_ERR(drv_led->gpioz);
		goto gpioz_ioremap_err;
	}	
	drv_led->gpioe = ioremap(GPIOE_Base,sizeof(GPIO_TypeDef));
	if(IS_ERR(drv_led->gpioe)){
		printk("drv_led_init: drv_led->gpioe ioremap is error\n");
		ret = PTR_ERR(drv_led->gpioe);
		goto gpioe_ioremap_err;
	}	

	drv_led->gpiof = ioremap(GPIOF_Base,sizeof(GPIO_TypeDef));
	if(IS_ERR(drv_led->gpiof)){
		printk("drv_led_init: drv_led->gpiof ioremap is error\n");
		ret = PTR_ERR(drv_led->gpiof);
		goto gpiof_ioremap_err;
	}		
	return ret;
(3)报错删除

        最后定义的要先删除,执行顺序从上往下,类似栈的后进先出

gpiof_ioremap_err:
		iounmap(drv_led->gpioe);
	gpioe_ioremap_err:
		iounmap(drv_led->gpioz);
	gpioz_ioremap_err:
		iounmap(drv_led->rcc);
	rcc_ioremap_err:
		device_destroy(drv_led->cls,MKDEV(drv_led->major, 2));	
	device_create_err:
		class_destroy(drv_led->cls);//删除类
	class_create_err:
		unregister_chrdev(drv_led->major,"drv_led");//删除设备号
	register_chrdev_err:
		kfree(drv_led);//当申请设备号失败,释放指针空间
		return ret;
出口函数static void __exit drv_led_exit (void
static void __exit drv_led_exit (void)
{
	printk("-------------%s-----------------\n",__FUNCTION__);
	iounmap(drv_led->gpiof);
	iounmap(drv_led->gpioe);
	iounmap(drv_led->gpioz);
	iounmap(drv_led->rcc);
	device_destroy(drv_led->cls,MKDEV(drv_led->major, 2)); //删除设备文件
	class_destroy(drv_led->cls);                 //删除类 
	unregister_chrdev(drv_led->major,"drv_led"); //删除设备号
	kfree(drv_led);                              //当申请设备号失败,释放指针空间
}
声明
module_init(drv_led_init); //声明入口函数是哪一个函数
module_exit(drv_led_exit); //声明出口函数是哪一个函数
MODULE_LICENSE("GPL"); 
三、示例代码

应用层调试程序

//写一个应用层程序调用测试(app_test.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_led",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文件

//写一个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:
    //将当前目录中的源码和内核源码一起编译,并将当前目录中的文件编译成.ko文件
	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 =drv_led_v1.o

GPIO和RCC的头文件

//封装好的头文件(GPIO和RCC)

#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__

内核模块程序 

// drv_led_v1.c文件
#include "linux/init.h"
#include "linux/module.h"
#include "linux/fs.h"
#include "linux/slab.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;  //设备对象
	RCC_TypeDef  *rcc;    //RCC寄存器
	GPIO_TypeDef *gpioz;  //GPIOZ寄存器
	GPIO_TypeDef *gpioe;
	GPIO_TypeDef *gpiof;
};

// 定义一个指针,指向led设备,用这个指针描述led设备
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->MP_AHB5ENSETR&(1<<0))){  //判断时钟是否是默认开启
		drv_led->rcc->MP_AHB5ENSETR |=(1<<0);   //如果时钟未开始,打开时钟
	}

	if(!(drv_led->rcc->MP_AHB4ENSETR&(1<<4))){   //判断时钟是否是默认开启
			drv_led->rcc->MP_AHB4ENSETR |=(1<<4);//如果时钟未开始,打开时钟
	}

	if(!(drv_led->rcc->MP_AHB4ENSETR&(1<<5))){   //判断时钟是否是默认开启
			drv_led->rcc->MP_AHB4ENSETR |=(1<<5);//如果时钟未开始,打开时钟
	}
	
	// 设置GPIOZ的第5,6,7个引脚的模式
	drv_led->gpioe->MODER     &=~((0x3<<16)|(0x3<<20)); //2个e引脚
    drv_led->gpioe->MODER     |=(0x1<<16)|(0x1<<20);
	 
	//配置8 10的GPIOE 10的GPIOF
	drv_led->gpioz->MODER     &=~((0x3<<10)|(0x3<<12)|(0x3<<14));
	drv_led->gpioz->MODER     |=(0x1<<10)|(0x1<<12)|(0x1<<14);

	drv_led->gpioe->MODER     &=~((0x3<<16)|(0x3<<20));
	drv_led->gpioe->MODER     |=(0x1<<16)|(0x1<<20);

	drv_led->gpiof->MODER     &=~(0x3<<20);
	drv_led->gpiof->MODER     |=(0x1<<20);

	// 默认关灯
	drv_led->gpioz->ODR      &=~((0x1<<5)|(0x1<<6)|(0x1<<7));
	drv_led->gpioe->ODR      &=~((0x1<<8)|(0x1<<10));
	drv_led->gpiof->ODR      &=~(0x1<<10);
	
	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->gpioz->ODR    |= ((0x1<<5)|(0x1<<6)|(0x1<<7));	
		drv_led->gpioe->ODR    |= ((0x1<<8)|(0x1<<10));
		drv_led->gpiof->ODR    |= (0x1<<10);
	}else{
		drv_led->gpioz->ODR    &= ~((0x1<<5)|(0x1<<6)|(0x1<<7));
		drv_led->gpioe->ODR    &= ~((0x1<<8)|(0x1<<10));
		drv_led->gpiof->ODR    &= ~(0x1<<10);
	}
	return ret;
}

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

// 2,入口函数----在加载,insmod的时候调用的函数
static int __init drv_led_init(void)
{
	int ret = 0;
	printk("-------------%s-----------------\n",__FUNCTION__);
	//首先给led这个对象分配空间(在内核中GFP_KERNEL分配)
	drv_led = kzalloc(sizeof(struct stm32mp157axxx),GFP_KERNEL);
	//判断分配空间是否成功
	if(IS_ERR(drv_led)){  //判断drv_led这个指针分配空间是否成功,如果指向空,IS_ERR(drv_led)的结果就为真
		printk("drv_led_init: drv_led kzalloc is error\n");
		ret = PTR_ERR(drv_led);  //获得错误码
		return ret;            //返回错误码
	}
#if 0
	//申请设备号-----静态分配
	drv_led->major   =198;
	ret = register_chrdev(drv_led->major,"drv_led",&fops);
	if(ret<0){
		printk("drv_led_init: drv_led register_chrdev is error\n");
		goto register_chrdev_err;
	}
#endif
	//1,申请设备号-----动态分配
	drv_led->major=register_chrdev(0,"drv_led",&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");
	//判断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, 2),NULL,"drv_led");
	//判断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(RCC_Base,sizeof(RCC_TypeDef));
	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->gpioz=ioremap(GPIOZ_Base,sizeof(GPIO_TypeDef));
	if(IS_ERR(drv_led->gpioz)){
		printk("drv_led_init: drv_led->gpioz ioremap is error\n");
		ret=PTR_ERR(drv_led->gpioz);
		goto gpioz_ioremap_err;
	}	

	drv_led->gpioe=ioremap(GPIOE_Base,sizeof(GPIO_TypeDef));
	if(IS_ERR(drv_led->gpioe)){
		printk("drv_led_init: drv_led->gpioe ioremap is error\n");
		ret=PTR_ERR(drv_led->gpioe);
		goto gpioe_ioremap_err;
	}	

	drv_led->gpiof=ioremap(GPIOF_Base,sizeof(GPIO_TypeDef));
	if(IS_ERR(drv_led->gpiof)){
		printk("drv_led_init: drv_led->gpiof ioremap is error\n");
		ret=PTR_ERR(drv_led->gpiof);
		goto gpiof_ioremap_err;
	}	
	
	return ret;
	gpiof_ioremap_err:
		iounmap(drv_led->gpioe);
	gpioe_ioremap_err:
		iounmap(drv_led->gpioz);
	gpioz_ioremap_err:
		iounmap(drv_led->rcc);
	rcc_ioremap_err:
		device_destroy(drv_led->cls,MKDEV(drv_led->major, 2));	
	device_create_err:
		class_destroy(drv_led->cls);//删除类
	class_create_err:
		unregister_chrdev(drv_led->major,"drv_led");//删除设备号
	register_chrdev_err:
		kfree(drv_led);//当申请设备号失败,释放指针空间
		return ret;
}

// 3,出口函数---在卸载这个模块,rmmod的时候调用
static void __exit drv_led_exit (void)
{
	printk("-------------%s-----------------\n",__FUNCTION__);
	iounmap(drv_led->gpiof);
	iounmap(drv_led->gpioe);
	iounmap(drv_led->gpioz);
	iounmap(drv_led->rcc);
	device_destroy(drv_led->cls,MKDEV(drv_led->major, 2));//删除设备文件
	class_destroy(drv_led->cls);                          //删除类
	unregister_chrdev(drv_led->major,"drv_led");          //删除设备号
	kfree(drv_led);                                       //当申请设备号失败,释放指针空间
}

// 声明
module_init(drv_led_init); //声明入口函数是哪一个函数
module_exit(drv_led_exit); //声明哪一个函数是出口函数
MODULE_LICENSE("GPL");
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值