Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Програмный 1-wire на прерываниях таймера
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
pokk
Моя первая реазилация 1-wire была на прерываниях от таймера и чисто на switch case,
это было дикая спагетти кода, потом частично завернул в функции, но все равно не нравилось как получилось,
последний раз написал на protothreads, уже как-то глаз радует, а теперь сижу и опять не доволен
не охото к каждому проекту пихать protothreads, + оборачивать макросами PT_BEGIN,PT_END

Как можно красиво написать похожее чисто на си ? Думаю в сторону переключения адреса, но как это сделать красиво ?
на protothreads получилось такое:
Код
//PT_YIELD(pt); - выход из функции, + сохранение место выхода
#define SetDelay_us(value) TCCR3B=0x01; TCNT3H=(0xFFFF-value*(CPU_CLK/1000000))>>8;\
                           TCNT3L=(0xFFFF-value*(CPU_CLK/1000000));\
                           PT_YIELD(pt);\    

PT_THREAD(Wire_Reset_thread(struct pt *pt,unsigned char *ReturnValue)){
static unsigned short int Ack[2];
PT_BEGIN(pt);
    //---------------------------------------------
    Ack[0]=0;
    Ack[1]=0;
    //--Переключаем линию на выход и записываем 0--
    Switch_Out();
    SetDelay_us(480);
    //--Переключаем линию на выход-----------------
    Switch_In();
    //---------------------------------------------
    SetDelay_us(80);
    //---------------------------------------------
    Ack[0]=T1;
    //---------------------------------------------
    SetDelay_us(250);
    //---------------------------------------------
    Ack[1]=T1;
    //---------------------------------------------
    if((Ack[0] == 0)&&(Ack[1] ==1)){
        *ReturnValue=SENSOR_INIT_VALIDATION_SUCCESS;
    }else{
        //------------------------------------------------------------------------------------------
        //*ReturnValue=SENSOR_INIT_VALIDATION_FAIL;
        if((Ack[0]==1)&&(Ack[1]==1)){
            *ReturnValue=TYPE_T_SHORT_CIRCUIT_1;
        }else{
            *ReturnValue=TYPE_T_SHORT_CIRCUIT_0;
        }
        //------------------------------------------------------------------------------------------
    }
    //---------------------------------------------
PT_END(pt);
}
Forger
Цитата(pokk @ Sep 25 2018, 12:18) *
Как можно красиво написать похожее чисто на си ?

Взять что-нибудь уже готовое: тыц.
А вопросы красиво/некрасиво - это все индивидуально и крайне субъективно laughing.gif


Цитата
не охото к каждому проекту пихать protothreads
Так поставьте нормальную RTOS
pokk
Цитата
Взять что-нибудь уже готовое: тыц.

Ну так там везде в функции tickDelay, ставят цикл ожидания, и все остальные процессы останавливаются на это время.
Цитата
Так поставьте нормальную RTOS

