|
Снова о TMR1 в PIC, Реализация Timer1_Errata на си |
|
|
|
Jun 8 2011, 11:19
|
Частый гость
 
Группа: Участник
Сообщений: 181
Регистрация: 26-11-10
Пользователь №: 61 198

|
Коллеги, прошу пардону если этот вопрос уже изжеван, но не сочтите за труд - объясните для "особо одаренных". Заметил в своем девайсе на PIC16LF628A незначительное (но больше расчетного) отставание часов - решил разобраться. Часы реализованы "классически" - прерывания по переполнению TMR1 в режиме асинхронного счета от внешнего кварца 32768 Гц: Код volatile signed long int actualTime;
void interrupt isr (void){ //... if (TMR1IF && TMR1IE){ // TMR1 Handler TMR1H |= 0x80; //Загрузка таймера для получения периода 1с. actualTime++; } //... } Выкурил микрочиповскую еррату по этому вопросу, и выяснил, что если загружать регистры таймера в неподходящий момент, то таймер может пропустить один такт. Это понятно. Поскольку у меня в основном коде периодически запрещаются прерывания на относительно долгие промежутки времени, ясно что такие неподходящие моменты имеют место быть. Решил сделать как написано в еррате - прежде чем загружать регистры таймера, дождаться его очередного приращения. Это тоже понятно. Но хоть убей не пойму, ЗАЧЕМ дрыгать клок с внешнего на системный и обратно, зачем выключать/включать таймер: Код BTFSC TMR1L,0 GOTO $-1 BTFSS TMR1L,0 GOTO $-1 ;Timer has just incremented, 31 μs before next rising edge to ;complete reload Update: BCF T1CON,TMR1CS ;Select system clock for Timer1 BSF TMR1H,7 ;Timer1 high byte 0x80 BCF T1CON,TMR1ON ;Timer1 off BSF T1CON,TMR1C ;Select external crystal BSF T1CON,TMR1ON ;Timer1 on Разве недостаточно просто делать запись в TMR1 сразу же после инкремента последнего: Код void interrupt isr (void){ //... if (TMR1IF && TMR1IE){ // TMR1 Handler while (TMR1L == TMR1L){ // Ожидание инкремента TMR1 ; } TMR1H |= 0x80; //Загрузка таймера для получения периода 1с.
actualTime++; } //... } В дизассемблере это выглядит так: Код 93: while (TMR1L == TMR1L){ 223 1283 BCF STATUS, 0x5 224 080E MOVF TMR1L, W 225 060E XORWF TMR1L, W 226 1903 BTFSC STATUS, 0x2 227 2A23 GOTO 0x223 94: ; 95: } 96: TMR1H |= 0x80; 228 178F BSF TMR1H, 0x7 Или я морочу голову себе и людям и надо сделать точно так как в примере из ерраты?
|
|
|
|
|
 |
Ответов
(1 - 4)
|
Jun 8 2011, 14:21
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(stas00n @ Jun 8 2011, 15:19)  Заметил в своем девайсе на PIC16LF628A незначительное (но больше расчетного) отставание часов - решил разобраться. Часы реализованы "классически" - прерывания по переполнению TMR1 в режиме асинхронного счета от внешнего кварца 32768 Гц: Код volatile signed long int actualTime; signed ? Цитата Поскольку у меня в основном коде периодически запрещаются прерывания на относительно долгие промежутки времени ... Беда. Цитата Но хоть убей не пойму, ЗАЧЕМ дрыгать клок с внешнего на системный и обратно, зачем выключать/включать таймер: Суть проблемы: внешний кварц асинхронен системному клоку. Если инкремент TMR1 от внешнего кварца придётся на незавершённую запись в TMR1 (н-р, от "TMR1H |= 0x80;"), то он будет пропущен. А если наоборот, то не сможет корректно выполниться "TMR1H |= 0x80;". При тактировании таймера от системного клока такого гарантированно не случится. Я бы только перед "TMR1CS = 0;" делал "T1CKPS1 = 1;", чтобы избежать инкремента TMR1 от системного клока, и потом, соответственно, наоборот. И, возможно, "while (TMR1L == TMR1L) continue;" оформил бы на ассемблере, съэкономив 1 такт.  Если Вы не укладываете контроллер в спячку, то проще сделать T1SYNC = 0. Тогда, скорее всего, будет достаточно Вашего исходного кода.
|
|
|
|
|
Jun 8 2011, 15:48
|
Частый гость
 
