荔枝派zero驱动开发04:GPIO操作(寄存器方式)

这篇具有很好参考价值的文章主要介绍了荔枝派zero驱动开发04:GPIO操作(寄存器方式)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

参考:https://wiki.sipeed.com/soft/Lichee/zh/Zero-Doc/Drive/GPIO_mmap.html

上一篇:荔枝派zero驱动开发03:设备树基础
下一篇:荔枝派zero驱动开发05:GPIO操作(使用GPIO子系统)

  • 关键词:ioremap/iounmap,copy_from_user/copy_to_user,readl/writel

设备树修改:

本文不涉及设备树操作,但由于默认设备树配置了LED,因此先在设备树中禁用默认的LED配置,重新编译设备树后,使用新的设备树启动

荔枝派zero驱动开发04:GPIO操作(寄存器方式),嵌入式Linux开发,驱动开发,linux,arm开发,物联网,嵌入式硬件

关键代码:

#define V3S_GPIO_BASE 0x01C20800
// 模式寄存器,4bit,最高位保留,000输入,001为输出
#define V3S_GPIOG_MODE (V3S_GPIO_BASE + 0xD8)
// 每个脚的输入输出数据,1bit
#define V3S_GPIOG_DATA (V3S_GPIO_BASE + 0xE8)

static void __iomem *GPIOG_MODE;
static void __iomem *GPIOG_DATA;

GPIOG_MODE = ioremap(V3S_GPIOG_MODE, 4);
GPIOG_DATA = ioremap(V3S_GPIOG_DATA, 4);

uint32_t val=0;
val=readl(GPIOG_MODE);
val &= ~(7 << (PIN_N * 4)); // 清除配置
val |= (1 << (PIN_N * 4));  // 配置为输出
writel(val, GPIOG_MODE);

val=readl(GPIOG_DATA);
val &= ~(1 << PIN_N);
writel(val, GPIOG_DATA); // 引脚初始化为低

根据v3s寄存器表,GPIO的物理地址基址为0x01C20800,GPIOG的模式寄存器地址为(0x01C20800+0xD8)
在裸机开发中,可以直接读写*(0x01C20800+0xD8)来操作GPIO寄存器;但在linux中,内核不能直接访问物理地址,必须映射为虚拟地址后才可以访问,映射和取消映射的函数为ioremap()和iounmap()。

writel() 向内存映射的物理地址上写数据,wirtel() 写入 32 位数据 (4字节);readl同理,读取32位数据 (4字节);类似的操作函数有writeb/writew/writel,分别对应8位、16位、32位操作

位操作比较清晰,清除指定的位,或在指定位写入数据;v3s的模式寄存器每个脚占用4bit,0b000输入,0b001输出,要配置为输出直接向指定的4bit写入0b001即可

static ssize_t led_gpio_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
    unsigned char val;
    copy_from_user(&val, user_buf, 1);
    printk("led_gpio_write: 0x%02x\n", val);
    if (val=='1' || val == 1) //默认输入为0x30即字符'0'
    {
        val=readl(GPIOG_DATA);
        val &= ~(1 << PIN_N); //低电平点亮
        writel(val, GPIOG_DATA);
    }
    else if (val=='0' || val == 0)
    {
        val=readl(GPIOG_DATA);
        val|= (1 << PIN_N); //高电平熄灭
        writel(val, GPIOG_DATA);
    }
    return 1;
}

用户空间内存不能直接访问内核空间的内存,需要借助函数 copy_from_user 将用户空间的数据复制到内核空间;LED控制较为简单,只需一个字符即可控制LED,这里复制1个字符即可,应用层也只需一个字符控制LED

测试:

其他参考之前的字符设备模板写即可,修改Makefile并编译,将生成的ledchar.ko拷贝到开发板,注意前述的设备树修改

使用echo测试

荔枝派zero驱动开发04:GPIO操作(寄存器方式),嵌入式Linux开发,驱动开发,linux,arm开发,物联网,嵌入式硬件

使用应用层APP测试