Что бы потом пихать в проект ещё и RTOS, когда надо снять данные с датчика температуры и выдать их в usart =)
Прерывания от таймера, это отдельный процесс как в RTOS, только без сохранения контекста =(
+ это очень легко можно запустить на любом процессоре, разобравшись только в таймере и кинув в него 1 функцию.
Forger
Коли вы так боитесь RTOS, то пользуйтесь и дальше неким protothreads. В чем проблема?
jcxz
Цитата(pokk @ Sep 26 2018, 04:15) *
Ну так там везде в функции tickDelay, ставят цикл ожидания, и все остальные процессы останавливаются на это время.

Учитесь обходиться без задержек. "Везде ставят цикл ожидания" только чайники.
pokk
Цитата
Учитесь обходиться без задержек
, это я умею (см первый пост там их нету).
MegaVolt
Цитата(jcxz @ Sep 26 2018, 09:30) *
Учитесь обходиться без задержек. "Везде ставят цикл ожидания" только чайники.
С чего это? Решение задачи определяется задачей. И если хватает времени ждать и нет необходимости делать в это время что-то ещё то задержки могут быть самым надёжным решением.
jcxz
Цитата(MegaVolt @ Sep 26 2018, 12:49) *
С чего это? Решение задачи определяется задачей.

Обратите внимание на слово "везде".
Forger
Имхо, вся эта тема какая-то бессмысленная. Как-будто ТС чем-то хотел похвастать, но не ясно чем именно wacko.gif
uriy
При отсутствии аппаратного 1wire принято использовать uart https://www.maximintegrated.com/en/app-note...ndex.mvp/id/214
Aner
Да использовал я такое ранее, инвертора от тини-лоджик брал. Особенность в точности UARTа, кварц у проца должен быть точный, с малым разбросом +/-20ppm или лучше и кратный стандарту скорости UARTA. Иначе чудеса.
Forger
Цитата(Aner @ Sep 26 2018, 22:08) *
Особенность в точности UARTа, кварц у проца должен быть точный, с малым разбросом +/-20ppm или лучше и кратный стандарту скорости UARTA. Иначе чудеса.

Какие 20ppm??? У 1-wire требования к таймингам крайне гуманные
О каком МК идет речь? Какой частоты был этот "волшебный" кварц и какая была тактовая частота МК?
k155la3
Цитата(pokk @ Sep 25 2018, 12:18) *
. . . Как можно красиво написать похожее чисто на си ? Думаю в сторону переключения адреса, но как это сделать красиво ? . . .

Красота наводит на мысль использования автомата (FSM) + события от таймера по сработке от CCR. CCR - перезагружается на требуемый интервал.
Но красиво не всегда (разве что самолет) оптимально.
Aner
QUOTE (Forger @ Sep 26 2018, 22:13) *
Какие 20ppm??? У 1-wire требования к таймингам крайне гуманные
О каком МК идет речь? Какой частоты был этот "волшебный" кварц и какая была тактовая частота МК?

Ну ка раскажите мне про этот гуманизм, особенно про джиттер фронтов у 1-wire. Даже если от STM 4-е армы, потребуют хороших инверторов перед UART. И не любой частоты кварц даст, через внутренний PLL, нужной точности частоту. Опять таки если придерживаемся стандарта 1-wire, а не его интепретаций, то есть для того чтобы работать с теми 1-wire датчиками, таблетками и тп.
Forger
Цитата(Aner @ Sep 26 2018, 22:36) *
Ну ка раскажите мне про этот гуманизм,

Да этому "стандарту" уже почти 30 лет, обсосан до костей.
Тайминги такие, что заведется с полпинка на любом античном МК даже на встроенном RC-генераторе.

Цитата
особенно про джиттер фронтов у 1-wire. Даже если от STM 4-е армы, потребуют хороших инверторов перед UART. И не любой частоты кварц даст, через внутренний PLL, нужной точности частоту.

Нда, случай крайне сложный, похоже на некий "распил".
Вангую, что для поддержки USB пришлось ставить термостабилизированный кварцевый генератор с 5-й приемкой laugh.gif
Aner
Потому вы и есть любители, не разу не прошедшие сертификацию ваших поделок - недоделок.
rx3apf
Т.е. по делу сказать нечего ? Кто б сомневался...
jcxz
Цитата(uriy @ Sep 26 2018, 21:45) *
При отсутствии аппаратного 1wire принято использовать uart https://www.maximintegrated.com/en/app-note...ndex.mvp/id/214

Кем принято?
Открываем указанную ссылку, читаем:
Для сброса и обнаружения устройства нужно послать код 0xF0 на скорости 9600. И:
1) "если устройства нет, то на RX получим 0xF0"; Утверждение не вызывает сомнения.
2) "если устройство есть, то на RX получим что-то отличное от 0xF0"; А вот тут уже - фиг вам!
Открываем описание интерфейса 1-wire, читаем:
Чтобы сформировать сигнал «Reset», — «Мастер» должен притянуть уровень на шине к нулю на время не менее 480 мкс и не более 960 мкс (480 мкс ≤ TRSTL < 960 мкс). После этого он должен отпустить шину и, не менее, чем на 480 мкс, прекратить свою активность на шине (TRSTH > 480 мкс). «Слэйвы», не ранее, чем через 15 мкс, но и не позднее, чем через 60 мкс после того, как «Мастер» отпустит шину (15 мкс < TPDH < 60 мкс), должны сформировать на шине низкий уровень (это и есть сигнал присутствия), длительностью от 60 до 240 мкс (60 мкс < TPDL < 240 мкс), после чего они также отпускают шину.
Оп-ля - приехали! 05.gif
При скорости 9600 длительность бита == 104мкс. Предположим что частоты генераторов мастера и слэйва идеально совпадают. Тогда, после завершения передачи мастером младших 4-х бит кода 0xF0, на шине наступит фронт сигнала. И в это время UART мастера начнёт приём 4-го бита. Точка сэмплирования этого бита находится на расстоянии 104/2=52мкс от указанного фронта (конца RESET). Но ведь из описания выше следует, что слэйв имеет право начать формировать сигнал присутствия через 60мкс после завершения RESET. Т.е. - уже после того как мастер считает с шины состояние 4-го бита! И мастер получит в 4-м бите лог.1. smile3009.gif
Окей, ладно - может хоть в следующем бите мастер сможет узреть на шине несчастного слэйва?? fman.gif
Но опять же - из описания выше можно узнать, что сигнал присутствия генерируемый слэйвом, может быть длительностью всего 60мкс. Но ведь мы помним, что между точками сэмплирования RX-битов мастером расстояния == 104 мкс (1 бит UART). Получается, что если импульс присутствия начнётся через 60 мкс после конца RESET и продлится всего 60 мкс, то он аккурат проскочит между двумя точками сэмплирования UART.RX мастера. И мастер его просто не заметит!
Хотя мальчик-то есть, но вот колхоз на UART его не видит.... crying.gif
А значит такую реализацию уже нельзя считать 1-wire. Ну никак нельзя.
И это мы ещё даже не учли влияния разности частот генераторов мастера и слэйва, а также влияние ёмкости линии. А также не учли, что делитель UART-а может быть дробным (а длина бита UART - переменной). Там будет ещё веселее.
После этого дальше читать указанную ссылку смысла уже нет.

