STM32MP157A单片机移植Linux驱动

news2025/2/21 14:42:21

在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无法正常打开字符设备,导致线程退出。

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

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

相关文章

UE引擎游戏加固方案解析

据VGinsights的报告&#xff0c;近年来UE引擎在过去几年中市场占比显著增长&#xff0c;其中亚洲市场增幅达到了30%&#xff0c;随着UE5的推出和技术的不断进步&#xff0c;UE引擎在独立开发者和移动游戏开发中的应用也在逐步增加。 UE引擎的优势在于强大的画面表现与视觉特效…

[kubelet-check] It seems like the kubelet isn‘t running or healthy.

执行k8s时报错&#xff1a; [kubelet-check] It seems like the kubelet isn’t running or healthy. [kubelet-check] The HTTP call equal to ‘curl -sSL http://localhost:10248/healthz’ failed with error: Get "http://localhost:10248/heal ** 解决办法如下&a…

C# 背景 透明 抗锯齿 (效果完美)

主要是通过 P/Invoke 技术调用 Windows API 函数 gdi32.dll/user32.dll&#xff0c;同时定义了一些结构体来配合这些 API 函数的使用&#xff0c;常用于处理图形绘制、窗口显示等操作。 运行查看效果 局部放大&#xff0c;抗锯齿效果很不错,尾巴毛毛清晰可见。 using System; u…

关于uniApp的面试题及其答案解析

我的血液里流淌着战意&#xff01;力量与智慧指引着我&#xff01; 文章目录 1. 什么是uniApp&#xff1f;2. uniApp与原生小程序开发有什么区别&#xff1f;3. 如何使用uniApp实现条件编译&#xff1f;4. uniApp支持哪些平台&#xff0c;各有什么特点&#xff1f;5. 在uniApp中…

【Java场景题】MySQL死锁排查

大家好&#xff0c;今天XiXi给大家分享一个MySQL死锁排查的实验&#xff0c;文章主要有&#xff1a; 通过show engine innodb status&#xff0c;查看最近一次死锁信息开启innodb_print_all_deadlocks&#xff0c;在错误日志中能够记录所有死锁信息通过解析binlog日志定位死锁…

LabVIEW心音信号采集与分析系统

基于LabVIEW软件的心音信号采集与分析系统能够实现心音的采集、去噪和分析。系统利用LabVIEW的强大功能和灵活性&#xff0c;通过模块化设计&#xff0c;实现了心音信号的高效处理和分析&#xff0c;具备深度学习和身份识别的实验能力&#xff0c;适用于医学和生物工程领域的研…

【Scrapy】Scrapy教程7——存储数据

