STM32MP157 Linux驱动入门第一篇——点亮LED灯

本文详细介绍了如何在STM32MP157开发板上,通过直接操作寄存器来控制GPIOI端口的LED灯,包括时钟配置、GPIO模式设置、输出模式选择、速度模式和上拉/下拉控制,以及Linux下对IO内存的映射和操作流程。
摘要由CSDN通过智能技术生成

STM32MP157(直接操作寄存器点灯)

如何通过寄存器方式点灯

linux下是不与寄存器寄存器打交道的,但作为开篇第一个实验体验一下驱动开发就先以直接操作寄存器方式实现
首先想要操作一个GPIO设备必然需要操作总线时钟、配置模式(中断/输入/输出模等模式式、上拉/浮空/下拉等模式),和裸机开发一样。

相关寄存器

GPIOI端口时钟寄存器

板载LED灯原理图

假设控制板载LED0 即为PI0引脚操作寄存器就会涉及到地址,我们通过查阅STM32MP157手册可得知GPIOI的基地址为0x5000A000为起始地址,同时该外设隶属于AHB4总线下
AHB4总线地址以及GPIOI地址

在设置GPIOI端口寄存器时还需使能AHB4总线下具体外设寄存器时钟RCC_MP_AHB4ENSETR,通过查看手册可得知该寄存器地址为0xA28即AHB4总线地址偏移0xA28,已知AHB4基地址为0x50000000,则RCC_MP_AHB4ENSETR寄存器地址值为0x50000A28
AHB4总线起始地址
AHB4总线下各外设时钟地址

GPIO模式寄存器 GPIOX_MODER

在这里插入图片描述
通过查阅手册可知GPIOX_MODER寄存器地址为0x00,则GPIOI_MODE地址即为GPIOI总线起始地址加上偏移地址0x00即为0x5000A000

GPIO输出模式寄存器 GPIOx_OTYPER

在这里插入图片描述

模式对应关系
0推挽输出
1开漏输出
一共32位,只有低16位有效,[15:0]对应GPIO15-0
GPIOx_OTYPER地址为0x04所以GPIOI_OTYPER地址为0x5000A000+0x04即0x5000A004,这里我们设置为推挽输出即可
GPIO速度模式寄存器 GPIOx_OSPEEDR

在这里插入图片描述

模式对应关系
00低速
01中等速度
10高速
11超高速

GPIOx_OSPEEDR地址为0x08所以GPIOI_OSPEEDR地址为0x5000A000+0x08即0x5000A008,这里我们设置为中等速度即可也就是01模式

GPIO上拉/下拉寄存器 GPIOx_PUPDR

在这里插入图片描述

寄存器为32位,每一个GPIO口对应两个位刚刚好一共15组

模式对应关系
00不上拉也不下拉
01上拉
10下拉
11暂时没用

GPIOx_PURDR地址为0x0C所以GPIOI_PURDR地址为0x5000A000+0x0C即0x5000A00C

GPIO设置电平寄存器 GPIOx_BSRR

在这里插入图片描述

GPIOx_BSRR地址为0x18所以GPIOI_BSRR地址为0x5000A000+0x18即0x5000A018
地址[15:0]是复位引脚的对应GPIO15-GPIO0,[31:16]是置位引脚对应GPIO15-GPIO0

控制流程

执行流程如下流程所示
初始化时钟->初始化GPIO口->设置GPIO口速度->设置GPIO口电平
在linux里面操作地址需要获取虚拟地址,所以关于地址的申请获取需要使用ioremap函数,当然有申请就会有注销对应的函数为iounmap函数

I/O内存访问函数

读操作函数

 u8 readb(const volatile void __iomem *addr)
 u16 readw(const volatile void __iomem *addr)
 u32 readl(const volatile void __iomem *addr)

写操作函数

void writeb(u8 value, volatile void __iomem *addr)
void writew(u16 value, volatile void __iomem *addr)
void writel(u32 value, volatile void __iomem *addr)

代码编写

驱动代码

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define LED_Major   200
#define LED_Name    "LED_Device"
#define License     "GPL"
#define Author      "shishukeya"

