本文以PE2作举例,通过测试APP控制IO口高低电平
一、驱动程序
我们先看全志的寄存器手册是这样的

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

被折叠的 条评论
为什么被折叠?



