树莓派内核驱动编写——控制GPIO的输出

一、地址

总线地址,物理地址,虚拟地址 参考博文:地址概念
树莓派
1.BCM2835 树莓派3b CPU型号,是ARM-cotexA53架构
2440 2410 CPU型号 是ARM9架构
2.树莓派是32位系统,1G 内存,只能识别949M
3.总线地址4G,物理地址1G,虚拟地址4G
在这里插入图片描述

二、芯片手册

1.芯片目录

  • Introduction 基本介绍
  • Auxiliaries: UART1 & SPI1, SPI2 串口开发章节
  • BSC
  • DMA Controller 快速内存拷贝
  • External Mass Media Controller
  • General Purpose I/O (GPIO)
  • Interrupts
  • PCM / I2S Audio
  • Pulse Width Modulator
  • SPI
  • SPI/BSC SLAVE
  • System Timer
  • UART
  • Timer (ARM side)
  • USB

2.GPIO章节

  • 树莓派官网:树莓派(看引脚)
  • 54个通用I/O,至少2个功能选择(输入输出)
  • GPIO有41个寄存器
  • 功能选择
    GPFSEL0 GPIO Function Select 0 功能选择(输入输出)
    GPFSEL0 控制0-9
    GPFSEL1 控制11-19
    在这里插入图片描述
  • 输出控制
    GPSET0 GPIO Pin Output Set 0 输出1
    0 = No effect
    1 = Set GPIO pin n
    GPCLR0 GPIO Pin Output Clear 0 清0
    0 = No effect
    1 = Clear GPIO pin n

描述
1.特别注意树莓派是总线地址,其他芯片是物理地址.
2.除了功能和输入输出控制外,后面还有上升沿边缘触发的寄存器
在这里插入图片描述

三、编写内核代码

  1. 添加寄存器地址,编写到函数pin4_init里
    volatile:指令不会因编译器的优化而省略,直接读取
    GPIO偏移量
    在这里插入图片描述
//定义无符号指针,volatile防止优化
volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0  = NULL;
volatile unsigned int* GPCLR0  = NULL;
//指针指向虚拟地址,0x3f200000是物理地址,ioremap把物理地址映射成虚拟地址
//物理地址:3f000000加偏移量200000
//ioremap使用参考内核
GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);
GPSET0  = (volatile unsigned int *)ioremap(0x3f20001C,4);
GPCLR0  = (volatile unsigned int *)ioremap(0x3f200028,4);
  1. 编写open函数
    配置功能寄存器
    按位操作:先清零12-14位,在赋值
*GPFSEL0 &= ~(6<<12);//配置pin4引脚为输出引脚
*GPFSEL0 |= 1<<12;
  1. 编写write函数
    读取上层write值:在内核中查找函数:copy_from_user,参考内核使用函数 userCmd和上层传的类型一样
    根据值操作io口:操作第4位
//获取上层write值
copy_from_user(&userCmd,buf,count);
//根据值操作io口
if(userCmd == 1){
	*GPSET0 |= 1<<4;
	printk("set 1\n");
}else if(userCmd == 0){
	*GPCLR0 |= 1<<4;
	printk("set 0\n");
}else{
	printk("undo\n");
}
  1. 解除映射
iounmap(GPFSEL0);//先解除虚拟地址映射,在销毁设备
iounmap(GPSET0);
iounmap(GPCLR0);

完整代码

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <asm/io.h>

volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0  = NULL;
volatile unsigned int* GPCLR0  = NULL;

static struct class *pin4_class;
static struct device *pin4_class_dev;

static dev_t devno;
static int major = 231;
static int minor = 0;
static char *module_name = "pin4";

static ssize_t pin4_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{	
	int userCmd;
	//获取上层write值
	copy_from_user(&userCmd,buf,count);
	printk("get value\n");
	//根据值操作io口
	if(userCmd == 1){
		*GPSET0 |= 1<<4;
		printk("set 1\n");
	}else if(userCmd == 0){
		*GPCLR0 |= 1<<4;
		printk("set 0\n");
	}else{
		printk("undo\n");
	}
	return 0;
}
static ssize_t pin4_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    printk("pin4_read\n");
    return 0;
}

