嵌入式Linux LED驱动开发

warren
2024-07-23 / 0 评论 / 4 阅读 / 正在检测是否收录...

地址映射(MMU)

MMU全称Memory Manage Unit,也就是内存管理单元
主要功能:完成虚拟空间到物理空间的映射
虚拟地址范围为0~2^32=4GB
需要用到两个函数实现物理内存与虚拟内存之间的转换

  ioremap//ioremap用于获取指定物理地址空间对应的虚拟地址空间
  iounmap//iounmao用于取消映射的虚拟地址空间

函数原型如下

 #define ioremap(cookie,size) __arm_ioremap((cookie), (size),MT_DEVICE)
void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size,unsigned int mtype)
  ioremap是个宏,正真起作用的是__arm_ioremap。__arm_ioremap有3个参数:phys_addr size mtype。
  phys_addr:要映射给的物理起始地址
  size:要映射的内存空间大小
  mtype:ioremap 的类型(ioremap 函数选择 MT_DEVICE)

  void iounmap (volatile void __iomem *addr)
  iounmap 只有一个参数 addr,此参数就是要取消映射的虚拟地址空间首地址

使用方法:
注意__arm_ioremap函数的返回值,返回值是一个void __iomem类型的指针。指向映射后的虚拟空间首地址
1.定义一个指针,并使用static修饰

static void __iomem ADDRESS

2.调用ioremap实现地址映射

ADDRESS = ioremap(0x00000000,4);

3.调用iounmap取消地址映射
iounmap(ADDRESS);

I/O内存访问函数

这里说的 I/O 不是我们学习单片机的时候讲的 GPIO 引脚。
这里涉及到两个概念(I/O 端口和 I/O 内存)
I/O 端口: 当外部寄存器或内存映射到 IO空间 时,称为 I/O 端口
I/O 内存: 当外部寄存器或内存映射到 内存空间 时,称为 I/O 内存。
对于ARM来说没有IO空间这个概念。只有IO内存,使用 ioremap 函数将寄存器的物理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址。
但是 但是 但是!!!
Linux 内核不建议这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作。操作函数如下

u8  readb(const volatile void __iomem *addr)
u16 readw(const volatile void __iomem *addr)
u32 readl(const volatile void __iomem *addr)

void writeb(u8  value, volatile void __iomem *addr)
void writew(u16 value, volatile void __iomem *addr)
void writel(u32 value, volatile void __iomem *addr)

驱动代码实现

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>


#define LED_MAJOR   200
#define LED_NAME    "led"

#define LED_OFF     0
#define LED_ON      1

#define CCM_CCGR1_BASE          (0x020C406C)
#define SW_MUX_GPIO1_IO03_BASE  (0x020E0068)
#define SW_PAD_GPIO1_IO03_BASE  (0x020E02F4)
#define GPIO1_DR_BASE           (0x0209C000)
#define GPIO1_GDIR_BASE         (0x0209C004)

static void __iomem *CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;


void led_switch(u8 sta)
{
    u32 val = 0;

    if(sta == LED_ON){
        val = readl(GPIO1_DR);
        val &= ~(1 << 3);
        writel(val, GPIO1_DR);
    }else if (sta == LED_OFF) {
        val = readl(GPIO1_DR);
        val |= (1 << 3);
        writel(val, GPIO1_DR);
    }
}

void led_device_init(void)
{
    u32 val = 0;

    /*寄存器地址映射*/
    CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

    /*使能GPIO1时钟*/
    val = readl(CCM_CCGR1);
    val &= ~(3<<26);
    val |= (1<<26);
    writel(val, CCM_CCGR1);

    /*设置GPIO1_IO3的复用功能*/
    writel(5, SW_MUX_GPIO1_IO03);

    /*设置IO属性*/
    writel(0x10B0, SW_PAD_GPIO1_IO03);
    /*设置GPIO1_IO3为输出功能*/
    val = readl(GPIO1_GDIR);
    val &= ~(1 << 3);
    val |= (1 << 3);
    writel(val, GPIO1_GDIR);
    /*LED默认关闭*/
    led_switch(LED_OFF);
}

void led_device_deinit(void)
{
    iounmap(CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);
}


static int led_open(struct inode *inode, struct file *filp)
{
    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    u32 val = 0;
    u32 led_status = 0;
    int retvalue = 0;

    val = readl(GPIO1_DR);
    led_status = !(val&(1<<3));
    retvalue = copy_to_user(buf, &led_status, sizeof(u32));
    return retvalue;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    u32 led_value = 0;
    int retvalue = 0;

    retvalue = copy_from_user(&led_value, buf, sizeof(u32));
    led_switch(led_value&0x01);
    return retvalue;
}

struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .release = led_release,
    .read = led_read,
    .write = led_write,
};



static int __init led_init(void)
{
    int retvalue = 0;

    led_device_init();
    retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
    printk("led init success\r\n");
    return retvalue;
}

static void __exit led_exit(void)
{
    led_device_deinit();
    unregister_chrdev(LED_MAJOR, LED_NAME);
    printk("led deinit success\r\n");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("warren");

测试代码实现

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"



int main(int argc, int argv[])
{   
    int retvalue = 0;
    int Dir = 0;
    char *filename;
    int fd = 0;
    unsigned int  buff;


    if(argc < 4){
        printf("Error Usage!\r\n");
        return -1;
    }
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("Open %s error!\r\n",filename);
        return -1;
    }
    Dir = atoi(argv[2]);
    if(Dir == 1){//写入
        buff = atoi(argv[3]);
        retvalue = write(fd, &buff, sizeof(unsigned int));
    }else if(Dir == 2){//读取
        retvalue = read(fd, &buff, sizeof(unsigned int));
        printf("Led status is %d\r\n", buff);
    }
    return 0;
}

脚本文件

编译脚本

#!/bin/bash
make clean
make
rm -rf ledApp
arm-linux-gnueabihf-gcc ledApp.c -o ledApp
rm -rf ../../tftpboot/*
cp *App *.ko ../../tftpboot
chmod 777 ../../tftpboot/*

tftp获取脚本

#!/bin/bash
rm -rf led*
tftp -g -r led.ko 192.168.0.100
tftp -g -r ledApp 192.168.0.100
chmod 777 ledApp

测试及实验

1.编译驱动模块及测试程序
lyyiik9b.png
2.在开发板上获取驱动模块及测试程序
lyyjhs4b.png
3.加载驱动模块

  insmod led.ko

lyyjkitc.png
4.创建设备节点

  insmod led.ko

lyyjls50.png
4.开打LED灯并获取状态

  ./ledApp /dev/led 1 1
  ./ledApp /dev/led 2 x

lyyjn893.png
5.关闭LED灯并获取状态

  ./ledApp /dev/led 1 0
  ./ledApp /dev/led 2 x

lyyjnqri.png
OVER 表情

1

评论 (0)

取消