對Arduino設為輸出(OUTPUT)的引腳進行寫操作(digitalWrite)的作用是什麼?

想通過按鍵開關得到一個信號,然後控制一些別的東西

用的一段代碼的片段:

pinMode(8,INPUT);
digitalWrite(8,HIGH);

問題1:我把8號引腳定義為輸入引腳了,然後再把他定義為高電平,

總覺得我不是讓它輸入了嗎,再digitalWrite不就輸出了又,總覺得有點奇怪啊,

這個digitalWrite電氣上是怎樣的一個過程?

問題2:開關一頭接地,一頭接8號引腳,開關一按,8號引腳成功讀出了低電平;

如果反過來,8號引腳digitalWrite為LOW,然後通過開關給8號接入個高電平,

8號又成功讀出了高電平。。。

為什麼這個輸入引腳本來自己是有電壓的(或高或低),卻又被輸入信號所決定?

貌似和問題一類似。read和write是怎麼個過程?


實名反對@李赧郎 和某匿名用戶拿STM32來講解Arduino。STM32的IO口結構跟Arduino的AVR還是有區別的。根據STM32的數據手冊來學習AVR會造成誤導。

首先要明確IO口的幾種常見模式。對於數字IO而言,常見的模式有:推挽輸出、開漏輸出、浮空輸入(高阻輸入)、上拉輸入、下拉輸入。STM32支持上述的所有模式,但很多單片機並不是支持上述所有模式。比如Arduino採用的AVR單片機就不支持開漏輸出和下拉輸入。

第二要明確「輸入阻抗」和「輸出阻抗」的概念。例如當IO口輸出一個高電平時,IO口內部並非像機械開關那樣把一根線直接插到了電源正極上,IO引腳和電源正極之間其實還有電阻的存在。當這個電阻很小的時候,我們稱之為「強」,當這個電阻較大時,我們稱之為「弱」。這個電阻的一端當然接的是IO引腳,另一端如果接到電源正極,則成為上拉電阻,如果接地,則稱為下拉電阻。

先說輸出模式。對於Arduino,用pinMode將IO口設為OUTPUT的時候,其實IO的狀態為「強推挽」,也就是說設為高電平時,IO口對電源正極的電阻比較小(強上拉),設為低電平時IO口對地的電阻也比較小(強下拉),這樣IO口就具備了較強的驅動能力。其實也沒有強到哪裡去,大概幾十毫安,能點亮LED而已。這裡順便提一下常見的51單片機,它的IO口總是接通了一個上拉電阻,這個上電阻比較大,所以稱為弱上拉,所以51單片機的拉電流驅動能力(IO引腳高電平時電流從IO引腳流向外部電路的能力)比較弱,大概只有100μA左右,這通常只能讓LED發出很微弱的光,所以51單片機IO口點亮LED的方式通常為灌電流(電流從外部電路流入IO引腳)。

再說輸入模式。對於Arduino,用pinMode將IO口設為INPUT的時候,其實IO的狀態為浮空輸入,浮空輸入也稱高阻輸入,也就是說輸入阻抗非常高。理想狀態下,可以認為輸入阻抗是無窮大的,大到就像這個引腳斷路了一樣。就像一個浮在空中的金屬絲一樣,沒有連上任何電路,你讓它的電壓是多少,它的電壓就是多少。這樣做是有意義的,因為只有輸入阻抗足夠大,才能接收到微弱的信號。如果輸入阻抗不夠大,比如輸入端跟地之間有一個1kΩ的電阻,那微弱的輸入信號很可能就被直接拉到0V,檢測不出來了。但是浮空輸入並非在任何情況下都是最好的選擇,比如題主在接開關時。開關一頭接IO口,一頭接地。按下時把IO口拉到0V,讀取,低電平,完全符合預期,贊!但是如果開關沒有按下,讀取,IO這時實際上沒有接到任何地方,處於浮空狀態,IO口上其實沒有電壓,或者說可能是任何的電壓,這時讀取IO口就會發現讀到的值是不一定的。有時高有時低,用手摸一下那個引腳,都會使得讀到的值抖動,傻眼了,咋辦?

這時就需要上拉電阻來救場了,準確地說是弱上拉。Arduino的AVR單片機內置了上拉電阻,只要通過程序打開就可以,開啟上拉電阻後,IO口會通過一個比較大的電阻(比如100kΩ)接到電源正極,儘管是比較大的電阻,但這個電阻仍然遠遠小於IO口浮空的輸入阻抗,所以這個電阻就可以在IO口沒有外部輸入時把IO口的電平可靠維持在一個比較高的水平,讀出的也都是高電平了。