Цитата(Forger @ Sep 26 2018, 22:48) *
Да этому "стандарту" уже почти 30 лет, обсосан до костей.
Тайминги такие, что заведется с полпинка на любом античном МК даже на встроенном RC-генераторе.

Да ладно?
Не будем углубляться в приём/передачу, но раз "обсосан", то объясните как с помощью UART хотя-бы обнаружить такой слэйв-девайс, который в полном соответствии с мануалом, формирует импульс присутствия с началом через 15...60 мкс после завершения RESET, и длительностью 60...240 мкс?
Это ведь как ловить рыбку сетью, у которой размер ячеи больше размера самой рыбки - конечно есть вероятность что поймаешь, но можно и с голоду опухнуть biggrin.gif
Forger
Цитата(jcxz @ Sep 27 2018, 01:07) *
то объясните как с помощью UART хотя-бы обнаружить такой слэйв-девайс,
Я не использую для этого UART, для этого есть аппаратные таймеры.
Smen
Цитата(Forger @ Sep 27 2018, 10:04) *
для этого есть аппаратные таймеры
Т.е. используются и таймеры и УАРТ?
В чём смысл?
Forger
Цитата(Smen @ Sep 27 2018, 08:23) *
Т.е. используются и таймеры и УАРТ?
Цитирую:
Цитата(Smen)
Я не использую для этого UART
VAI
Когда-то я выкладывал сюда код, работающий на STM32 и на MSP430.
Вот, смотрите, может подойдёт для Вас. http://electronix.ru/forum/index.php?showt...p;#entry1089884
toweroff
1-Wire вполне себе реализовывался на SPI 32bit. Правда, делал его на древнем уже LPC2103
Главное - пин MOSI правильно настроить, чтобы только вниз тянул, push-pull отключить, если возможно, или сгородить на двух транзисторах
Нажмите для просмотра прикрепленного файла
AlexandrY
Цитата(toweroff @ Sep 27 2018, 14:03) *
1-Wire вполне себе реализовывался на SPI 32bit. Правда, делал его на древнем уже LPC2103
Главное - пин MOSI правильно настроить, чтобы только вниз тянул, push-pull отключить, если возможно, или сгородить на двух транзисторах
Нажмите для просмотра прикрепленного файла

