应用层控制灯,自动创建设备节点,ioctl函数

news2024/11/16 9:24:45

目录

1. 使用应用层控制灯

1.1. 撰写驱动的.c文件

1.2. 撰写应用层的.c文件

1.3. 撰写makefile文件

1.4. 测验

2. 自动创建设备节点

2.1. 函数分析

2.2. 撰写驱动.c文件

2.3. 修改应用层.c文件

2.4. 编写makefile文件

2.5. 在开发板安装.ko驱动

3. ioctl函数(学到这里就可以完成简单的项目了)

3.1. 函数原型

3.2. 撰写.h文件

3.3. 撰写应用层.c文件

3.4. 撰写驱动的.c文件

3.5. 编写Makefile文件

4. 蜂鸣器原理图(不做过多讲解)


1. 使用应用层控制灯

1.1. 撰写驱动的.c文件

驱动是开发板上的驱动,编译依赖开发板上的内核

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h> 
#include <linux/uaccess.h> 
#include <linux/io.h>//添加内存映射的函数
#include <linux/string.h>

unsigned int major =0; 
#define NAME "hello"

int error_num=0;
char kbuf[128]={0};

#define RED_BASE 	0XC001A000
#define BLUE_BASE	0XC001B000
#define GREEN_BASE 	0XC001E000

unsigned int * red_base=NULL;
unsigned int * blue_base=NULL;
unsigned int * green_base=NULL;

//控制红灯的宏
#define RED_ON *red_base |=1<<28;
#define RED_OFF *red_base &=~(1<<28);
//控制蓝灯的宏
#define BLUE_ON *blue_base |=1<<12;
#define BLUE_OFF *blue_base &=~(1<<12);
//控制绿灯的宏
#define GREEN_ON *green_base |=1<<13;
#define GREEN_OFF *green_base &=~(1<<13);


ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t * offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
error_num=copy_to_user(ubuf,kbuf,size);
if(error_num)
{
	printk("copy_to_user error\n");
	return -1;
}
return 0;
}

ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
if(size>sizeof(kbuf)) 
{
size=sizeof(kbuf);
}
//因为是用户层控制内核层,来自用户控制在write中进行判断
error_num=copy_from_user(kbuf,ubuf,size);
if(error_num)
{
	printk("copy_from_user error\n");
	return -1;
}
//判断用户发来的数据
if(strncmp("RED_ON",kbuf,6)==0)
{
	RED_ON;
	return 0;
}else if(strncmp("RED_OFF",kbuf,7)==0)
{
	RED_OFF;
	return 0;
}else if(strncmp("BLUE_ON", kbuf,7)==0)
{
	BLUE_ON;
	return 0;
}else if(strncmp("BLUE_OFF",kbuf,8)==0)
{
	BLUE_OFF;
	return 0;
}else if(strncmp("GREEN_OFF",kbuf,9)==0)
{
	GREEN_OFF;
	return 0;
}else if(strncmp("GREEN_ON",kbuf,8)==0)
{
	GREEN_ON;
	return 0;
}
return 0;
}

int mycdev_open(struct inode *inode, struct file *file)
{
printk("hello open\n");
return 0;
}

int mycdev_release (struct inode *inode, struct file *file)
{
printk("hello close\n");
return 0;
}


struct file_operations fops={
.open=mycdev_open,
.write=mycdev_write,
.release=mycdev_release,
.read=mycdev_read,
};

static int __init hello_init(void)
{
major= register_chrdev(major, NAME, &fops);
if(major<0)
{
printk("register_chrdev error\n");
return major;
}

//在入口处将物理地址映射成虚拟地址
red_base=ioremap(RED_BASE, 36);//大小最大就是36
if(red_base ==NULL)
{
printk("ioremap error RED_BASE\n");
return -ENOMEM;	//返回内存溢出
}
 //注意解引用
*red_base&=~(1<<28); //灭红灯
*(red_base+1)|=(1<<28);//设置为输出模式
*(red_base+9)&=~(3<<24);//选择function 0


green_base=ioremap(GREEN_BASE,36);
if(green_base==NULL)
{
printk("ioremap error GREEN_BASE\n");
return -ENOMEM;
}

*green_base&=~(1<<13);//灭绿灯
*(green_base+1)|=(1<<13);//设置为输出模式
*(green_base+8)&=~(3<<26);//选择function 0

blue_base=ioremap(BLUE_BASE,36);
if(blue_base==NULL)
{
printk("ioremap error BLUE_BASE\n");
return -ENOMEM;
}

*blue_base&=~(1<<12);//灭蓝灯
*(blue_base+1)|=(1<<12);//设置为输出模式
*(blue_base+8)|=(1<<25);//将25位置为1
*(blue_base+8)&=~(1<<24);//将24位置为0

return 0;
}

