- PVSM.RU - https://www.pvsm.ru -

Примитивы для реализации 1-Wire master при помощи PWM и ICP на микроконтроллерах AVR AtMega

Если кто-нибудь использовал linux-драйвер шины 1-Wire на основе GPIO, то наверняка замечал, что в момент выполнения обмена данными загрузка SY% подскакивает чуть ли не до ста 100% (что вполне логично для bitbang-реализации). Конечно, можно поставить специализированный контроллер шины 1-Wire с подключением через I2C DS28E17 [1] или использовать UART [2], но… Все-таки использовать linux для приложений реального времени не лучшая идея. Пусть контроллер на нем занимается высокоуровневой логикой, а весь реалтайм можно вынести на отдельный процессор. Тем более, что задач для этого отдельного процессора в моем проекте более чем достаточно.

Нет, я не буду брать распиаренные сейчас STM32 — зачем стрелять из пушки по воробьям!? Возьмем старую добрую AtMega328P (ну или какую-нибудь Arduino [3], если так станет кому-либо легче) и соберем все на ней. Вот только делать все будем «по-взрослому», с работой в режиме разрешенных прерываний и с расчетом на последующую реализацию более высоких уровней при помощи protothreads (см. оригинал [4] и расжеванные примеры на русском языке [5]). Т.е. от bitbang-а отказываемся сразу и бесповоротно.

Также сохраним UART для будущего использования под другую периферию. Ибо он в нашем случае один, а мне бы еще протокол eBus сюда запихнуть. Ибо это тоже реалтайм.
I2C хорошо, но это еще один не самый дешевый кристалл с обвязкой, а оно нам надо? Итак, из интересного остается PWM и ICP.

Что такое PWM, я думаю, объяснять не надо. Это просто генерация импульсов заданной фазы и длительности, которая производится аппаратно (т.е. с точки зрения программы «в фоновом режиме»), но параметры которой можно изменять программно. А вот ICP достаточно интересная вещь: она позволяет аппаратно сохранять значение таймера в момент изменения уровня сигнала на определенном выводе микроконтроллера. Таким образом возможно достаточно точно получить момент возникновения события (изменения состояния вывода микроконтроллера) и/или измерить его продолжительность.

Итак, будем использовать аппаратный TIMER1 ATmega328 (таймеры 0 и 2 у меня используются для других целей). Для PWM можно использовать выводы OC1A и OC1B. Однако OC1B также используется в качестве сигнала SS, а подключать ATmega328 к целевому устройству я планировал именно по шине SPI. Это удобно с точки зрения апгрейда прошивок ATmega — делаем их при помощи целевого устройства, только один из CE-сигналов с его контроллера SPI заведем на контакт SS Atmega, а другой CE-сигнал заведем на контакт RESET. Да и с согласованием скоростей обмена возиться не нужно. Ну и в качестве еще одного довода против использования OC1B является то, что на плате Arduino MEGA 2560 [6] этот сигнал вообще не выведен во внешний мир.

Итак, для генерации PWM используем контакт OC1A. Для ICP вариантов вообще нет — вывод такого типа у этой микросхемы только один. Для режима «ACTIVE PULLUP» используем любой свободный IO port и настроим его на работу в режиме «OUT».

Так, со схемотехникой разобрались. Теперь освежим в памяти физику работы [7] шины 1-wire. В двух словах ее можно описать так:

  • Прием и передача тактируется master-устройством (т.е. нами)
  • Временной интервал между передаваемыми и/или принимаемыми битами не критичен (главное, чтобы он был не меньше минимально допустимого)
  • Критичным является длительность передаваемого импульса и длительность принимаемого (измеряемого) сигнала

Отлично, тогда выбираем режим работы Phase Correct PWM Mode (счетчик сначала считает «вверх» от значения BOTTOM до значения TOP, а после достижения значения TOP начинает считать «вниз» до значения BOTTOM). При этом значение BOTTOM фиксировано и всегда равно 0, а значение TOP может быть как одним из фиксированных значений (0xFF, 0x1FF, 0x3FF), так и значением из регистров OCR1A или ICR1. Два последних варианта нам не подходят, т.к. OCR1A мы будем использовать для формирования импульсов на выводе OC1A, а регистр ICR1 будет использоваться для измерения длительности низкого уровня на шине 1-Wire. Т.е. остается только использовать вариант с фиксированными значениями TOP, благо временной интервал между битами для нас не критичен.

В соответствии со схемой выход OC1A включен в инверсном режиме (т.е. установка на нем высокого уровня приведет к установке низкого уровня на шине 1-Wire и наоборот), будем использовать следующий режим PWM:

  • когда таймер считает «вниз» и его значение совпало со значением из регистра OCR1A, установим сигнал на выводе OC1A в высокий уровень (на шине 1-Wire будет низкий уровень).
  • когда таймер считает «вверх» и его значение совпало со значением из регистра OCR1A, установим сигнал на выводе OC1A в низкий уровень (и, возможно, начнем измерять момент перехода сигнала на выводе ICP с низкого уровня на высокий).

Примитивы для реализации 1-Wire master при помощи PWM и ICP на микроконтроллерах AVR AtMega - 1
Остальное все просто. Новое значение (длительность следующего передаваемого бита) в регистр OCR1A мы будем загружать по прерыванию TIMER1_OVF (в данном режиме оно генерируется при достижения счетчиком значения BOTTOM а активируется затем после достижения значения TOP), а по прерыванию TIMER1_ICP будем вычислять длительность нахождения шины 1-Wire в состоянии низкого уровня и, в зависимости от этого, делать вывод о принятии бита «1» или бита «0».

Таким образом, на каждую операцию приема или передачи одного бита у нас будут генерироваться одно прерывание OVF и одно прерывание ICP. Соответственно их ISR лучше бы оптимизировать, но пока пусть они будут написаны на языке C.

Ну а операцию RESET для шины 1-Wire будем выполнять, перепрограммировав TIMER1 в режим NORMAL (т.к. длительность импульса RESET и последующего возможного импульса PRESENCE достаточно большая, нам необходимо расширить «динамический диапазон» работы таймера. В режиме NORMAL он составляет значение 0xFFFF, чего вполне достаточно в нашем случае. Более того, мы его даже специально ограничим значением из регистра OCR1B, чтобы в случае отсутствия подключенных к шине 1-Wire устройств не ждать, пока таймер дойдет до 0xFFFF, а прервать операцию сразу после достижения максимального критического временного интервала).

Вручную считать параметры настройки таймера в зависимости от тактовой частоты ATmega это моветон (да и лениво проверять все граничные условия), поэтому заставим это делать preprocessor компилятора C воспользовавшись тем фактом, что у нас должна быть определенная константа F_CPU, значение которой равно используемой тактовой частоте в герцах (unsigned long, т.е. константа должна быть определена как 8000000UL либо 16000000UL, иначе preprocessor будет считать ее как int и потеряет значащие разряды).

