Skip to main content

【Arduino】Control I/O pin by avr-libc

Arduino Ports and Register

心血來潮看了一下Arduino底層對pin腳的控制,整理了一下並且分為兩部分:
第一部分:Arduino內部pin腳的port以及register的編號,並且如何使用這些register對腳位進行控制
第二部分:使用最底層的avr-libc進行pinMode、digitalWrite函數的改寫練習,發現使用avr-libc可以有效降低script的大小



Arduino UNO R3版子所用的Atmega328晶片有三個8-bit的PORTs,分別為:
  • B (digital pin 8 to 13)
  • C (analog input pins)
  • D (digital pin 0 to 7)
Port B, C, D在Arduino上的相對位置與Pin腳號碼如圖1:

圖1. Arduino UNO上PortsB, C, D的相對位置[1]

所以位於PortB的Digital Pin 8可以說是PB0,位於PortC的Analog Pin 2也可以說是PC2,而在之後要介紹的AVR-GCC中使用的便是PB0、PC2這種腳位編號方式。
另外在晶片內是透過暫存器(register)來對腳位進行設定,對於Arduino這種AVR晶片來說,每個Port都受三個暫存器控制,分別是(x可以用B, C, D代替):
  • DDRx register:決定腳位是INPUT或是OUTPUT,似pinMode()
  • PORTx register:控制腳位輸出訊號為HIGH或為LOW,似digitalWrite()
  • PINx register:存有腳位輸入的訊號,似analogRead()
DDRB、DDRC、DDRD register bit所代表的腳位如下表,其中Ax表示Analog pin、Dx表示Digital pin:
表1. Port Register bit所控制的腳位對應表

PORTx、PINx與腳位的對應都可以參考表1。關於Arduino UNO Ports and Registers,在官網上也有一些說明[2]


Getting Started with AVR-GCC

接下來要實作將Arduino原本的程式碼改以AVR-GCC函數撰寫,下列程式碼為Arduino自帶的blink範例程式,讓位於Pin 13上的LED兩秒一次的頻率閃爍:

由表1可知D13是由DDRB的bit 5(PB5)來控制,所以要用(1 << 5)做為位元遮罩(bit mask),來對特定位元進行操作。DDRx暫存器內,設定1為OUTPUT,而0是代表INPUT;PORTx暫存器內,設定1為輸出HIGH,而0為LOW。改寫後的程式碼如下:

在編譯後可以發現,原本範例的程式碼的binary sketch檔案大小(如圖2)為1082位元組,而改用AVR-GCC程式碼後的binary sketch則為680位元組,減少了將近40%的大小(如圖3)。


圖2. Blink範例的binary sketch檔案大小為1082 bytes

圖3.使用AVR-GCC改寫Blink範例後的binary sketch大小為680 bytes


使用_BV()巨集修改程式

在C語言中,我們可以使用位元運算子(bit operators)處理位元的運算(bitwise operation),範例如下:

在avr-libc中有_BV()巨集,透過輸入一個數值參數便可以轉成適當的位元遮罩,另外BV為bit value的縮寫。_BV()巨集的定義為:

在使用_BV()巨集時要include <avr/io.h>這支library[6],所以上面的範例程式碼可以改寫成下面這樣:

而在同時處理多個非連續位元的運算時,使用_BV()巨集可以讓程式碼保持簡單,程式碼比較如下:

最後我們可以將avr-libc中的_BV()巨集已及_delay_ms()函數替換剛剛以AVR-GCC改寫的blink範例,讓程式碼更清楚也更輕量:


圖4. 套用avr-libc函數改寫Blink範例後的binary sketch大小為514 bytes

 



參考資料:
[1] Pighixxx. Arduino UNI Pinout Diagram.
Available from: http://forum.arduino.cc/index.php/topic,146315.0.html

[2] Arduino.cc. Port Registers.
Available from: http://www.arduino.cc/en/Reference/PortManipulation
[3] Cooper Maa. 2.1) Blink part 1.
Available from: http://coopermaa2nd.blogspot.tw/2011/04/21-blink-part-1.html
[4] arduino吧. Arduino 下使用 avr gcc.
Available from: http://tieba.baidu.com/p/1292575395
[5] Cooper Maa. _BV() 巨集介紹.
Available from: http://coopermaa2nd.blogspot.tw/2011/04/bv.html

Comments

Popular posts from this blog

淺介I2C

I 2 C 起源 內部整合電路( Inter-Integrated Circuit, I 2 C, 讀做 I-square-C )是由飛利浦半導體公司開發的一種專用介面。 I 2 C 是以最少的連接線進行硬體佈線還要有靈活擴充的特性為目標而設計,最後出現了只有以序列資料線 SDA ( Serial DAta )及序列時脈線 SCL ( Serial CLock )來進行所有通訊的 I 2 C 介面, I 2 C 允許多主( master )多僕( slave )系統,其傳輸系統內每一個裝置都有唯一的地址可供辨識。資料的寫入和讀取都是由 master 主動發起, slave 無法主動向 master 回報,除非使用中斷腳通知 master 。 I 2 C 傳輸速度有慢(小於 100Kbps )、快( 400Kbps )及高速( 3.4Mbps )三種,每一種均可向下相容。 I 2 C 電路配置 如前所述 I 2 C 為兩線式,一為時脈線 SCL ,另一條為資料線 SDA ,硬體線路如圖 1 ,兩線皆為雙向性,且都需要透過高接電阻( pull-up, 對岸說的上拉電阻)接電。平常不使用時, SCL 與 SDA 的訊號都處於高電位。為了多裝置共線的功能,裝置的 SCL 和 SDA 腳位要為 開洩極( open-drain ) 或 開集極( open-collector ) 。一旦有一個腳位的開洩極導通接地,則整條線都為低電位,這種現象稱作 wired-AND 運作 ;如同邏輯 AND 運算,需要共接的腳位都是 1 (開洩極斷路),該條線的電位才是 1 。如果沒有開洩極的腳位,可以使用具內部高接電阻的腳位,當要輸出 1 時,則設定該腳位為高接型輸入腳;而輸出為 0 時,則改設定為輸出腳並輸出 0 的值。 圖 1. I 2 C 傳輸裝置接線 [1] I 2 C 通訊協定 為使說明部分更簡潔,首先介紹幾個名詞: 位元傳輸協定 當 master 要跟 slave 溝通時,會先有個起始條件( start condition )的訊號,結束時也會送出終止條件( stop condition )訊號。起始條件訊號

【Arduino】Timers, Registers, and Fast PWM Mode

由於Arduino預設的PWM控制方法僅有500Hz(好像還有另一個),想要知道怎樣才可以調整成其他頻率,以此做記錄。 先說明Timer設定方式、PWM Mode,之後會在Arduino上實做。 如果對_BV()沒有概念,可以參考 【Arduino I/O Ports】Control under avr-libc 這篇文章。

Emart Sunny Sale: 3D Shadow QR Code

  Emart是韓國連鎖超市,近期他們發現中午12:00到下午1:00的銷售量會明顯減少,為了提高銷售量他們製作出了帶有日晷概念的3D QR code。