# 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等級設置