static void __exit hello_exit(void)
{
//注销内存
iounmap(blue_base);//先注册的后注销
iounmap(green_base);
iounmap(red_base);

unregister_chrdev(major, NAME);

}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

1.2. 撰写应用层的.c文件

生成的可执行文件是在arm开发板上运行,所以编译使用交叉编译工具链

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

char buf[128] = {"my name is southernbird"}; //写入数据
int main(int argc, const char *argv[])
{
  int fd;
  int chose_numh;
  fd = open("./hello", O_RDWR);
  if (fd == -1)
  {
    perror("open error");
    return -1;
  }
  while (1)
  {
    printf("请输入对应选项:\n");
    printf("1.红开 2.红关\n");
    printf("3.绿开 4.绿关\n");
    printf("5.蓝开 6.蓝关\n");
    scanf("%d", &chose_numh);
    switch (chose_numh)
    {
    case 1:
      strcpy(buf, "RED_ON");
      break;
    case 2:
      strcpy(buf, "RED_OFF");
      break;
    case 3:
      strcpy(buf, "GREEN_ON");
      break;
    case 4:
      strcpy(buf, "GREEN_OFF");
      break;
    case 5:
      strcpy(buf, "BLUE_ON");
      break;
    case 6:
      strcpy(buf, "BLUE_OFF");
      break;
    default:
      printf("输入错误\n");
      continue;
    }
    write(fd, buf, sizeof(buf));
    memset(buf, 0, sizeof(buf)); //清空
    read(fd, buf, sizeof(buf));
    printf("%s\n", buf); //用来验证
  }
  close(fd);
  return 0;
}

1.3. 撰写makefile文件

在开发板上进行运行的时候每次都需要将文件移动到nfs挂载的文件下,耗时耗力,修改makefile能解决这个问题

#KERNELDIR:=/lib/modules/$(shell uname -r)/build
KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
	make -C $(KERNELDIR) M=$(PWD) modules
	arm-none-linux-gnueabi-gcc test.c
	cp *.ko a.out /opt/6818/rootfs/rootfs
clean:
	make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o

此时执行make命令能把生成的.ko文件和a.out文件加载到/opt/6818/rootfs/rootfs下

同步完成

1.4. 测验

在SecureCRTPortable输入下列命令

insmod hello.ko 安装驱动文件

mknod hello c 244 1 创建字符设备驱动

./a.out 执行应用层程序

2. 自动创建设备节点

每次我们都需要自己创建设备节点,站在用户层面想,用户是不会自己创建设备节点的,所以上面驱动还是需要优化

2.1. 函数分析

》1.函数原型:class_create(owner, name)	
功能:向用户空间提交目录信息
参数:
	owner:默认填写THIS_MODULE
    name :要创建的目录的名字
返回值:
	成功:返回struct class*指针
	失败:返回错误码指针

》2.函数原型:void class_destroy(struct class *cls)
功能:销毁目录
参数:struct class*指针
返回值:
	成功:返回struct class*指针
	失败:返回错误码指针

》3.函数原型:IS_ERR(cls)
功能:将错误码指针转换为错误码

》4.函数原型:struct device *device_create
(struct class *class,  
struct device *parent,
dev_t devt, 
void *drvdata, 
const char *fmt, ...)
功能:向用户空间提交文件信息
参数:
	class:class_create返回的struct class*指针
	parent:默认填NULL
	devt:设备号		MKDEV(major,0)
	fmt:文件名字
返回值:
	成功:返回struct device *指针
	失败:返回返回错误码指针
》5.函数原型:void device_destroy(struct class *class, dev_t devt)
功能:销毁文件
参数:
	class:class_create返回的struct class*指针
	devt:设备号		MKDEV(major,0)

