编写LED驱动前需要移植树莓派内核,以获得内核源码,内核移植相关方法请参照上一篇《树莓派3B内核移植》。

我们在编写驱动程序的时候,IO空间的起始地址是0x3f000000,加上GPIO的偏移量0x2000000,所以GPIO的物理地址应该是从0x3f200000开始的,然后在这个基础上进行Linux系统的MMU内存虚拟化管理,映射到虚拟地址上。

特别注意,BCM2708 和BCM2709 IO起始地址不同,BCM2708是0x20000000,BCM2709是0x3f000000,这是造成大部分人驱动出现“段错误”的原因。树莓派3B的CPU为BCM2709。

驱动代码:

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>    #define PIN                  26 //GPIO26 #define BCM2835_GPSET0                          0x001c
#define BCM2835_GPFSEL0                         0x0000
#define BCM2835_GPCLR0                          0x0028
#define BCM2835_GPIO_FSEL_OUTP      1  #define BCM2835_GPIO_BASE                   0x3f200000  int open_state = 0;         //文件打开状态    int bcm2835_gpio_fsel(uint8_t pin, uint8_t mode)
{    //初始化GPIOB功能选择寄存器的物理地址    volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);    volatile uint32_t * bcm2835_gpio_fsel = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10);    uint8_t   shift = (pin % 10) * 3;    uint32_t  value = mode << shift;    *bcm2835_gpio_fsel = *bcm2835_gpio_fsel | value;    printk("fsel address: 0x%lx : %x\n", (long unsigned int)bcm2835_gpio_fsel, *bcm2835_gpio_fsel);    return 0;
}    int bcm2835_gpio_set(uint8_t pin)
{    //GPIO输出功能物理地址    volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);    volatile uint32_t * bcm2835_gpio_set = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32;    uint8_t   shift = pin % 32;    uint32_t  value = 1 << shift;    *bcm2835_gpio_set = *bcm2835_gpio_set | value;    printk("set address:  0x%lx : %x\n", (long unsigned int)bcm2835_gpio_set, *bcm2835_gpio_set);    return 0;
}    int bcm2835_gpio_clr(uint8_t pin)
{    //GPIO清除功能物理地址    volatile uint32_t * bcm2835_gpio = (volatile uint32_t *)ioremap(BCM2835_GPIO_BASE, 16);    volatile uint32_t * bcm2835_gpio_clr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32;    uint8_t   shift = pin % 32;    uint32_t  value = 1 << shift;    *bcm2835_gpio_clr = *bcm2835_gpio_clr | value;    printk("clr address:  0x%lx : %x\n", (long unsigned int)bcm2835_gpio_clr, *bcm2835_gpio_clr);    return 0;
}   static int leds_open(struct inode *inode, struct file *filp)
{    if(open_state == 0)      {      open_state =  1;      printk("Open file suc!\n");      return 0;      }      else      {      printk("The file has opened!\n");      return -1;      }
}    static long leds_ioctl(struct file*filp, unsigned int cmd, unsigned long arg)
{    switch(cmd)      {      case 0:      bcm2835_gpio_clr(PIN);    printk("LED OFF!\n");    break;      case 1:      bcm2835_gpio_set(PIN);    printk("LED ON!\n");    break;      default:      return-EINVAL;      }      return 0;
}    static int leds_release(struct inode *inode, struct file *filp)
{    if(open_state == 1)      {      open_state =  0;      printk("close file suc!\n");      return 0;      }      else      {      printk("The file has closed!\n");      return -1;      }
}    static const struct file_operations leds_fops = {    .owner = THIS_MODULE,    .open = leds_open,    .unlocked_ioctl = leds_ioctl,    .release = leds_release,
};    static struct miscdevice misc = {    .minor =MISC_DYNAMIC_MINOR,    .name ="my_leds",    .fops =&leds_fops,
};    static int __init leds_init(void)
{    int ret;    //注册混杂设备    ret =misc_register(&misc);    //配置功能选择寄存器为输出    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_OUTP);    //设置输出电平为高电平,LED亮    bcm2835_gpio_set(PIN);    printk("ledsinit.\n");    return ret;
}    static void leds_exit(void)
{    //LED灭    bcm2835_gpio_clr(PIN);    misc_deregister(&misc);            printk("leds_exit\n");
}    module_init(leds_init);
module_exit(leds_exit);    MODULE_AUTHOR("yangwen");
MODULE_LICENSE("GPL"); 

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>  int main(int argc, char **argv)
{  int on;  int fd;  if (argc != 2 || sscanf(argv[1],"%d", &on) != 1 ||on < 0 || on > 1 ) {  fprintf(stderr, "Usage:%s 0|1\n",argv[0]);  exit(1);  }  fd = open("/dev/my_leds", 0);  if (fd < 0) {  perror("open device leds");  exit(1);  }  /*通过ioctl来控制灯的亮、灭*/  if(on){  printf("turn on leds!\n");  ioctl(fd, 1);  }  else {  printf("turn off leds!\n");  ioctl(fd, 0);  }  close(fd);  return 0;
} 

