В реальных проектах часто нужно, чтобы пин работал и как вход, и как выход в разное время. Например, для протокола 1-Wire (DS18B20) или для динамического управления LED-матрицей.
Проблема стандартного подхода:
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
// …
pinMode(pin, INPUT);
int val = digitalRead(pin);
Переключение через pinMode() занимает ~5 мкс — медленно для быстрых протоколов.
Хак: Используем ассемблер для мгновенного переключения
#define __SFR_OFFSET 0x00
#include <avr/io.h>
; Макрос для настройки пина как выход
.macro SET_PIN_AS_OUTPUT port, bit
SBI \port, \bit
.endm
; Макрос для настройки пина как вход
.macro SET_PIN_AS_INPUT port, bit
CBI \port, \bit
.endm
; Макрос для установки состояния HIGH
.macro SET_PIN_HIGH port, bit
SBI \port, \bit
.endm
; Макрос для установки состояния LOW
.macro SET_PIN_LOW port, bit
CBI \port, \bit
.endm
; Макрос для чтения пина (результат в R16)
.macro READ_PIN port, bit
SBIC \port, \bit
LDI R16, 1
SBIS \port, \bit
LDI R16, 0
.endm
; Пример: управление пином 7 (PD7) как двунаправленным
.global pin_toggle
pin_toggle:
; Настраиваем пин 7 как выход на 1 такт
SBI DDRD, 7 ; DDRD = 0x80 (пин 7 как выход)
SBI PORTD, 7 ; Подаем HIGH
CALL _delay_10us
; Переключаем на вход и читаем
CBI DDRD, 7 ; DDRD = 0x00 (пин 7 как вход)
SBIC PIND, 7 ; Читаем пин 7
LDI R16, 1
SBIS PIND, 7
LDI R16, 0
RET
Полный код для 1-Wire протокола (чтение DS18B20):
include
include
include
define ONE_WIRE_PIN PD2 // Пин 2
// Функция для отправки бита в 1-Wire
void ow_write_bit(uint8_t bit) {
// Настраиваем как выход
DDRD |= (1 << ONE_WIRE_PIN);
// Начинаем с LOW
PORTD &= ~(1 << ONE_WIRE_PIN);
_delay_us(2);
if (bit) {
PORTD |= (1 << ONE_WIRE_PIN);
_delay_us(60);
} else {
_delay_us(60);
PORTD |= (1 << ONE_WIRE_PIN);
_delay_us(2);
}
}
// Функция для чтения бита из 1-Wire
uint8_t ow_read_bit() {
uint8_t bit;
// Настраиваем как выход и ставим LOW
DDRD |= (1 << ONE_WIRE_PIN);
PORTD &= ~(1 << ONE_WIRE_PIN);
_delay_us(2);
// Переключаем на вход
DDRD &= ~(1 << ONE_WIRE_PIN);
_delay_us(2);
// Читаем состояние
if (PIND & (1 << ONE_WIRE_PIN)) {
bit = 1;
} else {
bit = 0;
}
_delay_us(60);
return bit;
}
// Отправка байта в 1-Wire
void ow_write_byte(uint8_t data) {
for (uint8_t i = 0; i < 8; i++) { ow_write_bit(data & 0x01); data >>= 1;
}
}
// Чтение байта из 1-Wire
uint8_t ow_read_byte() {
uint8_t data = 0;
for (uint8_t i = 0; i < 8; i++) {
data |= (ow_read_bit() << i);
}
return data;
}