2.2. 撰写驱动.c文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h> 
#include <linux/uaccess.h> 
#include <linux/io.h>//添加内存映射的函数
#include <linux/string.h>
#include <linux/device.h>//添加头文件


unsigned int major =0; 
#define NAME "hello"

int error_num=0;
char kbuf[128]={0};

#define RED_BASE 	0XC001A000
#define BLUE_BASE	0XC001B000
#define GREEN_BASE 	0XC001E000

unsigned int * red_base=NULL;
unsigned int * blue_base=NULL;
unsigned int * green_base=NULL;

//控制红灯的宏
#define RED_ON *red_base |=1<<28;
#define RED_OFF *red_base &=~(1<<28);
//控制蓝灯的宏
#define BLUE_ON *blue_base |=1<<12;
#define BLUE_OFF *blue_base &=~(1<<12);
//控制绿灯的宏
#define GREEN_ON *green_base |=1<<13;
#define GREEN_OFF *green_base &=~(1<<13);
//书写返回值声明
struct class*cls;
struct device *dev;	

ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t * offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
error_num=copy_to_user(ubuf,kbuf,size);
if(error_num)
{
	printk("copy_to_user error\n");
	return -1;
}
return 0;
}

ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
if(size>sizeof(kbuf)) 
{
size=sizeof(kbuf);
}
//因为是用户层控制内核层,来自用户控制在write中进行判断
error_num=copy_from_user(kbuf,ubuf,size);
if(error_num)
{
	printk("copy_from_user error\n");
	return -1;
}
//判断用户发来的数据
if(strncmp("RED_ON",kbuf,6)==0)
{
	RED_ON;
	return 0;
}else if(strncmp("RED_OFF",kbuf,7)==0)
{
	RED_OFF;
	return 0;
}else if(strncmp("BLUE_ON", kbuf,7)==0)
{
	BLUE_ON;
	return 0;
}else if(strncmp("BLUE_OFF",kbuf,8)==0)
{
	BLUE_OFF;
	return 0;
}else if(strncmp("GREEN_OFF",kbuf,9)==0)
{
	GREEN_OFF;
	return 0;
}else if(strncmp("GREEN_ON",kbuf,8)==0)
{
	GREEN_ON;
	return 0;
}
return 0;
}

int mycdev_open(struct inode *inode, struct file *file)
{
printk("hello open\n");
return 0;
}

int mycdev_release (struct inode *inode, struct file *file)
{
printk("hello close\n");
return 0;
}


struct file_operations fops={
.open=mycdev_open,
.write=mycdev_write,
.release=mycdev_release,
.read=mycdev_read,
};

static int __init hello_init(void)
{
major= register_chrdev(major, NAME, &fops);
if(major<0)
{
printk("register_chrdev error\n");
return major;
}

//在入口处将物理地址映射成虚拟地址
red_base=ioremap(RED_BASE, 36);//大小最大就是36
if(red_base ==NULL)
{
printk("ioremap error RED_BASE\n");
return -ENOMEM;	//返回内存溢出
}
 //注意解引用
*red_base&=~(1<<28); //灭红灯
*(red_base+1)|=(1<<28);//设置为输出模式
*(red_base+9)&=~(3<<24);//选择function 0


green_base=ioremap(GREEN_BASE,36);
if(green_base==NULL)
{
printk("ioremap error GREEN_BASE\n");
return -ENOMEM;
}

*green_base&=~(1<<13);//灭绿灯
*(green_base+1)|=(1<<13);//设置为输出模式
*(green_base+8)&=~(3<<26);//选择function 0

blue_base=ioremap(BLUE_BASE,36);
if(blue_base==NULL)
{
printk("ioremap error BLUE_BASE\n");
return -ENOMEM;
}
*blue_base&=~(1<<12);//灭蓝灯
*(blue_base+1)|=(1<<12);//设置为输出模式
*(blue_base+8)|=(1<<25);//将25位置为1
*(blue_base+8)&=~(1<<24);//将24位置为0

//所有资源申请完成后创建设备节点
cls=class_create(THIS_MODULE, NAME);
if(IS_ERR(cls))
{
printk("class_create error\n");
return IS_ERR(cls);
}
//向用户空间提交文件信息
dev=device_create(cls,NULL,MKDEV(major,1),NULL,NAME);
if(IS_ERR(dev))
{
printk("device_create error\n");
return IS_ERR(dev);
}
printk("this is ok\n");
return 0;
}

