海思hi3516ev200GPIO驱动开发
继续上个文章海思hi3516ev200引脚GPIO直接输出高低电平,本次使用linux驱动来对GPIO进行控制。
1、linux驱动开发,驱动代码如下(参考其他代码修改):
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#define DEV_NAME "led"
/* led */
#define GPIO6_BASE_ADDR 0x120b6000
#define GPIO6_DIR_ADDR ((GPIO6_BASE_ADDR) + (0x400))
#define GPIO6_DATA_OUT_ADDR ((GPIO6_BASE_ADDR) + (0x000))
#define GPIO6_7_SET_DATA__ADDR ((GPIO6_DATA_OUT_ADDR) + (0x200))
volatile unsigned long *gpio6_dir = NULL;
volatile unsigned long *gpio6_dataout = NULL;
volatile unsigned long *gpio6_setdata = NULL;
/* led end */
static int major = (-1); /* 初始化为无效值 */
static struct class *led_drv_class;
static int led_drv_open(struct inode *inode, struct file *file)
{
/* 将dir寄存器bit7置1:表示output功能 */
*gpio6_dir |= (1<<7);
return 0;
}
static int led_drv_release(struct inode *pinode , struct file *pfile)
{
/* 将data寄存器置1,表示输出高电平 */
//*gpio6_setdata = (1<<7);
return 0;
}
static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
char val;
int ret;
ret = copy_from_user(&val, buf, count);
if(!ret)
{
*gpio6_setdata = val;
}
return 0;
}
static struct file_operations led_drv_fops = {
.owner = THIS_MODULE,
.open = led_drv_open,
.release = led_drv_release,
.write = led_drv_write,
};
/* 驱动入口函数 */
static int led_drv_init(void)
{
major = register_chrdev(0, "led_drv", &led_drv_fops); // 注册, 告诉内核,返回值major为自动分配的主设备号
led_drv_class = class_create(THIS_MODULE, "leddrv");
device_create(led_drv_class, NULL, MKDEV(major, 0), NULL, DEV_NAME); /* insmod xxx后,会自动生成/dev/xyz设备,并且自动给该设备分配主设备号major */
/* 映射寄存器地址 */
gpio6_dir = (volatile unsigned long *)ioremap(GPIO6_DIR_ADDR, 32);
gpio6_dataout = (volatile unsigned long *)ioremap(GPIO6_DATA_OUT_ADDR, 32);
gpio6_setdata = (volatile unsigned long *)ioremap(GPIO6_7_SET_DATA__ADDR, 32);
return 0;
}
/* 驱动出口函数 */
static void led_drv_exit(void)
{
unregister_chrdev(major, "led_drv"); /* 卸载驱动程序,告诉内核 */
device_destroy(led_drv_class, MKDEV(major, 0));
class_destroy(led_drv_class);
/* 删除映射关系 */
iounmap(gpio6_setdata);
iounmap(gpio6_dataout);
iounmap(gpio6_dir);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("Dual BSD/GPL");
其中GPIO6_BASE_ADDR为0x120b6000,可以先去阅读前个文章直接对寄存器操作内容;对应配置好方向寄存器基地址和数据寄存器基地址,再通过ioremap把物理地址转换为虚拟地址,因为linux不能直接对物理地址进行操作,需要映射为linux可操作的虚拟地址才可以;此时映射好的虚拟地址gpio6_dir等就相当于物理地址一样,再配合write或者ioctl等函数进行操作即可。
Makefile:使用只需KERNEL_DIR修改为自己内核的路径才可以正常编译驱动。
KERNEL_DIR = /root/hi3516/linux-4.9.y
all:
make -C $(KERNEL_DIR) M=`pwd` modules
clean:
make -C $(KERNEL_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += led_drv.o
如上图,编译完 会在c文件目录生成.ko驱动文件,复制到海思开发板上面,使用insmod led_drv.ko命令即可加载驱动,使用lsmod查看系统已经加载的驱动,rmmod led_drv可以卸载驱动led_drv。
2、app开发
比较常规的文件读写操作就可以调用驱动,代码如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/*
* ledtest <dev> <on|off>
*/
void print_usage(char *file)
{
printf("Usage:\n");
printf("%s <dev> <on|off>\n",file);
printf("eg. \n");
printf("%s /dev/leds on\n", file);
printf("%s /dev/leds off\n", file);
printf("%s /dev/led1 on\n", file);
printf("%s /dev/led1 off\n", file);
}
int main(int argc, char **argv)
{
int fd;
char* filename=NULL;
char val;
if(argc !=3)
{
print_usage( argv[1]);//打印用法
}
filename = argv[1];
fd = open(filename, O_RDWR);//打开dev/firstdrv设备文件
if (fd < 0)//小于0说明没有成功
{
printf("error, can't open %s\n", filename);
return 0;
}
if(!strcmp(argv[2], "on"))
val = (1<<7); //此处驱动的GPIO6_7属于第7个引脚,所以需要把bit7置1
else
val = 0;
write(fd, &val, 1);//操作LED
printf("Set led GPIO to %d\n",val);
close(fd);
return 0;
}
具体编译器要根据自身海思芯片所自带的编译器而定,编译成功后生成led可执行文件,复制到开发板,对应增加可以执行权限即可chmod +x led, 执行./led /dev/led off即可输出低电平,执行./led /dev/led on输出高电平。
本文地址:https://blog.csdn.net/Sweets_JIE/article/details/107966029
上一篇: 物联网为什么需要5G?
下一篇: 处暑节吃的水果有哪些