【驱动开发】实现三盏灯的控制,编写应用程序测试

head.h

cs 复制代码
#ifndef __HEAD_H__
#define __HEAD_H__

//LED1:PE10
//LED2:PF10
//LED3:PE8

#define LED_RCC 0X50000A28         //使能GPIO

#define LED_MODER 0X50006000      //设置输出模式
#define LED_ODR 0X50006014        //设置输出高低电平

#define LED2_MODER 0X50007000       //设置输出模式
#define LED2_ODR 0X50007014         //设置输出高低电平

#endif

mychrdev.c

cs 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include "head.h"

unsigned int major; //保存主设备号
char kbuf[128] = {0};

unsigned int *vir_rcc;
unsigned int *vir_moder;
unsigned int *vir_odr;
unsigned int *vir_moder_led2;
unsigned int *vir_odr_led2;


//封装操作方法
int mycdev_open(struct inode *inode,struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); 
    return 0;
}
 
ssize_t mycdev_read(struct file *file,char *ubuf,size_t size,loff_t *lof)
{
   // printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    
    int ret;
    ret = copy_to_user(ubuf,kbuf,size);
    if(ret)
    {
        printk("copy_to_user err\n");
        return -EIO;
    }
    return 0;
}

ssize_t mycdev_write(struct file *file,const char *ubuf,size_t size,loff_t *lof)
{
    //printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); 

    int ret;
    ret = copy_from_user(kbuf,ubuf,size);
    if(ret)
    {
        printk("copy_from_user err\n");
        return -EIO;
    }

//控制LED1-LED3开和关
    if(kbuf[0] == '1')      //开灯
    {
        (*vir_odr) |= (0x1<<10);    //输出高电平
        (*vir_odr_led2) |= (0x1<<10);    //输出高电平
        (*vir_odr) |= (0x1<<8);    //输出高电平
    }
    else if(kbuf[0] == '0') //关灯
    {
        (*vir_odr) &= (~(0x1<<10));  //输出低电平
        (*vir_odr_led2) &= (~(0x1<<10));    //输出高电平
        (*vir_odr) &= (~(0x1<<8));    //输出高电平
    }
    return 0;
}

int mycdev_close(struct inode *inode,struct file *file)
{
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); 
    return 0;
}

struct file_operations fops={       //定义一个操作方法结构体对象并初始化
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};   



static int __init mycdev_init(void) //入口函数  安装内核模块时执行
{
    major=register_chrdev(0,"mychrdev",&fops);     //字符设备驱动的注册
    if(major<0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功 major=%d\n",major);


//进行LED控制相关寄存器的内存映射
    vir_rcc = ioremap(LED_RCC,4);
    if(vir_rcc == NULL)
    {
        printk("物理内存映射失败%d\n",__LINE__);
        return -EFAULT;
    }

    vir_moder = ioremap(LED_MODER,4);
    if(vir_moder == NULL)
    {
        printk("物理内存映射失败%d\n",__LINE__);
        return -EFAULT;
    }

    vir_odr = ioremap(LED_ODR,4);
    if(vir_odr == NULL)
    {
        printk("物理内存映射失败%d\n",__LINE__);
        return -EFAULT;
    }

    vir_moder_led2 = ioremap(LED2_MODER,4);
    if(vir_moder_led2 == NULL)
    {
        printk("物理内存映射失败%d\n",__LINE__);
        return -EFAULT;
    }

    vir_odr_led2 = ioremap(LED2_ODR,4);
    if(vir_odr_led2 == NULL)
    {
        printk("物理内存映射失败%d\n",__LINE__);
        return -EFAULT;
    }


    printk("寄存器内存映射成功\n");

//控制led1-led3硬件寄存器的初始化
    (*vir_rcc) |= (0x1<<4);         //RCC使能GPIO E组
    (*vir_rcc) |= (0x1<<5);         //RCC使能GPIO F组

    (*vir_moder) &= (~(0x3<<20));   //设置PE10为输出
    (*vir_moder) |= (0x1<<20);

    (*vir_moder_led2) &= (~(0x3<<20));   //设置PF10为输出
    (*vir_moder_led2) |= (0x1<<20);  

    (*vir_moder) &= (~(0x3<<16));       //设置PE8为输出
    (*vir_moder) |= (0x1<<16);    

    (*vir_odr) &= (~(0x1<<10));         //设置led1默认关灯
    (*vir_odr_led2) &= (~(0x1<<10));    //设置led2默认关灯
    (*vir_odr) &= (~(0x1<<8));          //设置led3默认关灯

    return 0;
}

static void __exit mycdev_exit(void)    //出口函数,卸载内核模块时执行
{
    iounmap(vir_moder);         //取消物理内存映射
    iounmap(vir_moder_led2);    //取消物理内存映射
    iounmap(vir_odr);           //取消物理内存映射
    iounmap(vir_odr_led2);      //取消物理内存映射
    iounmap(vir_rcc);           //取消物理内存映射
    
    unregister_chrdev(major,"mychrdev");  //注销字符设备驱动
}

module_init(mycdev_init);   //用于声明当前内核模块入口函数的地址
module_exit(mycdev_exit);   //用于声明当前内核模块出口函数的地址
MODULE_LICENSE("GPL");  //声明当前内核模块遵循GPL协议

led_test.c

cs 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char buf[128] = {0};
    int fd=open("/dev/mychrdev",O_RDWR);
    if(fd < 0)
    {
        printf("设备文件打开失败\n");
        exit(-1);
    }

    while(1)
    {   //控制LED亮和灭
        printf("请输入LED的控制命令:1(开灯),0(关灯) >>");
        fgets(buf,sizeof(buf),stdin);       //从终端输入数据传递到buf中
        buf[strlen(buf)-1] = '\0';          //末尾替换\n
        write(fd,buf,sizeof(buf));   
    }
    
    return 0;
}

测试结果如下:

相关推荐
TangDuoduo000541 分钟前
【Linux字符设备驱动】
linux·驱动开发
Max_uuc1 小时前
【C++ 硬核】摆脱开发板:用 Google Test + Mock 构建嵌入式 TDD (测试驱动开发) 体系
驱动开发·tdd
小龙报2 小时前
【51单片机】串口通讯从入门到精通:原理拆解 + 参数详解 + 51 单片机实战指南
c语言·驱动开发·stm32·单片机·嵌入式硬件·物联网·51单片机
dlz083619 小时前
POE驱动开发流程
驱动开发
嵌入式-老费21 小时前
Linux camera驱动开发(DVP接口的camera sensor)
驱动开发
VernonJsn1 天前
visual studio 2022的windows驱动开发
ide·驱动开发·visual studio
嵌入式郑工2 天前
RK3566 LubanCat 开发板 USB Gadget 配置完整复盘
linux·驱动开发·ubuntu
雾削木3 天前
树莓派 ESPHome 固件编译与烧录全攻略(解决超时与串口识别问题)
驱动开发
春日见4 天前
win11 分屏设置
java·开发语言·驱动开发·docker·单例模式·计算机外设
DarkAthena4 天前
【GaussDB】手动编译不同python版本的psycopg2驱动以适配airflow
驱动开发·python·gaussdb