static void __exit hello_exit(void)
{
//拆卸销毁内存
device_destroy(cls,MKDEV(major,1));
class_destroy(cls);
//注销内存
iounmap(blue_base);//先注册的后注销
iounmap(green_base);
iounmap(red_base);
unregister_chrdev(major, NAME);

}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

2.3. 修改应用层.c文件

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

char buf[128] = {"my name is southernbird"}; //写入数据
int main(int argc, const char *argv[])
{
  int fd;
  int chose_numh;
  fd = open("./dev/hello", O_RDWR);
  if (fd == -1)
  {
    perror("open error");
    return -1;
  }
  while (1)
  {
    printf("请输入对应选项:\n");
    printf("1.红开 2.红关\n");
    printf("3.绿开 4.绿关\n");
    printf("5.蓝开 6.蓝关\n");
    scanf("%d", &chose_numh);
    switch (chose_numh)
    {
    case 1:
      strcpy(buf, "RED_ON");
      break;
    case 2:
      strcpy(buf, "RED_OFF");
      break;
    case 3:
      strcpy(buf, "GREEN_ON");
      break;
    case 4:
      strcpy(buf, "GREEN_OFF");
      break;
    case 5:
      strcpy(buf, "BLUE_ON");
      break;
    case 6:
      strcpy(buf, "BLUE_OFF");
      break;
    default:
      printf("输入错误\n");
      continue;
    }
    write(fd, buf, sizeof(buf));
    memset(buf, 0, sizeof(buf)); //清空
    read(fd, buf, sizeof(buf));
    printf("%s\n", buf); //用来验证
  }
  close(fd);
  return 0;
}

2.4. 编写makefile文件

#KERNELDIR:=/lib/modules/$(shell uname -r)/build
KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
	make -C $(KERNELDIR) M=$(PWD) modules
	arm-none-linux-gnueabi-gcc test.c
	cp *.ko a.out /opt/6818/rootfs/rootfs
clean:
	make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o

2.5. 在开发板安装.ko驱动

insmod hello.ko 安装

安装后自动生成了hello文件(在dev目录下)

在开发板根目录下运行a.out 测试成功

ps:我不得不吐槽了,这板子是真的慢

3. ioctl函数(学到这里就可以完成简单的项目了)

3.1. 函数原型

用户使用:
》1.函数原型:int ioctl(int fd, int request, ...)
功能:操作文件描述符,用于设置硬件设备的一些配置信息
参数:
	fd:文件描述符
	request:请求码		#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
	...:可写可不写,要写写一个内存地址

内核使用:
》2.函数原型:long (*unlocked_ioctl) (struct file *, unsigned int,unsigned long)
功能:执行未加锁的 I/O 控制操作,接受文件指针和其他控制参数

3.2. 撰写.h文件

#ifndef __HEAD_H__
#define __HEAD_H__

#define type_red 'a' //红灯
#define type_green 'b' //绿灯
#define type_blue 'c' //蓝灯
#define type_buzzer 'd'//蜂鸣器

#define RED_ON _IO(type_red,0)
#define RED_OFF _IO(type_red,1)

#define GREEN_ON _IO(type_green,0)
#define GREEN_OFF _IO(type_green,1)

#define BLUE_ON _IO(type_blue,0)
#define BLUE_OFF _IO(type_blue,1)

#define BUZZ_ON _IO(type_buzzer,0)
#define BUZZ_OFF _IO(type_buzzer,1)

#endif

3.3. 撰写应用层.c文件

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

int LedCtl(int fd);    //控制灯函数
int BuzzerCtl(int fd); //控制蜂鸣器函数
int FindLed(int fd);   //读取灯状态

