• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Таймер иногда не перезапускается

Сделал прием-передачу по СОМ, похоже на modbus, окончание приема определяю по паузе. Паузу определяю с помощью таймера. При приеме каждого байта делается
gtimer_stop(...);
gtimer_start_periodical(...);
Все работает, может час проработать, но потом таймер не переинициализируется, т.е. начинает считать не с заданного значения, а с 0xffffffff. В таком случае до прерывания получается очень долго. Ни кто с таким не сталкивался?
 
У меня времена - единицы мс, т.е. слишком короткими для таймера с тиком 30мкс они не являются. После остановки gtimer_stop(...); таймер сбрасывается. Если сразу после запуска gtimer_start_periodical(...); прочитать значение в таймере gtimer_read_tick(...) получится 0xffffffff. Похоже он таким способом загружается установленным значением. Но видимо иногда что-то идет не так. Причину поймать не удается.
 

pvvx

Активный участник сообщества
Значит вызывается в разных task и/или прерываниях...
Ставьте семафоры.
Вообще драйвер не пишется на функциях верхнего уровня ОС.
Пишите сами обработку UART и выделите таймер для этого, исключив из списка других обращений...
UART сама дает прерывание тишины на линии в 4 символа после приема. Зачем там задействовать один из нескольких аппаратных таймеров? Soft-таймера там за глаза, чтобы ограничить время ответа.
 
Последнее редактирование:
Да, вызывается в разных прерываниях. Сделал лог вызова, наложения прерываний нет. Видимо придется искать другой вариант реализации, если есть прерывание по тишине, буду его использовать. И в остальном от таймеров откажусь, хотя это и странно, что оно не работает.
Все эти таймерные функции уже по шагам прошел, в принципе могу все реализовать прямым обращением к регистрам, но не думаю что это что-то изменит.
 

pvvx

Активный участник сообщества
И в остальном от таймеров откажусь, хотя это и странно, что оно не работает.
Оно работает, но под RTOS свои условия.
Сколько тестов не делал, пока такого не было. Сброс счетчика обычно и выполняется загоном 0xFFFF, чтобы он не сработал, пока туда будет вписано требуемое значение, если не предполагается остановка таймера...
А может всё проще - у вас включены какие режимы энергосбережения и/или включается wakelock?
 
Последнее редактирование:

pvvx

Активный участник сообщества
Используйте аппаратный таймер в режиме одиночного запуска минимальный интервал у него 10 мкс.
void hw_timer_arm (uint32 val)
uint32 val: timing In non autoload mode, range: 10 ~ 0x199999 μs.
можно и 5 задать, просто реально будет больше, чем 5.
Здесь такого таймера и функций нет. Вы не туда попали, как всегда :)
 
Прочитал про прерывание после паузы в 4 символа тут , но так и не понял как это можно использовать. У меня прерывание после приема каждого символа, serial_rx_fifo_level(&sobj, 0); Пробовал ждать 4 символа после последнего принятого, дополнительного прерывания не получал. Можно ли его вообще в таком варианте получить? Прерывания разрешал так serial_irq_set(&sobj, RxIrq, 1); serial_irq_set(&sobj, TxIrq, 1); Что я упустил? Но и ситуация, когда в FIFO символ лежит дольше 4х времен тут не складывается. Это не может повлиять?
 

pvvx

Активный участник сообщества
Наверно не разрешили само прерывание в регистрах аналога 16550.
Найдите примеры к 16550... их достаточно.
Та-же Modbus и была ориентирована на характеристики 16550.
Прерывание по приему символа не содержит время приема stop бита...
Для полу-дуплекса применяют переключение при передаче в режим RX TX loopback и зарядку прерывания тишины. Тогда, передача уходит, пусть по DMA, а в конце, через паузу возникает прерывание конца фрейма по паузе и тут переключаете направление и отключаете loopback.
Т.е. таймер вообще не требуется. Но отследить паузу в 1.5 символа не выйдет даже с таймером, если большие скорости...
И ешё раз - через функции API драйвера не пишут. Хотя-бы HAL...
 
