python環境下實現OrangePi Zero寄存器訪問及GPIO控制

來源:互聯網
上載者:User

標籤:assert   key   成員函數   ref   timer   set   port   開始   ini   

  最近入手OrangePi Zero一塊,程式上需要使用板子上內建的LED燈,在網上一查,不得不說OPi的支援跟樹莓派無法相比。自己摸索了一下,實現簡單的GPIO控制方法,作者的Zero安裝的是Armbian系統,使用python寫了一個讀寫寄存器的簡單模組,通過這個模組,即可實現對GPIO的控制。

  作者以前使用過STM32的MCU,這類MCU,如果要實現對GPIO的控制,只需要根據datasheet尋找相應GPIO寄存器並進行配置,即可實現IO控制,例如,要將記憶體位址為0x12345678的寄存器全部置為0xFFFFFFFF,只需要一條C語句:

1 (uint32 *)(0x12345678) = 0xFFFFFFFF;

  但是,這個方法在Linux中行不通,編譯啟動並執行時候,會提示"segmentation fault",這個段錯誤應該就是訪問了不可訪問的記憶體,這個記憶體區要麼是不存在的,要麼是受到系統保護的。所以只能使用其他方法。

  首先總結一下實現對OrangePi GPIO控制的兩種方法,第一種是通過Linux記憶體映射的方式,將實際CPU硬體的記憶體位址映射到使用者程式的記憶體空間,再進行操作;第二種是通過sysfs方式控制GPIO,在程式中,操作/sys/class/gpio目錄實現io口的控制。

  這兩種方式都有現成已完成的案例,例如在python環境使用的pyH3庫、C環境的WiringPi庫使用的就是第一種方式;從樹莓派移植的OPi.GPIO庫使用的是第二種方式。其中第二種方式個人感覺更加簡單,因為只需要在使用者的程式裡面讀寫系統目錄的檔案,即可實現對GPIO的控制,非常方便,但作者發現這種方式有個嚴重的問題,就是它無法使用那些板子上沒引出來的IO口,因為板載的兩個LED(紅燈和綠燈)分別是使用了PA17和PL10引腳,如果使用第二種方式控制這兩個引腳,會提示“Device or resource busy”的錯誤,通過以下命令:

cat /sys/kernel/debug/gpio

可以看出系統已經佔用的IO口如下:

第一個GPIO17即為PA17,系統預設已經把該引腳配置為輸出模式,共置為低電平。這裡需要說明一下系統裡對GPIO口的編號方法,系統是按照PA~PL共12組、每組32個引腳的方式對IO口進行統一編號的,如,GPIO-0~GPIO-31為PA口的IO,GPIO-32~GPIO-63為PB口的IO,以此類推。所以最後一行GPIO-362實際就是PL10引腳,即電源的綠燈引腳。

  迴歸本文,因為第二種方法無法操作PA17,所以只能使用第一種方法。

  第一種方法已經有現成的實現,通過深入研究庫源碼,內部實際都是通過C的mmap函數來實現CPU的物理地址映射到使用者程式的記憶體空間。作者習慣使用Pyhon在Linux環境下進行程式開發,pyH3庫使用感覺比較繁瑣。所以特意研究了一下能否在python環境下實現物理地址的映射。實際果然不出所料,C裡有mmap函數,python裡同樣有內建的mmap模組。說明文檔在這裡:https://docs.python.org/2/library/mmap.html

  其中最重要的就是mmap類的建構函式:

class mmap.mmap(fileno, length[, flags[, prot[, access[, offset]]]])

fileno: 檔案描述符,可以是file對象的fileno()方法,或者來自os.open(),在調用mmap()之前開啟檔案,不再需要檔案時要關閉。

length:要對應檔部分的大小(以位元組為單位),這個值為0,則映射整個檔案,如果大小大於檔案當前大小,則擴充這個檔案。

flags:MAP_PRIVATE:這段記憶體映射只有本進程可用;mmap.MAP_SHARED:將記憶體映射和其他進程共用,所有映射了同一檔案的進程,都能夠看到其中一個所做的更改;
prot:mmap.PROT_READ, mmap.PROT_WRITE 和 mmap.PROT_WRITE | mmap.PROT_READ。最後一者的含義是同時可讀可寫。

access:在mmap中有選擇性參數access的值有

ACCESS_READ:讀訪問。

ACCESS_WRITE:寫訪問,預設。

ACCESS_COPY:拷貝訪問,不會把更改寫入到檔案,使用flush把更改寫到

  fileno參數需要指定為系統“/dev/mem”的檔案描述符,可以通過open()函數和fileno()方法得到,flags、prot、access參數指定為讀寫訪問即可。

  length和offset參數比較重要,首先是offset參數,這個參數指示從哪個記憶體位址開始映射,注意,這個數值必須是頁大小的整數倍,在OrangePi Zero中,頁大小為4096位元組。根據datasheet,GPIO的記憶體位址是從0x01C200800開始,但這個值並不是頁大小的整數倍,所以只能往前截取,最近一個頁大小整數倍的地址是0x01C200000,offset就是要設定為這個值。

  那還有0x0800的位移量怎麼辦呢,這個就通過length參數來設定了,length參數指定了從這個offset開始,映射多少位元組的實體記憶體到使用者程式的記憶體空間,顯然,這個length必須足夠長把整個gpio模組的寄存器地址全部映射了,才能在使用者程式裡正常訪問GPIO寄存器,這裡設定為兩個頁大小,即8192位元組(0x01C20000 ~ 0x01C21FFF),從datasheet看,這個地址空間包含了CCU、PIO、TIMER、OWA、PWM、KEYADC模組的所有寄存器。

  映射之後,可以得到一個mmap類的對象,使用這個對象,我們可以像操作檔案一樣對寄存器進行讀寫操作。在以下模組的代碼中,實現了兩個方法:讀寄存器和寫寄存器。注意在操作寄存器的過程中,有一點必須注意,每次讀寫寄存器必須四位元組對齊,即一次讀取或寫入4個位元組(所有寄存器都是32位),讀寫的寄存器地址也必須是4的倍數,否則會操作失敗,板子會死機。