Лучше вот такая схема:

По I2C работает идеально. wink.gif
toweroff
Цитата(AlexandrY @ Sep 27 2018, 14:17) *
Лучше вот такая схема:
По I2C работает идеально. wink.gif

Не, ну кто бы спорил sm.gif
Вопрос в том, что у меня без всяких +200р к стоимости девайса... Хотя, если ценник ему ковырнадцать тысяч, то и DS2484 вполне себе уместна biggrin.gif
Integro
Цитата
Красивый выход из прерывания с переключением контекста

Вам нужно посмотреть на примеры реализации механизма callback.
Вкратце, реализовываете функцию для настройки таймера в one shot режиме и в прерывании таймера (через n us) вызываете необходимый вам callback
CODE
static void (*_cb)(void) = NULL;

void TIMERn_Handler(void) {
// TODO: Clear timer status or stop if neccesary
if( _cb ) {
_cb();
}
}

void setup_timer(uint32_t delay_us, void *callback ) {
_cb = callback ;
//TODO: configure hardware timer
}

Далее, набрасываем функции которые будут вызываться последовательно:
CODE
static void _save_t1(void);
static void _reset(void);

void start(void) {
Switch_Out();
setup_timer(480, _reset);
}

static void _reset(void) {
Switch_In();
setup_timer(80, _save_t1);
}

static void _save_t1(void) {
Ack[0]=T1;
//TODO ...
if(Ack[0] == 1) {
setup_timer(80, _something1);
} else {
setup_timer(10, _something2);
}
}

Думаю суть ясна, для начала транзакиции нужно вызвать start(), а дальше все пойдет по реализованной вами цепочке.
Если таких "прижков" выйдет много, локаничней релизовать таблицы для "линейных участков":
Код
static struct {
    uinr32_t delay_us;
    void *next_cb;
}_chain_init[] = {
    {.delay_ms = 480, .next_cb = _reset},
    {.delay_ms = 80, .next_cb = _save_t1},
};

Пользовать это можно так:
Код
    index++;
    setup_timer(_chain_init[index].delay_ms , _chain_init[index].next_cb);


Ну и понятно функция setup_timer, должны бать быстрой, что бы не поплыли микросекундные тайминги.
Код писал без проверки компиляции, так что извиняйте за описки.
uriy
Решение на uart 1wire Отлично работает даже с usb-uart преобразователем CP2102. CP2102 работает без кварца и скорее всего подстраивает частоту по SOF кадрам, думаю там разброс больше чем 20 ppm.
jcxz
Цитата(uriy @ Sep 28 2018, 07:54) *
Решение на uart 1wire Отлично работает даже с usb-uart преобразователем CP2102.
Кроме пустословия, по делу, есть что сказать?
Ещё раз читаем: Почему UART - не 1-wire

Цитата(uriy @ Sep 28 2018, 07:54) *
CP2102 скорее всего подстраивает частоту по SOF кадрам.
Бред.
Arlleex
Не знаю, зачем выделять на это целый UART... Я сделал на таймере в режиме сравнения. Благодаря этому можно использовать любую ножку свободную на МК. Обработчик таймера:
CODE
#define T1 4
#define T2 7
#define T3 51
#define T4 59
#define T5 89
#define T6 409
#define T7 499