char buf[128] = "QUERY_LED"; //写入数据
int main(int argc, const char *argv[])
{
  int fd;
  int chose_num;
  fd = open("./dev/hello", O_RDWR);
  if (fd == -1)
  {
    perror("open error");
    return -1;
  }
  while (1)
  {
    printf("请输入对应选项:\n");
    printf("1.控制灯状态\n");
    printf("2.控制蜂鸣器\n");
    printf("3.查询灯得状态\n");
    printf("4.退出\n");
    scanf("%d", &chose_num);
    if (chose_num == 1)
    {
      LedCtl(fd);
    }
    else if (chose_num == 2)
    {
      BuzzerCtl(fd);
    }
    else if (chose_num == 3)
    {
      FindLed(fd);
    }
    else if (chose_num == 4)
    {
      return 1;
    }
    else
    {
      printf("输入错误请重新输入\n");
    }
  }
  close(fd);
  return 0;
}
//读取灯状态
int FindLed(int fd)
{
  strcpy(buf, "QUERY_LED");
  write(fd, buf, sizeof(buf));
  memset(buf, 0, sizeof(buf)); //清空
  read(fd, buf, sizeof(buf));
  printf("%s\n", buf); //打印灯状态
}
//控制蜂鸣器函数
int BuzzerCtl(int fd)
{
  while (1)
  {
    printf("1.开启蜂鸣器 2.关闭蜂鸣器\n");
    printf("3.返回上层\n");
    printf("请选择:\n");
    int buzzer_num;
    scanf("%d", &buzzer_num);
    if (buzzer_num == 1)
    {
      ioctl(fd, BUZZ_ON);
    }
    else if (buzzer_num == 2)
    {
      ioctl(fd, BUZZ_OFF);
    }
    else if (buzzer_num == 3)
    {
      return 1;
    }
    else
    {
      printf("输入错误\n");
    }
  }
}

//控制灯函数
int LedCtl(int fd)
{
  while (1)
  {
    printf("1.红开 2.红关\n");
    printf("3.绿开 4.绿关\n");
    printf("5.蓝开 6.蓝关\n");
    printf("7.返回上层\n");
    printf("请选择:\n");
    int led_num;
    scanf("%d", &led_num);
    switch (led_num)
    {
    case 1:
      ioctl(fd, RED_ON);
      break;
    case 2:
      ioctl(fd, RED_OFF);
      break;
    case 3:
      ioctl(fd, GREEN_ON);
      break;
    case 4:
      ioctl(fd, GREEN_OFF);
      break;
    case 5:
      ioctl(fd, BLUE_ON);
      break;
    case 6:
      ioctl(fd, BLUE_OFF);
      break;
    case 7:
      return 1;
    default:
      printf("输入错误\n");
      continue;
    }
  }
}

3.4. 撰写驱动的.c文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h> 
#include <linux/uaccess.h> 
#include <linux/io.h>//添加内存映射的函数
#include <linux/device.h>//添加头文件
#include <linux/string.h>
#include "head.h"

static int redflag=0;
static int blueflag=0;
static int greenflag=0;

unsigned int major =0; 
#define NAME "hello"

int error_num=0;
char kbuf[128]={0};

#define RED_BASE 	0XC001A000
#define BLUE_BASE	0XC001B000
#define GREEN_BASE 	0XC001E000
#define BUZZ_BASE 0XC001C000

unsigned int * red_base=NULL;
unsigned int * blue_base=NULL;
unsigned int * green_base=NULL;
unsigned int * buzz_base=NULL;

//书写返回值声明
struct class*cls;
struct device *dev;

//ioctl函数自己写
long mycdev_ioctl(struct file*file,unsigned int request,unsigned long args)
{
int ret;
switch(request)
{
case RED_ON:
	 *red_base |=(1<<28);
	 redflag=1;
	 break;
case RED_OFF:
	 *red_base &=~(1<<28);
	  redflag=0;
	  break;
case BLUE_ON:
	 *blue_base |=(1<<12);
	  blueflag=1;
	  break;
case BLUE_OFF: 
	 *blue_base &=~(1<<12);
	  blueflag=0;
	  break;
case GREEN_ON:
	 *green_base |=(1<<13);
	 greenflag=1;
	  break;
case GREEN_OFF:
	 *green_base &=~(1<<13);
	 greenflag=0;
	  break;
case BUZZ_ON:
	* buzz_base |=(1<<14);
	break;
case BUZZ_OFF:
	* buzz_base &=~(1<<14);
	break;
}

}

