F1C200S挖坑日记(8)——IO口驱动(非设备树)

本文介绍了如何为全志处理器编写GPIO驱动程序,通过驱动框架设置寄存器来控制PE2引脚的高低电平。首先定义了设备主设备号和名称,然后实现了设备文件操作结构体中的open、read、write和release方法。接着详细展示了驱动初始化和退出函数,包括寄存器映射和配置。最后,文章提到了应用程序测试,通过读写内核模块来控制GPIO状态,并提供了编译和功能测试的步骤。

本文以PE2作举例,通过测试APP控制IO口高低电平

一、驱动程序

我们先看全志的寄存器手册是这样的

寄存器地址和功能还是比较容易理解的,我们主要关注Pn_CFGPn_DATA以及Pn_PUL寄存器,分别是功能配置、输入输出数据以及上下拉控制。

首先写好嵌入式驱动框架

#define IO_CTRL_MAJOR 200        //字符设备主设备号
#define IO_CTRL_NAME  "IO_CTRL"    //字符设备名称

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

static ssize_t IOCTRL_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{
    return 0;
}

static ssize_t IOCTRL_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
    return 0;
}

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


static struct file_operations IO_fops = {
    .owner = THIS_MODULE,
    .open = IOCTRL_open,
    .read = IOCTRL_read,
    .write = IOCTRL_write,
    .release = IOCTRL_release,
};

int __init IOCTRL_init(void)
{
    return 0;
}

void __exit IOCTRL_exit(void)
{
    
}

module_init(IOCTRL_init);
module_exit(IOCTRL_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Distance");

 然后在驱动框架中添加需要的代码,例如在IOCTRL_init函数中添加寄存器的映射并配置寄存器等。

最终代码如下

#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 IO_CTRL_MAJOR 200 /* 主设备号 */
#define IO_CTRL_NAME "IO_CTRL" /* 设备名 */

#define HIGH 1
#define LOW  0
 //////////////////////////////////////////////////////////////////////////////////
#define PA      0
#define PB      1
#define PC      2
#define PD      3
#define PE      4

#define PIO_BASE        0X01C20800

#define PIO_CFG0(Pn)    PIO_BASE+(Pn)*0x24+0x00
#define PIO_CFG1(Pn)    PIO_BASE+(Pn)*0x24+0x04
#define PIO_CFG2(Pn)    PIO_BASE+(Pn)*0x24+0x08
#define PIO_CFG3(Pn)    PIO_BASE+(Pn)*0x24+0x0C

#define PIO_DATE(Pn)    PIO_BASE+(Pn)*0x24+0x10

#define PIO_DRV0(Pn)    PIO_BASE+(Pn)*0x24+0x14
#define PIO_DRV1(Pn)    PIO_BASE+(Pn)*0x24+0x18

#define PIO_PUL0(Pn)    PIO_BASE+(Pn)*0x24+0x1C
#define PIO_PUL1(Pn)    PIO_BASE+(Pn)*0x24+0x20   

#define PIO_INT_CFG0(Pn)    PIO_BASE+0X200+(Pn)*0x20+0x0
#define PIO_INT_CFG1(Pn)    PIO_BASE+0X200+(Pn)*0x20+0x4  
#define PIO_INT_CFG2(Pn)    PIO_BASE+0X200+(Pn)*0x20+0x8  
#define PIO_INT_CFG3(Pn)    PIO_BASE+0X200+(Pn)*0x20+0xC  

#define PIO_INT_CTRL(Pn)    PIO_BASE+0X200+(Pn)*0x20+0x10
#define PIO_INT_STA(Pn)    PIO_BASE+0X200+(Pn)*0x20+0x14
#define PIO_INT_DEB(Pn)    PIO_BASE+0X200+(Pn)*0x20+0x18 

#define SDR_PAD_DRV     PIO_BASE+0x2C0
#define SDR_PAD_PUL     PIO_BASE+0X2C4

//////////////////////////////////////////////////////////////////////////////////////////
#define CCU_BASE        0X01C20000

#define AHP_APB_HCLK_CFG    CCU_BASE+0X0054
//////////////////////////////////////////////////////////////////////////////////////////
static void __iomem *MEM_PIO_BASE;
static void __iomem *MEM_PE_CFG0;
static void __iomem *MEM_PE_DATE;
static void __iomem *MEM_PE_DRV0;
static void __iomem *MEM_PE_PUL0;


void PIO_Switch(unsigned char sta)
{
    unsigned int val;
    if(sta==HIGH)
    {
        val=readl(MEM_PE_DATE);
        val|= (1 << 2);
        writel(val, MEM_PE_DATE);
    }
    else if (sta==LOW)
    {
        val=readl(MEM_PE_DATE);
        val &= ~(1 << 2);
        writel(val, MEM_PE_DATE);
    }
    
}


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

static ssize_t IOCTRL_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{
    return 0;
}

static ssize_t IOCTRL_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
    int retvalue;
    unsigned char databuf[1];
    unsigned char ledstat;

    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0) 
    {
        printk(KERN_EMERG"kernel write failed!\r\n");
        return -EFAULT;
    }

    /* 获取状态值 */
    ledstat = databuf[0];
    printk(KERN_EMERG"kernel received date = %d \r\n",ledstat);
    if(ledstat == HIGH) 
    {
        PIO_Switch(HIGH);
    } 
    else if(ledstat == LOW) 
    {
        PIO_Switch(LOW);
    } 
    printk(KERN_EMERG"register date = %x:%d \r\n",PIO_DATE(PE),readl(MEM_PE_DATE));
    return 0; 
}

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