// TypeOperation - тип запрашиваемой операции:
// ONE_WIRE_INIT - формирование импульса сброса;
// ONE_WIRE_GET_TIME_SLOT - формирование тайм-слота чтения;
// ONE_WIRE_SET_TIME_SLOT_0 - формирование тайм-слота записи лог. 0;
// ONE_WIRE_SET_TIME_SLOT_0 - формирование тайм-слота записи лог. 1.
//
// ResultOperation - результат чтения тайм-слота (только при чтении).
//
// StatusOperation - флаг завершенности работы автомата.
//
// Перед использованием автомата необходимо указать тип операции и включить
// таймер TIMER_START(). Затем дождаться завершения работы автомата.
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
static unsigned char FSMState = 0;

switch(TypeOperation)
{
// автомат формирования импульса сброса
case ONE_WIRE_INIT:
{
switch(FSMState)
{
case 0: TIM2->ARR = T7; RESET_LINE(WIRE); break;
case 1: TIM2->ARR = T5; SET_LINE(WIRE); break;
case 2: TIM2->ARR = T6; ResultOperation = GET_LINE(WIRE); break;
case 3: TIMER_STOP(); TIM2->ARR = T1; StatusOperation = 1; break;
}
FSMState = (FSMState + 1) & 0x3; break;
}

// автомат формирования тайм-слота чтения
case ONE_WIRE_GET_TIME_SLOT:
{
switch(FSMState)
{
case 0: TIM2->ARR = T1; RESET_LINE(WIRE); break;
case 1: TIM2->ARR = T2; SET_LINE(WIRE); break;
case 2: TIM2->ARR = T3; ResultOperation = GET_LINE(WIRE); break;
case 3: TIMER_STOP(); TIM2->ARR = T1; StatusOperation = 1; break;
}
FSMState = (FSMState + 1) & 0x3; break;
}

// автомат формирования тайм-слота записи
case ONE_WIRE_SET_TIME_SLOT_0: case ONE_WIRE_SET_TIME_SLOT_1:
{
switch(FSMState)
{
case 0: TIM2->ARR = (TypeOperation == HW_ONE_WIRE_SET_TIME_SLOT_0) ? T4 : T1; RESET_LINE(GPIO_ONE_WIRE); break;
case 1: TIM2->ARR = (TypeOperation == HW_ONE_WIRE_SET_TIME_SLOT_0) ? T1 : T4; SET_LINE(GPIO_ONE_WIRE); break;
case 2: TIMER_STOP(); TIM2->ARR = T1; ++FSMState; StatusOperation = 1; break;
}
FSMState = (FSMState + 1) & 0x3; break;
}
}

TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
}


Кстати, попутно тут хочу узнать, как красиво организовать счётчик, который считает до, например, 5 и сбрасывается в 0 без условия if(). Ну то есть вычитая, сдвигая... Интересно можно ли так сделать. Для ровных битовых чисел, как в коде выше, как видно, я битовой маской обнуляю. А вот интересно, как быть с числами, не являющихся степенями 2.
jcxz
Цитата(Arlleex @ Sep 28 2018, 14:15) *
Не знаю, зачем выделять на это целый UART... Я сделал на таймере в режиме сравнения.

Вы наверное перепутали: наверное всё-таки в режиме захвата (capture), а не сравнения (compare). rolleyes.gif
Да, это стандартное решение. Вообще не понимаю здешних товарищей, которые пытаются колхозить на чём угодно, только не делать как надо. cranky.gif
Arlleex
Цитата(jcxz @ Sep 28 2018, 14:21) *
Вы наверное перепутали: наверное всё-таки в режиме захвата (capture), а не сравнения (compare). rolleyes.gif

Почему? Именно по сравнению: как только заданный период прошёл (счётчик таймера сравнялся с указанным значением сравнения), формируется прерывание, в котором регистр сравнения перенастраивается на следующий интервал, а лапка дергается в соответствующую сторону laughing.gif
А захват - это защёлкивание текущего значения таймера по какому-либо событию, как я понимаю эту всю кухню.

