C语言编写的树莓派4B的GPIO驱动(没用现成的库)

本文用C 语言编写了一个利用树莓派的GPIO点亮一个LED 的驱动程序和应用程序,在我的树莓派4B上亲测可用。

目前网上有很多关于树莓派GPIO驱动的文章,但有些是针对老版本的树莓派写的,树莓派的IO的物理地址(或者是BUS地址)不适用于树莓派4B。利用树莓派官网上提供的库编程,可以获得树莓派4B的IO的物理地址(或者是BUS地址)是FE200000。
驱动程序源码和应用程序源码大部分是借鉴网上这篇文章
网址:https://www.pianshen.com/article/9594392405/
在此感谢原作者,如果构成侵权,请留言,我看到留言后立即删除。
驱动程序源码

   #include <linux/module.h>
   #include <linux/fs.h>
   #include <linux/cdev.h>
   #include <linux/device.h>
   #include <asm/io.h>
   #include <asm/uaccess.h>
   #include <linux/init.h>
   #include <linux/uaccess.h>
   #include <linux/moduleparam.h>  
   #define BCM2711_GPIO_BASE   0xfe200000
   #define LED_ON  1
   #define LED_OFF 0
   #define GPFSEL0 (0x0/4)
   #define GPFSEL1 (0x4/4)
   #define GPFSEL2 (0x8/4)
   #define GPFSEL3 (0xc/4)
   #define GPFSEL4 (0x10/4)
   #define GPFSEL5 (0x14/4)
   #define GPSET0 (0x1c/4)
   #define GPSET1 (0x20/4)
   #define GPCLR0 (0x28/4)
   #define GPCLR1 (0x2c/4)
   static dev_t devno;          //设备号
   static int major;           //主设备号
   static int minor;          //次设备号 
   static struct cdev led_dev;
   static struct class *cls;
   static struct device *test_device;
   unsigned int *global_gpio = NULL;
   static int led_open (struct inode *inode, struct file *filep)
   {//open
   int sel0_value = 0;
    //实现对pin 4引脚 设为输出操作
   sel0_value = *(global_gpio+GPFSEL0);	
	sel0_value |= (1<<12);
	sel0_value &= ~(1<<13);
	sel0_value &= ~(1<<14);
	*(global_gpio+GPFSEL0) = sel0_value;

	printk(KERN_INFO"led_open sel0_value = 0x%x\n", sel0_value);
	return 0;
	}
static ssize_t led_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)
{
   printk(KERN_INFO"led_read \n");
   return 0;
 }
  static ssize_t led_write(struct file *filep, const char __user *buf, size_t len, loff_t *pos)
  {	//实现对pin 4引脚 高电平 低电平操作
  int set0_value = 0;
  char led_value = 0;
  unsigned long count = 1; 

if(copy_from_user((void *)&led_value, buf, count))
{
	return -1;
}
    
printk(KERN_ERR"led_write led_num =%d \n",led_value);


if (led_value > 0)
{
	
	set0_value = *(global_gpio+GPSET0);
	set0_value |= (1<<4);
	 *(global_gpio+GPSET0) = set0_value;
	printk(KERN_ERR"0 write led_value =0x%x \n",set0_value);
}
else 
{
	set0_value = *(global_gpio+GPCLR0);
	set0_value |= (1<<4);
	 *(global_gpio+GPCLR0) = set0_value;
	printk(KERN_ERR"1 write led_value =0x%x \n",set0_value);
}

 printk(KERN_INFO"led_write end \n");

 return 0;
 }
 static struct file_operations led_ops = {

.owner    =  THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
};
static int __init led_init(void)
{
int ret;
printk (KERN_INFO"global_gpio = 0x%lx\n", (unsigned long)global_gpio);
global_gpio = ioremap(BCM2711_GPIO_BASE, 0x80);
printk (KERN_INFO"global_gpio = 0x%lx\n", (unsigned long)global_gpio);
ret = alloc_chrdev_region(&devno,0,1,"ledzfj");
printk(KERN_INFO"ret= %d\n", ret);
printk(KERN_INFO"devno= 0x%x\n", devno);
major=MAJOR(devno);
minor=MINOR(devno);
printk(KERN_INFO"major = %d \n",major);
printk(KERN_INFO"minor = %d \n",minor);
cdev_init(&led_dev, &led_ops);
led_dev.owner=THIS_MODULE;
ret = cdev_add(&led_dev,devno,1); // 把这个驱动加入到内核的链表中
cls = class_create(THIS_MODULE, "ledclass");//   注册class 让代码在dev自动生成设备  
   	if(IS_ERR(cls))
	{
		cdev_del(&led_dev);
		return -1;
	}
	test_device = device_create(cls,NULL,devno,NULL,"ledzfj");创建设备文件/dev/ledzfj
	if(IS_ERR(test_device))
	{
		class_destroy(cls);
		cdev_del(&led_dev);
		return -1;
	}
   printk(KERN_INFO"led init ok!\n");
   return 0;
   }
   static void __exit led_exit(void)
   {
iounmap(global_gpio);
device_destroy(cls,devno);
class_destroy(cls);
cdev_del(&led_dev);
    unregister_chrdev_region(devno, 1);   
printk(KERN_INFO"led_exit \n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

说明一下:驱动源代码几乎全部来源于网上文章,我只是改了一下 #define BCM2711_GPIO_BASE 的值,另外用新版接口注册设备。

应用程序源码

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

 int main(int argc, char **argv)
{
   int fd,pinvalue;

 fd = open("/dev/ledzfj",O_RDWR);
 if(fd<0)
 {
	 printf("open fail \n");
	 return -1;
 }
 printf("open success fd=%d \n", fd);

if( argc > 1 )
{
 pinvalue= atoi(argv[1]);
 write(fd,&pinvalue,sizeof(int));
 printf("str =%s \n", argv[1]);
 }
 close(fd);
 }

上述程序中包含的头文件可能有些是不需要的,但我对linux编程不熟悉,不知道该删除哪个,又懒得删掉一个,编译一次的看看是否通过的试验。网友们如果知道那个头文件是不需要的,可以留言告诉我。

取树莓派4B的IO的物理地址的程序源码如下:

#include <stdio.h>
#include <bcm_host.h>
int main()
{
 unsigned long*IO_PHY_ADDR=NULL;
unsigned long*IO_PHY_SIZE=NULL;
unsigned long*RAM_ADDR=NULL; 
IO_PHY_ADDR=(unsigned long *)bcm_host_get_peripheral_address();
IO_PHY_SIZE=(unsigned long *)bcm_host_get_peripheral_size(); 
RAM_ADDR=(unsigned long *)bcm_host_get_sdram_address();

  printf("peripheral physical address are mapped to :  0x%lx \n", (unsigned long)IO_PHY_ADDR);
  printf("peripheral physical address size:  0x%lx \n", (unsigned long)IO_PHY_SIZE);
  printf("bus address of RAM:  0x%lx \n", (unsigned long)RAM_ADDR);
}

程序中的 bcm_host_get_peripheral_address();
bcm_host_get_peripheral_size();
bcm_host_get_sdram_address();
是树莓派官方提供的库函数,源码和库已经集成在/opt/vc/中
编译参数如下:
cc 123.c -I /opt/vc/include -L /opt/vc/lib -l bcm_host -o 123
也可以按照这篇文章介绍的方法:https://www.pianshen.com/article/9594392405/获取IO的物理地址。
因为是CSDN 的新手,文章排版不美观。欢迎网友们能把这个程序改的更健壮,更好用。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值