這個時候就可以回答題主的問題了。

問題1問的是為什麼可以對一個輸入引腳執行write操作。答案就是,當對一個已經設定為輸入狀態的IO口digitalWrite為HIGH時,就會啟動這個引腳的上拉電阻。沒有為什麼,Arduino在實現digitalWrite這個函數時就是這麼寫的。很扯淡對吧?這其實是一個歷史遺留問題,Arduino在早期的版本中並未封裝對上拉電阻的操作,在那個時候,你的這種開關電路只能在外部連接一個上拉電阻。在Arduino 1.0之後,才在其庫中支持了上拉電阻。其實更好的方法是修改pinMode函數的實現,將INPUT分為INPUT_FLOATING和INPUT_PULLUP。但這就意味著之前所有玩家們開發的代碼都需要修改後才能運行,於是Arduino就想出了這麼一個歪招,用digitalWrite實現了上拉。需要注意的是,AVR不具備內置的下拉電阻,所以對已經設為INPUT的引腳digitalWrite為LOW,是沒有任何效果的。

問題2是為什麼可以對一個已經有電壓的引腳再接入別的電壓,從而改變其電壓,而不會造成短路。答案就是4個字——「輸出阻抗」。剛才說了在輸入引腳上的上拉電阻是個弱上拉,所以當你把弱上拉到高電平的引腳接地時,就相當於把電源正極和地之間連接了一個很大的電阻(例如100kΩ),假設弱上拉電阻是100kΩ,根據歐姆定律,只有50μA的電流從中通過,完全不會帶來傷害。但是如果你把設為輸出狀態的引腳設為高電平後再接地,情況就很不一樣了。由於輸出高電平時是強上拉的,直接接地的電流可能在50mA以上,超過了輸入狀態接地電流的1000倍。這其實就是所謂的短路了,所以一定不要這樣做,一個引腳長時間短路是可能造成這個引腳甚至整個單片機損壞的。

其實我認為這個題目並不需要貼圖解釋,因為Arduino本身的設計初衷就是避免用戶接觸IO口內部結構這種底層細節。但是為了糾正其他知友的錯誤,我還是從真正的AVR數據手冊里截了一張簡化版的IO口等效電路圖,其中的Rpu就是那個傳說中的上拉電阻。

說完了,可以點贊了。


我拿STM32的管腳結構圖改改示意一下,都差不多的。

能看到輸入和輸出是2條線,我打紅圈的就是你模式控制的開光,上下開光是互斥的,一個打開另一個就關閉。

所以當你選擇輸入時,上面的線路就打開,外面的信號就可以通過TTL肖特基觸發器進入單片機內部的輸入數據寄存器,然後被你的程序判斷是高還是低。

此時,你寫digitalWrite(8,HIGH);,只是把一個高電壓寫到了輸出數據寄存器,因為輸出控制是關閉的,信號沒有輸出出去,所以不會和輸入信號衝突。

另一種情況,如果管腳是輸出模式,圖中的P-mos或N-mos就要打開了,這時你如果強制管腳到一個不對的電壓上是會損壞管腳的。比如管腳輸出低,但被強行接到電源上。所以輸入腳都一般穿個電阻來限流。

玩arduino的還是要老老實實的看單片機手冊、學習硬體原理的,不看手冊、不看原理上來就用自然會有問題。


arduino設置成input之後在設置成高相當於上拉電阻


從效果角度

pinMode(8,INPUT);

digitalWrite(8,HIGH);

和 pinMode(8,INPUT_PULLUP);

效果完全一樣。

詳情可見 arduino安裝目錄下arduino-1.0.5-r2hardwarearduinocoresarduinowiring_digital.c

其他參考況琪的答案。


個人認為多讀Arduino Language Reference很有幫助。

digitalWrite() https://www.arduino.cc/en/Reference/digitalWrite

Description

Write a HIGH or a LOW value to a digital pin.

If the pin has been configured as an OUTPUT with pinMode(), its voltage will be set to the corresponding value: 5V (or 3.3V on 3.3V boards) for HIGH, 0V (ground) for LOW.

如果引腳被設置為INPUT,digitalWrite()會激活輸入引腳的上拉電阻。

If the pin is configured as an INPUT, digitalWrite() will enable (HIGH) or disable (LOW) the internal pullup on the input pin. It is recommended to set the pinMode() to INPUT_PULLUP to enable the internal pull-up resistor. See the digital pins tutorial for more information.

