[英]Programming AVR in C
我正在使用Atmega328。 我的輸出引腳沿引腳排列“隨機”分布,即它們不屬於同一端口。 例如,我的輸出引腳可以是PB0,PB4,PC1,PC3和PD1。
我一直使用Assembly對微控制器進行編程,因此這是我第一次使用C。我想知道的是,是否有一種避免在每個引腳上使用DDRx和Px進行設置或清除的方法。
例如,我想使用類似的方法來設置端口B的第一位:
#define NAME_1 DDRB,0
sbi NAME_1;
這可能嗎?
編輯:
也許我沒有表達清楚自己。 我想要的是能夠使用一些有意義的名稱引用某些I / O端口引腳。 例如,將PD3命名為“ blue_LED”,以便使代碼更具可讀性,並且如果稍后更改藍色LED的位置,則可以輕松修改代碼。 換句話說,我希望能夠打開和關閉某些引腳而其名稱未經過硬編碼。 有辦法嗎?
sbi
指令的特殊之處在於它可以直接在AVR平台的I / O端口中進行操作。 使用I / O端口的正常過程是,您必須使用其他指令(例如out
)在I / O端口和寄存器之間復制整個字。
就是說,C中沒有sbi
只是不了解某個特定平台的這些特殊功能。 對於您給出的程序集,您將使用C編寫:
DDRB |= 1<<0;
我個人認為這看起來很簡潔,但是您當然可以定義一個宏
#define sbi(x,b) (x) |= 1<<(b)
sbi(DDRB, 0);
(其中b
是“ 位數 ”),也許反過來
#define cbi(x,b) (x) &= ~(1<<(b))
cbi(DDRB, 0)
這可以工作,但是我建議 不要 使用它 。 而第一個符號DDRB |= 1<<0;
對於任何C程序員來說都是顯而易見的,使用這樣的宏可能並非如此。
最后一點,如果你關心性能:我沒有驗證這一點,但我敢肯定, avr-gcc
是足夠聰明,發出sbi
和cbi
指令時的I / O端口位掩碼操作只是有效改變一點。 編輯 :見JLH的答案的實驗結果是gcc-avr
的確是夠聰明,發出這些sbi
/ cbi
指令。
我使用avg-gcc來編程Atmel AVR,並且支持包絕對可以完全了解cbi,sbi以及與此相關的所有說明。 因此,除非您願意,否則無需求助於組裝。 這是一些反匯編的C來證明這一點。
PORTD |= (1 << PD0);
d8: 58 9a sbi 0x0b, 0 ; 11
現在,我將向您展示如何。 您可以將每個端口的每一位設置為非常容易地進行輸入或輸出。
DDRA |= (1<<PA0);
如圖所示,使端口A上的引腳0成為輸出:
bit76543210
00000001 // or-ing the 1 adds 1 leaving the other bits alone.
要同時使引腳A3成為輸出,請執行以下操作:
DDRA |= (1<<PA3);
bit76543210
00001001 // now the DDRA register looks like this, pins 0 and 3 set as outputs
乏味吧? 好吧,這些右側表達式的計算結果為常量,因此您可以將它們組合成一條語句:
DDRA |= (1<<PA0) | (1<<PA3);
然后編譯器會將右側折疊為一個常量,並將其|=
放入寄存器中。 因此,每個端口實際上只需要一個這樣的語句,編譯器使其效率很高。
這要注意方向-輸入或輸出。 接下來是設置和清除輸出。
設置輸出:
PORTD |= (1<<PD); // turns pin 0 on port D on (high) as an output.
PORTD &= ~(1<<PD); // turns pin 0 on port D off (low) as an output.
PORTD ^= (1<<PD); // toggles it (high-> low) or (low->high as an output.
編輯:
當然,對於您為使用有意義的名稱而命名的額外要求,您也可以這樣做。 我通常這樣做是為了避免必須記住什么連接了什么。 例如,來自同一項目:
#define LED_INDICATOR_PIN PA0
#define LED_INDICATOR_PORT PORTA
#define LED_INDICATOR_DDR DDRA
我在代碼中指的是“友好”名稱:
void initialize(void)
{
// Set up an output pin to blink an LED to show sign of life.
LED_INDICATOR_DDR |= (1 << LED_INDICATOR_PIN);
// Set up TTY port.
init_uart();
}
void toggleLED(void)
{
LED_INDICATOR_PORT ^= (1 << LED_INDICATOR_PIN);
}
更容易閱讀。 那是你追求的嗎? 我知道這是我每次所做的。
使用C ++,我寫的是這樣的:
//IO = I/O Subsystem
inline void Io_LEDred_Configure() {DDRB |= 1<<0; } //Port B0
inline void Io_LEDred_On() {PORTB |= 1<<0; }
inline void Io_LEDred_Off() {PORTB &= ~(1<<0);}
inline void Io_LEDgreen_Configure() {DDRD |= 1<<3; } //Port D3
inline void Io_LEDgreen_On() {PORTD |= 1<<3; }
inline void Io_LEDgreen_Off() {PORTD &= ~(1<<3);}
如果您必須將IO切換到其他端口,則只需更改這三行即可。
在C ++中,編譯器會准確地發出您作為匯編編碼器編寫的代碼。 在C語言中,您必須使用常規函數。 但是這些電話會帶來一些開銷。 如果要避免這種情況,則必須使用宏:
#define IO_LEDRED_CONFIGURE DDRB |= 1<<0
#define IO_LEDRED_ON PORTB |= 1<<0
...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.