Определяем параметры используемой платы

/* Arduino Pro Mini 1-Wire connection */
#define WIRE1_DDR       DDRB
#define WIRE1_PORT      PORTB
#define WIRE1_PIN       PINB
/* OC1A/PCINT1 */
#define WIRE1_OUT       PB1
/* (PCINT0/CLKO/ICP1) */
#define WIRE1_ICP       PB0
#define WIRE1_IN_ICP    PINB0

/* Active pullup PD7 */
#define WIRE1_PULLUP_DDR        DDRD
#define WIRE1_PULLUP_PORT       PORTD
#define WIRE1_PULLUP_OUT        PD7

/* Presence detection in idle mode: external interrupt #0 */
#define WIRE1_IDLE_PRESENCE_DETECT_IRQ           INT0_vect
#define WIRE1_IDLE_PRESENCE_DETECT_EICRA_MASK    (_BV(ISC01) | _BV(ISC00))
/* Presence detection in idle mode: low level generate IRQ */
#define WIRE1_IDLE_PRESENCE_DETECT_EICRA_ISC     0
#define WIRE1_IDLE_PRESENCE_DETECT_EIMSK_INT     INT0
#define WIRE1_IDLE_PRESENCE_DETECT_EIFR_INTF     INTF0
#define WIRE1_IDLE_PRESENCE_DETECT_DDR           DDRD
#define WIRE1_IDLE_PRESENCE_DETECT_PORT          PORTD
#define WIRE1_IDLE_PRESENCE_DETECT_PIN           PIND
#define WIRE1_IDLE_PRESENCE_DETECT_BIT           PD2


#define WIRE1_TCNT      TCNT1
#define WIRE1_TCCRA     TCCR1A
#define WIRE1_TCCRB     TCCR1B
#define WIRE1_TCCRC     TCCR1C
#define WIRE1_OCRA      OCR1A
#define WIRE1_OCRB      OCR1B
#define WIRE1_ICR       ICR1
#define WIRE1_TIFR      TIFR1
#define WIRE1_TIMSK     TIMSK1

#define WIRE1_ICP_IRQ   TIMER1_CAPT_vect
#define WIRE1_OVF_IRQ   TIMER1_OVF_vect
#define WIRE1_COMPA_IRQ TIMER1_COMPA_vect
#define WIRE1_COMPB_IRQ TIMER1_COMPB_vect

#define WIRE1_POWER_ON()                                                    
    power_timer1_enable()

Определяем временные параметры протокола 1-Wire

/* Продолжительность фаз RESET в единицах TSLOT */
#define RESET_DURATION_TSLOTS           8

/* Временные параметры обмена в мкс */
#define NORMAL_TLOWR_MIN                1
#define NORMAL_TLOWR_MAX                15
#define NORMAL_TSLOT_MIN                60
#define NORMAL_TSLOT_MAX                120
#define NORMAL_BIT1_MIN                 NORMAL_TLOWR_MIN
#define NORMAL_BIT1_MAX                 NORMAL_TLOWR_MAX
#define NORMAL_BIT0_MIN                 NORMAL_TSLOT_MIN
#define NORMAL_BIT0_MAX                 NORMAL_TSLOT_MAX
#define NORMAL_TREC_MIN                 1
#define NORMAL_RESET                    (RESET_DURATION_TSLOTS * NORMAL_TSLOT_MIN)
#define NORMAL_PRESENCE_START_MIN       15
#define NORMAL_PRESENCE_START_MAX       60
#define NORMAL_PRESENCE_MIN             60
#define NORMAL_PRESENCE_MAX             240
#define OVERDRIVE_TLOWR_MIN             1
#define OVERDRIVE_TLOWR_MAX             2
#define OVERDRIVE_TSLOT_MIN             6
#define OVERDRIVE_TSLOT_MAX             16
#define OVERDRIVE_BIT1_MIN              OVERDRIVE_TLOWR_MIN
#define OVERDRIVE_BIT1_MAX              OVERDRIVE_TLOWR_MAX
#define OVERDRIVE_BIT0_MIN              OVERDRIVE_TSLOT_MIN
#define OVERDRIVE_BIT0_MAX              OVERDRIVE_TSLOT_MAX
#define OVERDRIVE_TREC_MIN              1
#define OVERDRIVE_RESET                 (RESET_DURATION_TSLOTS * OVERDRIVE_TSLOT_MIN)
#define OVERDRIVE_PRESENCE_START_MIN    2
#define OVERDRIVE_PRESENCE_START_MAX    6
#define OVERDRIVE_PRESENCE_MIN          8
#define OVERDRIVE_PRESENCE_MAX          24

/* Максимальный требуемый период сигнала */
#define WIRE1_MAX_PERIOD                (NORMAL_TSLOT_MAX + NORMAL_TREC_MIN)

/* Минимально требуемая точность (д.б. лучше двойного минимального импульса) */
#define WIRE1_MIN_PRECISION             (OVERDRIVE_TLOWR_MAX / 2)

/* Максимально возможная продолжительность процедуры RESET */
#define WIRE1_RESET_PROCEDURE_DURATION  (RESET_DURATION_TSLOTS * NORMAL_TSLOT_MIN * 2)

Расчет параметров настройки таймера

/* Значение TOP в NORMAL MODE */
#define PWM_NORMAL_MODE_TOP             0xFFFF

/* Фиксированные значения TOP, поддерживаемые оборудованием */
#define PWM_FIXED_TOP_1                 0xFF
#define PWM_FIXED_TOP_2                 0x1FF
#define PWM_FIXED_TOP_3                 0x3FF

/* Длительность одного подсчета в мкс для заданного значения делителя */
#define PWM_PRECISION(_n)                                                   
    ((_n) * 1000000UL / F_CPU)

/* Продолжительность _cnt отсчетов в мкс */
#define PWM_DURATION(_n, _cnt)                                              
    ((_cnt) * (_n) * 1000000UL / F_CPU)

/* Длительность одного периода в мск для заданных значений делителя */
#define PWM_PERIOD(_n, _t)                                                  
    ((_t) * 2 * (_n) * 1000000UL / F_CPU)

/* Определение значения счетчика в зависимости от требуемого времени */
#define PWM_COUNT_VALUE(_n, _t)                                             
    (((F_CPU / (_n)) / 1000000UL) * (_t))

/* Определение значения OCRA в зависимости от желаемой длительности ямы */
#define PWM_BOTTOM_VALUE(_n, _t)                                            
    ((F_CPU / 2 / (_n) / 1000000UL) * (_t))