测试代码 Makefile:

CROSS= /home/yangwen/Raspberry/tools-master/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-
all: led_testled_test: led_test.c$(CROSS)gcc -o $@ led_test.c -staticclean:@rm -rf led_test *.o

在虚拟机分别编译,将模块、测试程序拷贝至树莓派,插入模块后运行测试程序。

sudo insmod xxx.ko

sudo ./led_test 1 打开LED

sudo ./led_test 0 关闭LED

树莓派LED驱动编写相关推荐

  1. RK3288 LED驱动编写

    开始编写LED驱动程序 我之前学习了rk3288 的led驱动编写的准备 然后也编写了led驱动的框架. 现在只要把框架适当的填充具体的硬件操作,就可以实现led的电灯了. 1.需要用到的函数iore ...

  2. RK3288 LED驱动编写准备

    3288LED驱动编写 1.LED硬件知识 2.GPIO的一般结构 3.普通的GPIO设置方法 4.GPIO寄存器的操作方法 5.3288的GPIO操作方法 5.1 怎么写LED驱动程序? 5.2 3 ...

  3. 树莓派引脚驱动编写(2)

    1.续上篇文章的代码测试过后这节真正来验证是否驱动成功 (1)首先改写驱动代码(在linux源码树目录的/driver/char下) 注意:在编译之前可以先备份以下 #include <linu ...

  4. 树莓派3B+ 驱动开发之GPIO

    1.查看树莓派GPIO地址映射基地址 方法一: cat /proc/iomem 结果  3f200000-3f2000b3 : /soc/gpio@7e200000.3f200000为基地址, 方法二 ...

  5. Linux驱动_设备树下LED驱动

    前言 学习完设备树基础知识后,完成设备树下LED驱动实验 一.修改设备树文件 在设备书根/节点下添加子节点led信息: alphaled {status = "okay";comp ...

  6. 如何写一个树莓派的驱动来控制GPIO LED

    一直以来,物联网开发者面对新的硬件都是一件头痛的事情.有些时候明明有现成的驱动,我们却没法直接利用.公司的PM/领导一句话下来,整套系统就要从新来,说明书一看就是一天,重复的软硬联调,日复一日的造轮子 ...

  7. linux驱动:二、LED灯驱动编写

    一.地址映射 在正式编写驱动前需要先简单了解一下 MMU 这个神器,MMU 全称叫做 Memory Manage Unit,也就是内存管理单元.在老版本的 Linux 中要求处理器必须有 MMU,但是 ...

  8. Linux驱动——LED驱动的编写与实验

    文章目录 1. LED驱动的原理分析 1.1 地址映射 1.1.1 ioremap函数 1.1.2 iounmap函数 1.2 I/O内存访问函数 1.3 读操作函数 1.4写操作函数 2. 硬件原理 ...

  9. 嵌入式驱动编写-点亮LED驱动程序

    在开发板上,有三个LED灯.如何通过应用程序点亮这三个灯如何编写驱动程序 操作硬件的时候,我们需要准备开发板的原理图和开发手册,,根据这两个文档来进行配置 在source insight 编写代码 1 ...

最新文章

  1. 为什么这门技术如此重要?错过这次黄金期,就晚了!
  2. 仅用几行Python代码就能帮小姐姐复制U盘文件,实用干货
  3. Trie UVALive 7192 Chip Factory (15长春J)
  4. Repository模式与UnitOfWorks模式的运用
  5. JVM 调优实战--常见的垃圾回收算法及垃圾收集器组合
  6. hibernate之工具类
  7. C#位图BitArray 小试牛刀
  8. mysql常用cmd指令_Mysql cmd 常用命令
  9. 高手追小萝莉的故事(洛谷P1184题题解,Java语言描述)
  10. 通过git命令将本地代码文件推送至github
  11. 2022美赛备赛资料大全
  12. 树莓派基础实验19:光敏传感器实验
  13. sql注入之时间注入
  14. C语言switch史上最详细的讲解
  15. 最近招聘和面试的感想
  16. Python 云标签——玩点浪漫!
  17. ArcEngine ISymbol效果预览
  18. Burpsuite配置抓apk流量代理设置脚本
  19. 初学C语言:判断输入的数是否能被5整除。
  20. linux 宝塔镇河妖

热门文章

  1. 计算机及应用专插本,2020广东财经大学华商学院专插本计算机科学与技术专业《数据库原理与应用》考试大纲...
  2. mpmath.psi python_Mol Cell Proteomics. |马臻| psims-一个用于编写HUPO-PSI标准下的mzML和mzIdentML的python库...
  3. vue 生成二维码工具
  4. BZOJ2209 [Jsoi2011]括号序列
  5. SpringBoot OSS实战之用户头像上传
  6. LeetCode 27 移除元素 题解
  7. 一学年的竞赛总结和回顾【我参加过的那些竞赛】
  8. django博客项目-文章详情页功能
  9. C++ 流 文件操作(输入输出流)
  10. 微营销实战demo coffee