ssize_t mycdev_read (struct file *file, char __user *ubuf, size_t size, loff_t * offs)
{
if(size>sizeof(kbuf))
{
size=sizeof(kbuf);
}
error_num=copy_to_user(ubuf,kbuf,size);
if(error_num)
{
	printk("copy_to_user error\n");
	return -1;
}
return 0;
}

ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offs)
{
if(size>sizeof(kbuf)) 
{
size=sizeof(kbuf);
}
//因为是用户层控制内核层,来自用户控制在write中进行判断
error_num=copy_from_user(kbuf,ubuf,size);
if(error_num)
{
	printk("copy_from_user error\n");
	return -1;
}
//每次写的时候清理下内核数组
memset(kbuf,0,sizeof(kbuf));
if(strncmp(ubuf,"QUERY_LED",9)==0)
{
    sprintf(kbuf, "红:%s--蓝:%s--绿:%s",
        (redflag == 0) ? "灭" : "亮",
        (blueflag == 0) ? "灭" : "亮",
        (greenflag == 0) ? "灭" : "亮");
}
return 0;
}

int mycdev_open(struct inode *inode, struct file *file)
{
printk("hello open\n");
return 0;
}

int mycdev_release (struct inode *inode, struct file *file)
{
printk("hello close\n");
return 0;
}


struct file_operations fops={
.open=mycdev_open,
.write=mycdev_write,
.release=mycdev_release,
.read=mycdev_read,
.unlocked_ioctl=mycdev_ioctl,
};

static int __init hello_init(void)
{
major= register_chrdev(major, NAME, &fops);
if(major<0)
{
printk("register_chrdev error\n");
return major;
}

//在入口处将物理地址映射成虚拟地址
red_base=ioremap(RED_BASE, 36);//大小最大就是36
if(red_base ==NULL)
{
printk("ioremap error RED_BASE\n");
return -ENOMEM;	//返回内存溢出
}
 //注意解引用
 redflag=0;
*red_base&=~(1<<28); //灭红灯
*(red_base+1)|=(1<<28);//设置为输出模式
*(red_base+9)&=~(3<<24);//选择function 0


green_base=ioremap(GREEN_BASE,36);
if(green_base==NULL)
{
printk("ioremap error GREEN_BASE\n");
return -ENOMEM;
}
greenflag=0;
*green_base&=~(1<<13);//灭绿灯
*(green_base+1)|=(1<<13);//设置为输出模式
*(green_base+8)&=~(3<<26);//选择function 0

blue_base=ioremap(BLUE_BASE,36);
if(blue_base==NULL)
{
printk("ioremap error BLUE_BASE\n");
return -ENOMEM;
}
blueflag=0;
*blue_base&=~(1<<12);//灭蓝灯
*(blue_base+1)|=(1<<12);//设置为输出模式
*(blue_base+8)|=(1<<25);//将25位置为1
*(blue_base+8)&=~(1<<24);//将24位置为0

//添加蜂鸣器模块
buzz_base=ioremap(BUZZ_BASE,36);
if(buzz_base==NULL)
{
printk("ioremap error BUZZER_BASE\n");
return -ENOMEM;
}

*buzz_base&=~(1<<14);//设置为低电平
*(buzz_base+1)|=(1<<14);//设置为输出模式
*(buzz_base+8)|=~(1<<28); //注意顺序,颠倒会对结果产生影响
*(buzz_base+8)&=~(1<<29);//注意顺序,颠倒会对结果产生影响




//所有资源申请完成后创建设备节点
cls=class_create(THIS_MODULE, NAME);
if(IS_ERR(cls))
{
printk("class_create error\n");
return IS_ERR(cls);
}
//向用户空间提交文件信息
dev=device_create(cls,NULL,MKDEV(major,1),NULL,NAME);
if(IS_ERR(dev))
{
printk("device_create error\n");
return IS_ERR(dev);
}
printk("this is ok\n");
return 0;
}

static void __exit hello_exit(void)
{
//拆卸销毁内存
device_destroy(cls,MKDEV(major,1));
class_destroy(cls);
//注销内存
iounmap(blue_base);//先注册的后注销
iounmap(green_base);
iounmap(red_base);
unregister_chrdev(major, NAME);

}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

3.5. 编写Makefile文件