/*
 * Алгоритм выбора значения делителя и TOP:
 * 
 * A. Обязательные условия
 * - период сигнала д.б. >= WIRE1_MAX_PERIOD
 * - разрешающая способность д.б. < WIRE1_MIN_PRECISION
 * 
 * B. Желательные условия
 * - период сигнала должен быть как можно меньше (т.е. минимальный TOP)
 */
#undef PWM_TOP

/* Проверяем возможность использования делителя 8 */
#if PWM_DURATION(8, PWM_FIXED_TOP_3) >= WIRE1_MAX_PERIOD
#if PWM_PRECISION(8) < WIRE1_MIN_PRECISION
#undef PWM_TOP
#define PWM_TOP PWM_FIXED_TOP_3
#undef PWM_DIVIDER
#define PWM_DIVIDER 8 
#undef PWM_TCCRA
#define PWM_TCCRA (_BV(WGM11) | _BV(WGM10))
#undef PWM_TCCRB
#define PWM_TCCRB _BV(CS11)
#endif
#endif

#if PWM_DURATION(8, PWM_FIXED_TOP_2) >= WIRE1_MAX_PERIOD
#if PWM_PRECISION(8) < WIRE1_MIN_PRECISION
#undef PWM_TOP
#define PWM_TOP PWM_FIXED_TOP_2
#undef PWM_DIVIDER
#define PWM_DIVIDER 8 
#undef PWM_TCCRA
#define PWM_TCCRA _BV(WGM11)
#undef PWM_TCCRB
#define PWM_TCCRB _BV(CS11)
#endif
#endif

#if PWM_DURATION(8, PWM_FIXED_TOP_1) >= WIRE1_MAX_PERIOD
#if PWM_PRECISION(8) < WIRE1_MIN_PRECISION
#undef PWM_TOP
#define PWM_TOP PWM_FIXED_TOP_1
#undef PWM_DIVIDER
#define PWM_DIVIDER 8 
#undef PWM_TCCRA
#define PWM_TCCRA _BV(WGM10)
#undef PWM_TCCRB
#define PWM_TCCRB _BV(CS11)
#endif
#endif

/* Проверяем возможность использования делителя 1 */
#if PWM_DURATION(1, PWM_FIXED_TOP_3) >= WIRE1_MAX_PERIOD
#if PWM_PRECISION(1) < WIRE1_MIN_PRECISION
#undef PWM_TOP
#define PWM_TOP PWM_FIXED_TOP_3
#undef PWM_DIVIDER
#define PWM_DIVIDER 1
#undef PWM_TCCRA
#define PWM_TCCRA (_BV(WGM11) | _BV(WGM10))
#undef PWM_TCCRB
#define PWM_TCCRB _BV(CS10)
#endif
#endif

#if PWM_DURATION(1, PWM_FIXED_TOP_2) >= WIRE1_MAX_PERIOD
#if PWM_PRECISION(1) < WIRE1_MIN_PRECISION
#undef PWM_TOP
#define PWM_TOP PWM_FIXED_TOP_2
#undef PWM_DIVIDER
#define PWM_DIVIDER 1
#undef PWM_TCCRA
#define PWM_TCCRA _BV(WGM11)
#undef PWM_TCCRB
#define PWM_TCCRB _BV(CS10)
#endif
#endif

#if PWM_DURATION(1, PWM_FIXED_TOP_1) >= WIRE1_MAX_PERIOD
#if PWM_PRECISION(1) < WIRE1_MIN_PRECISION
#undef PWM_TOP
#define PWM_TOP PWM_FIXED_TOP_1
#undef PWM_DIVIDER
#define PWM_DIVIDER 1
#undef PWM_TCCRA
#define PWM_TCCRA _BV(WGM10)
#undef PWM_TCCRB
#define PWM_TCCRB _BV(CS10)
#endif
#endif

#if !defined(PWM_TOP)
#error "Can't build I/O procedure with current F_CPU value"
#endif

/*
 * Процедуру RESET мы будем делать в normal mode, но требуется чтобы
 * время достижения TOP было не меньше удвоенного значения RESET
 * 
 */
#undef PWM_RESET_TCCRB

/* Проверяем возможность использования делителя 8 */
#if PWM_PERIOD(8, PWM_NORMAL_MODE_TOP) >= WIRE1_RESET_PROCEDURE_DURATION
#undef PWM_RESET_DIVIDER
#define PWM_RESET_DIVIDER 8
#undef PWM_RESET_TCCRB
#define PWM_RESET_TCCRB _BV(CS11)
#endif

/* Проверяем возможность использования делителя 1 */
#if PWM_PERIOD(1, PWM_NORMAL_MODE_TOP) >= WIRE1_RESET_PROCEDURE_DURATION
#undef PWM_RESET_DIVIDER
#define PWM_RESET_DIVIDER 1
#undef PWM_RESET_TCCRB
#define PWM_RESET_TCCRB _BV(CS10)
#endif

#if !defined(PWM_RESET_TCCRB)
#error "Can't build reset procedure with current F_CPU value"
#endif

Определяем некоторые полезные макрокоманды

/* Остановка таймера без изменения его остальных настроек */
#define DRV_STOP_CLOCK() do {                                               
    _SFR_BYTE(WIRE1_TCCRB) &= ~(_BV(CS12) | _BV(CS11) | _BV(CS10));         
    } while(0)

/* Установка режима работы "NORMAL MODE" */
#define DRV_TIMER_NORMAL_MODE() do {                                        
    _SFR_BYTE(WIRE1_TCCRB) &= ~(_BV(WGM13) | _BV(WGM12));                   
    _SFR_BYTE(WIRE1_TCCRA) &= ~(_BV(WGM11) | _BV(WGM10));                   
    } while(0)

/* Перевод пина OCRA в состояние по умолчанию (low level) */
#define DRV_DISCONNECT_OCRA_PIN() do {                                      
    _SFR_BYTE(WIRE1_TCCRA) &= ~(_BV(COM1A1) | _BV(COM1A0)); } while(0)

/* Перевод пина OCRA в состояние low level при достижении значения OCRA */
#define DRV_MATCH_OCRA_PIN_LOW() do {                                       
    _SFR_BYTE(WIRE1_TCCRA) =                                                
        (_SFR_BYTE(WIRE1_TCCRA) & ~_BV(COM1A0)) | _BV(COM1A1); } while(0)

/* Перевод пина OCRA в состояние high level при достижении значения OCRA */
#define DRV_MATCH_OCRA_PIN_HIGH() do {                                      
    _SFR_BYTE(WIRE1_TCCRA) |= _BV(COM1A1) | _BV(COM1A0); } while(0)

/* Отлавливаем переход с высокого на низкий уровень (1 => 0) */
#define DRV_CAPTURE_FALLING_EDGE() do {                                     
    _SFR_BYTE(WIRE1_TCCRB) &= ~_BV(ICES1); } while(0)

