全志V3s之应用层点灯

2023-12-14 01:27:43

1、全志V3s的管脚控制简介:

全志V3s一共有5个端口可以作为输入输出,其数量如图所示:
在这里插入图片描述
端口的基地址为:PIO : 0x01c20800。其中,每个端口都有自己的偏移地址,其偏移计算公式如表所示:
在这里插入图片描述
PB端口为例,其偏移地址为0x24,默认值为0x77777777,共32位,0~2、4~6、8~10、12~14、16~18、20~22、24~26、28~30分别对应0、1、2、3、4、5、6、78个管脚。配置方式如图所示:
在这里插入图片描述
PB的数据寄存器的偏移地址为0x34,默认数值为0x00000000,配置如图所示:
在这里插入图片描述

2、点灯程序编写:

根据上述的寄存器,可以在应用层直接控制寄存器进行点灯操作。其程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

#define GPIO_REG_BASE   0x01C20800      //GPIO物理基地址 (小页4kb)
#define MAP_SIZE        0x400 //MMU页大小
#define GPIO_BASE_OFFSET (GPIO_REG_BASE & 0X00000FFF) //GPIO基地址偏移计算
#define GPIO_PAGE_OFFSET (GPIO_REG_BASE & 0XFFFFF000) //获得页偏移
/**********************修改的************************/
#define rPB_CFG0 0X24  //PB_CFG0寄存器地址偏移
#define rPB_DAT 0X34    //PB_DAT寄存器地址偏移
/***************************************************/
int led_on(unsigned char *MAP_BASE);
int led_off(unsigned char *MAP_BASE);
int main(int argc, char **argv)
{
    static int dev_fd;
    unsigned char *map_base;
    printf("led OK\r\n");

    if(argc!=2 || (strcmp(argv[1],"on") && strcmp(argv[1],"off"))){
        printf("argv_error!please input 'on' or 'off'!\n");
        exit (0);
    }

    dev_fd = open("/dev/mem", O_RDWR );
    if (dev_fd < 0){
        printf("open(/dev/mem) failed.\n");
        return 0;
    }
    printf("Modified GPIO_PAGE_OFFSET: 0x%08X\n", GPIO_PAGE_OFFSET);
    map_base = (unsigned char *)mmap(NULL, 0x400,PROT_READ | PROT_WRITE, MAP_SHARED,dev_fd, GPIO_PAGE_OFFSET); //把物理地址映射到虚拟地址
    printf("Modified GPIO_PAGE_OFFSET: 0x%08X\n", GPIO_PAGE_OFFSET);
    printf("Modified map_base: 0x%08X\n", map_base);
    if(!strcmp(argv[1],"on")) led_on(map_base); //点亮LED
    if(!strcmp(argv[1],"off")) led_off(map_base);//关闭LED
    if(dev_fd) close(dev_fd);
    munmap(map_base,MAP_SIZE);//解除映射关系
    return 0;
}


//led_on
int  led_on(unsigned char *MAP_BASE)
{
    unsigned int PB_CFG0,PB_DAT;
    printf("Modified MAP_BASE: 0x%08X\n", MAP_BASE);
    PB_CFG0=*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0);
    printf("Modified PB_CFG0: 0x%08X\n", PB_CFG0);
    PB_DAT=*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_DAT);
    printf("Modified PB_DAT: 0x%08X\n", PB_DAT);
    *(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0)=((PB_CFG0 & 0XFFFFFF0F)|0X00000010);//PB1 第2个引脚
    *(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_DAT)=((PB_DAT & 0XFFFFFFDF)|0X0000002);
}
//led_off
int  led_off(unsigned char *MAP_BASE)
{
    unsigned int PB_CFG0,PB_DAT;
    PB_CFG0=*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0);
    PB_DAT=*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_DAT);
    *(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0)=((PB_CFG0 & 0XFFFFFF0F)|0X00000010);//PB1 第2个引脚
    *(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_DAT)=((PB_DAT & 0XFFFFFFFD));
}

3、编译执行:

a、编译生成可执行文件:

arm-linux-gnueabihf-gcc gpio_app.c -o gpio_app

通过交叉编译生成可执行文件
在这里插入图片描述

b、执行gpio_app可执行文件:

开灯:
在这里插入图片描述
在这里插入图片描述
关灯:
在这里插入图片描述
在这里插入图片描述