#KERNELDIR:=/lib/modules/$(shell uname -r)/build
KERNELDIR:=/home/hq/temp/kernel-3.4.39/
PWD:=$(shell pwd)
all:
	make -C $(KERNELDIR) M=$(PWD) modules
	arm-none-linux-gnueabi-gcc test.c
	cp *.ko a.out /opt/6818/rootfs/rootfs
clean:
	make -C $(KERNELDIR) M=$(PWD) clean
obj-m:=hello.o

4. 蜂鸣器原理图(不做过多讲解)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/695655.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年最新互联网Java面试八股文出炉(附大厂P5-P8技术栈)

为什么感觉 Java 面试变难了&#xff1f; 几年前&#xff0c;你只需要简单的ssm框架&#xff0c;就能轻松找到一份Java的工作&#xff0c;但现在不一样了&#xff0c;随着涌入这个行业的人越来越多&#xff0c;同一个岗位需要筛选掉更多人&#xff0c;要求自然水涨船高&#x…

mysql大文件导入sql,没有配置my-ini的情况下,或者配置文件不生效命令修改

找到mysql的bin用管理员登录mysql mysql -u root -p 修改最大传输包 set global max_allowed_packet 510241024*100;

3D扫描仪在汽车零部件产业的应用零部件逆向设计偏差检测-CASAIM

汽车零部件作为汽车工业的基础&#xff0c;是支撑汽车工业持续健康发展的必要因素。当前汽车行业正在轰轰烈烈、如火如荼开展的自主开发与创新&#xff0c;需要高效的零部件设计和质量控制解决发方案&#xff0c;CASAIM 3D扫描仪在汽车零部件产业中具有广泛的应用。 1. 零部件逆…

vue3案例2

效果图&#xff1a; <template><section><aside><ul class"nav-ul"><liclass"nav-li":class"nav.active && grey-li"v-for"nav in navArr":key"nav.id"click"onNav(nav)&qu…

实在RPA数字员工横扫618“物流拦截”难题,晨光拦截效率狂飙20倍!

历时三年疫情后&#xff0c;电商首个618大促告捷。 “百亿补贴”“击穿底价”“全网最低”“天天都是618”……在天猫的618启动会上&#xff0c;淘天集团CEO戴珊宣布这将是“历史上最大投入的一届618”&#xff1b;紧随其后的京东618启动会上&#xff0c;京东零售CEO辛利军也高…

消息队列中间件(二)- RabbitMQ(一)

RabbitMQ&#xff08;一&#xff09; 概念核心概念模式 名词安装 概念 接收&#xff0c;存储&#xff0c;转发消息 核心 概念 生产者 交换机 队列 消费者 模式 简单模式 工作模式 发布 路由模式 主题模式 发布订阅模式 名词 Broker 接收和分发消息的应用Virtual host 虚拟分组…

第二章 数据结构(二、三)——Trie树,并查集,堆与哈希表

文章目录 Trie树并查集堆哈希表拉链法开放寻址法字符串前缀哈希 Trie树练习题835. Trie字符串统计143. 最大异或对 并查集练习题836. 合并集合837. 连通块中点的数量240. 食物链 堆练习题838. 堆排序839. 模拟堆 哈希练习题840. 模拟散列表841. 字符串哈希 Trie树 用来高效的存…

面向初学者的卷积神经网络

卷积神经网络在机器学习中非常重要。如果你想做计算机视觉或图像识别任务&#xff0c;你根本离不开它们。但是很难理解它们是如何工作的。 在这篇文章中&#xff0c;我们将讨论卷积神经网络背后的机制、它的优点和应用领域。 什么是神经网络&#xff1f; 首先&#xff0c;让…

K8s是什么?

K8s是开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用。&#xff08;docker只能是单主机上的容器化的应用&#xff09; 职责&#xff1a;管理容器应用 目标&#xff1a;让部署容器化的应用简单高效 Rancher 可以通过界面管理 K8s 平台 一、让我们先来了解一个什…

nginx+lua(openresty) lua-protobuf 安装及使用(三)

前言 前2章已经讲述 openresty 安装及使用 这章主要讲述 openresty 环境下 lua-protobuf 安装及使用 1:环境 ubuntu18 lua-protobuf https://github.com/starwing/lua-protobuf protobuf https://github.com/protocolbuffers/protobuf/releases/tag/v3.9.1 ##最的版本自行选择…

