Часть 21: Двунаправленные порты — читаем и пишем на одних пинах

В реальных проектах часто нужно, чтобы пин работал и как вход, и как выход в разное время. Например, для протокола 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;
}

Вам также может понравиться

About the Author: ардуинчиков

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *