一、业务需求与分析
业务需求:用驱动设备点灯输入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");