• Система автоматизации с открытым исходным кодом на базе 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);
....
 
Последнее редактирование:
Сверху Снизу