在stm32mp157a单片机移植Linux操作系统,并移植内核驱动,在应用程序中使用3个线程,分别实现控制单片机上3个led流水灯的功能、蜂鸣器控制的功能、风扇控制的功能。
需求整理:
1.驱动程序-->led1.c,led2.c,led3.c,beep.c,fan.c。实现对led字符设备驱动的注册(register_chrdev)和注销(unregister_chrdev)功能、对字符设备相关操作方法的封装(用户空间和内核空间进行数据传递-->copy_to_user,copy_from_user)、物理内存映射虚拟内存(ioremap,iounmap),通过原理图可知---led1->PE10,led2->PF10,led3->PE8,beep->PB6,fan->PE9;
2.头文件-->led.h,beep.h,fan.h。物理内存的寄存器地址;
3.脚本文件生成驱动程序-->Makefile-->生成led1.ko,led2.ko,led3.ko,beep.ko,fan.ko;
4.流水灯应用程序-->application_test.c。使用3个线程实现对led流水灯的控制、蜂鸣器控制、风扇控制。
5.shell命令创建设备文件-->/dev/led1,/dev/led2,/dev/led3,/dev/beep,/dev/fan。用于流水灯应用程序对led的文件描述符。
分析:
仅使用open、write、read、close对io进行控制,直接在write对应的回调函数中进行数据处理,再此基础上实现多线程驱动的逻辑代码。
方案实现:
1.驱动程序
led1.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "led.h"
//字符设备描述符
unsigned int major;
//接收数据buf
char myled1_buf[128] = {};
//虚拟映射寄存器地址
unsigned int* vir_rcc;
unsigned int* vir_moder;
unsigned int* vir_odr;
int myled1_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
//
ssize_t myled1_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_from_user(myled1_buf, ubuf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (myled1_buf[0] == 1)
{
(*vir_odr) |= (0x1<<10); //高电平
}
else if (myled1_buf[0] == 0)
{
(*vir_odr) &= (~(0x1<<10)); //低电平
}
return 0;
}
ssize_t myled1_read(struct file *file, char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_to_user(ubuf, myled1_buf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int myled1_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
//定义字符设备的回调函数接口
struct file_operations fops = {
.open = myled1_open,
.read = myled1_read,
.write = myled1_write,
.release = myled1_close,
};
//字符设备注册
static int __init myled1_init(void)
{
major = register_chrdev(0, "myled1", &fops);
if (major < 0)
{
printk("字符设备注册失败\n");
}
printk("字符设备注册成功 major = %d\n", major);
//硬件寄存器映射到虚拟寄存器地址 ioremap
vir_rcc = ioremap(PHY_RCC_GPIO, 4);
if (vir_rcc == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
return -1;
}
vir_moder = ioremap(PHY_GPIOE_MODER, 4);
if (vir_moder == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
return -1;
}
vir_odr = ioremap(PHY_GPIOE_ODR, 4);
if (vir_odr == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
iounmap(vir_moder);
return -1;
}
printk("物理内存映射成功\n");
//初始化PE10
(*vir_rcc) |= (0x1<<4); //使能rcc
(*vir_moder) &= (~(0x3<<20));
(*vir_moder) |= (0x1<<20);
(*vir_odr) &= (~(0x1<<10));//默认低电平
return 0;
}
//字符设备注销
static void __exit myled1_exit(void)
{
iounmap(vir_rcc);
iounmap(vir_moder);
iounmap(vir_odr);
unregister_chrdev(major, "myled1");
printk("字符设备注销成功\n");
}
//模块
module_init(myled1_init);
module_exit(myled1_exit);
//遵循GPL开源协议
MODULE_LICENSE("GPL");
led2.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "led.h"
unsigned int major;
char myled2_buf[128] = {};
unsigned int* vir_rcc;
unsigned int* vir_moder;
unsigned int* vir_odr;
int myled2_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
//
ssize_t myled2_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_from_user(myled2_buf, ubuf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (myled2_buf[0] == 1)
{
(*vir_odr) |= (0x1<<10); //高电平
}
else if (myled2_buf[0] == 0)
{
(*vir_odr) &= (~(0x1<<10)); //低电平
}
return 0;
}
ssize_t myled2_read(struct file *file, char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_to_user(ubuf, myled2_buf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int myled2_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
struct file_operations fops = {
.open = myled2_open,
.read = myled2_read,
.write = myled2_write,
.release = myled2_close,
};
static int __init myled2_init(void)
{
major = register_chrdev(0, "myled2", &fops);
if (major < 0)
{
printk("字符设备注册失败\n");
}
printk("字符设备注册成功 major = %d\n", major);
//硬件寄存器映射到虚拟寄存器地址 ioremap
vir_rcc = ioremap(PHY_RCC_GPIO, 4);
if (vir_rcc == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
return -1;
}
vir_moder = ioremap(PHY_GPIOF_MODER, 4);
if (vir_moder == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
return -1;
}
vir_odr = ioremap(PHY_GPIOF_ODR, 4);
if (vir_odr == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
iounmap(vir_moder);
return -1;
}
printk("物理内存映射成功\n");
//初始化PF10
(*vir_rcc) |= (0x1<<5); //使能rcc
(*vir_moder) &= (~(0x3<<20));
(*vir_moder) |= (0x1<<20);
(*vir_odr) &= (~(0x1<<10));//默认低电平
return 0;
}
static void __exit myled2_exit(void)
{
iounmap(vir_rcc);
iounmap(vir_moder);
iounmap(vir_odr);
unregister_chrdev(major, "myled2");
printk("字符设备注销成功\n");
}
module_init(myled2_init);
module_exit(myled2_exit);
MODULE_LICENSE("GPL");
led3.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "led.h"
unsigned int major;
char myled3_buf[128] = {};
unsigned int* vir_rcc;
unsigned int* vir_moder;
unsigned int* vir_odr;
int myled3_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
//
ssize_t myled3_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_from_user(myled3_buf, ubuf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (myled3_buf[0] == 1)
{
(*vir_odr) |= (0x1<<8); //高电平
}
else if (myled3_buf[0] == 0)
{
(*vir_odr) &= (~(0x1<<8)); //低电平
}
return 0;
}
ssize_t myled3_read(struct file *file, char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_to_user(ubuf, myled3_buf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int myled3_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
struct file_operations fops = {
.open = myled3_open,
.read = myled3_read,
.write = myled3_write,
.release = myled3_close,
};
static int __init myled3_init(void)
{
major = register_chrdev(0, "myled3", &fops);
if (major < 0)
{
printk("字符设备注册失败\n");
}
printk("字符设备注册成功 major = %d\n", major);
//硬件寄存器映射到虚拟寄存器地址 ioremap
vir_rcc = ioremap(PHY_RCC_GPIO, 4);
if (vir_rcc == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
return -1;
}
vir_moder = ioremap(PHY_GPIOE_MODER, 4);
if (vir_moder == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
return -1;
}
vir_odr = ioremap(PHY_GPIOE_ODR, 4);
if (vir_odr == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
iounmap(vir_moder);
return -1;
}
printk("物理内存映射成功\n");
//初始化PE8
(*vir_rcc) |= (0x1<<4); //使能rcc
(*vir_moder) &= (~(0x3<<16));
(*vir_moder) |= (0x1<<16);
(*vir_odr) &= (~(0x1<<8));//默认低电平
return 0;
}
static void __exit myled3_exit(void)
{
iounmap(vir_rcc);
iounmap(vir_moder);
iounmap(vir_odr);
unregister_chrdev(major, "myled3");
printk("字符设备注销成功\n");
}
module_init(myled3_init);
module_exit(myled3_exit);
MODULE_LICENSE("GPL");
beep.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "beep.h"
unsigned int major;
char mybeep_buf[128] = {};
unsigned int* vir_rcc;
unsigned int* vir_moder;
unsigned int* vir_odr;
int mybeep_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
//
ssize_t mybeep_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_from_user(mybeep_buf, ubuf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
printk("%s:%s:%d:mybeep_buf=%d\n", __FILE__, __func__, __LINE__, mybeep_buf[0]);
if (mybeep_buf[0] == 1)
{
(*vir_odr) |= (0x1<<6); //高电平
}
else if (mybeep_buf[0] == 0)
{
(*vir_odr) &= (~(0x1<<6)); //低电平
}
return 0;
}
ssize_t mybeep_read(struct file *file, char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_to_user(ubuf, mybeep_buf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int mybeep_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
struct file_operations fops = {
.open = mybeep_open,
.read = mybeep_read,
.write = mybeep_write,
.release = mybeep_close,
};
static int __init mybeep_init(void)
{
major = register_chrdev(0, "mybeep", &fops);
if (major < 0)
{
printk("字符设备注册失败\n");
}
printk("字符设备注册成功 major = %d\n", major);
//硬件寄存器映射到虚拟寄存器地址 ioremap
vir_rcc = ioremap(PHY_RCC_GPIO, 4);
if (vir_rcc == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
return -1;
}
vir_moder = ioremap(PHY_GPIOB_MODER, 4);
if (vir_moder == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
return -1;
}
vir_odr = ioremap(PHY_GPIOB_ODR, 4);
if (vir_odr == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
iounmap(vir_moder);
return -1;
}
printk("物理内存映射成功\n");
//初始化PB6
(*vir_rcc) |= (0x1<<1); //使能rcc
(*vir_moder) &= (~(0x3<<12));
(*vir_moder) |= (0x1<<12);
(*vir_odr) &= (~(0x1<<6));//默认低电平
return 0;
}
static void __exit mybeep_exit(void)
{
iounmap(vir_rcc);
iounmap(vir_moder);
iounmap(vir_odr);
unregister_chrdev(major, "mybeep");
printk("字符设备注销成功\n");
}
module_init(mybeep_init);
module_exit(mybeep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Johnson");
MODULE_DESCRIPTION("A simple Linux char driver for beep control");
MODULE_VERSION("1.0");
fan.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "fan.h"
unsigned int major;
char myfan_buf[128] = {};
unsigned int* vir_rcc;
unsigned int* vir_moder;
unsigned int* vir_odr;
int myfan_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
//
ssize_t myfan_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_from_user(myfan_buf, ubuf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
printk("%s:%s:%d:myfan_buf[0]=%d\n", __FILE__, __func__, __LINE__, myfan_buf[0]);
if (myfan_buf[0] == 1)
{
(*vir_odr) |= (0x1<<9); //高电平
}
else if (myfan_buf[0] == 0)
{
(*vir_odr) &= (~(0x1<<9)); //低电平
}
return 0;
}
ssize_t myfan_read(struct file *file, char __user *ubuf, size_t size, loff_t *lft)
{
unsigned long n = copy_to_user(ubuf, myfan_buf, size);
if (n > 0)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return n;
}
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int myfan_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
struct file_operations fops = {
.open = myfan_open,
.read = myfan_read,
.write = myfan_write,
.release = myfan_close,
};
static int __init myfan_init(void)
{
major = register_chrdev(0, "myfan", &fops);
if (major < 0)
{
printk("字符设备注册失败\n");
}
printk("字符设备注册成功 major = %d\n", major);
//硬件寄存器映射到虚拟寄存器地址 ioremap
vir_rcc = ioremap(PHY_RCC_GPIO, 4);
if (vir_rcc == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
return -1;
}
vir_moder = ioremap(PHY_GPIOE_MODER, 4);
if (vir_moder == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
return -1;
}
vir_odr = ioremap(PHY_GPIOE_ODR, 4);
if (vir_odr == NULL)
{
printk("硬件寄存器映射失败:%d\n", __LINE__);
iounmap(vir_rcc);
iounmap(vir_moder);
return -1;
}
printk("物理内存映射成功\n");
//初始化PE9
(*vir_rcc) |= (0x1<<4); //使能rcc
(*vir_moder) &= (~(0x3<<18));
(*vir_moder) |= (0x1<<18);
(*vir_odr) &= (~(0x1<<9));//默认低电平
return 0;
}
static void __exit myfan_exit(void)
{
iounmap(vir_rcc);
iounmap(vir_moder);
iounmap(vir_odr);
unregister_chrdev(major, "myfan");
printk("字符设备注销成功\n");
}
module_init(myfan_init);
module_exit(myfan_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Johnson");
MODULE_DESCRIPTION("A simple Linux char driver for fan control");
MODULE_VERSION("1.0");
2.头文件
由于都是控制GPIO,使能相同的RCC时钟,为便利这里分为3个头文件。
led.h
#ifndef __LED_H__
#define __LED_H__
#define PHY_RCC_GPIO 0x50000a28
#define PHY_GPIOE_MODER 0X50006000
#define PHY_GPIOE_ODR 0X50006014
#define PHY_GPIOF_MODER 0x50007000
#define PHY_GPIOF_ODR 0x50007014
#endif
beep.h
#ifndef __BEEP_H__
#define __BEEP_H__
#define PHY_RCC_GPIO 0x50000a28
#define PHY_GPIOB_MODER 0X50003000
#define PHY_GPIOB_ODR 0X50003014
#endif
fan.h
#ifndef __FAN_H__
#define __FAN_H__
#define PHY_RCC_GPIO 0x50000a28
#define PHY_GPIOE_MODER 0X50006000
#define PHY_GPIOE_ODR 0X50006014
#endif
3.Makefile
Makefile
modname ?= led
arch ?= arm
ifeq ($(arch),arm)
KERNELDIR := ~/FSMP1A/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.4.31-r0/linux-5.4.31/
else
KERNELDIR := /lib/modules/$(shell uname -r)/build/
endif
PWD := $(shell pwd)
all:
make -C $(KERNELDIR) M=$(PWD) modules
obj-m = $(modname).o
#obj-m = demo.o
clean:
make -C $(KERNELDIR) M=$(PWD) clean
4.应用程序
application_test.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
char buf[5] = {};
void* led_control(void* args)
{
char led1_buf[1] = {};
char led2_buf[1] = {};
char led3_buf[1] = {};
int fd1 = open("/dev/myled1", O_RDWR);
if (fd1 < 0)
{
perror("/dev/myled1");
pthread_exit(NULL);
return 0;
}
int fd2 = open("/dev/myled2", O_RDWR);
if (fd2 < 0)
{
perror("/dev/myled2");
close(fd1);
pthread_exit(NULL);
return 0;
}
int fd3 = open("/dev/myled3", O_RDWR);
if (fd3 < 0)
{
perror("/dev/myled3");
close(fd1);
close(fd2);
pthread_exit(NULL);
return 0;
}
while(1)
{
led1_buf[0] = buf[0];
led2_buf[0] = buf[1];
led3_buf[0] = buf[2];
write(fd1, led1_buf, sizeof(led1_buf));
write(fd2, led2_buf, sizeof(led2_buf));
write(fd3, led3_buf, sizeof(led3_buf));
sleep(1);
}
close(fd1);
close(fd2);
close(fd3);
pthread_exit(NULL);
return 0;
}
void* beep_control(void* args)
{
char beep_buf[1] = {};
int fd1 = open("/dev/mybeep", O_RDWR);
if (fd1 < 0)
{
perror("/dev/mybeep");
pthread_exit(NULL);
return 0;
}
while(1)
{
beep_buf[0] = buf[3];
write(fd1, beep_buf, sizeof(beep_buf));
sleep(1);
}
close(fd1);
pthread_exit(NULL);
return 0;
}
void* fan_control(void* args)
{
char fan_buf[1] = {};
int fd1 = open("/dev/myfan", O_RDWR);
if (fd1 < 0)
{
perror("/dev/myfan");
pthread_exit(NULL);
return 0;
}
while(1)
{
fan_buf[0] = buf[4];
write(fd1, fan_buf, sizeof(fan_buf));
sleep(1);
}
close(fd1);
pthread_exit(NULL);
return 0;
}
void* main_control(void* args)
{
int module, control;
while(1)
{
printf("请选择控制的模块:1(led1)2(led2)3(led3)4(beep)5(fan)>");
scanf("%d", &module);
if (module != 1 && module != 2 && module != 3 && module != 4 && module != 5)
{
printf("模块[%d]不存在\n", module);
}
switch(module)
{
case 1:
printf("模块[%d]请选择开灯(1)或关灯(0)>", module);
scanf("%d", &control);
if (control != 0 && control != 1)
{
printf("输入错误,请重新选择\n");
break;
}
buf[0] = control;
break;
case 2:
printf("模块[%d]请选择开灯(1)或关灯(0)>", module);
scanf("%d", &control);
if (control != 0 && control != 1)
{
printf("输入错误,请重新选择\n");
break;
}
buf[1] = control;
break;
case 3:
printf("模块[%d]请选择开灯(1)或关灯(0)>", module);
scanf("%d", &control);
if (control != 0 && control != 1)
{
printf("输入错误,请重新选择\n");
break;
}
buf[2] = control;
break;
case 4:
printf("模块[%d]请选择蜂鸣器响(1)或灭(0)>", module);
scanf("%d", &control);
if (control != 0 && control != 1)
{
printf("输入错误,请重新选择\n");
break;
}
buf[3] = control;
break;
case 5:
printf("模块[%d]请选择风扇开(1)或关(0)>", module);
scanf("%d", &control);
if (control != 0 && control != 1)
{
printf("输入错误,请重新选择\n");
break;
}
buf[4] = control;
break;
default:
break;
}
sleep(1);
}
pthread_exit(NULL);
return 0;
}
int main(int argc, char const *argv[])
{
pthread_t threadled, threadbeep, threadfan, threadmain;
int thread_led = 10;
int thread_beep = 10;
int thread_fan = 10;
int thread_main = 10;
if (pthread_create(&threadled , NULL, led_control, &thread_led) != 0)
{
perror("pthread_create");
exit(EXIT_FAILURE);
}
if (pthread_create(&threadbeep , NULL, beep_control, &thread_beep) != 0)
{
perror("pthread_create");
exit(EXIT_FAILURE);
}
if (pthread_create(&threadfan , NULL, fan_control, &thread_fan) != 0)
{
perror("pthread_create");
exit(EXIT_FAILURE);
}
if (pthread_create(&threadmain , NULL, main_control, &thread_main) != 0)
{
perror("pthread_create");
exit(EXIT_FAILURE);
}
if (pthread_join(threadled, NULL) != 0)
{
perror("pthread_join");
exit(EXIT_FAILURE);
}
if (pthread_join(threadbeep, NULL) != 0)
{
perror("pthread_join");
exit(EXIT_FAILURE);
}
if (pthread_join(threadfan, NULL) != 0)
{
perror("pthread_join");
exit(EXIT_FAILURE);
}
if (pthread_join(threadmain, NULL) != 0)
{
perror("pthread_join");
exit(EXIT_FAILURE);
}
// 主线程退出
return 0;
}
5.mknod创建字符设备文件
mknod 字符设备文件名 字符设备类型(c/b) 主设备号 次设备号
mknod /dev/led1 c 主设备号 次设备号
mknod /dev/led2 c 主设备号 次设备号
mknod /dev/led3 c 主设备号 次设备号
mknod /dev/beep c 主设备号 次设备号
mknod /dev/fan c 主设备号 次设备号
在终端执行以上命令创建字符设备文件,否则在应用程序中open无法正常打开字符设备,导致线程退出。