这里也写了一个简单的测试app,源码附在文后,调用本条命令进行编译,编译出目标文件拷贝到开发板

/opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -o ledcharApp ledcharApp.c

荔枝派zero驱动开发04:GPIO操作(寄存器方式),嵌入式Linux开发,驱动开发,linux,arm开发,物联网,嵌入式硬件

APP运行测试:

荔枝派zero驱动开发04:GPIO操作(寄存器方式),嵌入式Linux开发,驱动开发,linux,arm开发,物联网,嵌入式硬件

一点心得:

本篇是最接近裸机的开发方式了,简单直接、深入底层,不涉及Linux的程序框架与造好的轮子;实际开发中几乎不会使用这种开发方式,但有助于理解驱动框架和硬件的关系,可以有一个直观感受。

源码

ledcharApp.c

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

int main(int argc, char *argv[])
{
	int fd, ret;
	char *filename;
	char val = 0;

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

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

	val = atoi(argv[2]);
	ret = write(fd, &val, sizeof(val));
	if (ret < 0)
		printf("cannot write file:%s\r\n", filename);
	else
		printf("app:set led:%d\r\n", val);

	ret = close(fd);
	if (ret < 0)
	{
		printf("cannot close file:%s\r\n", filename);
		return -1;
	}
	return 0;
}

ledchar.c文章来源地址https://www.toymoban.com/news/detail-808037.html

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/printk.h>
#include <linux/uaccess.h>

struct ledchar_dev
{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
};
struct ledchar_dev ledchar = {
    .major = 0,
};

#define PIN_N 0 // 第0个引脚,PG0,绿色
#define DEV_NAME "led"
#define LED_ON  0 // 上拉,低电平亮
#define LED_OFF 1

#define V3S_GPIO_BASE 0x01C20800
// 模式寄存器,4bit,最高位保留,000输入,001为输出
#define V3S_GPIOG_MODE (V3S_GPIO_BASE + 0xD8)
// 每个脚的输入输出数据,1bit
#define V3S_GPIOG_DATA (V3S_GPIO_BASE + 0xE8)
// 驱动能力,2bit,0-3逐级递增
#define V3S_GPIOG_DRIVING (V3S_GPIO_BASE + 0xEC)
// 上拉下拉,2bit,0浮空,1上拉,2下拉
#define V3S_GPIOG_PULL (V3S_GPIO_BASE + 0xF4)

// 中断配置,4bit,0上升,1下降,2高电平,3低电平,4双边沿
#define V3S_GPIOG_INTCFG (V3S_GPIO_BASE + 0x240)
// 中断使能,1bit,1使能
#define V3S_GPIOG_INT_CTRL (V3S_GPIO_BASE + 0x250)
// 中断状态,1bit,1发生中断,写1清除
#define V3S_GPIOG_INT_STA (V3S_GPIO_BASE + 0x254)
// 中断时钟及分频配置
#define V3S_GPIOG_INT_DEB (V3S_GPIO_BASE + 0x258)

static void __iomem *GPIOG_MODE;
static void __iomem *GPIOG_DATA;
static void __iomem *GPIOG_DRIVING;
static void __iomem *GPIOG_PULL;


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

static int led_gpio_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    return 0;
}

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

static ssize_t led_gpio_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
    unsigned char val;
    copy_from_user(&val, user_buf, 1);
    printk("led_gpio_write: 0x%02x\n", val);
    if (val=='1' || val == 1) 
    {
        val=readl(GPIOG_DATA);
        val &= ~(1 << PIN_N); //低电平点亮
        writel(val, GPIOG_DATA);
    }
    else if (val=='0' || val == 0)
    {
        val=readl(GPIOG_DATA);
        val|= (1 << PIN_N); //高电平熄灭
        writel(val, GPIOG_DATA);
    }
    return 1;
}

static const struct file_operations ledchar_fops = {
    .open = led_gpio_open,
    .read = led_gpio_read,
    .release = led_gpio_release,
    .write = led_gpio_write,
};

