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、7
8个管脚。配置方式如图所示:
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
引脚相关的配置信息。其原始值为0x77777777
与0XFFFFFF0F
按位与可得0x77777707
。- 通过按位或操作
|
,将PB1
引脚的新配置信息0X00000010
加入到寄存器中。0x77777707
与0X00000010
按位或,则可得到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
加入到寄存器中。将0x0
与0X0000002
按位或可得0X0000002
,其二进制表示为:0010
。