簡體   English   中英

用C編程AVR

[英]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是足夠聰明,發出sbicbi指令時的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM