orangepi3lts led灯驱动

本文展示了如何在Linux内核中编写一个简单的LED驱动程序,使用字符设备驱动模型,并通过ioctl调用来控制GPIO口的LED灯。驱动程序通过ioremap映射物理地址,实现LED的开启和关闭。同时,给出了用户空间的测试程序,通过打开设备文件并调用ioctl命令来交替开关LED。最后,驱动加载和卸载的输出以及dmesg日志证实了驱动的正确工作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

驱动程序led_test.c,可以使用miscdevice会更简单些

/*
 * 审请设备号与cdev初始化和注册合并
 * int register_chrdev(MAJOR major, char *name, file_operations *fops);
 * 参数:
 *    major: 主设备号,为0自动分配,不为0静态分配
 *    name:  内核中的设备对应名称
 *    fops:  操作指令集
 *
 * 返回值:
 *    小于0:出错
 *    major为0,自动分配主设备号
 *    major不为0,返回0表示成功,否则失败
 *
 * led 使用
 * 内核和用户空间都能使用 __IO(type, id)建立命令
 * type是一个字符
 *
 * 物理内存映射
 * ioremap(phy, size)
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/io.h>

#define LED_ON _IO('I', 0)
#define LED_OFF _IO('I', 1)

dev_t dev_id;

int __init led_init(void);
void __exit led_exit(void);

// gpiol_base address
void __iomem *gpiol_base = NULL;
#define register(address) (*(volatile unsigned long *)address)

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XXX");
MODULE_VERSION("0.0.1");

static int led_open(struct inode *node, struct file *file){
  printk("KERN_INFO led module is opened\n");
  return 0;
}

static int led_close(struct inode *node, struct file *file){
  printk("KERN_INFO led module is closed\n");
  return 0;
}
long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
  switch(cmd){
    case LED_ON:
      
      *(unsigned long *)(gpiol_base + 0x10) |= (1 << 4);
      *(unsigned long *)(gpiol_base + 0x10) &= ~(1 << 7);
      printk("KERN_INFO The led is on\n");
      break;
    case LED_OFF:
      printk("KERN_INFO The led is off\n");
      *(unsigned long *)(gpiol_base + 0x10) |= (1 << 7);
      *(unsigned long *)(gpiol_base + 0x10) &= ~(1 << 4);
      break;
    default:
      printk("KERN_ ERROR Unknow Command\n");
      return -EINVAL;
  }
  return 0;
}

struct file_operations fops = {
  .owner = THIS_MODULE,
  .open = led_open,
  .release = led_close,
  .unlocked_ioctl = led_ioctl,
};

// struct class
struct class *dev_class;
// struct device
struct device *dev_device;

// 自动在/dev/目录下创建设备文件
int dev_fs_auto_create(void){
  int ret = 0;

  // create class
  dev_class = class_create(THIS_MODULE, "led_class");

  if(IS_ERR(&dev_class)){
    unregister_chrdev(dev_id, "led");
    ret = PTR_ERR(dev_class);
    printk("KERN_INFO class create failed\n");
    return ret;
  }

  dev_device = device_create(dev_class, NULL, dev_id, NULL, "led");

  if(IS_ERR(dev_device)){
    //放class
    class_destroy(dev_class);
    unregister_chrdev(dev_id, "led");
    ret = PTR_ERR(dev_device);
    printk("KERN_INFO device create failed\n");
    return ret;
  }


  return ret;
}

int __init led_init(void){
  // 动态审请设备号
  int ret = 0;

  ret = register_chrdev(0, "led", &fops);

  if(ret < 0){
    printk("KERN_INFO register_chrdev error\n");
    return ret;
  }

  dev_id = MKDEV(ret, 0);

  dev_fs_auto_create();
  
  // 映射GPIOL
  gpiol_base = ioremap(0x07022000, 0x24);
  printk("KERN_INFO module led %d is register and and add into the kernel\n", dev_id);
  if(IS_ERR(gpiol_base)){
    printk("KERN_INFO ioremap error\n");
    device_destroy(dev_class, dev_id);
    // 放class
    class_destroy(dev_class);
    unregister_chrdev(dev_id, "led");
    return -1;
  }
  // 设置输出
  *(unsigned long *)gpiol_base &= ~(0xf << (4 * 4));
  *(unsigned long *)gpiol_base &= ~(0xf << (4 * 7));

  *(unsigned long *)(gpiol_base +0x1c) |= (0x2 << (2 * 4));
  *(unsigned long *)(gpiol_base +0x1c) |= (0x2 << (2 * 7));
  return 0;
}

void __exit led_exit(void){
  // 放device
  device_destroy(dev_class, dev_id);
  // 放class
  class_destroy(dev_class);
  unregister_chrdev(dev_id, "led");
  printk("KERN_INFO module led %d is unregister and remove from the kernel\n", dev_id);
}

Makefile

CONFIG_MODULE_SIG=n
KERNDIR:=/lib/modules/5.10.75-sunxi64/build
PWD:= $(shell pwd)

obj-m:= led_test.o

all:
	make -C $(KERNDIR) M=$(PWD) modules ; \
	gcc -o main main.c

clean:
	make -C $(KERNDIR) M=$(PWD) clean	; \
	rm main

测试程序

#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define LED_ON _IO('I', 0)
#define LED_OFF _IO('I', 1)

void delay(long sec){
  int i = 0;
  for(; i < sec; i++){
    for(int j = 0; j < 100; j++)
      ;
  }
}

int main(int argc, char **argv){
  int fd = 0;
  char c = 0;

  if((fd = open("/dev/ioctl", O_RDWR)) < 0){
    fprintf(stderr,"Open /dev/ioctl error\n");
    return -1;
  }

  while(1){
    delay(1000000);
    ioctl(fd, LED_ON);
    delay(1000000);
    ioctl(fd, LED_OFF);
  }

  return 0;
}

使用的开发板上GPIOL4和GPIOL7,因为系统有使用这两个灯,因此设置上拉或下拉会出错,不能设置上拉和下拉。但可以直接使用。

# 编译
make 
# 加入内核
insmod led_test.ko
# 用测试程序测试
./main
# 下面是输出结果
Led on
Led off
Led on
Led off
# 下面是dmesg输出
[ 1519.648891] KERN_INFO module led 251658240 is register and and add into the kernel
[ 1522.234352] KERN_INFO led module is opened
[ 1524.288121] KERN_INFO The led is on
[ 1526.296431] KERN_INFO The led is off
[ 1528.304713] KERN_INFO The led is on
[ 1530.313120] KERN_INFO The led is off
[ 1532.321388] KERN_INFO The led is on
[ 1534.329778] KERN_INFO The led is off
[ 1536.338042] KERN_INFO The led is on
[ 1538.346303] KERN_INFO The led is off
[ 1540.354723] KERN_INFO The led is on
[ 1542.362982] KERN_INFO The led is off
[ 1542.980757] KERN_INFO led module is closed

正常开发板上的红灯和绿灯会交替开关,至此linux驱动led完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值