# C语言方式(mmap)
这里提供接近单片机寄存器操作的一种应用层GPIO操作方式,也封装成库给大家使用。
## GPIO 寄存器介绍
在[V3S datasheet](http://lichee.jicm.cn/doc/V3S/Allwinner_V3s_Datasheet_V1.0.pdf)第224页是GPIO控制器的相关介绍。
V3S从有PB/C/E/F/G 五个GPIO端口,每个都是32位端口(实际引脚没有引出那么多), 也是32位寄存器。
每个端口由以下几个寄存器组成:
(n=1,2,4,5,6;寄存器基址为0x01C20800)
|Register Name|Offset)|Description|详细描述|
|---|----|---|--|
|Pn_CFG0|n*0x24+0x00|Port n Configure Register 0 (n=1,2,4,5,6)| 每个脚4bit,最高位保留。000 输入 ; 001 输出 ; 010 外设功能1 ; 011 外设功能2 ; 100 外设功能3 ; 101 外设功能4 ; 110 EINT中断 ; 111 IO失能 |
|Pn_CFG1|n*0x24+0x04|Port n Configure Register 1 |同上|
|Pn_CFG2|n*0x24+0x08|Port n Configure Register 2 |同上|
|Pn_CFG3|n*0x24+0x0C|Port n Configure Register 3 |同上|
|Pn_DAT|n*0x24+0x10|Port n Data Register |每位代表输入输出值|
|Pn_DRV0|n*0x24+0x14|Port n Multi-Driving Register 0 |0~3逐级递增|
|Pn_DRV1|n*0x24+0x18|Port n Multi-Driving Register 1 |同上|
|Pn_PUL0|n*0x24+0x1C|Port n Pull Register 0 |0浮空,1上拉,2下拉,3保留|
|Pn_PUL1|n*0x24+0x20|Port n Pull Register 1 |同上|
|Pn_INT_CFG0|0x200+n*0x20+0x00|PIO Interrrupt Configure Register0|0上升,1下降,2高电平,3低电平,4双边沿|
|Pn_INT_CFG1|0x200+n*0x20+0x04|PIO Interrrupt Configure Register1|同上|
|Pn_INT_CFG2|0x200+n*0x20+0x08|PIO Interrrupt Configure Register2|同上|
|Pn_INT_CFG3|0x200+n*0x20+0x0C|PIO Interrrupt Configure Register3|同上|
|Pn_INT_CTL|0x200+n*0x20+0x10|PIO Interrupt Control Register|0失能,1使能|
|Pn_INT_STA|0x200+n*0x20+0x14|PIO Interrupt Status Register|0未发生中断,1发生中断。写1清除|
|Pn_INT_DEB|0x200+n*0x20+0x18|PIO Interrupt Debounce Register|bit0,选择中断时钟,0,32Khz 低速时钟;1,24MHz主时钟。bit6:4,去抖时钟分频,选择的时钟源2^n分频,即最大256分频。|
|寄存器|地址|
|---|----|
|PB配置|1C20824|
|PC配置|1C20848|
|PE配置|1C20890|
|PF配置|1C208B4|
|PG配置|1C208D8|
## mmap简介
mmap简单来说就是把一片物理内存空间(或者文件)映射到应用的虚拟内存空间,这样,直接在应用层就能操作 CPU的寄存器,类似于单片机的寄存器操作。我们只要封装好寄存器操作的库函数,就能在以后的程序里简单调用了~
详细的mmap介绍可以参考附录的链接。
为了操作寄存器,我们需要用到/dev/mem设备,这个设备是是物理内存的全映像,可以用来访问物理内存,一般用法是open("/dev/mem",O_RDWR|O_SYNC),然后mmap,接着就可以用mmap的地址来访问物理内存,这实际上就是实现用户空间驱动的一种方法。
~~~
#include <sys/mmap.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
//start: 映射区的起始地址,0的话由接口自动返回
//length: 映射区的长度
//prot: 内存保护标志,不能与文件的打开模式冲突。以下值可以 或 组合。
//PROT_EXEC 页内容可以被执行
//PROT_READ 页内容可以被读取
//PROT_WRITE 页可以被写入
//PROT_NONE 页不可访问
//flag: 指定映射对象的类型,是否可以共享等。
//fd: 文件描述符
//oft: 被映射对象内容的起点偏移。映射物理内存的话,就是物理内存地址。**必须页对齐。**
int munmap(void *start, size_t length);
//start: 前面获得的地址
//length: 映射区的大小。
int msync ( void * addr , size_t len, int flags)
//一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。
//但是对于映射物理内存来说是直接作用的。
~~~
代码片段:
~~~
#include <sys/mmap.h>
char dev_name[] = "/dev/mem";
GPIO_REGISTER *gpio_base;
fd = open(dev_name,O_RDWR);
if(fd<0){
printf("open %s is error\n",dev_name);
return -1 ;
}
gpio_base = (GPIO_REGISTER *)mmap( 0, 0x32, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0x40060000 );
if(gpio_base == NULL){
printf("gpio base mmap is error\n");
close(fd);
return -1;
}
//后面就是对寄存器操作了
//结束后解除映射
munmap(gpio_base, 0x32);
~~~
我基于mmap写了个应用层调试寄存器的小程序,reg-dbger, 在github上可以下载使用。
使用方法为:
~~~
reg-dbger r reg_addr //读寄存器
reg-dbger rb reg_addr bit_oft bit_cnt //读寄存器的bit_oft开始的bit_cnt位
reg-dbger w reg_addr value //写寄存器
reg-dbger wb reg_addr bit_oft bit_cnt value //写寄存器的bit_oft开始的bit_cnt位
reg-dbger dump reg_addr cnt //批量dump出cnt个寄存器值
~~~
比如操作gpio寄存器,点亮熄灭Zero上的绿色LED:
~~~
# PG0
# 配置寄存器 0x01C20800+6*0x24+0=1C208D8
# 数据寄存器 0x01C20800+6*0x24+0x10 = 1C208E8
reg-dbger r 1C208D8
reg-dbger r 1C208E8
reg-dbger wb 1C208D8 0 3 1 #输出状态
reg-dbger wb 1C208E8 0 1 0 #输出0,点亮
~~~
同样基于mmap写了个应用层操作GPIO的小程序,lpi-gpio, 在github上可以下载使用。
使用方法为:
~~~
lpi-gpio set PG0 out/in 0/1/2 //设置为输出的话,0低电平,1,2高电平;设置为输入,0下拉,1上拉,2浮空。
lpi-gpio r PG0
lpi-gpio w PG0 0/1
lpi-gpio pwm PG0 100 200 //PG0 pwm输出,两个参数分别表示高低电平的微秒数(>60us)
lpi-gpio test PG0 //测试PG0用函数翻转IO的最大速率,结果为1.85MHz
lpi-gpio tfast PG0 //测试PG0用软件翻转IO的最大速率,结果为10MHz
~~~
为方便在C语言里调用,我生成了gpio操作的动态库**libgpio.so**,大家可以在c程序中调用。
~~~
int lpi_gpio_initlib(void);
int lpi_gpio_init(int port, int pin, int dir, int val); ////PG0: port=6, pin=0 //val: 设置为输出的话,0低电平,1,2高电平;设置为输入,0下拉,1上拉,2浮空。
int lpi_gpio_r(int port, int pin);
void lpi_gpio_w(int pin, int pin, int val);
void lpi_gpio_deinitlib(void);
~~~
这里是一个简单的使用例程:
~~~
#include "lpi_gpio.h"
#define USLEEP_T 61
int main()
{
lpi_gpio_initlib();
lpi_gpio_init(6, 0, 1, 0);
while(1)
{ //generate 1KHz PWM
lpi_gpio_w(6, 0, 1);
usleep(500-USLEEP_T);
lpi_gpio_w(6, 0, 0);
usleep(500-USLEEP_T);
}
lpi_gpio_deinitlib();
return;
}
~~~
~~~
//gcc -fPIC -shared -o libgpio.so lib_gpio.c //编译生成动态库
gcc test_gpio.c -L. -lgpio -o test_gpio //编译生成应用程序
LD_LIBRARY_PATH=. ./test_gpio //运行应用程序,手工指定动态库位置
//or add libgpio.so to /etc/ld.so.conf, ldconfig
~~~
## 附录
mmap参考资料:http://blog.chinaunix.net/uid-26669729-id-3077015.html
linux动态库:http://www.cnblogs.com/jiqingwu/p/linux_dynamic_lib_create.html
linux静态库:http://www.cnblogs.com/jiqingwu/p/4325382.html
- 前言
- 荔枝派TODO任务领取
- linux使用小贴士
- 入门篇
- 板卡介绍
- 开箱指南
- 烧录启动系统
- 联网方法
- 镜像使用
- 镜像说明
- buildroot系统使用
- debian系统使用
- 外设操作
- 外设操作概览
- 低速外设
- GPIO
- GPIO模拟低速接口
- UART
- PWM
- I2C
- SPI
- 高速接口
- SDIO
- USB
- EtherNet
- DVP CSI
- MIPI CSI
- 模拟外设
- CODEC
- LRADC
- 常见设备驱动
- USB摄像头
- USB 3G/4G 网卡
- 舵机
- 开发篇
- UBOOT适配
- UBOOT编译
- UBOOT配置
- UBOOT配置屏幕分辨率
- UBOOT配置SPI启动
- Linux内核开发
- Linux内核编译
- BSP Linux内核编译.md
- Linux内核选项
- 外设驱动与设备树
- RTL8723BS驱动
- 根文件系统定制
- buildroot定制系统
- buildroot添加软件包
- openwrt定制系统
- emdebian定制系统
- camdriod开发
- camdriod编译
- 主线Uboot引导Camdriod
- 系统镜像打包
- XBOOT适配
- 荔枝运行XBOOT
- 应用篇
- 游戏机-基于EmulationStation
- 游戏机-gnuboy
- 语音识别-科大讯飞云
- GUI-QT5
- 语音识别-离线关键词识别
- 路由器-Lichee Zero
- 投稿文章
- 荔枝派Zero开箱指南
- Zero i2c oled使用指南
- zero SPI LCD使用指南
- Zero u-boot编译和使用指南
- TF WiFi使用方法
- Zero Ethernet使用指南
- Zero 移植Qt5.4.1
- ZeroSpiNorFlash启动系统制作指南
- Visio-uboot-sunxi流程
- lichee 编译踩坑记录(ilichee ZERO)
- lichee_zero_外设GPIO接口
- TF WIFI 小白编
- 从零开始LicheePi Zero的开发
- 认识Zero的硬件
- 搭建Zero的开发环境
- 主线Uboot
- 主线kernel
- BSP kernel
- BSP内核启动
- bsp内核的摄像头使用
- BSP内核中的保留内存
- uboot启动BSP内核常见错误
- BSP内核 FBTFT移植
- BSP内核启动错误及警告解决
- buildroot 根文件系统
- emdebian 根文件系统
- SPI Flash 系统编译
- sunxi-fel增加对16M 以上flash的支持
- overlayfs的使用
- jffs2系统挂载不上的常见原因
- JFFS2 文件系统简介
- uboot对spi flash的识别
- bsp内核的SPI flash启动
- Docker开发环境
- Docker 命令速查
- 基础ubuntu系统配置
- docker离线镜像
- Zero系统烧录
- dd镜像烧录
- 分区镜像烧录
- SPI Flash系统烧录
- 一键镜像烧录
- Zero外设把玩
- I2C操作
- PWM输出
- CODEC的使用
- 以太网使用指南
- GPIO操作
- 文件IO方式
- C语言接口(mmap)
- Python操作GPIO
- pinctrl-sunxi介绍
- UART操作
- 点屏
- 点屏之RGB屏
- 点屏之SPI屏 ili9341
- 点屏之SPI OLED
- 点屏之I2C OLED
- 点屏之SPI屏 ili9488
- 点屏之MCU屏
- 点屏之触摸屏驱动
- 点屏之simple-framebuffer
- 点屏之屏幕时序
- 时钟控制器CCM
- 摄像头
- BSP DVP摄像头
- BSP MIPI 摄像头
- 主线DVP摄像头
- 主线 MIPI摄像头
- SPI 操作
- 应用层开发
- 开机自启动
- Segment Fault调试
- Zero通过OTG共享PC网络
- USB摄像头使用
- 基于QT的GUI开发
- 移植tslib
- 移植QT5.9.1
- 移植QT4.8.7
- QtCreator使用
- Qt5.x移植到Qt4.8
- Qt字体相关
- Qt移植总结
- Qt裁剪
- Qt去除鼠标指针显示
- zero_imager使用
- 驱动开发
- 设备树简介
- GPU/DRM 显示驱动
- sys下设备树查看
- atmel触摸屏驱动分析
- atmel触摸屏中断改轮询
- uboot下gpio操作
- helloworld驱动编译演示
- FBTFT分析
- 内核模块静态加载的顺序
- SPI驱动分析
- SPI 驱动编写
- Uboot开发
- 开机logo
- 看门狗的使用
- 关于系统reboot
- 内核printk等级设置