科技资讯|苹果Vision Pro头显处方镜片外观曝光

苹果公司在几周前推出了售价 3499 美元的 Vision Pro 头显&#xff0c;这款头显可以让用户体验增强现实和虚拟现实的内容。对于许多佩戴眼镜的用户来说&#xff0c;他们最关心的问题是&#xff0c;近视眼能够使用 Vision Pro。 Vision Pro 的一个亮点是&#xff0c;它可以配备…

前端修改单选框和多选框的样式(美化)

html提供了单选框和多选框的写法&#xff0c;但是样式并不好看&#xff0c;如图所示&#xff1a; 有时候设计图并不是这样的&#xff0c;需要修改&#xff0c;比如&#xff1a; 这里给大家提供思路&#xff0c;隐藏input框&#xff0c;文字加label标签&#xff0c;使得点击文字…

SPSSPRO数据分析之——CSI指纹数据分析(预处理、降维等)

目录 一、前言 二、数据准备 三、进行预处理 四、进行降维任务 五、正态性检测 六、描述性统计 七、频数分析 八、代码功能 一、前言 SPSSPRO是一款全新的在线数据分析平台&#xff0c;可以用于科研数据的分析、数学建模等&#xff0c;对于那些不会编程或者刚进入科…

浅析数据中台

note 很多企业更倾向于数据集中采集、存储&#xff0c;分层建设&#xff0c;集中管理运营数据资产。阿里的中台是从管理的角度出发&#xff0c;以中台事业部集中数据搜索&#xff0c;技术及产品&#xff0c;数据共享等多个部门的功能。 文章目录 note一、数据中台1. 传统企业的…

数字孪生百科之海康威视安防系统

智能安防是指利用先进的技术手段和系统&#xff0c;以提升安全防护能力和监控效果的安全领域。数字化则是指将信息以数字形式进行处理和存储的过程。智能安防与数字化密切相关&#xff0c;通过数字化的手段和技术&#xff0c;可以实现对安全领域的全面监控、数据分析和智能决策…

Xcode 15 beta 2 - Apple Vision Pro安装下载

1. 更新OS系统 已更新的忽略这步 2. 下载Xcode 15 beta 2 Xcode 15 Xcode 15 让你能够开发、测试和分发适用于所有 Apple 平台的 App。借助增强的代码补齐功能、交互式预览和实时动画&#xff0c;更快地推进 App 的编码和设计。利用 Git 暂存功能直接改进下次要提交的文件&…

汽车通用LCD显示驱动电路芯片DP6524替代PT6524

DP6524是一款利用CMOS技术专门设计的通用LCD驱动IC&#xff0c;完全替代PT6524,采用单片机控制的电子调谐器。它的最大行驶速度可以达到204段输出&#xff0c;可控制多达12个通用输出端口。引脚分配和应用电路都进行了优化&#xff0c;易于PCB布局和节省成本的优势。 主要特性…

iPhone手机如何移除桌面便签软件小组件?

对于很多iPhone手机用户来说&#xff0c;app小组件是非常便捷实用的一个功能&#xff0c;它可以让用户在不打开软件的情况下查看里面的相关内容。如果在iPhone手机上添加了多个桌面便签小组件&#xff0c;想要移除一个的话应该怎么操作呢&#xff1f;以iPhone手机端敬业签便签软…

前端excel文件处理,vue2 、file-saver、xlsx, excel文件生成与excel文件链接数据导出

1、前端excel文件生成 安装插件 npm install file-saver --save如使用TS开发&#xff0c;可安装file-saver的TypeScript类型定义 npm install types/file-saver --save-dev下载文件流 import { saveAs } from file-saver /**** param {*} fileStream // 文件流* param {*} …

vue+leaflet地图实现根据省份区域划分颜色渐变图

效果图 实现代码如下 let separatedColors [rgb(255, 153, 209)];// 离散颜色&#xff0c;如效果图上的粉色 let maxVal 300000;// 定义一个最大值 data.forEach((item) > {for (let i 10; i > 0; i--) {if (item.us_year < maxVal * i) {item.opacity i / 10;}…