Функция digitalWrite(pin, val)
Arduino работает, сначала извлекая адрес памяти регистра данных порта для соответствующего 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)
преобразует номер пина Arduino в число, которое идентифицирует порт ATmega (PB / PC / PD) с использованием массива, хранящегося в SRAM:
// 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
?)