/* Отлавливаем переход с низкого на высокий уровень (0 => 1) */
#define DRV_CAPTURE_RISING_EDGE() do {                                      
    _SFR_BYTE(WIRE1_TCCRB) |= _BV(ICES1); } while(0)

/* Возвращает != 0, если отлавливается переход с низкого уровня на высокий */
#define DRV_IS_CAPTURE_RISING()                                             
    (_SFR_BYTE(WIRE1_TCCRB) & _BV(ICES1))

/* Значение OCRA при передаче значения бита 1 в режиме normal */
#define NORMAL_BIT1_VALUE                                                   
    PWM_BOTTOM_VALUE(PWM_DIVIDER, (NORMAL_BIT1_MIN + NORMAL_BIT1_MAX) / 2)

/* Значение OCRA при передаче значения бита 1 в режиме overdrive */
#define OVERDRIVE_BIT1_VALUE                                                
    PWM_BOTTOM_VALUE(PWM_DIVIDER, (OVERDRIVE_BIT1_MIN + OVERDRIVE_BIT1_MAX) / 2)

/* Значение OCRA при передаче значения бита 0 в режиме normal */
#define NORMAL_BIT0_VALUE                                                   
    PWM_BOTTOM_VALUE(PWM_DIVIDER, (NORMAL_BIT0_MIN + NORMAL_BIT0_MAX) / 2)

/* Значение OCRA при передаче значения бита 0 в режиме overdrive */
#define OVERDRIVE_BIT0_VALUE                                                
    PWM_BOTTOM_VALUE(PWM_DIVIDER, (OVERDRIVE_BIT0_MIN + OVERDRIVE_BIT0_MAX) / 2)

/* Минамально возможная длительность бита 1 в режиме normal в тиках таймера */
#define NORMAL_BIT1_MIN_VALUE                                               
    PWM_COUNT_VALUE(PWM_DIVIDER, NORMAL_BIT1_MIN)

/* Минамально возможная длительность бита 1 в режиме overdrive в тиках таймера */
#define OVERDRIVE_BIT1_MIN_VALUE                                            
    PWM_COUNT_VALUE(PWM_DIVIDER, OVERDRIVE_BIT1_MIN)

/* Максимально возможная длительность бита 1 в режиме normal в тиках таймера */
#define NORMAL_BIT1_MAX_VALUE                                               
    PWM_COUNT_VALUE(PWM_DIVIDER, NORMAL_BIT1_MAX)

/* Максимально возможная длительность бита 1 в режиме overdrive в тиках таймера */
#define OVERDRIVE_BIT1_MAX_VALUE                                            
    PWM_COUNT_VALUE(PWM_DIVIDER, OVERDRIVE_BIT1_MAX)

/* Максимально возможная длительность бита 0 в режиме normal в тиках таймера */
#define NORMAL_BIT0_MAX_VALUE                                               
    PWM_COUNT_VALUE(PWM_DIVIDER, NORMAL_BIT0_MAX)

/* Максимально возможная длительность бита 0 в режиме overdrive в тиках таймера */
#define OVERDRIVE_BIT0_MAX_VALUE                                            
    PWM_COUNT_VALUE(PWM_DIVIDER, OVERDRIVE_BIT0_MAX)

Определяем типы данных для облегчения экономии RAM

/* Тип контекста драйвера */
typedef struct {
    /* Состояние драйвера, линии и т.п. */
    volatile uint8_t state;
    /* Параметры, зависящие от выполняемой задачи */
    union {
        /* Параметры для задачи выполнения операции RESET */
        struct {
            /* Измеренное значение presence TPDH */
            volatile uint16_t tpdhMeasure;
            /* Измеренное значение presence TPDL*/
            volatile uint16_t tpdlMeasure;
        } reset;
        /* Параметры для задачи передачи либо приема байта данных */
        struct {
            /* Значение, которое действует в compare unit при capture event */
            volatile uint16_t icpOCRA;
            /* На входе - передаваемое значение, на выходе - принятое значение */
            volatile uint8_t value;
            /* Кол-во битов, оставшихся до завершения операции */
            volatile uint8_t pending;
        } io;
    } param;
} drv_1wire_context_t;

/* state: Операция RESET завершена */
#define DRV_1WIRE_STATE_RESET_COMPLETE      0b10000000
/* state: Был обнаружен импульс PRESENCE и значения tpdhMeasure/tpdlMeasure валидны */
#define DRV_1WIRE_STATE_PRESENCE_DETECTED   0b01000000
/* state: Устройства могут работать в режиме overdrive */
#define DRV_1WIRE_STATE_OVERDRIVE_DETECTED  0b00100000
/* state: процедура обмена байтом завершена */
#define DRV_1WIRE_STATE_IO_COMPLETE         0b00010000
/* state: при выполнении обмена данными обнаружена ошибка */
#define DRV_1WIRE_STATE_IO_ERROR            0b00001000
/* state: как минимум одно устройство на шине использует parasite power */
#define DRV_1WIRE_STATE_BUS_PARASITE_POWER  0b00000100
/* state: после завершения передачи заданного кол-ва битов требуется активация pullup.
 * Имеет смысл только при выполнении операции передачи либо приема битов */
#define DRV_1WIRE_STATE_ACTIVATE_PULLUP     0b00000010
/* state: Проверка управления: установка OCA в high привела к переходу ICP в low. 
 * Имеет смысл только после завершения операции RESET. */
#define DRV_1WIRE_STATE_OCA_LOW_PASSED      0b00000010
/* state: Проверка управления: установка OCA в low привела к переходу ICP в high. 
 * Имеет смысл только после завершения операции RESET. */
#define DRV_1WIRE_STATE_OCA_HIGH_PASSED     0b00000001
/* Был обнаружен адаптер 1-wire. 
 * Имеет смысл только после завершения операции RESET. */
#define DRV_1WIRE_STATE_1WIRE_DETECTED                                           
    (DRV_1WIRE_STATE_OCA_LOW_PASSED | DRV_1WIRE_STATE_OCA_HIGH_PASSED)

/* Условие состояния, при котором после операции сброса на шине обнаружено устройство */
#define DRV_1WIRE_STATE_DEVICE_DETECTED(_s)                                 
    ((_s) & DRV_1WIRE_STATE_PRESENCE_DETECTED)

/* Условие состояния, при котором операция ввода/вывода завершилась успешно */
#define DRV_1WIRE_STATE_IO_OK(_s)                                           
    (((_s) & (DRV_1WIRE_STATE_PRESENCE_DETECTED | DRV_1WIRE_STATE_IO_ERROR)) == DRV_1WIRE_STATE_PRESENCE_DETECTED)