#define GPIOI_CLK           0X50000000
#define GPIOI_BASE          0x5000A000
#define RCC_MP_AHB4ENSETR   (GPIOI_CLK+0xA28)      //时钟地址
#define GPIOI_MODER         (GPIOI_BASE+0x00)       //模式地址
#define GPIOI_OTYPER        (GPIOI_BASE+0x04)       //输出模式地址       
#define GPIOI_OSPEEDR       (GPIOI_BASE+0x08)       //速度地址
#define GPIOI_PUPDR         (GPIOI_BASE+0x0C)       //上下拉地址
#define GPIOI_BSRR          (GPIOI_BASE+0x18)       //置位/复位地址


static void __iomem  *RCC_MP_AHB4ENSETR_Virtual;
static void __iomem  *GPIOI_MODE_Virtual;
static void __iomem  *GPIOI_OTYPER_Virtual;
static void __iomem  *GPIOI_OSPEEDR_Virtual;
static void __iomem  *GPIOI_PUPDR_Virtual;
static void __iomem  *GPIOI_BSRR_Virtual;



static int led_open(struct inode *inode, struct file *filp)
{
	int res = 0;
	return res;
}
static int led_release(struct inode *inode, struct file *filp)
{
	int res = 0;
	return res;
}

/*
* @description : LED初始化,申请虚拟地址
* @param - NULL : NULL
* @param - NULL : NULL
* @param - NULL : NULL
* @param - NULL : NULL
* @return : 写入的字节数,如果为负值,表示写入失败
*/
void Led_Init(void)
{
    u32  ahb4_clock_val = 0,mode_val = 0,otyper_val = 0,ospeed_val,pupdr_val = 0,bsrr_val = 0;
    RCC_MP_AHB4ENSETR_Virtual = ioremap(RCC_MP_AHB4ENSETR,4);
    GPIOI_MODE_Virtual        = ioremap(GPIOI_MODER,4);
    GPIOI_OTYPER_Virtual      = ioremap(GPIOI_OTYPER,4);
    GPIOI_PUPDR_Virtual       = ioremap(GPIOI_PUPDR,4);
    GPIOI_OSPEEDR_Virtual     = ioremap(GPIOI_OSPEEDR,4);
    GPIOI_BSRR_Virtual        = ioremap(GPIOI_BSRR,4);

    //初始化时钟
    ahb4_clock_val = readl(RCC_MP_AHB4ENSETR_Virtual);
    ahb4_clock_val &= ~(1 << 8);
    ahb4_clock_val |= (1 << 8);
    writel(ahb4_clock_val,RCC_MP_AHB4ENSETR_Virtual);

    //设置为输出模式
    mode_val =  readl(GPIOI_MODE_Virtual);
    mode_val &= ~(0x03 << 0);
    mode_val |= (0x01 << 0);
    writel(mode_val,GPIOI_MODE_Virtual); 

    //设置为推挽输出
    otyper_val = readl(GPIOI_OTYPER_Virtual);
    otyper_val &= 0xFFFFFFFE;
    writel(otyper_val,GPIOI_OTYPER_Virtual);

    //设置GPIO速度
    ospeed_val = readl(GPIOI_OSPEEDR_Virtual);
    mode_val &= ~(0x03 << 0);
    ospeed_val |= 0x01;
    writel(ospeed_val,GPIOI_OSPEEDR_Virtual);  

    //设置GPIO不上拉也不下拉
    pupdr_val = readl(GPIOI_PUPDR_Virtual);
    pupdr_val &= 0xFFFFFFFC;
    writel(pupdr_val,GPIOI_PUPDR_Virtual);

    //初始化关闭LED灯
    bsrr_val = readl(GPIOI_BSRR_Virtual);
    bsrr_val |= 0x01;
    writel(bsrr_val,GPIOI_BSRR_Virtual);
}


/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/