激活上拉電阻的重要應用之一便是檢測開關是否按下。開關兩端接pin和GND。當開關閉合時,digitalRead()==LOW。當開關斷開時,無電流通過上拉電阻,無壓降,digitalRead()==HIGH。

原理圖如圖片下半部分所示,debouncing capacitor用於減少虛碰造成的波動。因為ATmega晶元內置上拉電阻,實際接線按照上半部分接即可,省時省力。

為什麼要上拉呢?如果不上拉,開關斷開時pin處於懸空狀態(i.e. tristate)。digitalRead()無法準確反映開關的狀態。

如果沒有將pinMode() 設置為OUTPUT,然後將一個LED連接至pin。當調用digitalWrite(HIGH)時,LED會很暗淡。因為如果沒有明確的設置pinMode(),digitalWrite()將默認啟用內部上拉電阻,這個電阻有很強的限流作用。

NOTE: If you do not set the pinMode() to OUTPUT, and connect an LED to a pin, when calling digitalWrite(HIGH), the LED may appear dim. Without explicitly setting pinMode(), digitalWrite() will have enabled the internal pull-up resistor, which acts like a large current-limiting resistor.

ATmega General Digital I/O 原理圖

附上期末複習時整理的筆記,暫且不翻譯了。

PORTx

  • A register that stores data that is available at the physical pin. Stores data on WPx rising edge.
  • This register can be both written to and read from. Outputs to the data bus if RRx is enabled (you can use it to check what is already there)
  • The register (all 8bits) are referred to as PORTx if referring to a particular bit you refer to it as PORTxn where x is the register, n is the bit.
  • AVR uses the most significant bit as the highest bit (0x80 is the MSB, 0x01 is the LSB)

DDRx

  • Data Direction Register x
  • A register that stores whether the output register (PORTx) is available on the physical pin. Stored when there is a rising edge on WDx
  • Connected to the tri-state buffer at the output of PORTx.
  • This register can be written and readfrom (you can use it to check what is already there). Available on data bus when RDx is enabled

Metastability

  • If the clock edge and the data edge occurs at the same time then a race condition occurs.
  • It will remain in this state until noise knocks it out of that state.
  • The time it is in the metastable state varies but takes on a probabilistic curve.

PINx

  • Diodes ensure that voltage on the pin are not 1 diode drop higher than VCC or 1 diode drop lower than GND
  • Rpu is between 20k and 50k ohms

What is the main use of the 2 stage synchronizer connected to each I/O pin?

  • Two registers. If metastability occurs, it occurs at the first register.
  • By the time it arrives at the next register PINx, it should be stable.
  • There is a two I/O clock cycle delay before data arrives at the data bus.
  • When RPx is enabled the state at PINx is available on the bus.


四張圖的順序分別是:官方文檔的原圖.我畫的簡圖.問題1答案,問題2答案.

實際上那兩句函數就是分別操作了兩組寄存器中對應的位

熟練了可以直接寫寄存器,假如操作的埠是PB2;

問題一的寫法

DDRB = ~(1&<&<2);

PORTB |= (1&<&<2);

問題二的寫法

DDRB = ~(1&<&<2);

PORTB = ~(1&<&<2);

讀取PB2電平

a = (PINB&>&>2)1; // a = 1 or 0

比調函數快10倍以上.不過,很明顯,標準用法業餘的看起來反而費勁,


個人認為 Arduino中的 digitalWrite( ) 這個詞容易讓人誤解,誤以為只能用於 OUTPUT , 因為在我們的思維定勢中, Write 總是與 Output 對應 , Read 總是與 Input 對應, 所以我每次看到 digitalWrite( ) 時, 都自動腦補成 digitalSet( )


手機黨,盜圖一下,你先設置為輸入模式,再寫write,等於圖的上拉電阻的開關合上,使整個輸入變為 上拉輸入。但他是一個弱上拉,即上拉電阻的阻抗很大,所以IO口外面接一個下拉電阻再接地後,是依然能檢測到低電平的。比如IO內部的上拉電阻10M,外接開關的下拉電阻10K,檢測到電平:3.3/(1000000+10000)*10000約=0V.

內部設為下拉輸入(你所說的LOW),再接一個高電平,同理。


推薦閱讀:

為什麼一般情況下從事軟體工作比硬體待遇高?
有沒有大學自學嵌入式系統成功的?
軟體轉行去做醫療器械軟體,怎麼樣?
嵌入式開發,從開發板到產品的過程是什麼樣的?
不是說嵌入式系統的人才大量缺口嗎,為什麼它的工資水平也不是很高?

TAG:嵌入式系統 | Arduino | 嵌入式開發 |