import mmapimport structclass GPIO:    #-------------------------------------------------------------------------------------##定義GPIO相對0x01C20000的位移地址    PIO_ADDR_OFFSET = 0x0800    #定義GPIOA的寄存器相對0x01C20000的位移地址#作者唯寫了GPIOA的寄存器定義,如果需要使用其他IO,請參考datasheet在下面增加定義    PIO_PA_CFG0_REG = PIO_ADDR_OFFSET + 0x00    PIO_PA_CFG1_REG = PIO_ADDR_OFFSET + 0x04    PIO_PA_CFG2_REG = PIO_ADDR_OFFSET + 0x08    PIO_PA_CFG3_REG = PIO_ADDR_OFFSET + 0x0C    PIO_PA_DATA_REG = PIO_ADDR_OFFSET + 0x10    PIO_PA_DRV0_REG = PIO_ADDR_OFFSET + 0x14    PIO_PA_DRV1_REG = PIO_ADDR_OFFSET + 0x18    PIO_PA_PUL0_REG = PIO_ADDR_OFFSET + 0x1C    PIO_PA_PUL1_REG = PIO_ADDR_OFFSET + 0x20#-------------------------------------------------------------------------------------#    #以下是建構函式和解構函式    def __init__(self):        self.m_mmap = None        self.fd = None    def __del__(self):        if(self.m_mmap != None):            self.m_mmap.close()        if(self.fd != None):            self.fd.close()    #-------------------------------------------------------------------------------------#    #以下是成員函數    def Init(self):        """        GPIO初始化函數        函數會開啟/dev/mem檔案,並映射從0x01C20000地址開始,共8192位元組長度(2頁)的記憶體空間到使用者的虛擬位址        傳回值:無        """        START_ADDR = 0x01C20000        self.fd = open("/dev/mem", "rb+")        self.m_mmap = mmap.mmap(self.fd.fileno(), 4096 * 2, mmap.MAP_SHARED, mmap.PROT_WRITE | mmap.PROT_READ, mmap.ACCESS_WRITE, START_ADDR)        assert self.m_mmap != None,"Init Fails"    def ReadReg(self,reg_addr):        """        讀取一個寄存器的值        reg_addr:要讀取的寄存器地址(必須為4的倍數),且範圍在2個pagesize內,即小於8192        傳回值:寄存器的值(4位元組)        """        assert self.m_mmap != None,"Init Fails"        assert reg_addr%4 == 0,"reg_addr must be mutiple of 4"        assert 0<=reg_addr<=8192,"reg_addr must be less than 8192,which is 2 pagesize"                self.m_mmap.seek(reg_addr)        ReadBytes = self.m_mmap.read(4)        return struct.unpack(‘L‘,ReadBytes)[0]    def WriteReg(self,reg_addr,value):        """        寫一個寄存器的值        reg_addr:要寫入的寄存器地址(必須為4的倍數),且範圍在2個pagesize內,即小於8192        value:要寫入的值,整形,一次寫入四個位元組長度的整數,即0xffffffff        傳回值:無        """             assert self.m_mmap != None,"Init Fails"        assert reg_addr%4 == 0,"reg_addr must be mutiple of 4"        assert 0<=reg_addr<=8192,"reg_addr must be less than 8192,which is 2 pagesize"        assert 0<=value<=0xFFFFFFFF,"value must be less than 0xFFFFFFFF,which is 4 bytes"                self.m_mmap.seek(reg_addr)        BytesToWrite = struct.pack(‘L‘,value)        self.m_mmap.write(BytesToWrite)        return

  要使用這個模組,只需要把這個模組的py檔案放在使用者程式同一個目錄下,直接匯入即可,以下是令PA17(紅色LED)閃爍的範例。

  說明:

  1、OPiZero_GPIO是上面定義的模組的檔案名稱,直接匯入使用即可。

  2、PIO_PA_CFG2_REG寄存器的第4~第7位為Pin17的模式配置,配置為001即輸出模式

  3、PIO_PA_DATA_REG寄存器的第17位為Pin17的高底電平輸出控制,這裡採用了一個巧妙的方法,讀取PIO_PA_DATA_REG的值與0x00020000按位異或即可實現第17位的取反。

  4、切勿直接往寄存器裡寫入資料,因為PA口有很多IO用作板子內部使用,直接寫入的話很容易導致其他IO口邏輯輸出錯誤,導致板子死機,作者已親身體驗n次,務必使用讀-修改-寫的模式修改寄存器的值。

import OPiZero_GPIOimport time#以下為主程式GPIO = OPiZero_GPIO.GPIO()GPIO.Init();#PA17配置為輸出模式GPIO.WriteReg(GPIO.PIO_PA_CFG2_REG,GPIO.ReadReg(GPIO.PIO_PA_CFG2_REG) | 0x00000010)while(1):    GPIO.WriteReg(GPIO.PIO_PA_DATA_REG,GPIO.ReadReg(GPIO.PIO_PA_DATA_REG) ^ 0x00020000)    time.sleep(0.3)

實際效果如下:   

  最後把源碼附上:

  https://files.cnblogs.com/files/qzrzq1/OPiZero_GPIO.zip

  https://pan.baidu.com/s/1yiely1q_4LPZ4Bs8gyKDGg

 

python環境下實現OrangePi Zero寄存器訪問及GPIO控制

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.