Поклонники продукции Microchip Technology Inc тусуются тут.
Ответить

TMR1 & RTC

Вт сен 05, 2017 07:01:30

Хьюстон, у нас проблема.

Делаю часики на pic18f24j11 с использованием модуля RTCC. Всё тикает. И тут вдруг мне пришла в голову мысль сделать автоматический расчет коррекции. В RTCC есть такой регистр RTCCAL в который можно прописать сколько лишних импульсов надо добавить или отнять, чтобы часы шли по-точнее. Для этой цели я решил использовать счетчик TMR1. Получается так, что от одного T1OSC тикают двое часиков: RTCC и TMR1. Да вот проблема - TMR1 отстаёт! И крупно отстаёт. Вот вчера вечером поставил часы, проверил - всё точно. Сегодня утром TMR1 отстаёт от RTCC 2 секунды. Но они же идут от одного и того же генератора.

Я понимаю, что это моё устройство и кроме меня никто не может найти где что теряется, но мне нужны идеи как поймать, где и что пропадает. Вот обработчик прерывания TMR1:
Код:
// Прерывание от TMR1
    if (TMR1IE && TMR1IF) {
        TMR1H |= 0x80u;
        TMR1IF = 0;
        if (flag.timeset)  unixtime++;
    }

на дисплей я могу вызвать значение unixtime, который показывает время с последнего сброса секунд. Вот например, сейчас на границе минуты показания были 0xEF85, что при делении на 60 в остатке даёт 57, а не 0 или хотя бы 59. Секунда туда-сюда допустима, так как часы обновляются по ежесекундному прерыванию от RTCC и при сдвиге показания могут запаздывать, но при расчете используется захваченные значения, а вот там точно белиберда. Флаг timeset в коде только устанавливается при сбросе секунд и более никогда не снимается.

Re: TMR1 & RTC

Вт сен 05, 2017 07:37:59

