STM32MP157A单片机驱动--控制拓展版的灯实现流水效果
1.注册字符设备驱动 int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) 功能:进行字符设备驱动的注册,申请了256个次设备号(0-255) 参数: major: >0:手动指定的当前驱动的主设备号 ==0:由系统内核动态分配一个主设备号 name:设备名或者驱动名 fops:操作方法对象指针 /* 应用程序调用文件操作接口对设备文件进行操作时,驱动中要有操作方法被回调,想要这些操作方法被回调,需要将 操作方法的指针放在一个操作方法对象中进行统一管理 struct file_operations { int (*open) (struct inode *, struct file *); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); int (*release) (struct inode *, struct file *); }; */ 返回值: 调用失败返回错误码 成功: major==0,成功返回动态申请的主设备号 major>0,成功返回0
2.注销字符设备驱动 void unregister_chrdev(unsigned int major, const char *name) 功能:进行字符设备驱动的注销 参数: major:注册时得到的主设备号 name:注册时填写的设备名 返回值:无
led.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define LED1_PHY_GPIO_MODER 0x50006000
#define LED1_PHY_GPIO_ODR 0x50006014
#define LED1_PHY_RCC_GPIO 0x50000A28
#define LED2_PHY_GPIO_MODER 0x50007000
#define LED2_PHY_GPIO_ODR 0x50007014
#define LED2_PHY_RCC_GPIO 0x50000A2C
#define LED3_PHY_GPIO_MODER 0x50006000
#define LED3_PHY_GPIO_ODR 0x50006014
#define LED3_PHY_RCC_GPIO 0x50000A28
unsigned int major; // 保存主设备号
char kbuf[128] = {0};
unsigned int *led1_vir_moder;
unsigned int *led1_vir_odr;
unsigned int *led1_vir_rcc;
unsigned int *led2_vir_moder;
unsigned int *led2_vir_odr;
unsigned int *led2_vir_rcc;
unsigned int *led3_vir_moder;
unsigned int *led3_vir_odr;
unsigned int *led3_vir_rcc;
// 封装各种操作方法
int myled_open(struct inode *inode, struct file *file) {
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t myled_read(struct file *file, char __user *ubuf, size_t size, loff_t *lft) {
unsigned long ret = copy_to_user(ubuf, kbuf, size);
if (ret) {
printk("copy_to_user failed\n");
return -ret;
}
return 0;
}
ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lft) {
unsigned long ret = copy_from_user(kbuf, ubuf, size);
if (ret) {
printk("copy_from_user failed\n");
return -ret;
}
if (strcmp(kbuf, "1") == 0) { // 点亮 LED1
*led1_vir_odr |= (0x1 << 10);
printk("点亮 LED1\n");
} else if (strcmp(kbuf, "2") == 0) { // 点亮 LED2
*led2_vir_odr |= (0x1 << 10);
printk("点亮 LED2\n");
} else if (strcmp(kbuf, "3") == 0) { // 点亮 LED3
*led3_vir_odr |= (0x1 << 8);
printk("点亮 LED3\n");
} else if (strcmp(kbuf, "0") == 0) { // 全部熄灭
*led1_vir_odr &= ~(0x1 << 10);
*led2_vir_odr &= ~(0x1 << 10);
*led3_vir_odr &= ~(0x1 << 8);
printk("全部熄灭\n\n");
}
return 0;
}
int myled_close(struct inode *inode, struct file *file) {
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
// 定义一个操作方法结构体变量
struct file_operations fops = {
.open = myled_open,
.read = myled_read,
.write = myled_write,
.release = myled_close,
};
static int __init mycdev_init(void) {
// 进行字符设备驱动的注册
major = register_chrdev(0, "myled", &fops);
if (major < 0) {
printk("字符设备驱动注册失败\n");
return major;
}
printk("字符设备驱动注册成功 major=%d\n", major);
// 进行 LED1 寄存器的地址映射
led1_vir_moder = ioremap(LED1_PHY_GPIO_MODER, 4);
if (led1_vir_moder == NULL) {
printk("LED1 物理内存映射失败%d\n", __LINE__);
return -ENOMEM;
}
led1_vir_odr = ioremap(LED1_PHY_GPIO_ODR, 4);
if (led1_vir_odr == NULL) {
printk("LED1 物理内存映射失败%d\n", __LINE__);
iounmap(led1_vir_moder);
return -ENOMEM;
}
led1_vir_rcc = ioremap(LED1_PHY_RCC_GPIO, 4);
if (led1_vir_rcc == NULL) {
printk("LED1 物理内存映射失败%d\n", __LINE__);
iounmap(led1_vir_moder);
iounmap(led1_vir_odr);
return -ENOMEM;
}
// 进行 LED2 寄存器的地址映射
led2_vir_moder = ioremap(LED2_PHY_GPIO_MODER, 4);
if (led2_vir_moder == NULL) {
printk("LED2 物理内存映射失败%d\n", __LINE__);
iounmap(led1_vir_moder);
iounmap(led1_vir_odr);
iounmap(led1_vir_rcc);
return -ENOMEM;
}
led2_vir_odr = ioremap(LED2_PHY_GPIO_ODR, 4);
if (led2_vir_odr == NULL) {
printk("LED2 物理内存映射失败%d\n", __LINE__);
iounmap(led1_vir_moder);
iounmap(led1_vir_odr);
iounmap(led1_vir_rcc);
iounmap(led2_vir_moder);
return -ENOMEM;
}
led2_vir_rcc = ioremap(LED2_PHY_RCC_GPIO, 4);
if (led2_vir_rcc == NULL) {
printk("LED2 物理内存映射失败%d\n", __LINE__);
iounmap(led1_vir_moder);
iounmap(led1_vir_odr);
iounmap(led1_vir_rcc);
iounmap(led2_vir_moder);
iounmap(led2_vir_odr);
return -ENOMEM;
}
// 进行 LED3 寄存器的地址映射
led3_vir_moder = ioremap(LED3_PHY_GPIO_MODER, 4);
if (led3_vir_moder == NULL) {
printk("LED3 物理内存映射失败%d\n", __LINE__);
iounmap(led1_vir_moder);
iounmap(led1_vir_odr);
iounmap(led1_vir_rcc);
iounmap(led2_vir_moder);
iounmap(led2_vir_odr);
iounmap(led2_vir_rcc);
return -ENOMEM;
}
led3_vir_odr = ioremap(LED3_PHY_GPIO_ODR, 4);
if (led3_vir_odr == NULL) {
printk("LED3 物理内存映射失败%d\n", __LINE__);
iounmap(led1_vir_moder);
iounmap(led1_vir_odr);
iounmap(led1_vir_rcc);
iounmap(led2_vir_moder);
iounmap(led2_vir_odr);
iounmap(led2_vir_rcc);
iounmap(led3_vir_moder);
return -ENOMEM;
}
led3_vir_rcc = ioremap(LED3_PHY_RCC_GPIO,4);
if (led3_vir_rcc == NULL) {
printk("LED3 物理内存映射失败%d\n", __LINE__);
iounmap(led1_vir_moder);
iounmap(led1_vir_odr);
iounmap(led1_vir_rcc);
iounmap(led2_vir_moder);
iounmap(led2_vir_odr);
iounmap(led2_vir_rcc);
iounmap(led3_vir_moder);
iounmap(led3_vir_odr);
return -ENOMEM;
}
printk("物理内存映射成功\n");
//PE10设置为输出
(*led1_vir_moder) &= (~(0X3<<20));
(*led1_vir_moder) |= (0x1<<20);
//让PE10默认输出低电平
(*led1_vir_odr) &= (~(0X1<<10));
//PF10设置为输出
(*led2_vir_moder) &= (~(0X3<<20));
(*led2_vir_moder) |= (0x1<<20);
//让PF10默认输出低电平
(*led2_vir_odr) &= (~(0X1<<10));
//PE8设置为输出
(*led3_vir_moder) &= (~(0X3<<16));
(*led3_vir_moder) |= (0x1<<16);
//让PE8默认输出低电平
(*led3_vir_odr) &= (~(0X1<<8));
return 0;
}
static void __exit mycdev_exit(void)
{
//取消物理内存的映射
iounmap(led1_vir_moder);
iounmap(led1_vir_odr);
iounmap(led1_vir_rcc);
iounmap(led2_vir_moder);
iounmap(led2_vir_odr);
iounmap(led2_vir_rcc);
iounmap(led3_vir_moder);
iounmap(led3_vir_odr);
iounmap(led3_vir_rcc);
//进行字符设备驱动的注销
unregister_chrdev(major,"myled");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
app.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int fd = open("/dev/myLed", O_RDWR);
if (fd < 0) {
printf("设备文件打开失败\n");
return -1;
}
char cmd[2] = {0}; // 优化缓冲区大小
while(1) {
// LED1亮
cmd[0] = '1';
if(write(fd, cmd, sizeof(cmd)) < 0) {
perror("write error");
break;
}
sleep(1); // 延时1秒
// LED2亮(保持LED1亮)
cmd[0] = '2';
write(fd, cmd, sizeof(cmd));
sleep(1);
// LED3亮(保持前两盏亮)
cmd[0] = '3';
write(fd, cmd, sizeof(cmd));
sleep(1);
// 全部熄灭
cmd[0] = '0';
write(fd, cmd, sizeof(cmd));
sleep(1); // 全灭保持1秒
}
close(fd);
return 0;
}