static int __init led_driver_init(void)
{
    int ret;
    

    GPIOG_MODE = ioremap(V3S_GPIOG_MODE, 4);
    GPIOG_DATA = ioremap(V3S_GPIOG_DATA, 4);
    GPIOG_DRIVING = ioremap(V3S_GPIOG_DRIVING, 4);
    GPIOG_PULL = ioremap(V3S_GPIOG_PULL, 4);

    uint32_t val=0;
    val=readl(GPIOG_MODE);
    val &= ~(7 << (PIN_N * 4)); // 清除配置
    val |= (1 << (PIN_N * 4));  // 配置为输出
    writel(val, GPIOG_MODE);

    val=readl(GPIOG_DATA);
    val &= ~(1 << PIN_N);
    writel(val, GPIOG_DATA); // 引脚初始化为低

    if (ledchar.major) // 定义了设备号,静态设备号
    {
        ledchar.devid = MKDEV(ledchar.major, 0);
        ret = register_chrdev_region(ledchar.major, 1, DEV_NAME);
        if (ret < 0)
        {
            pr_err("cannot register %s char driver.ret:%d\r\n", DEV_NAME, ret);
            goto exit;
        }
    }
    else // 没有定义设备号,动态申请设备号
    {
        ret = alloc_chrdev_region(&ledchar.devid, 0, 1, DEV_NAME);
        if (ret < 0)
        {
            pr_err("cannot alloc_chrdev_region,ret:%d\r\n", ret);
            goto exit;
        }
        ledchar.major = MAJOR(ledchar.devid);
        ledchar.minor = MINOR(ledchar.devid);
    }
    printk("led major=%d,minor=%d\r\n", ledchar.major, ledchar.minor);

    ledchar.cdev.owner = THIS_MODULE;
    cdev_init(&ledchar.cdev, &ledchar_fops);

    ret = cdev_add(&ledchar.cdev, ledchar.devid, 1);
    if (ret < 0)
        goto del_unregister;

    ledchar.class = class_create(THIS_MODULE, DEV_NAME);
    if (IS_ERR(ledchar.class))
        goto del_cdev;

    ledchar.device = device_create(ledchar.class, NULL, ledchar.devid, NULL, DEV_NAME);
    if (IS_ERR(ledchar.device))
        goto destroy_class;

    return 0;

    // 注意  goto后的标签没有return操作,将顺序执行多个label直至return,这里反向写
destroy_class:
    class_destroy(ledchar.class);
del_cdev:
    cdev_del(&ledchar.cdev);
del_unregister:
    unregister_chrdev_region(ledchar.devid, 1);
exit:
    printk("chardev_init failed\r\n");
    return -EIO;
}

static void __exit led_driver_exit(void)
{
    iounmap(GPIOG_MODE);
    iounmap(GPIOG_DATA);
    iounmap(GPIOG_DRIVING);
    iounmap(GPIOG_PULL);

    cdev_del(&ledchar.cdev);
    unregister_chrdev_region(ledchar.devid, 1);
    device_destroy(ledchar.class, ledchar.devid);
    class_destroy(ledchar.class);
}

module_init(led_driver_init);
module_exit(led_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("USER");
MODULE_INFO(intree, "Y");

到了这里,关于荔枝派zero驱动开发04:GPIO操作(寄存器方式)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 荔枝派Zero(全志V3S)驱动开发之串口

    修改设备树打开 uart1 和 uart2,在 buildroot 移植 minicom 用来测试 uart1 和 uart2。 ①、查看 V3S 原理图,查看 uart1 和 uart2 对应的引脚 PE21 - UART1_TX PE22 - UART_RX PB0 - UART2_TX PB1 - UART2_RX ②、修改 sun8i-v3s.dtsi 在 linux 目录下 在 sun8i-v3s.dtsi 中添加 ③、修改 sun8i-v3s-licheepi-zero-dock.dts 在 lin

    2024年02月11日
    浏览(43)
  • 【ARM裸机编程 | 海思SS528】- 操作 GPIO 寄存器输出低电平点亮 LED 灯

    这篇文章主要介绍在 海思SS528 开发板,去操作某个 GPIO 寄存器输出高、低电平,来熄灭或点亮 LED 灯。 首先,了解一下 ARM裸机编程 ,也就是在一块没有移植操作系统的ARM开发板去编程,相当于把它当成单片机去使用,很多与硬件相关的操作都需要直接读写该硬件的寄存器。

    2024年02月10日
    浏览(43)
  • 荔枝派Zero(全志V3S)驱动开发之USB摄像头

    上一篇博客实现了在ubuntu打开usb摄像头,也确认了 usb 摄像头支持 UVC,这节我们将 usb 驱动移植到荔枝派开发板上,并实现拍照的功能以及做 mjpeg-streamer 视频流服务器测试。 1、进入 linux 源码目录,执行 2、Device Drivers - USB support 按照下图进行配置 输出识别的每个usb设备的基

    2024年02月11日
    浏览(48)
  • STM32 | STM32时钟分析、GPIO分析、寄存器地址查找、LED灯开发(第二天)

    寄存器 :寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成 在计算机领域,寄存器是CPU内部的元件,包括通用寄存器、专用寄存器和 控制寄存器 。寄存

    2024年03月08日
    浏览(52)
  • 荔枝派Zero(全志V3S)驱动开发之RGB LCD屏幕显示bmp图片

    了解 framebuffer 字符设备 了解 bmp图片格式 通过操作 /dev/fb0 字符设备来实现在 RGB LCD 屏幕上显示 bmp 图片。 显示设备例如 LCD,在 Linux 中用 Framebuffer 来表征, Framebuffer 翻译过来就是帧缓冲,简称 fb,在 /dev 目录下显示设备一般表示成这样: /dev/fbn ,应用程序通过访问这个设备

    2024年02月11日
    浏览(42)
  • STM32 入门 —— 寄存器与 GPIO

    STM32 总线构图: 什么是寄存器 根据百度百科介绍,寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。简单来说,寄存器就是存放东西的东西,存放的东西是指令、数据或地址 存放数据的寄存器最容易理解,不同

    2024年02月04日
    浏览(51)
  • STM32 寄存器配置笔记——GPIO配置输出

           本文主要介绍GPIO 作为输出时的寄存器配置。包括时钟配置,输出模式配置。以STM32F10xxx系列为例,配置PA8、PD2端口作为输出,输出高/低电平。         1)GPIO外设时钟          通过查找STM32F10xxx中文参考手册得知,GPIO PORT口的时钟配置在RCC_APB2ENR寄存器的第2~6位,

    2024年01月24日
    浏览(72)
  • 明解STM32—GPIO理论基础知识篇之寄存器原理​

    一、前言         在之前的STM32的GPIO理论基础知识中,分别对基本结构和工作模式进行了详细的介绍。GPIO基本结构中主要对GPIO内部的各个功能电路逐一的进行的分析;GPIO工作模式中主要介绍GPIO应用在不同的使用场景下,GPIO端口的静态特征配置和动态的工作模式,同时对

    2024年02月16日
    浏览(50)
  • STM32F103ZET6 GPIO工作模式介绍+使用寄存器点亮第一个LED灯

    目录  GPIO的工作模式介绍 1.输入模式(模拟、上拉、下拉、浮空) 2.输出模式(推挽/开漏) 3.复用功能(推挽/开漏) 4.模拟输入输出(上下拉无影响) 如何使用寄存器点亮第一个LED灯         在输入模式时,施密特触发器打开,输出被禁止。可通过输入数据寄存器 GPIOx_

    2024年02月06日
    浏览(71)
  • STM32 | GPIO口的普通与复用如何配置与用法,本文降从最底层教你如何查看手册运用寄存器来实现GPIO口的配置

    🎊【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -  蓝桥杯嵌入式_勾栏听曲_0的博客 🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️ 📌个人主

    2024年02月14日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包