#define DRV_1WIRE_TXBITS(_v, _n, _p)                                        
    drv1WireStartIo((_v), (_n), (_p))

#define DRV_1WIRE_RXBITS(_n)                                                
    DRV_1WIRE_TXBITS(0xFF, (_n), 0)

/* Процедура передачи байта данных на линию */
#define DRV_1WIRE_TXBYTE(_v, _p)                                            
    DRV_1WIRE_TXBITS((_v), 8, (_p))

#define DRV_1WIRE_RXBYTE()                                                  
    DRV_1WIRE_RXBITS(8)

#define DRV_1WIRE_RESET()                                                   
    drv1WireStartReset(DRV_1WIRE_STATE() & DRV_1WIRE_STATE_OVERDRIVE_DETECTED)

#if defined(WIRE1_PULLUP_OUT)
#define DRV_1WIRE_PULLUP_ON()                                               
    drv1WirePullupOn()

#define DRV_1WIRE_PULLUP_OFF()                                              
    drv1WirePullupOff()
#else /* defined(WIRE1_PULLUP_OUT) */
#define DRV_1WIRE_PULLUP_ON()
#define DRV_1WIRE_PULLUP_OFF()
#endif /* defined(WIRE1_PULLUP_OUT) */

Исходный код реализации примитивов

/* Контекст драйвера */
drv_1wire_context_t ctx1WireDriver;

/**
 * Завершение процедуры RESET
 * 
 * @param _flags - флаги по результатам завершения
 */
__INLINE void resetOperationComplete(uint8_t _flags) {
    /* Запрещаем все прерывания, т.к. процедура завершена */
    _SFR_BYTE(WIRE1_TIMSK) = 0;
    
    /* Отмечаем достигнутое состояние */
    ctx1WireDriver.state |= _flags | DRV_1WIRE_STATE_RESET_COMPLETE;
    
    /* !? */
    DRV_STOP_CLOCK();
    
    /* Переводим пин OCA в режим low (на шине получаем high level) */
    DRV_MATCH_OCRA_PIN_LOW();
    _SFR_BYTE(WIRE1_TCCRC) |= _BV(FOC1A);
    
    /* Процедура обнаружения precence завершена */
    VOS_KERNEL_FIRE_SIGNIFICANT_EVENT(VOS_SE_REASON_1WIRE_PRESENCE_DETECTOR);
}

/**
 * Завершение процедуры обмена
 * 
 * @param _flags
 */
__INLINE void ioOperationComplete(uint8_t _flags) {
    /* Запрещаем все прерывания, т.к. процедура завершена */
    _SFR_BYTE(WIRE1_TIMSK) = 0;

    /* Отмечаем достигнутое состояние */
    ctx1WireDriver.state |= _flags | DRV_1WIRE_STATE_IO_COMPLETE;

    /* Останавливаем таймер и начинаем приведение его режима к normal */
    _SFR_BYTE(WIRE1_TCCRB) = 0;

    /* Таймер в режиме normal, мы готовы к принудительной установки OCA в low */
    _SFR_BYTE(WIRE1_TCCRA) = _BV(COM1A1);

    /* Убеждаемся в том, что сигнал OCA имеет низкий уровень */
    _SFR_BYTE(WIRE1_TCCRC) |= _BV(FOC1A);
    
    /* Процедура приема или передачи байта данных завершена */
    VOS_KERNEL_FIRE_SIGNIFICANT_EVENT(VOS_SE_REASON_1WIRE_IO_BYTE_COMPLETE);
}

/**
 * Выполнение подготовки к передаче очередного бита
 */
__INLINE __OPTIMIZE_SPEED void txBit() {
    if(ctx1WireDriver.param.io.value & 0x01) {
        /* Будет передаваться бит со значением 1 */
        _SFR_WORD(WIRE1_OCRA) = 
                (ctx1WireDriver.state & DRV_1WIRE_STATE_OVERDRIVE_DETECTED) ?
                OVERDRIVE_BIT1_VALUE : NORMAL_BIT1_VALUE;
    } else {
        /* Будет передаваться бит со значением 0 */
        _SFR_WORD(WIRE1_OCRA) = 
                (ctx1WireDriver.state & DRV_1WIRE_STATE_OVERDRIVE_DETECTED) ?
                OVERDRIVE_BIT0_VALUE : NORMAL_BIT0_VALUE;
    }
}


void drv1WireAttach() {
    /* Разрешаем питание устройства */
    WIRE1_POWER_ON();

    /* Убеждаемся в остановке таймера */
    DRV_STOP_CLOCK();

    /* Z-состояние без pullup */
    _SFR_BYTE(WIRE1_PORT) &= ~(_BV(WIRE1_OUT) | _BV(WIRE1_ICP));

    /* Переводим пин OCA в режим low (на шине получаем high level) */
    DRV_MATCH_OCRA_PIN_LOW();
    _SFR_BYTE(WIRE1_TCCRC) |= _BV(FOC1A);

    /* Set WIRE1_OUT out direction and low signal level, force ICP is in direction */
    _SFR_BYTE(WIRE1_DDR) = (_SFR_BYTE(WIRE1_DDR) & ~_BV(WIRE1_ICP)) | 
                           _BV(WIRE1_OUT);

#if defined(WIRE1_IDLE_PRESENCE_DETECT_BIT)
    /* Z-состояние без pullup */
    _SFR_BYTE(WIRE1_IDLE_PRESENCE_DETECT_PORT) &= ~_BV(WIRE1_IDLE_PRESENCE_DETECT_BIT);

    /* Убедимся в том, что пин детектирования presence в idle mode является входом */
    _SFR_BYTE(WIRE1_IDLE_PRESENCE_DETECT_DDR) &= ~_BV(WIRE1_IDLE_PRESENCE_DETECT_BIT);
#endif /* defined(WIRE1_IDLE_PRESENCE_DETECT_BIT) */

#if defined(WIRE1_PULLUP_OUT)
    /* Z-состояние без pullup */
    drv1WirePullupOff();

    /* pullup pin всегда является выходом */
    _SFR_BYTE(WIRE1_PULLUP_DDR) |= _BV(WIRE1_PULLUP_OUT);
#endif /* defined(WIRE1_PULLUP_OUT) */
    
    /* Начальное состояние драйвера */
    ctx1WireDriver.state = 0;
}

#if defined(WIRE1_PULLUP_OUT)
void drv1WirePullupOn() {
    _SFR_BYTE(WIRE1_PULLUP_PORT) |= _BV(WIRE1_PULLUP_OUT);
}

void drv1WirePullupOff() {
    _SFR_BYTE(WIRE1_PULLUP_PORT) &= ~_BV(WIRE1_PULLUP_OUT);
}
#endif /* defined(WIRE1_PULLUP_OUT) */