Группа: Участник
Сообщений: 181
Регистрация: 26-11-10
Пользователь №: 61 198

|
Цитата(xemul @ Jun 8 2011, 17:21)  signed ? Да. Уж не помню из каких соображений. Девайс работает с компом, считает unix time, а оно знаковое, потому сделал так же. Но до 2038 года это не принципиально  Цитата(xemul @ Jun 8 2011, 17:21)  Беда. Почему? Насчет "длительных интервалов" я несколько утрировал - это десятки, максимум сотни микросекунд, прерывания запрещаются на время действий с многобайтовыми volatile переменными (к примеру, с тем же временем), так что прерывание пропущено не будет, просто отработает с некоторой задержкой. Цитата(xemul @ Jun 8 2011, 17:21)  если инкремент от внешнего кварца TMR1 придётся на незавершённую запись в TMR1 (н-р, от "TMR1H |= 0x80;"), то он будет пропущен. Насколько я понял, в еррате сказано, что инкремент будет пропущен, если запись в TMR1 произвести как бы во время низкого уровня тактового сигнала, - то есть, если первый фронт тактового сигнала после события записи - положительный. Чтобы этого не происходило, они и рекомендуют дождаться инкремента, (он идет по положительному фронту), и немедленно делать запись в таймер. По моим подсчетам, на это есть примерно 15 мкс или чуть меньше. При системной тактовой 4 МГц на установку бита в TMR1H уйдет микросекунда, ну плюс еще парочка на "детектирование момента", то есть, по идее времени достаточно, чтобы первый фронт после записи был отрицательным, так? Зачем остальные телодвижения? Единственное мое предположение - для медленного системного такта (если, к примеру используется LP генератор?) Цитата(xemul @ Jun 8 2011, 17:21)  А если наоборот, то не сможет корректно выполниться "TMR1H |= 0x80;". При тактировании таймера от системного клока такого гарантированно не случится. Немного не понял, что значит "наоборот"? Цитата(xemul @ Jun 8 2011, 17:21)  Если Вы не укладываете контроллер в спячку, то проще сделать T1SYNC = 0. Тогда, скорее всего, будет достаточно Вашего исходного кода. Спячка как раз-таки используется, причем 99.9% времени, так что синхронный режим не катит...
|
|
|
|
|
Jun 8 2011, 17:11
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(stas00n @ Jun 8 2011, 19:48)  Насколько я понял, в еррате сказано, что инкремент будет пропущен, если запись в TMR1 произвести как бы во время низкого уровня тактового сигнала, - то есть, если первый фронт тактового сигнала после события записи - положительный. Чтобы этого не происходило, они и рекомендуют дождаться инкремента, (он идет по положительному фронту), и немедленно делать запись в таймер. По моим подсчетам, на это есть примерно 15 мкс или чуть меньше. При системной тактовой 4 МГц на установку бита в TMR1H уйдет микросекунда, ну плюс еще парочка на "детектирование момента", До 8 мкс в Вашем варианте (в текущей интерпретации компилятора), до 4 мкс в эрратином. Цитата то есть, по идее времени достаточно, чтобы первый фронт после записи был отрицательным, так? Зачем остальные телодвижения? Единственное мое предположение - для медленного системного такта (если, к примеру используется LP генератор?) По идее - достаточно. Попробуйте, может и сработает. Цитата Немного не понял, что значит "наоборот"? Я, когда строил RTC на пике (16Cкакой-то, давно было), столкнулся с другой веселухой: "bsf TMR1H, 7" иногда давала причудливые результаты на выходе. Последовательными обрезаниями пришёл к выводу, что приключалось оно при попадании / перепада клока между фазами Q2 и Q4 исполнения команды. Эрратин воркэраунд от этого тоже спасает. Возможно, сейчас такой траблы уже и нет. Тогда же просто вынес RTC в какой-то мелкий пик (вспомнил - 12C508) и завёл его генератор на часовом кварце.
|
|
|
|
|
Jun 8 2011, 21:30
|
Частый гость
 
Группа: Участник
Сообщений: 181
Регистрация: 26-11-10
Пользователь №: 61 198

|
В общем, помудохался я немного и решил что не фиг изобретать велосипед, вставлю-ка я в код пример из ерраты и все. Сроки по проекту поджимают. Сейчас посмотрел на ассемблерный код своей конструкции while(TMR1L == TMR1L){} - просто идиотизм  срабатывать будет в лучшем случае через раз, в худшем - вообще все нафиг зависнет прямо в прерывании... xemul, спасибо за консультацию!
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|