上一节我们对爬虫程序的默认回调函数parse做了改写,提取的数据可以在Scrapy的日志中打印出来了,光打印肯定是不行的,还需要把数据存储,数据可以存到文件,也可以存到数据库,我们一一来看。 存储数据到文件 首先我们看看如何将数据存储到文件,在讲[[【Scrapy】Scrapy教程…

基础入门-算法解密散列对称非对称字典碰撞前后端逆向MD5AESDESRSA

知识点&#xff1a; 0、算法类型-单向散列&对称性&非对称性 1、算法识别加解密-MD5&AES&DES&RSA 2、解密条件寻找-逻辑特征&源码中&JS分析 应用场景&#xff1a; 1、发送数据的时候自动将数据加密发送&#xff08;只需加密即可&#xff09; 安全…

在UBUNTU下搭建Deepseek

在UBUNTU下搭建Deepseek 一、安装UBUNTU 这个就不多说了&#xff0c;无外乎下载UBUNTU的iso&#xff0c;然后用UltraIso制作U盘&#xff0c;然后重启设置启动盘&#xff0c;安装… 二、安装Ollama curl -sSfL https://ollama.com/install.sh | sh这里可能需要你先安装curl工…

O1 Embedder:让检索器思考后再行动

25年2月来自中科大和北京智源研究院的论文“O1 Embedder: Let Retrievers Think Before Action”。 大语言模型 (LLM) 的功能日益强大&#xff0c;彻底改变人们获取和利用信息的方式。值得注意的是&#xff0c;LLM 擅长执行细粒度数据表示&#xff0c;这有助于精确检索信息。它…

Ubuntu系统3分钟本地部署DeepSeek-R1蒸馏模型,支持联网

本文提供Ubuntu ollama Page Assist&#xff0c;3步快速安装DeepSeek-R1蒸馏模型&#xff0c;支持联网&#xff0c;支持API。 目录 DeepSeek-R1安装分3步&#xff1a; Step 1, 安装ollama&#xff08;已安装可忽略&#xff09; Step 2, 下载DeepSeek-R1模型 Step 3, 从…

谷粒商城—分布式高级②.md

认证服务 1. 环境搭建 创建gulimall-auth-server模块,导依赖,引入login.html和reg.html,并把静态资源放到nginx的static目录下 2. 注册功能 (1) 验证码倒计时 //点击发送验证码按钮触发下面函数 $("#sendCode").click(function () {//如果有disabled,说明最近…

C语言-----操作符的分类

1. 操作符的分类 •算术操作符&#xff1a; 、- 、 * 、/、% 移位操作符:<< >> 位操作符: & | ^ 赋值操作符: / 、 % 、 、- 、 *、/、 %、 <<、 >>、&、| 、 ^ 单⽬操作符&#xff1a;&#xff01;、 、- 、 & 、 * 、 、 …

PWM(脉宽调制)技术详解:从基础到应用实践示例

PWM&#xff08;脉宽调制&#xff09;技术详解&#xff1a;从基础到应用实践示例 目录 PWM&#xff08;脉宽调制&#xff09;技术详解&#xff1a;从基础到应用实践示例学前思考&#xff1a;一、PWM概述二、PWM的基本原理三、PWM的应用场景四、PWM的硬件配置与使用五、PWM的编程…

AI智能成长系统 | 应用探讨研究

研究背景 在现代家庭中&#xff0c;三岁宝宝的成长环境日益复杂。由于宝宝每天接触的人群多样&#xff0c;包括家庭成员、同龄小朋友以及可能的陌生人&#xff0c;其语言环境也相应地变得复杂多变。这种环境下&#xff0c;宝宝很容易接触到一些不适宜的语言&#xff0c;即俗称…

java 网络安全感知 网络安全学java

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 实验五 java网络编程及安全 实验内容 1&#xff0e;掌握Socket程序的编写&#xff1b;2&#xff0e;掌握密码技术的使用&#xff1b;3&#xff0e;设计安全传输…

VisionMaster4.4 python脚本 图像处理 转换函数 爱之初体验

最近有接触过一丢丢VM4.3的模块开发. 一直有把python图像处理部分模块移植进来的打算 不过时间不够没来得及折腾.偶尔发现4.4支持py脚本 于是拿来折腾.一下午. 发现4.4支持python脚本,好开心. 首先安装VM4.4 注意一定要是4.4 打开后拖了一个模块. 但是发现import numpy imp…

python-leetcode 40.二叉树的层序遍历

题目&#xff1a; 给定二叉树的根节点root,返回其节点值得层序遍历&#xff08;即逐层从左到右访问所有节点&#xff09; 方法&#xff1a;广度优先搜索 # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val0, leftNone, rightNon…

蓝桥杯学习大纲

&#xff08;致酷德与热爱算法、编程的小伙伴们&#xff09; 在查阅了相当多的资料后&#xff0c;发现没有那篇博客、文章很符合我们备战蓝桥杯的学习路径。所以&#xff0c;干脆自己整理一篇&#xff0c;欢迎大家补充&#xff01; 一、蓝桥必备高频考点 我们以此为重点学习…

小米AX3000T 路由器如何开启 SSH 安装 OpenWRT 系统,不需要降级 v1.0.91 (2025)

小米AX3000T 路由器如何开启 SSH 安装 OpenWRT 系统&#xff0c;不需要降级 v1.0.91 &#xff08;2025&#xff09; 本文内容需要你有一定的 Linux 操作基础&#xff0c;最好是程序员那种&#xff0c;英文水平足够用才行。一般人不需要使用这么复杂的路由器操作系统&#xff0c…