/**
 * Реализация процедуры "RESET"
 * 
 * @param _overdrive - используется режим OVERDRIVE ( != 0 - да)
 * 
 * Идея следующая:
 * 1. Переводим таймер в NORMAL MODE с исходным значением TCNT = 0
 * 2. Устанавливаем OCRA = 0 и при помощи FOCA принудительно выставляем
 * значение пина OCRA в HIGH (шина будет переведена в состояние LOW)
 * 3. Устанавливаем значение OCRA на длительность периода LOW
 * 4. Устанавливаем функцию сброса пина OCRA по достижению значения
 * (т.е. шина будет переведена в состояние HIGH)
 * 5. Запускаем таймер
 * 
 * По достижению таймером значения OCRA пин OCRA будет автоматом переведен
 * в состояние LOW (на шине выставится уровень high) и произойдет прерывание
 * 
 */
void drv1WireStartReset(uint8_t _overdrive) {
    /* Убеждаемся в отключении strong pullup, чтобы не жечь лишний раз MOSFET */
    DRV_1WIRE_PULLUP_OFF();

    /* Текущее состояние */
    ctx1WireDriver.state &= 
            ~(DRV_1WIRE_STATE_RESET_COMPLETE | 
              DRV_1WIRE_STATE_PRESENCE_DETECTED | 
              DRV_1WIRE_STATE_1WIRE_DETECTED);

    /* Устанавливаем длительность периода LOW, генерируемого нами */
    if(_overdrive) {
        ctx1WireDriver.param.reset.tpdhMeasure = PWM_COUNT_VALUE(PWM_RESET_DIVIDER, OVERDRIVE_RESET);
    } else {
        ctx1WireDriver.param.reset.tpdhMeasure = PWM_COUNT_VALUE(PWM_RESET_DIVIDER, NORMAL_RESET);
    }
    
    /* Запрещаем прерывания */
    VOS_KERNEL_ENTER_MODE(VOS_KERNEL_RING_HARD);
    
    /* Убеждаемся в остановке таймера */
    DRV_STOP_CLOCK();

    /* Переводим таймер в режим NORMAL MODE */
    DRV_TIMER_NORMAL_MODE();
    
    /* Инициализируем счетчик */
    _SFR_WORD(WIRE1_TCNT) = 0;
    
    /* Переводим пин OCRA в режим high (на шине получаем low level) */
    DRV_MATCH_OCRA_PIN_HIGH();
    _SFR_BYTE(WIRE1_TCCRC) |= _BV(FOC1A);
    
    /* Устанавливаем длительность периода LOW */
    _SFR_WORD(WIRE1_OCRA) = ctx1WireDriver.param.reset.tpdhMeasure;
    
    /* Устанавливаем длительность всей процедуры */
    _SFR_WORD(WIRE1_OCRB) = (ctx1WireDriver.param.reset.tpdhMeasure << 1);
    
    /* По достижению OCRA пин OCRA переведем в low (на шине станет high) */
    DRV_MATCH_OCRA_PIN_LOW();
    
    /* Убеждаемся в том, что признаки достижения значения OCRA и OCRB сброшены (производится записью 1) */
    _SFR_BYTE(WIRE1_TIFR) |= _BV(OCF1A) | _BV(OCF1B);
    
    /* Разрешаем прерывания по достижению значения OCRA и OCRB */
    _SFR_BYTE(WIRE1_TIMSK) |= _BV(OCIE1A) | _BV(OCIE1B);
    
    /* Отслеживать будем переход сигнала ICP с уровня HIGH в уровень LOW */
    DRV_CAPTURE_FALLING_EDGE();
    
    /* Запускаем таймер в работу (также разрешим noise canceller) */
    _SFR_BYTE(WIRE1_TCCRB) |= PWM_RESET_TCCRB | _BV(ICNC1);
    
    /* Было выпоолнено много команд, уровень на входной шине уже должен измениться */
    if(!(_SFR_BYTE(WIRE1_PIN) & _BV(WIRE1_ICP))) {
        /* Входной сигнал низкого уровня, так и должно быть */
        ctx1WireDriver.state |= DRV_1WIRE_STATE_OCA_LOW_PASSED;
    }
    
    /* Разрешаем прерывания */
    VOS_KERNEL_ENTER_MODE(VOS_KERNEL_RING_SOFT);
}

/**
 * Инициирование процедуры обмена байтом (прием или передача)
 * 
 * @param _value - передаваемое значение
 * @param _bits - кол-во передаваемых битов (обычно 8)
 * @param _pullup - требуется активация pullup после завершения передачи всех битов
 * 
 * Подразумевается, что вызов данной функции возможен только при остановленном
 * таймере и сигнале OCA == 0 (исходное состояние).
 */
void drv1WireStartIo(uint8_t _value, uint8_t _bits, uint8_t _pullup) {
    /* Убеждаемся в отключении strong pullup, чтобы не жечь лишний раз MOSFET */
    DRV_1WIRE_PULLUP_OFF();

    /* Состояние: процедура обмена только что начата и ошибок пока не обнаружено */
    ctx1WireDriver.state &= 
            ~(DRV_1WIRE_STATE_IO_COMPLETE | DRV_1WIRE_STATE_IO_ERROR);

    /* Признак необходимости активации pullup после завершения передачи */
    if(_pullup) {
        ctx1WireDriver.state |= DRV_1WIRE_STATE_ACTIVATE_PULLUP;
    } else {
        ctx1WireDriver.state &= ~DRV_1WIRE_STATE_ACTIVATE_PULLUP;
    }

    /* Передаваемое значение */
    ctx1WireDriver.param.io.value = _value;
    
    /* Кол-во битов, подлежащих обмену */
    ctx1WireDriver.param.io.pending = _bits;
    
    /* Запрещаем прерывания */
    VOS_KERNEL_ENTER_MODE(VOS_KERNEL_RING_HARD);

    /* Убеждаемся в том, что признаки capture и bottm reach сброшены (производится записью 1) */
    _SFR_BYTE(WIRE1_TIFR) |= _BV(ICF1) | _BV(TOV1);

    /* Нас интересуют прерывания capture и достижение bottom */
    _SFR_BYTE(WIRE1_TIMSK) |= _BV(ICIE1) | _BV(TOIE1);
    
    /* Значение OCRA при передаче первого бита */
    txBit();
    
    /* Значение счетчика выберем на 1 меньше текущего top,
     * чтобы через 1 clock значение OCRA применилось в схеме сравнения
     */
    _SFR_WORD(WIRE1_TCNT) = PWM_TOP - 1;
    
    /*
     * Режим работы:
     * Phase Correct PWM с фиксированным значением TOP,
     * установка OCA=1 при совпадении с OCRA при подсчете вниз и
     * установка OCA=0 при совпадении с OCRA при подсчете вверх
     */
    _SFR_BYTE(WIRE1_TCCRA) = PWM_TCCRA | _BV(COM1A1);
    
    /*
     * Остаток настроек:
     * WGM13 и WGM12 == 0 для Phase Correct PWM с фиксированным значением TOP
     * установка делителя частоты
     * установка Input Capture Noise Canceler
     * capture будет происходить по переходу ICP с низкого на высокий уровень
     * 
     * Таймер запущен и началась процедура передачи первого бита.
     */
    _SFR_BYTE(WIRE1_TCCRB) = PWM_TCCRB | _BV(ICNC1) | _BV(ICES1);
    
    /* Разрешаем прерывания */
    VOS_KERNEL_ENTER_MODE(VOS_KERNEL_RING_SOFT);
}

