2013年12月27日 星期五

Beaglebone Black I/O library 開發歷程(三) - GPIO module


How to access GPIO using mamp in TI AM335x CPU

Base on Beaglebone Black

C Library development note


Author : Cai , Meng-Lun , (VegetableAvenger) , 2013/12/19

Introduction :

這份筆記的內容正式涵蓋了BBBio library的內容,這份文件將介紹如何再Beaglebone black上利用C語言與mmap來進行GPIO的操作,以user space的方式來控制硬體,而不用去撰寫專用的Device Driver,同時粗略的介紹一下AM335xSystem Interconnect架構,以瞭解module的布局方式以及Clock manager的管理,最後將演示一段透過BBBio library,使用按鈕開關來控制LED閃爍速度的程式碼。


This note contain :
  • AM335x System Interconnect Architecture
  •  AM335x GPIO module introduction
  •  Memory map of AM335x GPIO module
  •  Enable/Disable Clock of GPIO module via CM_PER/CM_WKUP register
  •  GPIO register
  •  Get BBBio
  •  LED Demo

部份文字內容或圖片 reference from : AM335x Technical Reference Manual


AM335x System Interconnect Architecture :

AM335xinterconnect Architecture 2-Level架構,由L3L4組成,而L3則分為L3FL3S,而L4則是分為L4_FASTL4_PERL4_WKUP,結構如圖1所示,由其可看出L4_PERL4_WKUP是掛接在L3_S下,而L4_FAST則是接在L3F下。





1L3 Topology (AM335x TRM page 1231)

GPIO模組則是分佈在L4之中,GPIO 0掛在L4_WKUPGPIO 1~3掛在L4_PER中,整體上如圖2所示。




2L4 Topology (AM335x TRM page 1234)


瞭解GPIO模組所屬的Interconnect Topology有助於瞭解各module間的Clock互動情形,即瞭解module所屬的Clock Domain,像是GPIO 1~3所處的Clock DomainPD_PER_L4LS_GCLK,而GPIO 0 則處在PD_WKUP_L4_WKUP_GCLK這個Clock domain上,而透過圖2L4 Topology布局方式便可以清楚的瞭解為何GPIO模組會處於不同的Clock Domain上。

為了要讓module省電,所以沒有必要使用的moduleClock常處於Disable的狀態,而每一個GPIO模組擁有其各自的register來控制是否Disable Clock,瞭解其Clock domain之後,便可以搜尋其對應的Clock control register,接著才可以在需要使用時,將GPIOClock Enable/Disable,下表為各GPIOClock control register




GPIO

Clock Domain

Speed

Clock Control Register

GPIO 0

PD_WKUP_L4_WKUP_GCLK

100M Hz

CM_WKUP_GPIO0_CLKCTRL

GPIO 1

PD_PER_L4LS_GCLK

100M Hz

CM_PER_GPIO1_CLKCTRL

GPIO 2

PD_PER_L4LS_GCLK

100M Hz

CM_PER_GPIO2_CLKCTRL

GPIO 3

PD_PER_L4LS_GCLK

100M Hz

CM_PER_GPIO3_CLKCTRL

1GPIO Clock Domain

AM335x GPIO Module Introduction :

AM335x 擁有4GPIO moduleGPIO 0GPIO 1GPIO 2GPIO 3,每一個module32支接腳,故總共有128GPIO的腳位可以使用,每支腳位提供以下的基本功能:
  1. Data Input/ Output
  2. Debounce cell
  3. Synchronous interrupt
  4. Wake-up request (Only GPIO 0)

本筆記主要記錄最基本的Data Input/OutputDebouncing為次章節介紹,而剩餘功能尚未完整測試。

基本上每一組GPIO都提供了1~3的基本功能,而GPIO 0 則額外提供了Wake-Pp的功能,是與其他GPIO較為不同的地方,如圖3所示,可以發現GPIO 0的模組配置較GPIO 1~3不同,而圖中各GPIO modulePIDBCLK則為Debouncing Clock的輸入訊號,而POINTRPEND1 /2則是用來產生interrupt的訊號。

 


3GPIO0~3 Module Integeration (AM335x TRM page 4630)