Хотя, кстати, да, можно (скорее нужно) делать по захвату. Дёрнулась лапка - захватили значение таймера и проконтролировали рамки прихода ожидаемого фронта...
jcxz
Цитата(Arlleex @ Sep 28 2018, 14:15) *
Кстати, попутно тут хочу узнать, как красиво организовать счётчик, который считает до, например, 5 и сбрасывается в 0 без условия if(). Ну то есть вычитая, сдвигая... Интересно можно ли так сделать. Для ровных битовых чисел, как в коде выше, как видно, я битовой маской обнуляю. А вот интересно, как быть с числами, не являющихся степенями 2.

Код
int i, i1;
i1 = i - 5 >> 31;
i = i - i1 & i1;

Будет 3 или 4 команды (в зависимости от оптимизации).

Цитата(Arlleex @ Sep 28 2018, 14:54) *
Почему? Именно по сравнению: как только заданный период прошёл (счётчик таймера сравнялся с указанным значением сравнения), формируется прерывание, в котором регистр сравнения перенастраивается на следующий интервал, а лапка дергается в соответствующую сторону laughing.gif

А чтение значения ноги когда?
Если по этому прерыванию, то так делать - ненадёжно, так как будет зависимо от задержек входа в ISR. К тому же - требует в 2 раза большей частоты прерываний.

Цитата(Arlleex @ Sep 28 2018, 14:54) *
А захват - это защёлкивание текущего значения таймера по какому-либо событи'юю, как я понимаю эту всю кухню.

Вот именно. А потом анализируем защёлкнутое значение и решаем что это было - 0 или 1.
Arlleex
Цитата(jcxz @ Sep 28 2018, 15:02) *
Код
int i, i1;
i1 = i - 5 >> 31;
i = i - i1 & i1;

Будет 3 или 4 команды (в зависимости от оптимизации).

Благодарю! Потестирую sm.gif
jcxz
Цитата(Arlleex @ Sep 28 2018, 15:07) *
Благодарю! Потестирую sm.gif

Можно ещё проще, но значения чисел будут не через 1, а через большие промежутки. Типа так:
Код
#define LIMIT 5
#define STEP ((u32)(((1u << 31) + LIMIT - 1) / LIMIT))  //значение шага округлённое до ближайшего бОльшего целого
u32 i;
i = __USAT(i + STEP, 31);
Всего две операции. wink.gif Значения шагов будут: STEP*0, STEP*1, STEP*2, STEP*3, STEP*4, а на следующем шаге получим опять STEP*0.
Для switch-а такие значения конечно неудобны. sad.gif

Ещё вариант - использовать отрицательный диапазон чисел:
Код
enum {LIMIT = 5};
int i = -LIMIT;
i -= i >> 31 | LIMIT; //-5,-4,-3,-2,-1,0
Всего 3 операции cool.gif
toweroff
Цитата(jcxz @ Sep 28 2018, 14:21) *
Вообще не понимаю здешних товарищей, которые пытаются колхозить на чём угодно, только не делать как надо. cranky.gif

RTOS
предлагаете всё в SysTick вешать, у которого наивысший приоритет?
Forger
Цитата(toweroff @ Sep 28 2018, 23:35) *
предлагаете всё в SysTick вешать, у которого наивысший приоритет?

Вы наверно удивитесь, но в RTOS системному таймеру SysTick назначают как раз самый низкий приоритет (наравне с PendSV - переключатель контекста)
И вообще, если не используется RTOS, то ничего не мешает настроить Systick так, чтобы использовать его как самый простой аппаратный таймер, только назначив уже нужный приоритет.
Именно так я сделал в примитивных проектах, где RTOS ни к чему, но нужны простые временные выдержки, в частности с кратностью 1us и 1ms. Systick отлично для этого подходит.
jcxz
Цитата(toweroff @ Sep 28 2018, 23:35) *
предлагаете всё в SysTick вешать, у которого наивысший приоритет?

SysTick то тут при чём???? wacko.gif Само собой оптимально делается - на режиме захвата таймера.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.