/**
 * Прерывание по достижению BOTTOM, т.к. для обмена данными мы используем режим
 * Phase Correct PWM Mode.
 * В этом режиме в точке BOTTOM мы можем загрузить значение OCRA, которое
 * будет автоматически передано на схему сравнения только после достижения
 * точки TOP.
 */
__OPTIMIZE_SPEED ISR(WIRE1_OVF_IRQ) {
    if(ctx1WireDriver.param.io.pending--) {
        /* Выталкиваем переданный бит и подготавливаем место для записи принятого */
        ctx1WireDriver.param.io.value >>= 1;

        /* Действующее в данный момент значение в compare unit */
        ctx1WireDriver.param.io.icpOCRA = _SFR_WORD(WIRE1_OCRA);
        
        /* Загружаем в OCRA значение, которое будет использоваться при передаче
         * следующего бита. Реально оно начнет использоваться только после
         * достижения очередного TOP.
         */
        if(ctx1WireDriver.param.io.pending) {
            txBit();
        } else {
            /* Передан последний бит, сдвиг делать не нужно */
        }
    } else {
        /* Процедура обмена не завершилась на capture после всех битов.
         * Это означает ошибку обмена.
         */
        ioOperationComplete(DRV_1WIRE_STATE_IO_ERROR);
    }
}

/**
 * Прерывание по capture
 */
__OPTIMIZE_SPEED ISR(WIRE1_ICP_IRQ) {
    if(ctx1WireDriver.state & DRV_1WIRE_STATE_RESET_COMPLETE) {
        /*
         * Процедура RESET завершена. Раз мы сюда все-таки попали, значит
         * идет стандартная процедура обмена данными. В этом случае мы
         * отлавливаем момент перехода сигнала ICP с уровня 0 в уровень 1.
         * Продолжительность низкого уровня (общая, включая длительность строба)
         * составляет io.icpOCRA + _SFR_WORD(WIRE1_ICR) единиц таймера.
         * 
         * Т.к. все имеющиеся у нас длительности в мкс не превышают 255,
         * будем использовать арифметику uint8_t.
         */

        /* Длительность низкого уровня входного сигнала в тиках таймера */
        uint16_t lowDuration = 
                ctx1WireDriver.param.io.icpOCRA + _SFR_WORD(WIRE1_ICR);
        
        if(ctx1WireDriver.state & DRV_1WIRE_STATE_OVERDRIVE_DETECTED) {
            /* Режим OVERDRIVE */
            if((lowDuration < OVERDRIVE_BIT1_MIN_VALUE) || 
               (lowDuration > OVERDRIVE_BIT0_MAX_VALUE)) {
                /* Не выдержаны временные параметры протокола обмена */
                ioOperationComplete(DRV_1WIRE_STATE_IO_ERROR);
                
                /* Дальнейшая работа смысла не имеет */
                return;
            }
            
            /* Выполняем декодирование бита */
            if(lowDuration < OVERDRIVE_BIT1_MAX_VALUE) {
                /* Принята нормальная лог. 1*/
                ctx1WireDriver.param.io.value |= 0x80;
            }
        } else {
            /* Нормальный режим */
            if((lowDuration < NORMAL_BIT1_MIN_VALUE) || 
               (lowDuration > NORMAL_BIT0_MAX_VALUE)) {
                /* Не выдержаны временные параметры протокола обмена */
                ioOperationComplete(DRV_1WIRE_STATE_IO_ERROR);
                
                /* Дальнейшая работа смысла не имеет */
                return;
            }
            
            /* Выполняем декодирование бита */
            if(lowDuration < NORMAL_BIT1_MAX_VALUE) {
                /* Принята нормальная лог. 1*/
                ctx1WireDriver.param.io.value |= 0x80;
            }
        }
        
        if(!ctx1WireDriver.param.io.pending) {
            /* Было передано (и, возможно, принято) все заданное кол-во битов */
            if(ctx1WireDriver.state & DRV_1WIRE_STATE_ACTIVATE_PULLUP) {
                /* После завершения обмена была запрошена активация pullup */
                DRV_1WIRE_PULLUP_ON();
            }

            /* Операция ввода/вывода завершена успешно */
            ioOperationComplete(0);
        }
    } else {
        /* Выполнение процедуры RESET.
         * Сигнал ICP только что перешел из состояния HIGH в состояние LOW.
         * Возможно это начало presence pulse.
         */
        if(DRV_IS_CAPTURE_RISING()) {
            /* ICES установлен, захват по возрастанию уровня */

            /* Продолжительность TPDL */
            ctx1WireDriver.param.reset.tpdlMeasure = _SFR_WORD(WIRE1_ICR) - ctx1WireDriver.param.reset.tpdlMeasure;

            /* Т.к. обнаружен переход на высокий уровень, то и тестирование
             * адаптера на переход на высокий уровень при снятии сигнала OCR
             * также прошла успешно.
             */
            ctx1WireDriver.state |= DRV_1WIRE_STATE_OCA_HIGH_PASSED;

            /* Операция завершена, presence обнаружен */
            resetOperationComplete(DRV_1WIRE_STATE_PRESENCE_DETECTED);
        } else {
            /* ICES сброшен, захват по спаду уровня */
            ctx1WireDriver.param.reset.tpdlMeasure = _SFR_WORD(WIRE1_ICR);

            /* Продолжительность TPDH */
            ctx1WireDriver.param.reset.tpdhMeasure = ctx1WireDriver.param.reset.tpdlMeasure - ctx1WireDriver.param.reset.tpdhMeasure;

            /* Теперь будем делать захват по возрастанию уровня сигнала */
            DRV_CAPTURE_RISING_EDGE();
        }
    }
}

/**
 * Прерывание по достижению значения OCRA
 */