由圖3可知,GPIO模組操作起來所需要的條件並沒有很多,若是不考慮wake up功能的話,基本上GPIO 0~3功能是一樣的,只需要注意在L4 WakeUP InterconnectL4 Peripgeral InterconnectClock有輸入進GPIO模組,接著只要在GPIO module相對應的register之中進行操作,即可控制pin腳的狀態。


Memory map of AM335x GPIO module

接著從AM335x之中,查出GPIO對應的Memory Map的位址,以利用mmap進行存取動作

AM335x TRMpage .171Table 2-2. L4_WKUP Peripheral Memory Map

AM335x TRMpage .173~174Table 2-3. L4_PER Peripheral Memory Map



Region Name

Start Address

End Address

Size

Description

CM_PER

0x44E0_0000

0x44E0_3FFF

0x4000

Clock Module Peripheral Registers

CM_WKUP

0x44E0_0400

0x44E0_04FF

0x100

Clock Module Wakeup Registers

GPIO0

0x44E0_7000

0x44E0_7FFF

0x1000

GPIO register

GPIO1

0x4804_C000

0x4804_CFFF

0x1000

GPIO2

0x481A_C000

0x481A_CFFFF

0x1000

GPIO3

0x481A_E000

0x481A_EFFFF

0x1000

2GPIO Memory map
 

Enable/Disable Clock of GPIO module via CM_PER/CM_WKUP register

Clock上要特別注意,由先前提過的表1可知,GPIO 0 屬於PD_WKUP_L4_WKUP_GCLKClock domain,控制RegisterCM_WKUP_GPIO0_CLKCTRL,而GPIO 1~3屬於PD_PER_L4LS_GCLKClock domain,控制RegisterCM_PER_GPIOx_CLKCTRL (x1~3),要小心不要控制錯了,名字是有差別的。

GPIO模組基本上會有2Clock的輸入:Interface ClockDebouncing ClockInterface ClockL4 Iterface提供,100MHz的頻率,而Debouncing Clock則是可選的(不需要就Disable沒關係),為32.768KHz,如果interface clock 沒有Enable,那整組GPIOregister access 都會error,並回傳Bus Error的選項。

瞭解要控制的Register之後,接著就是去看他的格式,相對應的頁數列於表3,而基本上這4reigister的控制參數格式是一模一樣的,如圖4所示,因此我們只需要將Bit 0~1MODULEMODE欄位設為0x2 (Enable)即可打開相對應GPIOClock




Register Name

Page

Session

CM_WKUP_GPIO0_CLKCTRL

983

Session 8.1.12.2.3

CM_PER_GPIO1_CLKCTRL

947

Session 8.1.12.1.29

CM_PER_GPIO2_CLKCTRL

948

Session 8.1.12.1.30

CM_PER_GPIO3_CLKCTRL

949

Session 8.1.12.1.31

3GPIO Clock Register page Reference
 

而這裡有一個需要注意的欄位,OPTFCLKEN_GPIO_x_GDBCLK (bit 18),這個欄位是用來Enable/Disable Debouncing Clock的,即圖3PIDBCLK



4GPIO Clock Register format
 

mmap程式碼:

BBBio/BBBio.ciolib_init fnuction之中,需要map 2+4map,分別是控制Clokc的:CM_PERCM_WKUP,和GPIO moduleGPIO0~3

其中需要特別注意CM_WKUP,其memory map位址為0x44E0_0400,這個位址直接下去mmap,會回傳error,同時error code為:EINVAL,這是因為0x44E0_0400這個address並沒有alignment page,這裡的page4K,所以mmap沒有辦法map;不過這是可以處理的,在BBBio中,先mmap CM_PER (0x44E0_0000)這個register,在透過offset 0x400,存取CM_WKUP,即可mmap CM_WKUP,因為在mmap CM_PER時,其maplength已經涵蓋了CM_WKUP



cm_per_addr = mmap(忽略参數) ;

cm_wkup_addr =(void *)cm_per_addr + 0x400 ;

Enable/Disable程式碼:

BBBio/BBBio.cBBBIO_sys_Enable_GPIOBBBIO_sys_Disable_GPIO fnuction之中,
 

GPIO register

GPIO module基本上需要:InputOutputI/O方向設定,3個功能,而AM335xGPIO module則是將這些功能透過以下register控制