static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
	int res=0;
    char led_val[1] ={0};
    u32  set_val = 0;
	res = copy_from_user(led_val,buf,cnt);
	if(res >= 0)
	{
        if(led_val[0] == '0')
        {
            set_val = readl(GPIOI_BSRR_Virtual);
            set_val &= 0xFFFFFFFE;
            set_val |= (0x1 << 16);           
            writel(set_val,GPIOI_BSRR_Virtual); 
        }
        else if(led_val[0] == '1')
        {
            set_val = readl(GPIOI_BSRR_Virtual);
            set_val &= 0xFFFEFFFF;
            set_val |= 0x01;
            writel(set_val,GPIOI_BSRR_Virtual); 
        }      
	}
	else
	{
		printk("write led error\r\n");
	}
	return res;
}

void led_unmap(void)
{
    iounmap(RCC_MP_AHB4ENSETR_Virtual);
    iounmap(GPIOI_MODE_Virtual);
    iounmap(GPIOI_OTYPER_Virtual);
    iounmap(GPIOI_PUPDR_Virtual);
    iounmap(GPIOI_OSPEEDR_Virtual);   
    iounmap(GPIOI_BSRR_Virtual);  
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt,loff_t *offt)
{
	int res = 0;
    return res;
}

static const struct file_operations led_fops = {
	.owner =	THIS_MODULE,
	.read =		led_read,
	.write =	led_write,
	.open =		led_open,
	.release =	led_release,
};

static int __init led_init(void)
{
	int res = 0;
     Led_Init();
	res = register_chrdev(LED_Major, LED_Name, &led_fops);
    if(res < 0)
    {
        printk("led init error\r\n");
        goto fail_map;
    }
	printk("led init\r\n");
	return res;

fail_map:
    led_unmap();
    return -1;
}

static void __exit led_exit(void)
{
	unregister_chrdev(LED_Major, LED_Name);
	printk("led exit\r\n");
}

module_init(led_init);			 //驱动注册函数
module_exit(led_exit);			//驱动卸载函数

MODULE_LICENSE(License); 					
MODULE_AUTHOR(Author);			
MODULE_INFO(intree,"Y");

应用层代码

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "stdio.h"
#include "string.h"
#include <stdlib.h>

int main(int argc,char *argv[])
{
    int fp,res;
    unsigned char buff[1]={0};
    if(argc !=3)
    {
        printf("missing parameter\r\n");
        return -1;
    }
    else
    {
        /* 打开文件 */
        fp = open(argv[1],O_RDWR);          //打开制定文件
        if(fp != 0)
        {
//            printf("open %s file success\r\n",argv[1]);
            if(atoi(argv[2]) == 1)
            {
                res = write(fp,"1",1);
                if(res < 0)
                {
                    printf("user write error\r\n");
                }
            }
            else if(atoi(argv[2]) == 0)
            {
                res = write(fp,"0",1);
                if(res < 0)
                {
                    printf("user write error\r\n");
                }
            }            
            res = close(fp);
            if(res == 0)
            {
 //               printf("close %s file success\r\n",argv[1]);
            }
            else
            {
                printf("close %f file error\r\n",argv[1]);
                return -1;
            }
        }
        else
        {
            printf("open %s file error\r\n",argv[1]);
            return -1;
        }
               
    }
    return 0;
    
}

验证实验

先编译代码,在文件夹路径下执行make
shishukeya@ubuntu:~/linux/Linux_Drivers/02_regled$ make

编译应用层代码
shishukeya@ubuntu:~/linux/Linux_Drivers/02_regled$ arm-none-linux-gnueabihf-gcc LedAPP.c -o APP

将应用层以及驱动拷贝到开发板根文件系统下
shishukeya@ubuntu:~/linux/Linux_Drivers/02_regled$ sudo cp APP led.ko /home/shishukeya/linux/nfs/rootfs/ -f

去开发板挂载驱动
[root@ATK-stm32mp1]:/$ insmod led.ko

手动创建节点
[root@ATK-stm32mp1]:/$ mknod /dev/led c 200 0

执行代码观察现象
在这里插入图片描述

[root@ATK-stm32mp1]:/$ ./APP /dev/led 0
可以看到我们输入命令让GPIO输出低电平对应原理图低电平点亮,开发板实际灯亮起
在这里插入图片描述

[root@ATK-stm32mp1]:/$ ./APP /dev/led 1
输入1使GPIO引脚输出高电平对应原理图熄灭LED,开发板实际也是熄灭
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值