Последнее редактирование:
Не нашел в описаниях 16550 отдельного разрешения прерывания по паузе FIFO. Есть просто включение FIFO, у меня оно выполняется при вызове serial_rx_fifo_level(&sobj, 0); при этом в RUART_FIFO_CTL_REG_OFF пишется (RUART_FIFO_CTL_REG_DMA_ENABLE | RUART_FIFO_CTL_REG_FIFO_ENABLE), в моем случае 0х09. Есть разрешение прерывания при приеме, у меня оно выполняется при вызове serial_irq_set(&sobj, RxIrq, 1); в моем случае в регистр разрешения прерываний пишется 0х05 (разрешения для приема и для состояния линии), и судя по тому, что прерывания при приходе символа у меня формируются, тут ошибок нет.
Смущает то, что при приеме я все принятые символы вычитываю. Должно ли в таком случае формироваться прерывание по таймауту FIFO, если в этой FIFO ни чего нет?
 

pvvx

Активный участник сообщества
Не нашел в описаниях 16550 отдельного разрешения прерывания по паузе FIFO. Есть просто включение FIFO, у меня оно выполняется при вызове serial_rx_fifo_level(&sobj, 0); при этом в RUART_FIFO_CTL_REG_OFF пишется (RUART_FIFO_CTL_REG_DMA_ENABLE | RUART_FIFO_CTL_REG_FIFO_ENABLE), в моем случае 0х09. Есть разрешение прерывания при приеме, у меня оно выполняется при вызове serial_irq_set(&sobj, RxIrq, 1); в моем случае в регистр разрешения прерываний пишется 0х05 (разрешения для приема и для состояния линии), и судя по тому, что прерывания при приходе символа у меня формируются, тут ошибок нет.
Смущает то, что при приеме я все принятые символы вычитываю. Должно ли в таком случае формироваться прерывание по таймауту FIFO, если в этой FIFO ни чего нет?
C FIFO оно никак не связано. Это отдельный таймер, в самой UART, считающий 4 символа тишины на RX выводе после последнего принятого символа. Срабатывает один раз, т.е. только после приема символа.
RTL00_WEB/rtl8195a_uart.h at master · pvvx/RTL00_WEB · GitHub

Какие-то куски из моих тестов перед созданием драйвера ModbusRTU RS-485:
Код:
void _UartIrqHandle(void *data){
    PHAL_RUART_ADAPTER pru = (PHAL_RUART_ADAPTER) data;
    u32 puart = UART0_REG_BASE + (pru->UartIndex*RUART_REG_OFF);
     u8 val = HAL_READ8(puart, RUART_INT_ID_REG_OFF);
     switch((val>>1) & 7) {
            case 6: // character timeout <---- тут
            case 2: // received data available
            case 1: // THR empty
            case 3: // receiver line status / busy detect?
            case 0:  // modem status?
        }
}
....
    serial_init(&sobj, UART_TX, UART_RX);
    serial_baud(&sobj, rs485cfg.baud);
    serial_format(&sobj, 8, rs485cfg.parity, rs485cfg.stop & 7);
    serial_send_comp_handler(&sobj, (void*)uart_send_string_done, (uint32_t) &sobj);
    serial_recv_comp_handler(&sobj, (void*)uart_recv_string_done, (uint32_t) &sobj);

    serial_irq_handler(&sobj, uart_irq, (uint32_t)&sobj);
    serial_irq_set(&sobj, RxIrq, 1);
    serial_irq_set(&sobj, TxIrq, 1);
    InterruptUnRegister(&sobj.hal_uart_adp.IrqHandle);
    sobj.hal_uart_adp.IrqHandle.IrqFun = (IRQ_FUN)x_UartIrqHandle;
    InterruptRegister(&sobj.hal_uart_adp.IrqHandle);

    u32 puart = UART0_REG_BASE + (sobj.hal_uart_adp.UartIndex*RUART_REG_OFF);
    HAL_WRITE32(puart, RUART_MODEM_CTL_REG_OFF, HAL_READ32(puart, RUART_MODEM_CTL_REG_OFF) | RUART_MCR_LOOPBACK);
    sobj.hal_uart_adp.Interrupts |= RUART_IER_PTIME; <---- разрешить прерывание PTIME
    HalRuartSetIMRRtl8195a(&sobj.hal_uart_adp);
....
 
Последнее редактирование:
Сверху Снизу