Action

Register Name

Offset

I/O Setting

GPIO_OE

0x134

Input

GPIO_DATAIN

0x138

Output

GPIO_CLEARDATAOUT

GPIO_SETDATAOUT

GPIO_DATAOUT

0x190

0x194

0x13C

4GPIO Control Register

I/O Setting

透過GPIO_OE register來控制PIN腳是要Inpout還是Output,一次設定整組的GPIO I/O status,共32bit,每一個bit對應1個腳位,bit設為0代表output,設為1代表Input

例如:GPIO 0GPIO_OE register bit 0代表 GPIO 0 [0]這支pin腳,若是寫入0x00000001,代表將pin0腳位設為input,其他設為output

請特別注意:建議透過邏輯運算| (or),來進行操作,因為有些GPIO不是你可以亂動的,以Beaglebone black來說,其外部接角插槽只有96pin,而且有些pin不是GPIO,代表不是每一個GPIO都有拉出去,若是你亂改到非外拉的腳位狀態,或許會造成系統/裝置錯誤,因此,設定數值前請三思並檢查。

 Input Setting

透過GPIO_DATAIN來取得PIN腳目前輸入的狀態,一次讀入整組的GPIO input status,共32bit,每一個bit對應1個腳位,例如GPIO 0GPIO_DATAIN register bit 0代表 GPIO 0 [0]這支pin腳,因此讀出GPIO_DATAIN的數值之後,你需要自己利用邏輯運算& (and),來判斷需要的腳位狀態。

Output Setting

Output的設定就比較有趣了,這裡我將他分成2種操作模式:獨立控制直接控制

 獨立控制代表一次操作下,只可以控制腳位是輸出0還是1

直接控制代表一次操作下,可以一次控制一整組的GPIO狀態,Pin可為0 也可為1



模式

Register

解說

獨立控制

GPIO_CLEARDATAOUT

GPIO_SETDATAOUT

設定Pin腳輸出0

設定Pin腳輸出1

直接控制

GPIO_DATAOUT

設定GPIO輸出

5GPIO Output Control Register

假設你需要將8LED 同一時間一次點亮,那你可以使用獨立控制或是直接控制,但若是同一時間下,需要點亮4個,滅掉4LED,那就只能使用直接控制的功能,否則你必須使用2次獨立控制的register,先下GPIO_SETDATAOUT 4個,在下GPIO_CLEARDATAOUT指令滅4個。

 獨立控制 寫入方式:

一次寫入代表寫入整組的GPIO Output status,共32bit,每一個bit對應1個腳位;要讓某pin腳設為1則在GPIO_SETDATAOUT相對應的bit寫入1,要設為0,則在GPIO_CLEARDATAOUT寫入1,你沒看錯,都是寫入1,因為這2register分別用來進行Output 0Output 1,寫入1代表此PIN腳要進行設定,寫入0則沒有任何效果。

 直接控制 寫入方式:

直接控制的寫入方式就很直覺了,一樣一次寫入代表寫入整組的GPIO Output status,共32bit,每一個bit對應1個腳位;要某pin腳設為1則在GPIO_DATAOUT相對應的bit寫入1要設為0,則在GPIO_DATAOUT寫入0跟一般的用法無異。

請特別注意:以上運算一樣建議透過邏輯運算| (or),來進行操作,因為有些GPIO不是你可以亂動的,以Beaglebone black來說,其外部接角插槽只有96pin,而且有些pin不是GPIO,代表不是每一個GPIO都有拉出去,若是你亂改到非外拉的腳位狀態,或許會造成系統/裝置錯誤,因此,設定數值前請三思並檢查。


Get BBBio

BBBioShabaz開發,Cai,Meng-Lun維護的Beaglebone black I/O library,為一套自由軟體,任何人可以自由更改,沒有授權問題,目前置放於github :

https://github.com/VegetableAvenger/BBBIOlib

 LED Demo

5為實際範例圖和電路圖,詳細的程式碼請參閱BBBio library中的Demo/Deno_LED_GPIO/




 

5LED Demo & CircuitLayout




2013年12月25日 星期三

Beaglebone Black I/O library 開發歷程(二) - mmap


How to access register using mamp in TI AM335x CPU