static int pin4_open(struct inode * inode, struct file * filp)
{
	*GPFSEL0 &= ~(6<<12);//配置pin4引脚为输出引脚
	*GPFSEL0 |= 1<<12;
    printk("pin4_open\n");//内核的打印函数
    return 0;
}

//在内核源码查找struct file_operations看结构体成员,添加用到的函数
static const struct file_operations pin4_fops = {
    .owner = THIS_MODULE,
    .write = pin4_write,//函数指针
    .open  = pin4_open,
    .read  = pin4_read,
};


static int __init pin4_init(void)//驱动入口
{
	int ret;
	printk("insmod driver pin4 success\n");
	devno = MKDEV(major,minor);//创建设备号
	ret = register_chrdev(major,module_name,&pin4_fops);//注册驱动,把这个驱动加入到内核链表
	pin4_class = class_create(THIS_MODULE,"myfirstdemo");//代码自动生成设备
	pin4_class_dev = device_create(pin4_class,NULL,devno,NULL,module_name);//创建设备文件

	GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);//物理地址转换为虚拟地址,io口寄存器映射成普通内存单元进行访问
	GPSET0  = (volatile unsigned int *)ioremap(0x3f20001C,4);
	GPCLR0  = (volatile unsigned int *)ioremap(0x3f200028,4);
	return 0;
}

static void __exit  pin4_exit(void)
{
	iounmap(GPFSEL0);//先解除虚拟地址映射,在销毁设备
    iounmap(GPSET0);
	iounmap(GPCLR0);
	device_destroy(pin4_class,devno);//销毁设备
	class_destroy(pin4_class);//销毁类
	unregister_chrdev(major,module_name);//卸载设备
}

module_init(pin4_init);//入口,是个宏
module_exit(pin4_exit);
MODULE_LICENSE("GPL v2");

四、编写上层代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
        int fd;
        int cmd;
        int data;

        fd = open("/dev/pin4",O_RDWR);
        if(fd < 0){
                printf("open failed\n");
                perror("reson:");
                exit(-1);
        }else{
                printf("open success\n");
        }
        printf("input commnd :1/0  \n1:set pin4 high \n0:set pin4 low\n");
        scanf("%d",&cmd);

        if(cmd == 1){
                data = 1;
        }else{
                data = 0;
        }
        printf("data = %d\n",data);
        write(fd,&data,1);
        close(fd);
        return 0;
}

五、编译运行实现功能

1’编译参考博文:树莓派内核驱动编写——添加与调用
2.执行上层,查看GPIO输出

查看GPIO:gpio readall

在这里插入图片描述

师承上官可编程 —— 陈立臣

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
树莓派是一种基于Linux系统的微型电脑,具有丰富的GPIO (General-purpose input/output) 接口,可以通过这些接口连接到各种外部设备。要利用网页控制树莓派GPIO,可以按照以下步骤进行操作: 1. 首先,确保在树莓派上安装了适当的操作系统,如Raspbian。这个操作系统提供了对GPIO的直接访问和控制。 2. 在树莓派上运行一个Web服务器,如Apache或Nginx。这将使树莓派能够接收和处理来自Web浏览器的请求。 3. 在Web服务器上创建一个简单的网页,用于控制GPIO。这个网页可以包括按钮、滑块或其他交互元素,用于控制GPIO引脚的状态。 4. 在树莓派编写一个脚本或程序,用于解析网页发送的请求,并根据请求的内容来控制GPIO。可以使用Python编程语言来编写这样的脚本,因为Python具有丰富的GPIO库。 5. 运行这个脚本,并确保它与Web服务器进行通信。可以使用CGI (Common Gateway Interface) 或其他类似的技术来处理网页请求和响应的交互。 6. 最后,在Web浏览器上输入树莓派的IP地址和网页的URL,就可以打开控制页面了。通过点击按钮或拖动滑块,可以向树莓派发送相应的请求,树莓派将相应地控制GPIO引脚的状态。 总之,利用网页控制树莓派GPIO需要安装适当的操作系统、Web服务器和编写相应的脚本或程序。通过这种方式,可以在网络上远程控制树莓派GPIO,实现远程监控、智能家居和其他各种应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dz小伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值