古典风格网站模版,做网站技术选择,湛江商城网站制作公司,wordpress带用户中心主题在stm32mp157a单片机移植Linux操作系统#xff0c;并移植内核驱动#xff0c;在应用程序中使用3个线程#xff0c;分别实现控制单片机上3个led流水灯的功能、蜂鸣器控制的功能、风扇控制的功能。
需求整理#xff1a;
1.驱动程序--led1.c#xff0c;led2.c#xff…在stm32mp157a单片机移植Linux操作系统并移植内核驱动在应用程序中使用3个线程分别实现控制单片机上3个led流水灯的功能、蜂鸣器控制的功能、风扇控制的功能。
需求整理
1.驱动程序--led1.cled2.cled3.cbeep.cfan.c。实现对led字符设备驱动的注册register_chrdev和注销unregister_chrdev功能、对字符设备相关操作方法的封装用户空间和内核空间进行数据传递--copy_to_user,copy_from_user、物理内存映射虚拟内存ioremapiounmap通过原理图可知---led1-PE10,led2-PF10,led3-PE8,beep-PB6,fan-PE9
2.头文件--led.hbeep.hfan.h。物理内存的寄存器地址
3.脚本文件生成驱动程序--Makefile--生成led1.koled2.koled3.kobeep.kofan.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) | (0x110); //高电平}else if (myled1_buf[0] 0){(*vir_odr) (~(0x110)); //低电平}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);//硬件寄存器映射到虚拟寄存器地址 ioremapvir_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) | (0x14); //使能rcc(*vir_moder) (~(0x320));(*vir_moder) | (0x120);(*vir_odr) (~(0x110));//默认低电平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.hunsigned 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) | (0x110); //高电平}else if (myled2_buf[0] 0){(*vir_odr) (~(0x110)); //低电平}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);//硬件寄存器映射到虚拟寄存器地址 ioremapvir_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) | (0x15); //使能rcc(*vir_moder) (~(0x320));(*vir_moder) | (0x120);(*vir_odr) (~(0x110));//默认低电平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.hunsigned 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) | (0x18); //高电平}else if (myled3_buf[0] 0){(*vir_odr) (~(0x18)); //低电平}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);//硬件寄存器映射到虚拟寄存器地址 ioremapvir_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) | (0x14); //使能rcc(*vir_moder) (~(0x316));(*vir_moder) | (0x116);(*vir_odr) (~(0x18));//默认低电平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.hunsigned 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) | (0x16); //高电平}else if (mybeep_buf[0] 0){(*vir_odr) (~(0x16)); //低电平}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);//硬件寄存器映射到虚拟寄存器地址 ioremapvir_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) | (0x11); //使能rcc(*vir_moder) (~(0x312));(*vir_moder) | (0x112);(*vir_odr) (~(0x16));//默认低电平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.hunsigned 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) | (0x19); //高电平}else if (myfan_buf[0] 0){(*vir_odr) (~(0x19)); //低电平}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);//硬件寄存器映射到虚拟寄存器地址 ioremapvir_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) | (0x14); //使能rcc(*vir_moder) (~(0x318));(*vir_moder) | (0x118);(*vir_odr) (~(0x19));//默认低电平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#endifbeep.h
#ifndef __BEEP_H__
#define __BEEP_H__#define PHY_RCC_GPIO 0x50000a28
#define PHY_GPIOB_MODER 0X50003000
#define PHY_GPIOB_ODR 0X50003014#endiffan.h
#ifndef __FAN_H__
#define __FAN_H__#define PHY_RCC_GPIO 0x50000a28
#define PHY_GPIOE_MODER 0X50006000
#define PHY_GPIOE_ODR 0X50006014#endif3.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/
endifPWD : $(shell pwd)all:make -C $(KERNELDIR) M$(PWD) modulesobj-m $(modname).o
#obj-m demo.oclean:make -C $(KERNELDIR) M$(PWD) clean4.应用程序
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.hchar 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(请选择控制的模块1led12led23led34beep5fan);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无法正常打开字符设备导致线程退出。