Base on Beaglebone Black


C Library development note

Author : Cai , Meng-Lun , (VegetableAvenger) , 2013/12/19

Introduction :


這份筆記紀錄了BBBio library的開發過程,提供了一個基本的簡介來使用mmap函數,同時這份筆記也提供了一些流程,透過mmap來存取Device memory map,這份筆記提供了一個簡約的方法,在user space來存取I/O,以下過程均以C語言開發。

This note contain :

Ÿ   mmap basic introduction.
Ÿ   Memory mapped I/O
Ÿ   Memory map of ARM Cortex-A8 (AM335x)
Ÿ   Access memory map register via C

mmap basic introduction :


mmap 的精要解釋如下:

map files or devices into memory .

在檔案的R/W中,在read/writefunction中都存在一個緩衝區,當使用這些指令時,資料均需透過這個緩衝區才能實際的R/W到對應的檔案之中,而透過mmap可以免去這個過程,把檔案當作記憶體般的存取,加快了檔案存取的速度,以下部落格提供了圖文並茂的解釋,歡迎各位閱讀:

http://stenlyho.blogspot.tw/2008/08/zero-copy.html

 以上是其中一個用法,而這份筆記記載的是mmap種多用法之一,:Memory mapped I/O

 Memory mapped I/O :


I/Omemory共用記憶體空間,不用特別的指令來存取I/O,以記憶體讀寫的方式來進行I/O port的存取,在本文中使用的是ARM的核心,TI AM335X,便提供這種方式來進行I/O port的存取。

透過 memory mapped I/O的方式,許多外接元件或是module,都可以透過memory access的方式來進行存取,從C語言的角度來看,則是只需要用指標就可以存取這些記憶體,只要知道mapped的範圍,便可以存取裝置,在使用上非常直覺。
1Memory mapped I/O



Memory map of ARM Cortex-A8 (AM335x)



這篇筆記使用的是TI AM335XSoC,其spec可以從網路上載到:


 AM335x Technical Reference Manual


http://www.ti.com/product/am3359

 這是一篇長達4千多頁的PDF,介紹了這一整個SoC的概要,而memory map則列於page .168~176頁,在這裡截錄一段USB sub system memory map的表格:


2Memory map sample (USBSS)

圖2的格中清楚標明了USB sub systemmemory map範圍,而以Block為名是因為每一個裝置均由多個register控制,而這些register為一個群組,即為一群Block,以USBSS (0x4740_0000~0x4740_0FFF)為例,此Block內含的register列在page 2283session 16.5.10table 16-29,內含約42個左右的register來表明各種狀態(由命名大概可推測為interrupt相關的操作),如圖3列出其中一部份 :


 

3Block registers(USBSS)

而這些register才是真正用來存取device的關鍵,以SYSCONFIG register為例,其詳細列在page.2288session 16.5.4之中,如圖4所示,可以看出其資料布局的方式,同時也解釋這個Register的詳細作用,像是bit0用來resetUSB modulebit 8~11用來控制clock等。


4SYSCONFIG register

上述一個簡單的memory map查表,用來尋找USB subsystem對應的memory map位址,同時瞭解其對應位址下的register的意義,而詳細的操作方法,則需要參閱相對應的章節,才可以正確的操作device

Access memory map register via C


在此章節將介紹如何透過C來進行memory map的方法,同時將上述所提到的SYSCONFIG register的資料read出來,詳細的code放在github上:


程式碼主要分為2個部份:
  1. 使用mmap 映射記憶體
  2. 存取register

1.      使用mmap映射記憶體

首先需要先定義要映射的記憶體空間與映射長度,以SUBSS為例,由圖2可知,其start address 0x4740_0000 (hex)end address0x4740_0FFF,故可知此blocklength0x1000,透過這些資訊,定義2define


#define USBSS_ADDR     0x47400000
#define USBSS_LEN         0x1000

 有了這些資訊後,接著必須使用mmap來進行記憶體的映射,程式碼如下:


int memh = 0;
volatile unsigned int *USBSS_p = NULL;
memh=open("/dev/mem", O_RDWR);
USBSS_p = mmap(0, USBSS_LEN, PROT_READ | PROT_WRITE, MAP_SHARED, memh, \
USBSS_ADDR); 

 透過open的方式,來開啟/dev/mem這個檔案,關於此檔案,我找到一段不錯的解釋:

Mr. Shickadance"mem is a character device file that is an image of the main memory of the computer. It may be used, for example, to examine (and even patch) the system. Byte addresses in mem are interpreted as physical memory addresses."

接著再透過mmap函數來進行memory mapped,將其mapR/W模式,同時使用MAP_SHARED,而非MAP_PRIVATE,因為存取的是device,是公用的裝置,若是同時有2個程式mmap,使用MAP_PRIVATE沒辦法得知對方的存取動作;剛剛定義的USBSS_LENUSBSS_ADDR則是做為map lengthoffset

注意在這裡使用了volatile修飾詞,因為存取的是實際上的裝置,為了避免在暫存器上產生的誤動作,因此使用此修飾詞,讓每次存取時都到記憶體中拿資料,而不要透過暫存器。

 2.      存取register

接著再透過指標存取的方式,到SYSCONFIG這個registerread資料:



volatile unsigned int *reg = NULL ;

reg=(void *)USBSS_p + SYSCONFIG;

printf("USBSS - SYSCONFIG register : %X (hex)\n", *reg);

在筆者平台顯示的值為0x14,在根據page.2288session 16.5.4的描述可以得知此值代表:打開usb0~1clockno-idleno-standby的模式。

 由這2個簡單的步驟,即可完成一個device memory map的讀取。



2013年12月24日 星期二

Beaglebone Black I/O library 開發歷程(一)

10月時我從網路上看到以下的文章,是由一名外國網友Shabaz 發表在element14上,有關於Beaglebone black I/O library。


BBB - BeagleBone Black I/O (GPIO) Library for C :
http://www.element14.com/community/community/knode/single-board_computers/next-gen_beaglebone/blog/2013/10/10/bbb--beaglebone-black-io-library-for-c


當下看到文章其實滿興奮的,因為我一直想透過Beaglebone black學習linux I/O的方法,但是在driver方面的學習,有點略餘緩慢,花了約1個禮拜,仍舊還處在翻閱書籍的時期。


看到這篇文章時,他的library得使用方法實在讓我非常驚艷,透過mamory map的方式來進行I/O的存取,Yes,學校OS課程有教過mamory mappd I/O的方法,但是僅提到理論的東西,實做連邊都沒碰到,手動也沒動一下,現在看到這個library,實在讓我心中無限感動,原來阿! 原來memory mapped I/O 可以這樣做阿! 實在太奧妙,太有趣了。


雖然一個大學畢業生講這些話實在是很慚愧阿....但接觸這個Library之後,讓我心中的熱血整個爆發開來!!!


當我點亮第一個LED的時候,心中可以說是無限的喜悅,靠這股動力,我學會了看CPU spec的一些基礎,也學習到了基本GPIO的應用。


天阿,現在CPU的設計,大概不能用奧妙來形容了,簡直是有趣到的一個不可思議的境界。
我一頭栽進這個library的開發,而我也大言不慚的跟原作者Shabaz請求了一份程式碼授權,開始著手開發自己的BBBio library。


嵌入式領域實在非常得有趣,很遺憾我念完大學四年,研究所休學之後才找到自己喜歡的東西,我喜歡純軟體,但我更愛嵌入式領域的知識。


我將我擴展過的BBBio library 放在 github上:



這是一套為Beaglebone black打造的User space I/O library,目前實做了GPIO / PWM / SPI的功能,而我也加入了一些Demo和電路,來做為使用的範例,雖然PWM 和McSPI沒有將完整的功能全部打開(沒有中斷跟DMA),但我想基本的實做應該都已經展示出來了,雖然遺憾,但我仍在努力。

希望有緣看到此網誌的各位,若有興趣的話,歡迎試用看看這個Library,也給我批評與指教,謝謝。

這系列文章我打算分成以下進行 :
一、簡介
二、AM335x GPIO module 介紹-I
三、AM335x GPIO module 介紹-II
四、AM335x EPWMSS module 介紹
五、AM335x McSPI module 介紹

I2C / UARY / DMA 之類的,不知道在我當兵有沒有機會做完整,大概只能推延了。