static struct file_operations IO_fops = {
    .owner = THIS_MODULE,
    .open = IOCTRL_open,
    .read = IOCTRL_read,
    .write = IOCTRL_write,
    .release = IOCTRL_release,
};

int __init IOCTRL_init(void)
{
    unsigned int val;
    unsigned int retvalue;
    //IO MEM remap;
    //MEM_PIO_BASE = ioremap(PIO_BASE(PE),4);
    MEM_PE_CFG0  = ioremap(PIO_CFG0(PE),1);
    MEM_PE_DATE  = ioremap(PIO_DATE(PE),1);
    MEM_PE_DRV0  = ioremap(PIO_DRV0(PE),1);
    MEM_PE_PUL0  = ioremap(PIO_PUL0(PE),1);
    

    //IO config
    val =   readl(MEM_PE_CFG0);     //CFG
//    printk(KERN_EMERG"MEM_PE_CFG0 = %x",val);
    val &= ~(0x07<<8);
    val |=  0x01<<8;
    writel(val,MEM_PE_CFG0);

    val =   readl(MEM_PE_PUL0);     //PUL
    val |=  0x00<<4;                //00 disable 01 pull-up 10 pull-down 11 reserved
    writel(val,MEM_PE_PUL0);

    val =   readl(MEM_PE_DRV0);     //multi-driving
    val |=  0x03<<4;
    writel(val,MEM_PE_DRV0);

    //register chardev
    retvalue = register_chrdev(IO_CTRL_MAJOR, IO_CTRL_NAME, &IO_fops);
    if(retvalue < 0)
    {
        printk(KERN_EMERG"register IO_DEV failed!\r\n");
        return -EIO;
    }
    printk(KERN_EMERG"register IO_DEV ready!\r\n");
    return 0;
}

void __exit IOCTRL_exit(void)
{
    /* 取消映射 */
    iounmap(MEM_PE_CFG0);
    iounmap(MEM_PE_DATE);
    iounmap(MEM_PE_DRV0);
    iounmap(MEM_PE_PUL0);

    /* 注销字符设备驱动 */
    printk(KERN_EMERG"IO_Dev unregistered!\r\n");
    unregister_chrdev(IO_CTRL_MAJOR, IO_CTRL_NAME);
}

module_init(IOCTRL_init);
module_exit(IOCTRL_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Distance");



Makefile文件参考正点原子,需要根据自己的文件名称进行更改

KERNELDIR :=/home/distance/Desktop/F1C200S/Linux
			

CURRENT_PATH := $(shell pwd)

obj-m := IOctrl.o


build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

编写完成后进行编译

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-

二、应用程序测试

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

#define HIGH 1
#define LOW 0

int main(int argc,char *argv[])
{
    int fd;
    int retvalue;
    char *filename;
    unsigned char databuf;

    printf("APP Run! \r\n");

    if(argc !=3)
    {
        printf("Error Usage!!\r\n");
        return -1;
    }

    filename=argv[1];
    
    fd=open(filename,O_RDWR);
    if (fd<0)
    {
        printf("file %s open failed!\r\n",argv[1]);
        return -1;
    }
    
    databuf=atoi(argv[2]);

    retvalue=write(fd,&databuf,sizeof(databuf));
    printf("write date = %d \r\n",databuf);
    if(retvalue<0)
    {
        printf("IO control failed!\r\n");
        close(fd);
        return -1;
    }

    retvalue=close(fd);
    if(retvalue<0)
    {
        printf("file %s close failed!\r\n",argv[1]);
        return -1;
    }
    return 0;
}

 完成后进行编译,这里使用了buildroot编译时产生的编译链。

arm-buildroot-linux-gcc IOctrlAPP.c -o IOctrlAPP

三、功能测试

在内核启动后cd到模块的存储路径,使用insmod加载模块

insmod IOctrl.ko

然后创建节点

mknod /dev/iotest c 200 0

之后就可以通过APP进行IO口操作了

//PE2输出低电平
./IOctrlAPP /dev/iotest 0    

//PE2输出高电平
./IOctrlAPP /dev/iotest 1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Distance_90

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值