Регистр коррекции хода касается самого RTC (его счетчиков) и частоты появления соответствующего прерывания.
Это аналогично корректуре точности хода в автономном RTC типа DS3231 в случае автономного хода (кормится от батарейки).
Кроме самого RTC есть еще куча всяческих прерываний. Максимальную неприятность приносят именно они (неконтролируемая задержка при их взаимоперекрытии/отработке).
Для повышения стабильности стоит проработать структуру программы по отношению к участку, обеспечивающему генерацию секундного интервала - далее влияние "взаимоперекрытия" обработчиков прерываний уже не критично.
:roll:
Если использовать несколько независимо считающих таймеров, то для повышения точности у них должен быть единый "генератор секунды", а время их суммарных обработчиков не превышать половины секунды (с запретом иных прерываний во время обработки массива таймеров) - итогом возможна погрешность до +/- 1 секунды между таймерами. Дополнительно реакция на "момент запуска" - Это уже комплекс из алгоритмов обработчика клавиатуры/дисплея включая время реакции на нажатие и вывода отображения на индикацию.
:(

Re: TMR1 & RTC

Вт сен 05, 2017 07:58:38

в ds3231 корректируются не счетчики и нагрузка на кварц, т.е. частота кварцевого генератора - это совсем другое. Собственно, этой ночью обработчик прерываний был дополнен проверкой:
Код:
// Прерывание от TMR1
    if (TMR1IE && TMR1IF) {
        if (TMR1H & 0x7F) {
            flag.lostsec = 1;
        }
        TMR1H |= 0x80u;
        TMR1IF = 0;
        if (flag.timeset)  unixtime++;
}
флаг lostsec за ночь не установился. Это значит, что прерывание было обработано менее чем за 1/128-ю секунды от переполнения счетчика. В дизассемблированном коде обработчика тоже криминала не вижу (правде не могу придумать, как его сюда скопировать).

В спящем режиме прерывание TMR1 не запрещается - часы должны тикать:
Код:
        if (POWER_LOST) { // если лог.1, то питания нет
            // ложимся спать
            clock_mode = sleep_mode;
            TMR2IE = 0; // выключаем динамическую индикацию
            RTCCIE = 0;
            INT1IE = 1; // разрешаем прерывание по появлению питания
            DIVIDER = 0;
            COMMA = 0;
            START_CONV = 0;
            LATA = 0x48;         // выключаем аноды
            LATB = 0x00;           // выключаем катоды
//          OSCCON = 0x00; // тактовая 2МГц.
            // спим
            do {
                SLEEP();
                NOP();
            } while (POWER_LOST);
            // выход из спячки:
            OSCCON = _OSCCON_IRCF0_MASK | _OSCCON_IRCF1_MASK | _OSCCON_IRCF2_MASK; // 8MHz
            clock_mode = show_time;
            TMR2IE = 1;
            RTCCIE = 1;
            INT1IE = 0;
            INT1IF = 0;
        }
Но мне не кажется, что питание не пропадало.

Re: TMR1 & RTC

Вт сен 05, 2017 07:59:11

Немного не понятно, как можно делать коррекцию, отталкиваясь от нестабильного источника ? Тем более, когда тактовая частота одна и та же, и для корректируемого значения, и для эталонного :dont_know:
У меня, например, даже в голове не укладывается алгоритм корректировки таким способом :roll:

Re: TMR1 & RTC

Вт сен 05, 2017 08:09:46

Алгоритм прост: Нажимаем сброс секунд в начале минуты и с этого момента считаем секунды (в переменной unixtime). Спустя некоторое время, заметив отличие в несколько секунд, снова производим коррекцию по началу минут и фиксируем сколько секунд насчитали и за какой промежуток времени. Т.е. если мы ноль секунд поставили когда на часах было еще только 57 секунд, значит надо ускорить часы на 3 секунды за изменный интервал. Дальше - элементарная пропорция.

Код:
// тут сбрасываем секунды у RTCC
                            RTCCFGbits.RTCPTR1 = 0;
                            RTCCFGbits.RTCPTR0 = 0;
                            RTCVALL = 0;
                            RTCCFGbits.RTCWREN = 0;
                            flag.holduntilreleased = 0;
                            flag.update = 1;
// делаем захват значений                           
                            GIE = 0;
                            timetorunh = unixtime;
                            timetorunl = TMR1H & 0x7Fu;
                            unixtime = 0;
                            TMR1L = 0;
                            TMR1H = 0x80;
                            flag.timeset = 1;
                            GIE = 1;

                            if (timetorunh & 0xffff0000) { // измеренный интервал должен быть почти сутки как минимум 65536сек (18ч))
                                if (timetorunh & 0xFF000000u) {
                                    correction = timetorunh % 60;
                                    if (correction > 30) {
                                        correction = 60 - correction;
                                        correctionsign = plus;
                                    } else correctionsign = minus;
                                } else {
                                    timetorunh = (timetorunh << 7) | timetorunl;
                                    correction = timetorunh % (60 << 7);
                                    if (correction > (30 << 7)) {
                                        correction = (60 << 7) - correction;
                                        correctionsign = plus;
                                    } else correctionsign = minus;
                                }
                                timetorun_saved = timetorunh;
                                result = correction * 2 * 491520ul / timetorunh;
                                if (result & 1) result++;
                                result >>= 1;
                                result_saved = result;

Re: TMR1 & RTC

Вт сен 05, 2017 08:25:40

Для случая корректировки хода единственно корректным является изменение коэффициента пересчета первичного делителя генератора секундного интервала.
Остальное будет иметь множество "паразитных зависимостей".
Не знаю как в конкретном pic18f24j11, но у ПИКов среднемладших при установке кварца 32768 на С/Т1 провести такую операцию корректно все же затруднительно...
Другое дело автоперезагружаемый коэффициент деления у АВР/MCS51 или С/Т2 для среднемладшей (но для них кварц не подключается :( )
Помимо прочего расчет такой коррекции с применением кнопок/индикатора с "непосредственно ручным управлением"...
Там должен быть внутренний синхронный запуск и внутренний автоматический захват ВСЕХ результатов одновременно.
Да и корректирующее значение...
Одно дело +/-1 относительно 32768 (или большем, если за основу берется системный кварц "за 12МГЦ") и совсем иное там, где корректируется уже пониженное значение - иногда даже +/-1 в таком случае будет давать результат в +/- значительно более секунды.
:roll:

Re: TMR1 & RTC

Вт сен 05, 2017 08:40:40

Это всё решаемо. Для часов без RTC, только на TMR1 - я применил алгоритм Брезенхема и легко ввожу коррекции с точностью 0.1 сек в сутки. И особой точности обработки не нужно. Если я вижу что за 10 дней часы ушли на секунду - значит коррекция нужна 0.1 сек в сутки.

у этого пика сделано так, что добавляется/отнимается заданное число импульсов от генератора раз в 15 секунд. Вернее в даташите написано сколько то импульсов в минуту, но ввести можно только кратное 4 число:
Код:
bit 7-0 CAL<7:0>: RTC Drift Calibration bits
01111111 = Maximum positive adjustment; adds 508 RTC clock pulses every minute
...
00000001 = Minimum positive adjustment; adds four RTC clock pulses every minute
00000000 = No adjustment
11111111 = Minimum negative adjustment; subtracts four RTC clock pulses every minute
...
10000000 = Maximum negative adjustment; subtracts 512 RTC clock pulses every minute
Что обеспечивает шаг около 0.2 секунды в сутки.

Да, но мы отвлеклись. Вопрос как поймать за руку разбег двух часов идущих от одного и того же генератора.

Re: TMR1 & RTC

Вт сен 05, 2017 09:08:41

Алгоритм прост: Нажимаем сброс секунд в начале минуты и с этого момента считаем секунды (в переменной unixtime). Спустя некоторое время, заметив отличие в несколько секунд, снова производим коррекцию по началу минут и фиксируем сколько секунд насчитали и за какой промежуток времени.

Тогда не понятно, для чего тут нужен второй генератор (тот, который на TMR1) ? В качестве эталонного выступаете Вы, нажимая на кнопку и синхронизируя с реальным временем.
Нажали, программа видит отклонение секунд, натиканных с RTC, от реальных. Далее - обычная математика.

uldemir писал(а):Вопрос как поймать за руку разбег двух часов идущих от одного и того же генератора.
Может уже установлено какое-нибудь значение корректировки RTC ?

Re: TMR1 & RTC

Вт сен 05, 2017 13:34:39

Ой, вы пробовали расчитать промежуток времени между двумя календарными датами? Это же мрак. С другой стороны тут фишка в том, что часы сбрасывают секунды по нажатию, а стартуют (и фиксируется расхождение) по отпусканию кнопки установки. А так как у меня валялся совершенно свободный счетчик, зачем мне корячиться? Вот только нарвался на такие грабли.

Этой ночью стояла коррекция 2 (это я прикинул по результатам предыдущих наблюдений). Но, это всего 2*4*1440=11520 тактовых импульсов в сутки. Это 0,35 секунды в сутки, а не 2. Проблема-то всплыла именно от того, что мои наблюдения давали коррекцию где-то между 2 и 3, а часы показали 17.

Сейчас пришла в голову идея фиксировать по прерыванию от TMR1 значение бита HalfSec - если будет расхождение, то он будет меняться. Сейчас только придумаю как мне это визуализировать, чтобы в течении дня заметить это изменение.

Добавлено after 4 hours 7 minutes 35 seconds:
Хе... часики два часа уже тикают... Сделал так, что дисплей обновляется и по прерыванию от часов, и по прерыванию от первого таймера. В то время как визуально смотрю на интернетовские часы замечаю, что RTC имеет очень маленькое отставание, а таймерное - чуток больше! Хотя полусекундная ловушка еще не сработала (но, похоже, сработает!). Решил погуглить. И вот что нагуглил:
Analysis:

I added some debug code to the ISR and used a logic analyser to try to find out what was going on. The results proved quite interesting.

I noticed that when TMR1H was preset to 0xc0 the TMR1L register would randomly MISS a count even though the 32 KHz oscillator was still running.

I am using a 16F7x7 device and the datasheet gives various warnings about reading and writing the timer1 registers when enabled and the characteristic behaviour when first enabled. There was no mention of missing counts while being used as the basis for a RTC.

More debug code revealed that if TMR1H is preset when the 32 kHz source oscillator output is high TMR1L counts correctly and accuracy is consistent. If TMR1H is preset when the oscillator output is low TMR1L misses a count and the RTC looses time.

Вот что-то не припоминаю, чтобы была такая эррата. Но если это правда, надо успевать модифицировать старший байт таймера менее чем за 15мкс от начала прерывания. Похоже, придётся включать фичу приоритетов прерываний или считать по 2 секунды немодифицируя счетчик.

Ан, нифига! есть такая эррата 80329B.pdf

p.s. Ловушка сработала. есть полусекундный сдвиг.

Re: TMR1 & RTC

Ср сен 06, 2017 17:14:51

Нет в жизни счастья. Попробовал сделать приоритеты. Причем, TMR1 имеет высокий приоритет, все остальные прерывания - низкий. Думал, поможет. Ан нет - не очень. Ловушка еще не сработала, но расхождение всё-равно начинаю видеть. Думал, чесал репу.... Глянул дизассемблер... ё-моё! от начала прерывания по адресу 0x0008 до BSF TMR1H, 7, ACCESS - 42 инструкции! На 8Мгц тактовой это 21 (плюс вызов прерывания и недефинированная задержка) микросекунды! При этом период кварцевого генератора 30.5 мкс.

И теперь, я не вьезжаю как с этим бороться.

Погуглил. Оказывается, это "фича" свободной версии XC8 - никакой оптимизации, сохраняем все возможные регистры, вне зависимости используются или нет.

Поискал workaround. Нашел, но минимум необходимого не влазит между адресами 0х008 и 0х018:
Код:
void ASMISR_HI(void) @ 0x0008 {   
#asm   
 // Pure assembly ISR here   
                BSF TMR1H, 7
                BCF PIR1, 0
                MOVLW   1
                ADDWF   _unixtime, F
                BTFSC   0xFD8, 0
                ADDWF   _unixtime+1, F
                BTFSC   0xFD8, 0
                ADDWF   _unixtime+2, F
                BTFSC   0xFD8, 0
                ADDWF   _unixtime+3, F
                RETFIE  F                  ; return from interrupt
#endasm
}
Если убрать последние два сложения с соотв. проверками, то влазит. Но этого мало.

Re: TMR1 & RTC

Пт сен 08, 2017 13:21:51

Придумалось мне написать обработчик на ассемблере. Даже разобрался с секциями линкера. Но, не могу въехать в один момент. Тривиальная операция:
Код:
    BTFSC   TMR1IE
    BTFSS   TMR1IF
компилируется в BTFSC 0x9d, 0, BANKED, а должно быть, вроде, ACCESS. Вот только добавляя этот ACCESS получаю синтаксическую ошибку. Пока обошел это тем, что просто прописал числовую константу "0". Вот что у меня получилось:
Код:
#include "xc.inc"
    GLOBAL    _unixtime
    
; This is the high priority interrupt handler. It is placed at address 8.
    PSECT intcode,class=CODE,reloc=2
    GOTO    HI_ISR_ROUT

    
    PSECT isr_routine
,class=CODE,reloc=2
HI_ISR_ROUT
:
    BTFSC   TMR1IE, 0
    BTFSS   TMR1IF
, 0
    BRA        INTEXIT
    BSF        TMR1H
, 7, 0
    BCF        TMR1IF
, 0
    MOVLW   1
    MOVLB   high
(_unixtime)
    ADDWF   _unixtime+0, F
    MOVLW   0
    ADDWFC  _unixtime
+1, F
    ADDWFC  _unixtime
+2, F
    ADDWFC  _unixtime
+3, F
INTEXIT
:
    RETFIE  F
Здесь я никакие регистры не сохраняю, так как W, STATUS, BSR сохраняются автоматически и RETFIE F - их так же автоматически должен восстановить. А другие регистры я не трогаю.

Код пока еще в кристалл не заливал - сейчас залита прошивка где сделал с одним уровнем приоритета, просто с ожиданием в цикле прохождения счетного импульса и делается измерение (думаю, недельку потомить). Uptime уже 0x23e79 секунд. Но после, думаю, залью этот вариант. Может есть еще предложения, идеи?

Re: TMR1 & RTC

Пт сен 08, 2017 13:41:40

Обрабатывается область ОЗУ 0x000-0x07F и РСФ из 0xF80-0xFFF приведенные к полю 0x080-0x0FF.
Кроме прочего там в команде еще должен быть указан признак обращения, т.к. "по умолчанию" ставится 1 (bsr ram).
Если уж по классике то должно быть :
BTFSC имя регистра,номер/имя бита,0
( т.е. вводим BTFSC 0x9d,0,0 - регистр 0x9d бит 0 банк быстрого доступа )
Жаль пока что с 18 только теоретически занимался...
:dont_know:
Да... там еще можно "быстрый стек" для прерываний высшего уровня попользовать...разбив имеющиеся на два приоритета...
:roll:

Re: TMR1 & RTC

Пт сен 08, 2017 16:27:55

Ну я тоже, в 18-х кодах "плаваю". на асме только 16-е программировал. Так вот я тож поначалу написал BTFSC PIE1, TMR1IE и получал ошибки. Оказалось, что TMR1IE является дефайном (нашел в pic18f24j11.inc):
Код:
#define BANKMASK(addr) ((addr) and 0FFh)
#define TMR1IE                           BANKMASK(PIE1), 0
Поэтому я так его написал. И он развернулся в BTFSC ((PIE1) and 0FFh), 0

Добавлено after 2 hours 29 minutes 13 seconds:
Странно. Почитал еще и выяснилось, что если оператору передаётся 12-ти битный адрес операнда, то MPASM сам правильно его обрезает и расставляет access и banked. В данном случае это не сработало, по причине ихнего же макроса BANKMASK который обрезал адрес до 8 бит. Но это же из их родного inc-файла! Не понимаю.

Тут еще почитал один блог, где один человек в пух и прах разносил этот компилятор обвиняя микрочип в намеренном раздувании и торможении кода в бесплатном режиме. Но потом высказал мысль, что судя по новым веяниям, микрочип всё же хочет вскоре отказаться от разделения Free/Pro и выдать инструмент за бесплатно... Только я посмотрел на дату статьи - 2014-й год, январь и понял, что чуда не дождусь.
Ответить