__OPTIMIZE_SPEED ISR(WIRE1_COMPA_IRQ) {
    /* Значение OCRA достигнуто при выполнении процедуры RESET.
     * Это означает, что сигнал на пине OCRA только что был переведен
     * в состояние low, а сигнал на шине начал переводиться в состояние high.
     * Теперь нам надо определить интервал, через который сигнал на шине
     * будет вновь переходить из состояния high в состояние low (т.е. обнаружить
     * начало presence pulse, если таковой вообще будет).
     */

    /* Принудительно сбрасываем флаг ICF (производится записью 1) */
    _SFR_BYTE(WIRE1_TIFR) |= _BV(ICF1);

    /* Разрешаем прерывания по capture */
    _SFR_BYTE(WIRE1_TIMSK) |= _BV(ICIE1);    
}

/**
 * Прерывание по достижению значения OCRB
 * 
 * Нами оно используется только в процедуре выполнения операции RESET для
 * ограничения максимально допустимого времени ожидания сигнала PRESENCE.
 */
__OPTIMIZE_SPEED ISR(WIRE1_COMPB_IRQ) {
    /* Значение OCRB достигнуто при выполнении процедуры RESET.
     * Это означает, что сигнал presence обнаружен не был и устройства на
     * шине отсутствуют.
     * Однако мы можем проверить функционирование интерфейса 1-wire с точки
     * зрения корректной передачи сигналов.
     */

    if(_SFR_BYTE(WIRE1_PIN) & _BV(WIRE1_ICP)) {
        /* Входной сигнал высокого уровня, так и должно быть */
        ctx1WireDriver.state |= DRV_1WIRE_STATE_OCA_HIGH_PASSED;
    }
    
    /* Операция завершена, presence не обнаружен */
    resetOperationComplete(0);
}

Вот пример осциллограммы выполнения приема:

Примитивы для реализации 1-Wire master при помощи PWM и ICP на микроконтроллерах AVR AtMega - 2

Канал C подключен к выходу OC1A (импульсы одинаковой длительности), а канал B подключен к шине 1-Wire. Видно, что длительность 1-го и 3-го импульса на канале B больше, чем длительность соответствующего синхроимпульса на канале C. Т.е. slave-устройство в битах 1 и 3 передает значение «0». А длительность импульса 2 на каналах B и C приблизительно равна, что соответствует биту «1».

Так как используемый драйвер шины 1-Wire поддерживает также режим «ACTIVE PULLUP» (что также реализовано в примитивах), ниже приводятся соответствующие осциллограммы:

Примитивы для реализации 1-Wire master при помощи PWM и ICP на микроконтроллерах AVR AtMega - 3

Активация режима «ACTIVE PULLUP» после завершения передачи последнего бита команды (сигнал канала B переходит из низкого уровня в высокий, после чего в течении не больше 10мкс сигнал активации PULLUP также переходит в активное состояние, подавая через транзистор Q5 напряжение питания на сигнальную шину).

Примитивы для реализации 1-Wire master при помощи PWM и ICP на микроконтроллерах AVR AtMega - 4

Тот же режим «ACTIVE PULLUP», но в меньшем масштабе (передача команды, активация PULLUP, деактивация PULLUP и команда чтения с получением результатов).

Примитивы для реализации 1-Wire master при помощи PWM и ICP на микроконтроллерах AVR AtMega - 5

Окончание режиме «ACTIVE PULLUP» с передачей команды на чтение результатов в укрупненном виде.

Примитивы для реализации 1-Wire master при помощи PWM и ICP на микроконтроллерах AVR AtMega - 6

Процедура RESET (первый импульс низкого уровня на канале B, совпадающий по длительности с импульсом высокого уровня на канале C) с последующим PRESENCE от устройства, подключенного к шине 1-Wire (второй импульс низкого уровня на канале B, когда на канале C также присутствует низкий уровень).

Примитивы для реализации 1-Wire master при помощи PWM и ICP на микроконтроллерах AVR AtMega - 7

То же самое, но после окончания режима «ACTIVE PULLUP».

Вопрос к читателям: имеет ли смысл писать продолжение, где будет реализация команд протокола обмена 1-Wire на основе этих примитивов и библиотеки protothreads [8]? В принципе там все очень просто и пишется просто сходу, подглядывая одним глазком в соответствующий datasheet.

Список литературы

  1. Драйвер шины 1-Wire для контроллеров питанием меньше 5V [9]
  2. Четыре метода для подключения 1-Wire устройств к Raspberry Pi [10]
  3. 1-Wire Communication Through Software [11]
  4. 1-Wire-to-I2C Master Bridge [12]
  5. Single-Channel 1-Wire Master [13]
  6. Using a UART to Implement a 1-Wire Bus Master [14]
  7. А здесь на пальцах объясняется функционирование устройств 1-Wire (на русском языке) [15]
  8. Protothreads by Adam Dunkels [16]
  9. Protothread и кооперативная многозадачность [17]
  10. ATmega328/P datasheet [18]
  11. Arduino PRO mini [19]
  12. Arduino MEGA 2560 [20]

Автор: Vedga

Источник [21]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/arduino/246766

Ссылки в тексте:

[1] DS28E17: #i2c1wire

[2] UART: #uart1wire

[3] Arduino: #megapromini

[4] оригинал: #protothreads_eng

[5] расжеванные примеры на русском языке: #protothreads_rus

[6] Arduino MEGA 2560: #mega2560

[7] физику работы: #microsin

[8] protothreads: http://protothreads_rus

[9] Драйвер шины 1-Wire для контроллеров питанием меньше 5V: https://geektimes.ru/post/286374/

[10] Четыре метода для подключения 1-Wire устройств к Raspberry Pi: http://blog.gegg.us/2013/03/4-different-methods-of-1-wire-access-on-raspberry-pi/

[11] 1-Wire Communication Through Software: https://www.maximintegrated.com/en/app-notes/index.mvp/id/126

[12] 1-Wire-to-I2C Master Bridge: https://www.maximintegrated.com/en/products/interface/controllers-expanders/DS28E17.html

[13] Single-Channel 1-Wire Master: https://www.maximintegrated.com/en/products/interface/controllers-expanders/DS2482-100.html

[14] Using a UART to Implement a 1-Wire Bus Master: https://www.maximintegrated.com/en/app-notes/index.mvp/id/214

[15] А здесь на пальцах объясняется функционирование устройств 1-Wire (на русском языке): http://microsin.ru/content/view/508/44

[16] Protothreads by Adam Dunkels: http://dunkels.com/adam/pt/index.html

[17] Protothread и кооперативная многозадачность: http://Protothread и кооперативная многозадачность

[18] ATmega328/P datasheet: http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf

[19] Arduino PRO mini: https://www.arduino.cc/en/Main/arduinoBoardProMini

[20] Arduino MEGA 2560: https://www.arduino.cc/en/Main/arduinoBoardMega2560

[21] Источник: https://habrahabr.ru/post/322710/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best