4、部分函数解释:

a、程序一:

map_base = (unsigned char *)mmap(NULL, 0x400,PROT_READ | PROT_WRITE, MAP_SHARED,dev_fd, GPIO_PAGE_OFFSET);

这行代码使用mmap函数将物理地址映射到虚拟地址。

  • NULL: 映射的开始地址。由于通常将其设置为NULL,操作系统会自动选择合适的地址。
  • 0x400: 这是映射的长度,也就是要映射的字节数。0x400,即1024字节
  • PROT_READ | PROT_WRITE: 这是保护位,指定对映射区域的访问权限。PROT_READ 表示可读,PROT_WRITE 表示可写。所以这个映射区域是可读可写的。
  • MAP_SHARED: 这表示映射区域对所有映射到这个对象的进程可见,对映射到同一个对象的其他进程也可见。通常用于共享内存。
  • dev_fd: 这是打开的设备文件描述符,指定了映射的对象。
  • GPIO_PAGE_OFFSET: 映射的偏移量,通常是物理地址的页面大小的倍数。在这里,是 GPIO_REG_BASE 的页面偏移。

这行代码的作用是创建一个大小为 0x400 字节的映射区域,使得你可以通过 map_base 指针访问这段内存。在这个例子中,它用于将物理地址映射到虚拟地址,以便后续的操作可以通过虚拟地址来访问 GPIO 寄存器。

b、程序二:

munmap(map_base,MAP_SIZE);

munmap 是一个系统调用,用于解除内存映射,即取消之前使用 mmap 函数创建的映射关系。

  • map_base: 是通过 mmap 映射得到的指向映射内存起始位置的指针。
  • MAP_SIZE: 是通过 mmap 映射的内存区域的大小。

这行代码的作用是取消 map_base 所指向的内存区域的映射,并释放相关资源。

c、程序三:

PB_CFG0=*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0);

这行代码用于从内存映射的地址中读取 PB_CFG0 寄存器的值。

  • MAP_BASE:这是通过 mmap 函数映射得到的内存的起始地址,它是一个指向映射内存区域的指针。
  • GPIO_BASE_OFFSET:这是 GPIO 基地址的偏移计算,它通常是为了从 GPIO_REG_BASE 中提取低12位的偏移。在这个上下文中,它可能是为了获取到 GPIO 寄存器的正确位置。
  • rPB_CFG0:这是 PB_CFG0 寄存器的地址偏移。
  • *(volatile unsigned int *) 就是告诉编译器将一个特定地址的内存解释为一个 volatile unsigned int 类型的值。这个表达式通常在底层系统编程中用于直接访问硬件寄存器或者内存地址。

整体来说,这行代码的作用是从内存映射区域中读取 PB_CFG0 寄存器的值,并将其赋给 PB_CFG0 变量。

d、程序四:

*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_CFG0)=((PB_CFG0 & 0XFFFFFF0F)|0X00000010);

这行代码的作用是配置 PB1 引脚的控制寄存器(PB_CFG0)

  • PB_CFG0 寄存器的原始值与 0XFFFFFF0F 进行按位与操作,目的是清除该寄存器中与 PB1 引脚相关的配置信息。其原始值为0x777777770XFFFFFF0F 按位与可得0x77777707
  • 通过按位或操作|,将 PB1 引脚的新配置信息 0X00000010 加入到寄存器中。0x777777070X00000010 按位或,则可得到0x77777717。其意思为:将PB0、PB2~PB7设置为IO Disable,将PB1设置为Output

e、程序五:

*(volatile unsigned int *)(MAP_BASE+GPIO_BASE_OFFSET+rPB_DAT)=((PB_DAT & 0XFFFFFFDF)|0X0000002);

这行代码的作用是设置 PB1 引脚的数据输出寄存器(PB_DAT),即控制 PB1 引脚的高低电平。

  • PB_DAT 寄存器的原始值与 0XFFFFFFDF 进行按位与操作,目的是清除该寄存器中与 PB1 引脚相关的数据信息。其原始值为0x0,与 0XFFFFFFDF 进行按位与操作,可得0x0
  • 通过按位或操作 |,将 PB1 引脚的新数据信息 0X0000002 加入到寄存器中。将0x00X0000002 按位或可得0X0000002 ,其二进制表示为:0010

文章来源:https://blog.csdn.net/a_qwq_a/article/details/134981202
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。