Arduino的digitalWrite(pin, val)
函数的工作方式是:首先为相应的pin
检索端口数据寄存器的内存地址,然后修改该地址的值。这是wiring_digital.c
的实际实现:
void digitalWrite(uint8_t pin, uint8_t val)
{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *out;
if (port == NOT_A_PIN) return;
// If the pin that support PWM output, we need to turn it off
// before doing a digital write.
if (timer != NOT_ON_TIMER) turnOffPWM(timer);
out = portOutputRegister(port);
uint8_t oldSREG = SREG;
cli();
if (val == LOW) {
*out &= ~bit;
} else {
*out |= bit;
}
SREG = oldSREG;
}
首先,digitalPinToPort(pin)
使用存储在SRAM中的数组将Arduino的引脚号转换为标识ATmega的端口(PB / PC / PD)的数字:
// Arduino.h
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
// pins_arduino.h
const uint8_t PROGMEM digital_pin_to_port_PGM[] = {
PD, /* 0 */
PD,
PD,
PD,
PD,
PD,
PD,
PD,
PB, /* 8 */
PB,
PB,
PB,
PB,
PB,
PC, /* 14 */
PC,
PC,
PC,
PC,
PC,
};
然后,此端口号用于获取实际端口数据寄存器的内存地址。端口数据寄存器的地址存储在SRAM中,并可以通过portOutputRegister(port)
宏进行访问:
// Arduino.h
#define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )
// pins_arduino.h
const uint16_t PROGMEM port_to_output_PGM[] = {
NOT_A_PORT,
NOT_A_PORT,
(uint16_t) &PORTB,
(uint16_t) &PORTC,
(uint16_t) &PORTD,
};
此地址设置为局部变量out
。为什么不声明volatile
却声明oldSREG
?它们都是寄存器。
(另一个问题是,为什么port_to_output_PGM
数组是uint16_t
而不是uint8_t
?)
答案 0 :(得分:0)
volatile修饰符基本上表示可以在外部更改此变量。它迫使编译器(尤其是优化器)不优化对该变量的访问。
因此,如果您存储了SREG的内容就可以将其恢复到以前的状态,则不需要此操作,但是您绝对不希望PINx寄存器的值错误,因为编译器会因为丢失而优化了实际的读取挥发性修饰符。
例如:
while (PIND & _BV(PD2)); // wait for the button press (active low)
在PIND不稳定的情况下无法正常工作。它将被读取一次,再也不会读取。