|
|
  |
Работа таймера TMR0 (PIC16) |
|
|
|
May 23 2011, 15:43
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
На данный момент проблема в смутном описании бита T0CS регистра OPTION: Цитата бит 5: T0CS: Выбор тактового сигнала для TMR0 1 = внешний тактовый сигнал с вывода RA4/T0CKI 0 = внутренний тактовый сигнал CLKOUT внутренний тактовый сигнал - это Fosc/4?
|
|
|
|
|
May 25 2011, 05:42
|
Участник

Группа: Участник
Сообщений: 41
Регистрация: 14-01-10
Из: Россия, Самара
Пользователь №: 54 801

|
Блок-схема TMR0 тебе в помощь!
Эскизы прикрепленных изображений
|
|
|
|
|
May 25 2011, 11:20
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Спасибо. Но лучше бы авторы даташита делали его в одном стиле, с большим числом информации. Пробую получить с помощью TMR0 временной интервал в 1 сек - не работает. Цифры не меняются. Код // MK pic16f72
#include <pic.h>
__CONFIG (XT & WDTDIS & PWRTDIS & BOREN & UNPROTECT); #define XTAL FREQ 4MHZ #define all_1 RC4 // общие провода 1-4 разрядов #define all_2 RC5 #define all_3 RC6 #define all_4 RC7 unsigned char time1 = 0; // объявляем глобальные переменные счетчика 1-4 разрядов и обнуляем их. unsigned char time2 = 0; unsigned char time3 = 0; unsigned char time4 = 0; unsigned char tmp100 = 0; bit DDF = 0; // переменная "защелка"
void podgot (void) { ADCON1 = 0x07; // отключение АЦП TRISA = 0b111111; // (0/1 - выход/вход, нумерация битов в регистре справо-налево) TRISB = 0b00000000; // (0/1 - выход/вход, нумерация битов в регистре справо-налево) TRISC = 0b00000000; // (0/1 - выход/вход, нумерация битов в регистре справо-налево) RBPU = 1; PORTA = 0; PORTB = 0b11111111; PORTC = 0b11111111; }
// для общего анода const unsigned char arr_seg [10] = { 0b00000011, // «0» (справа-налево) 0-горит 0b10011111, // «1» 0b00100101, // «2» 0b00001101, // «3» 0b10011001, // «4» 0b01001001, // «5» 0b01000001, // «6» 0b00011111, // «7» 0b00000001, // «8» 0b00001001, // «9» };
void init (void) { // настройка TMR0 на 100 Hz (сотые доли секунды). // регистр OPTION T0CS = 0; // bit 5 TMR0 Выбор источника сигнала 0 - Fosc/4 (внутренний); 1 - подача на T0CKI T0SE = 0; // bit 4 TMR0 Выбор фронта приращения TMR0 при внешнем тактовом сигнале (0-передний фронт) PSA = 0; // bit 3 Выбор включения предделителя: 0 - перед TMR0, 1 - перед WDT PS2 = 1; // bit 2 Настройка предделителя PS1 = 0; // bit 1 Настройка предделителя PS0 = 1; // bit 0 Настройка предделителя TMR0 = 100; // предзагрузка TMR0, сбрасывается при переполнении. // конец настройки TMR0 на 100 Hz (сотые доли секунды). }
void stTMR0 (void) // запуск TMR0 { GIE = 1; // разрешены все немаскированные прерывания PEIE = 1; // разрешены все немаскированные прерывания от переферийных модулей T0IE = 1; // Разрешение прерывания по переполнению TMR0 T0IF = 0; // сброс флага прерывания по переполнению TMR0 }
void intTMR0 (void) // обработчик прерываний TMR0 (вариант для 1 сек): { while (tmp100 < 100) { if(T0IF == 1) { tmp100 = tmp100 + 1; // прибавление до 100 (в сумме 1 сек) GIE = 1; // разрешены все немаскированные прерывания T0IF = 0; // сброс флага прерывания по переполнению TMR0 T0IE = 1; // Разрешение прерывания по переполнению TMR0 TMR0 = TMR0 + 100; // предзагрузка TMR0 } } time1 = time1 + 1; }
void main (void) { podgot(); init(); for(;;) { all_1 = 0; PORTB = arr_seg [time1]; if ((RA0 == 0)|(DDF == 1)) // запуск TMR0 или продолжение счета при DDF == 1 { DDF = 1; stTMR0(); // запуск TMR0 intTMR0(); // обработчик прерываний TMR0 (вариант для 1 сек): } if (RA1 == 0) // останов TMR0 { DDF = 0; T0IE = 0; // запрет прерывания по переполнению TMR0 } if (time1 > 9) // чтобы "time1" не вышла за пределы массива { time1 = 0; } } } вроде все правильно , а в итоге "фиг вам".
|
|
|
|
|
May 25 2011, 11:51
|
Участник

Группа: Свой
Сообщений: 72
Регистрация: 31-01-10
Из: Минск
Пользователь №: 55 176

|
программка жесткая какая-то, в частности выносы в функции меня в данном случае только больше запутывают  ну а по теме: если вы хотите использовать прерывания то добавьте вот это после main() { } Код interrupt isr() { if (T0IF) { чего то делаем T0IF = 0; }
|
|
|
|
|
May 25 2011, 11:52
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Обработчик прерываний для мелких пиков должен выглядеть Код interrupt void isr(void) { if(T0IF) // или if(T0IF && T0IE) { ... }
if(TMR1IF) { ... } } Имя функции обработчика прерывания не важно, но слово interrupt в её объявлении должно присутствовать.
|
|
|
|
|
May 25 2011, 12:46
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
sargein, как работает оператор Код if (T0IF) ? xemul, при чем тут Код TMR1IF ? как правильно объявить функцию обработчика прерываний и как ее вызвать? Код interrupt void isr(void) при вызове выдает ошибку Код interrupt isr(); Error [195] D:\Work\PIC_CI\My_program\work_program_timer_dima\1.c; 95.1 expression syntax
Сообщение отредактировал loghir - May 25 2011, 13:47
|
|
|
|
|
May 25 2011, 13:58
|
Участник

Группа: Свой
Сообщений: 72
Регистрация: 31-01-10
Из: Минск
Пользователь №: 55 176

|
пишите в конце своей программы вот это: Код interrupt isr() {
if (T0IF) { tmp100 = tmp100 + 1; TMR0 = TMR0 + 100; T0IF = 0; } Вызывать ничего не надо, это прерывание, оно автоматически вызывается в случае T0IF = 1
|
|
|
|
|
May 25 2011, 14:26
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Если Код interrupt isr() {
if (T0IF) { tmp100 = tmp100 + 1; TMR0 = TMR0 + 100; T0IF = 0; } писать до main, компилятор выдает ошибку: Warning [349] D:\Work\PIC_CI\My_program\work_program_timer_dima\1.c; 69.1 non-prototyped function declaration for "" Warning [349] D:\Work\PIC_CI\My_program\work_program_timer_dima\1.c; 69.1 non-prototyped function declaration for "isr" А если до main так: Код void interrupt isr (void) {
if (T0IF) { tmp100 = tmp100 + 1; TMR0 = TMR0 + 100; T0IF = 0; } то программа просто не работает: цифры не меняются.
|
|
|
|
|
May 25 2011, 14:37
|
Участник

Группа: Свой
Сообщений: 72
Регистрация: 31-01-10
Из: Минск
Пользователь №: 55 176

|
писать надо после main() { } я ведь это уже два раза написал зы. по поводу программы, естественно ее еще отлаживать нужно, я просто указал как правильно обработку прерывания оформлять ззы. рекомендую вот тут FAQ почитать
Сообщение отредактировал sargein - May 25 2011, 14:44
|
|
|
|
|
May 25 2011, 14:55
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(loghir @ May 25 2011, 16:46)  xemul, при чем тут Код TMR1IF ? Я забыл добавить ... после if(TMR1IF) {...}, т.к. описывал общий случай. У мелких пиков одноуровневая система прерываний, и все возможные прерывания обрабатываются в одной функции. Больше наивных предположений и вставок на подумать делать не буду. Цитата(loghir @ May 25 2011, 18:26)  Если Код interrupt isr() {
if (T0IF) { tmp100 = tmp100 + 1; TMR0 = TMR0 + 100; T0IF = 0; } писать до main, компилятор выдает ошибку: Warning [349] D:\Work\PIC_CI\My_program\work_program_timer_dima\1.c; 69.1 non-prototyped function declaration for "" Warning [349] D:\Work\PIC_CI\My_program\work_program_timer_dima\1.c; 69.1 non-prototyped function declaration for "isr" В мануале писс есть описания всех сообщений компилятора. Warning - предупреждение (в Вашем случае - о небрежном стиле программирования). Не знаю, какими букварями по С Вы пользуетесь, но, имхо, в любом написано, что функция должна быть объявлена до её использования. Т.к. обработчик прерывания явно не вызывается, то его всегда стоит объявлять явно. Код void interrupt isr (void); // объявление (прототип) функции int foo(int boo); // объявление другой функции с параметром и возвращаемым значением
bit fTMR0;
void main(void) { if(fTMR0) { foo(...); fTMR0 = 0; } }
void interrupt isr (void) // сама функция (реализация) { if(T0IF) { T0IF = 0; fTMR0 = 1; } }
int foo(int boo) { return boo+1; } Обычно прототипы функций помещают до main(), а реализацию - после или вообще в другом файле (тогда функция объявляется со словом extern). Цитата А если ... то программа просто не работает: цифры не меняются. Наверное, так прога написана. Предлагаете нам догадаться, как именно?
|
|
|
|
|
May 25 2011, 16:39
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Спасибо за подсказку. Буквари ценны тем, что быстро узнаешь структуру простейшей программы. А вот реализация у них, мягко говоря, хромает. Прога не работает оттого, что c TMR0 проблема. TMR1 в этом же коде прекрасно работает без функции обработчика прерываний. Вся беда в том, что в программе мне надо 3 временных промежутка... Основное достоинство самоучителей - что в одном не упомянут, в другом есть.
Сообщение отредактировал loghir - May 25 2011, 18:17
|
|
|
|
|
May 26 2011, 07:26
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(loghir @ May 25 2011, 20:39)  Вся беда в том, что в программе мне надо 3 временных промежутка... Определите минимально необходимый дискрет времени, заведите какой-нить аппаратный таймер с таким дискретом (пока контроллеру делать нечего - без разницы, с прерыванием или опросом), все требуемые интервалы сделайте на программных счётчиках. Код unsigned char FRCnt;
void main(void) { ... for(;;) { if(T0IF) { TMR0 += TMR0_PRESET; // TMR0_PRESET - константа, расчёт которой можно поручить препроцессору С, // задав частоту тактирования таймера и тот самый дискрет FRCnt++; // просто free-run counter, который поможет отсчитывать бОльшие интервалы // синхронные задачи // вызываемые на каждом дискрете dynamic_indication(); timers(); // вызываемые, н-р, на каждом 16-ом дискрете if(!(FRCnt & (16-1))) { keyscan(); // сканирование кнопок keyparse(); // разбор нажатий/отжатий кнопок timers_16(); } // вызываемые на каждом 256-ом дискрете if(!FRCnt) { запорожскиеказакипишутписьмотурецкомусултану(); timers_256(); FRCnt1++; // эта музыка будет вечной } } // а здесь можно разместить какие-нить асинхронные контроллеру задачи // и неспешные задачи, которые можно выполнять по кускам ... } }
void keyparse(void) { if(Key0.b.Pressed) { LED0_OnTmr = LED_MAX_ON_TIME; } if(Key0.b.Released) { LED0 = 0; LED0_OnTmr = 0; } }
void timers16(void) { if(LED0_OnTmr) { if(!--LED0_OnTmr) // в пиках сюда укладывается инструкция decfsz, // поэтому удобнее использовать down-counter'ы { LED0 = 0; } else { LED0 = 1; } } } Это, естесно, "рыба" на подумать, хоть я и обещал больше так не делать.
|
|
|
|
|
May 26 2011, 16:03
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Цитата все требуемые интервалы сделайте на программных счётчиках. Спасибо, принцип мне понятен. И таймер удалось запустить. Как и сказано в #8, обработчик прервываний вызывается при появлении флага. Но вот связать настройку предделителя с частотой на выходе... При кварце 4 МГц Fosc/4 = 1 МГц. Как считать частоту прерываний при коэфф. деления предделителя 2, 4... 256 ?
Сообщение отредактировал loghir - May 26 2011, 16:11
|
|
|
|
|
May 26 2011, 17:39
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Цитата я убедился в том, что удивительный лодырь, обладающий определенным талантом напрячь окружающих на решение ваших задач. Сами думать вы, почему то, категорически не хотите. Я всего лишь прошу инфу по работе предделителя. А применю формулу расчета частоты прерываний самостоятельно. Лодырь тот, кто не написал ее в даташите. Я понимаю, что надо все делать через...осциллограф в данном случае. Может, сразу частотомер купить? (ну не даст осциллоскоп нужной точности...) Для эмпирического нахождения секретной формулы, которую никто не знает, а я напрягаю бедный форум ее выводить?
|
|
|
|
|
May 26 2011, 19:11
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(loghir @ May 26 2011, 20:03)  Но вот связать настройку предделителя с частотой на выходе... При кварце 4 МГц Fosc/4 = 1 МГц. Как считать частоту прерываний при коэфф. деления предделителя 2, 4... 256 ? Можно в уме, можно в столбик на бамажке, можно в куркуляторе. По-любому, сначала нужно представлять, как оно работает. 1. Задаёте частоту тактирования таймера и желаемую частоту F (или период) его прерываний. 2. Находите частное (или произведение) первого и второго. Обзовём его A. 3. (ceil(log2(A)) - (разрядность таймера)) даст Вам требуемый коэффициент деления прескейлера 4. A/2^(ceil(log2(A)) - (разрядность таймера)) даст Вам TMRx_PRESET В случае, если у Вас есть причины заставить TMRx работать с определённым коэффициентом деления прескейлера, п.3. пропускается. Когда надоедает считать самому, после недолгих раздумий эта работа поручается препроцессору. Правда, п.1 таки приходится выполнять самому. Цитата Я всего лишь прошу инфу по работе предделителя... Лодырь тот, кто не написал ее в даташите. Предделитель делит. Трансформатор делает "у-у-у-у". "- Лодка? Она утонула." Капитан Очевидность уже спешит к Вам на помощь. Цитата Я понимаю, что надо все делать через...осциллограф в данном случае. В МПЛаб есть симулятор (МПСим), View->Logic Analizer позволит Вам в данном случае съэкономить на частотомере и осциллоскопе. Если ещё и ман к МПСим прочитаете, то не придётся так часто менять ZF-сокет. А потом и до ICSP доберётесь.
|
|
|
|
|
May 27 2011, 02:20
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
xemul, спасибо. Буду разбираться. Проблема в том, что без предзагрузки таймер физически не может выдать желаемую частоту прерываний... Пока что считаю для 8-разрядного таймера так: Частота прерываний TMR0 (при Fosc/4, для 4 МГц, коэфф. - 256) = (1000000)/256/256 = 15,2587890625 Гц Частота прерываний TMR0 (при Fosc/4, для 4 МГц, коэфф. - 64) = (1000000)/256/64 = 61,03515625 Гц Частота прерываний TMR0 (при Fosc/4, для 4 МГц, коэфф. - 16) = (1000000)/256/16 = 244,184375 Гц P.S. srergeeff, Мозги - вот главный прибор разработчика электронной аппаратуры. Тому, кто первый начал, нечего жаловаться по поводу ответного ехидства.
|
|
|
|
|
May 27 2011, 06:39
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(loghir @ May 27 2011, 06:20)  Буду разбираться. Проблема в том, что без предзагрузки таймер физически не может выдать желаемую частоту прерываний... Пока что считаю для 8-разрядного таймера так: Частота прерываний TMR0 (при Fosc/4, для 4 МГц, коэфф. - 256) = (1000000)/256/256 = 15,2587890625 Гц Частота прерываний TMR0 (при Fosc/4, для 4 МГц, коэфф. - 64) = (1000000)/256/64 = 61,03515625 Гц Частота прерываний TMR0 (при Fosc/4, для 4 МГц, коэфф. - 16) = (1000000)/256/16 = 244,184375 Гц Ну объясните мне, с чем здесь разбираться? Всё на уровне арифметики, если держать в голове 2^n для n хотя бы до 16. Пусть хочется получить 50 Гц. 1000000/50 = 20000 - столько периодов тактовой частоты таймера помещается в 1 период требуемой частоты. Сначала ищете требуемый прескейлер: (1000000/50)/256 = 78.125 Округляете вверх до степени 2: = 128 (в предыдущем посте я сначала неправильно написал, что округлять нужно вниз) Считаете предустановку таймера: (1000000/50)/128 = 156 Далее в симуляторе ставите точку останова в нужном месте и смотрите, насколько врёт таймер. (а врать он будет и из-за целочисленности арифметики, и из-за сброса прескейлера при записи в таймер) Если считать ручками, можно ещё проще. 20000 округляете вверх до степени 2: = 32768 = 2^15 Из 15 на таймер приходится 8 разрядов, значит степень предделителя 7.
|
|
|
|
|
May 27 2011, 10:05
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Цитата Ну объясните мне, с чем здесь разбираться? с информацией из #17. С Протеусом пока нехочу связываться - на реальном железе быстрее. Ненадо ни инсталляху искать, ни разбираться с управлением. Цитата Это инструмент для действительного контроля работы вашей программы на реальном железе не спорю. Но в моем случае глазом видно, что при суммировании временных промежутков прерываний TMR0 частота слишком велика. Мой С1-68 все равно ее точно не покажет, не цифровик чай. (Лодырям вот сюда, на калькулятор http://pictimer.picbingo.com/download/index.php который просчитает и настройку, и предзагрузку, и даже код на С выдаст.)
Сообщение отредактировал loghir - May 27 2011, 10:06
|
|
|
|
|
May 27 2011, 11:10
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(loghir @ May 27 2011, 14:05)  С Протеусом пока нехочу связываться - на реальном железе быстрее. Заниматься такими странностями Вам никто и не предлагал. Отладка в железе может быть быстрее только при наличии железного отладчика или продуманного и отлаженного самопального способа общения с программой. У Вас нет ни того, ни другого, поэтому и тычетесь наугад в потёмках. Я не понимаю, почему Вы упорно не желаете отлаживать свои программы в симуляторе МПЛаба - используемые Вами фичи контроллера он показывает во всех подробностях. А если ещё поймёте, зачем нужны стимулы, то можете справиться и с не-симулируемыми фичами. Через неделю Вы или поймёте, как оно (и контроллер, и симулятор) работает, или ... не поймёте.
|
|
|
|
|
May 29 2011, 14:19
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Цитата Вопрос на засыпку: откуда вы знаете, что ваш обработчик прерывания по таймеру 0 работает? цифры на индикаторе меняются. Но очень быстро. Код void interrupt isr (void) { if (T0IF) { while (tmp100 < 100) // выполняется до тех пор, пока истинно (tmp100 < 100) { T0IE = 0; // Запрет прерываний по переполнению TMR0 tmp100 = tmp100 + 1; // прибавление до 100 (в сумме 1 сек) TMR0 = TMR0 + 100; // предзагрузка TMR0 PS2 = 1; // bit 2 Настройка предделителя PS1 = 0; // bit 1 Настройка предделителя PS0 = 1; // bit 0 Настройка предделителя GIE = 1; // разрешены все немаскированные прерывания T0IF = 0; // сброс флага прерывания по переполнению TMR0 T0IE = 1; // Разрешение прерывания по переполнению TMR0 } time1 = time1 + 1; // счет сотен циклов TMR0. Это число выводится 7-сегментный индикатор. tmp100 = 0; // обнуление счетчика } } где-то ошибка... Но где? Цитата Отладка в железе может быть быстрее только при наличии железного отладчика или продуманного и отлаженного самопального способа общения с программой. У Вас нет ни того, ни другого, поэтому и тычетесь наугад в потёмках. В процессе тыканья отрабатываю самопальный способ. Всего-то надо 2 таймера запустить. Цитата Я не понимаю, почему Вы упорно не желаете отлаживать свои программы в симуляторе МПЛаба нет времени и смысла разбираться, как он работает. (Там наверняка свои глюки есть. Вот будет смеху, если в в симуляторе МПЛаба все заработает, а в железе - нет.) Ради запуска 2-х таймеров?! В моей задаче точность не нужна. Всегда можно положить на таймеры и сделать простым суммированием машинных циклов.
|
|
|
|
|
May 29 2011, 14:36
|
Участник

Группа: Свой
Сообщений: 72
Регистрация: 31-01-10
Из: Минск
Пользователь №: 55 176

|
смутная программка  а не могли бы вы еще раз уточнить, что конкретно нужно в итоге получить?
|
|
|
|
|
May 29 2011, 14:58
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(loghir @ May 29 2011, 18:19)  где-то ошибка... Но где? "- В ДНК." (из анекдота) (я устал комментировать бездумное нагромождение букафф) Цитата В процессе тыканья отрабатываю самопальный способ. Всего-то надо 2 таймера запустить.
нет времени и смысла разбираться, как он работает. (Там наверняка свои глюки есть. Вот будет смеху, если в в симуляторе МПЛаба все заработает, а в железе - нет.) Ради запуска 2-х таймеров?! В моей задаче точность не нужна. Всегда можно положить на таймеры и сделать простым суммированием машинных циклов. Флаг в руки, барабан на шею. Каждый сам себе злобный буратин.
|
|
|
|
|
May 29 2011, 15:34
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
xemul в #14 я всего-навсего спросил, как рассчитать частоту прерываний TMR0 в зависимости от записанного в битах PS0...PS2. И более ничего! Нужную мне частоту я получу простым суммированием. Если много информации, я просто в ней теряюсь. Поэтому я не принимаю во внимание некоторые советы, ибо стараюсь делать программу (и процесс ее написания) попроще.
по поводу обработчика прерываний TMR0: он точно запускается, поскольку цифры на индикаторе меняются, и после нажатия RA1 счет прекращается. Частота прерываний TMR0 после предделителя у меня 100 Гц. Суммируя 100 временных интервалов прерываний я получаю 1 Гц, что и требуется. В #25 я написал мой обработчик прерываний для TMR0. Но цифры на индикаторе очень быстро меняются! Что-то там не так...
|
|
|
|
|
May 29 2011, 16:48
|
Знающий
   
Группа: Свой
Сообщений: 812
Регистрация: 22-01-05
Из: SPb
Пользователь №: 2 119

|
Цитата(loghir @ May 29 2011, 19:34)  xemul в #14 я всего-навсего спросил, как рассчитать частоту прерываний TMR0 в зависимости от записанного в битах PS0...PS2. И более ничего! Эта информация есть в даташите в том месте,где описаны биты PS0...PS2. (описание регистра OPTION). Коэффициент деления, вносимый пресалером, там указан в виде таблички. Точно вычислить коэффициент предзагрузки TMR0 "по формуле" не представляется возможным, поскольку не определен интервал времени между моментом прерывания и моментом предзагрузки - этот интервал зависит от того, как написана ваша подпрограммы обработки прерывания(ППОП). Обычно сначала грубо расчитывается частота прерваний, а более точно она подгоняется в дебагере. Там все очень просто. Ставите точку останова на первом операторе ППОП и замеряете время между двумя остановами (для этого надо открыть и обнулить окно StopWatch). Затем вы корректируете константу предзагрузки в нужную сторону. Цитата по поводу обработчика прерываний TMR0: он точно запускается, поскольку цифры на индикаторе меняются, и после нажатия RA1 счет прекращается. Частота прерываний TMR0 после предделителя у меня 100 Гц. Суммируя 100 временных интервалов прерываний я получаю 1 Гц, что и требуется. В #25 я написал мой обработчик прерываний для TMR0. Но цифры на индикаторе очень быстро меняются! Что-то там не так... У вас там все не так. Не надо трогать в ППОП никаких флагов, кроме флага запроса прерывания. Т.е кроме T0IF = 0; все остальное, включая установки для PS0...PS2. надо делать один раз вначале основной программы. Все переменные, которые вы используете а ППОП (tmp100) надо описывать как volatile . Предзагрузка делается не так: TMR0 = TMR0 + 100; а вот так TMR0 = 100; И еще, если позволите, маленький совет. Позиция "какие м...ки пишут эти даташиты!! Там ничего нельзя понять! Да и советы тут дают бестолковые.." не является оптимальной. Гараздо более продуктивно встать в такую "позу" : "Да, я, конечно, ламер и туго соображаю, но .. Братцы, выручайте!! Курсовик горит, а препод-зверь смотрит на меня как удав на кролика. ПАМАЖИТЕ!!!" Такая позиция, как мне кажется, сможет вызвать к вам сочувствие и желание помочь. В отличие от первой позиции. Удачи!
|
|
|
|
|
May 29 2011, 17:14
|
Участник

Группа: Свой
Сообщений: 72
Регистрация: 31-01-10
Из: Минск
Пользователь №: 55 176

|
ну как то примерно так Код unsigned char tmp100 = 0; unsigned char time1 = 0;
void main(void) { бла бла настройки таймера и прочей фигни
for (;;) { if (tmp100 == 100) { time1++; tmp100 = 0; функция вывода числа time1 на индикатор (); } }
}
void interrupt isr (void) { if (T0IF) { TMR0 = 100; tmp100++; T0IF = 0; } }
|
|
|
|
|
May 29 2011, 17:28
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(SKov @ May 29 2011, 20:48)  Предзагрузка делается не так: TMR0 = TMR0 + 100; а вот так TMR0 = 100; Первый вариант позволяет таки учесть время, прошедшее с момента возникновения прерывания до момента собственно его обработки. Если требуется формирование не единичного интервала, а периода, равномерно размазанного вокруг расчётного значения, то он предпочтительнее. Цитата(sargein @ May 29 2011, 21:14)  Код volatile unsigned char tmp100; ...
|
|
|
|
|
May 29 2011, 18:01
|
Знающий
   
Группа: Свой
Сообщений: 812
Регистрация: 22-01-05
Из: SPb
Пользователь №: 2 119

|
Цитата(xemul @ May 29 2011, 21:28)  Первый вариант позволяет таки учесть время, прошедшее с момента возникновения прерывания до момента собственно его обработки. Не совсем так, т.к. при загрузке TMR0 сбрасывается прескалер. Тогда уж логично сбрасывать не только прескалер, а и сам основной счетчик. В этом случае мы как раз и получаем второй вариант.Ну, на самом деле это в принципе одно и то же (первый и второй вариант). Просто второй вариант действительно предзагрузка, а первый - предсуммирование или преддобавка, что звучит уж совсем коряво  Относительно "равномерно размазанного" интервала я не понял. Если предзагрузку ставить вначале ППОП (до ветвлений и других участков ППОП с неопределенным временем исполнения), то никакой "размазаности" не будет. Будет четкий период.
|
|
|
|
|
May 29 2011, 19:00
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(SKov @ May 29 2011, 22:01)  Не совсем так, т.к. при загрузке TMR0 сбрасывается прескалер. Тогда уж логично сбрасывать не только прескалер, а и сам основной счетчик. В этом случае мы как раз и получаем второй вариант.Ну, на самом деле это в принципе одно и то же (первый и второй вариант). Просто второй вариант действительно предзагрузка, а первый - предсуммирование или преддобавка, что звучит уж совсем коряво  Относительно "равномерно размазанного" интервала я не понял. Если предзагрузку ставить вначале ППОП (до ветвлений и других участков ППОП с неопределенным временем исполнения), то никакой "размазаности" не будет. Будет четкий период. Рассмотрите случай с несколькими источниками прерываний, когда прерывание от таймера возникает после проверки его флага, но внутри ППОП. Сброс прескейлера, в предположении, что он равномерно размазан на все состояния прескейлера, учитывается совершенно отдельно - типа "TMR0 += TMR0_PRESET + 1;" (или, ещё точнее "TMR0 += TMR0_PRESET + 0.5;" - т.е. 1 добавляется через раз).
|
|
|
|
|
May 29 2011, 19:46
|
Знающий
   
Группа: Свой
Сообщений: 812
Регистрация: 22-01-05
Из: SPb
Пользователь №: 2 119

|
Цитата(xemul @ May 29 2011, 23:00)  Рассмотрите случай с несколькими источниками прерываний, когда прерывание от таймера возникает после проверки его флага, но внутри ППОП. Сброс прескейлера, в предположении, что он равномерно размазан на все состояния прескейлера, учитывается совершенно отдельно - типа "TMR0 += TMR0_PRESET + 1;" (или, ещё точнее "TMR0 += TMR0_PRESET + 0.5;" - т.е. 1 добавляется через раз). Случай нескольких прерываний можно заменить, например, случаем, когда в основной программе есть куски с запретом прерываний. Другими словами, мы говорим о случае, когда время реакции на прерывания непредсказуемо. В этом случае надо смотреть на конкретный код программы. Если задержка реакции укладывается в несколько "тиков" таймера, тогда суммирование может нас спасти и обеспечить точность периода прерываний в пределах одного тика таймера. А если задержка может быть большая, то вообще теряется смысл предустановки. Можно рассмотреть и другой случай. Пусть задержка лежит в районе одного тика таймера плюс-минус несколько тактов Fosc/4. В этом случае прибавление константы дает нам "дрожание" периода прерываний примерно на величину одного тика таймера. А если мы выполняем загрузку таймера константой, то "дрожание" периода имеет величину всего лишь нескольких тактов Fosc/4. Так что бывают случаи, когда загрузка лучше суммирования
|
|
|
|
|
May 30 2011, 06:51
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(SKov @ May 29 2011, 23:46)  Так что бывают случаи, когда загрузка лучше суммирования  Я могу обосновать чиста математически, что загрузка в любом случае будет не лучше суммирования в смысле получения минимальной средней (или средней квадратичной - без разницы) ошибки формирования некоторого периода на любом числе периодов. Случай, когда загрузка будет равна по ошибке суммированию, вообще один - прерывания только от таймера с минимальным прескейлером, никаких критических секций в коде. Из очевидных минусов загрузки - собственно загружаемую константу придётся подрихтовать ручками, чтобы учесть задержку от возникновения прерывания до момента загрузки, или вставлять каким-либо образом этот учёт в считалку препроцессора. При суммировании оно учитывается само, и остаётся только решить, компенсировать ли сброс прескейлера или и так сойдёт.
|
|
|
|
|
May 30 2011, 07:29
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Спасибо за гору инфы! Еще бы переварить... Добавил квалификатор volatile а всем переменным, используемым в ППОП. От предзагрузки таймера временно отказался, пока не разберусь, что происходит. Пока мне точность не нужна. Никак не докопаюсь, для чего нужен Код if (T0IF){} ППОП и так вызывается при появлении прерывания по переполнению TMR0. Также не пойму, как в некоторых случаях работает оператор while. В книге по микроС сказано: Цитата Ключевое слово while используется для организации условных циклов. Синтаксис оператора while: while (expression) statement. Оператор statement (тело цикла) выполняется раз за разом пока величина выражения expression не станет ложной. Проверка производится перед выполнением оператора statement. Таким образом, если expression ложно в самом первом проходе, цикл не выполняется. У Кернигана и Ритчи иная точка зрения: Цитата while (выражение) оператор. Здесь вначале вычисляется выражение. Если оно не равно нулю, выполняется оператор, а затем выражение вычисляется снова. Эти действия повторяются до тех пор, пока выражение не станет равным нулю. после этого управление передается на следующий оператор. И где истина?
Сообщение отредактировал loghir - May 30 2011, 07:33
|
|
|
|
|
May 30 2011, 07:40
|
Знающий
   
Группа: Свой
Сообщений: 812
Регистрация: 22-01-05
Из: SPb
Пользователь №: 2 119

|
Цитата(xemul @ May 30 2011, 10:51)  Я могу обосновать чиста математически, что загрузка в любом случае будет не лучше суммирования в смысле получения минимальной средней (или средней квадратичной - без разницы) ошибки формирования некоторого периода на любом числе периодов. Интересна не столько средняя ошибка, сколько ее дисперсия. Она определяет "дрожание" периода прерываний. Я выше приводил пример, когда время задержки между событием, вызывающим прерывание и моментом загрузки гуляет около одного тика таймера.(тик таймера = период срабатывания прескалера). Будем считать, что идеальным вариантом для нас является суммирование прескалера, когда задержка равна точно одному тику. Этот лишний тик мы учтем в прибавляемой константе. Это будет соответствовать нулевой ошибке. Если время задержки в какой-то момент оказалось чуть-чуть меньше тика таймера, то ошибка при суммирование таймера будет равна почти одному тику из-за обнуления прескалера, который к этому моменту накопил почти целый тик. Если задержка чуть-чуть больше одного тика, то при суммировании будет ошибка небольшая. Короче говоря, ошибка при суммировании будет определяться состоянием прескалера на момент суммировния. В среднем это примерно половина тика. В случае загрузки константы в моем примере дрожание периода определяется только интервалом времени, в котором гуляет задержка. Это интервал между "чуть-чуть меньше тика" и "чуть-чуть больше тика". Разница между этими моментами времени может быть существенно меньше времени тика таймера (и даже его половины). И следовательно, дисперсия периода будет небольшой. Чиста математически  Цитата(loghir @ May 30 2011, 11:29)  Никак не докопаюсь, для чего нужен Код if (T0IF){} . Флаг запроса взводится автоматически при возникновении события, могущего вызвать прерывание. А сбрасывать его должен сам программер в ППОП. Сложность в том, что вы попадаете в одно и то же ППОП при срабатывнии прерываний от нескольких разных источников. Вы должны определить, какой источник вас "позвал" в ППОП. Это делается опросом флагов запроса прерывания (по-умному это называется "полинг прерываний".) Если у вас разрешен только один источник прерываний, то эта проверка не нужна. Цитата Также не пойму, как в некоторых случаях работает оператор while. В книге по микроС сказано: У Кернигана и Ритчи иная точка зрения: И где истина? Истина в вине  В смысле - оба источника правы. Т.к. "Ложь" кодируется нулем, а все остальное - "Правда"!
|
|
|
|
|
May 30 2011, 08:18
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(SKov @ May 30 2011, 11:40)  Интересна не столько средняя ошибка, сколько ее дисперсия. Дисперсия == (средняя квадратичная ошибка)^2. Цитата Она определяет "дрожание" периода прерываний. Занятное определение, ну да ладно. Мне это было интересно во времена, когда у пиков система прерываний отсутствовала как класс, а строить какие-то диспетчеры задач и формировать интервалы всё равно приходилось. Повозил немного карандашом, пришёл к выводу... С тех пор математика (точнее, арифметика) работы таймеров не изменилась. Но у каждого кулика своё болото.  Цитата(loghir) Добавил квалификатор volatile а всем переменным, используемым в ППОП. Он нужен только переменным, _модифицируемым_ в прерываниях.
|
|
|
|
|
May 30 2011, 08:42
|
Знающий
   
Группа: Свой
Сообщений: 812
Регистрация: 22-01-05
Из: SPb
Пользователь №: 2 119

|
Цитата(xemul @ May 30 2011, 12:18)  Дисперсия == (средняя квадратичная ошибка)^2. Ну, это если "чиста математически".  Квадратичная ошибка, да еще и в квадрате - это сильно!  А если посмореть в букваре (можно в Википедии), то можно убедиться, что Дисперсия ошибки == средний квадрат отклонения от средней ошибки. Чувствуете разницу?
|
|
|
|
|
May 30 2011, 14:32
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Вот что получилось: Код void init (void) { // настройка TMR0 на 100 Hz (сотые доли секунды). // регистр OPTION T0CS = 0; // bit 5 TMR0 Выбор источника сигнала 0 - Fosc/4 (внутренний); 1 - подача на T0CKI T0SE = 0; // bit 4 TMR0 Выбор фронта приращения TMR0 при внешнем тактовом сигнале (0-передний фронт) PSA = 0; // bit 3 Выбор включения предделителя: 0 - перед TMR0, 1 - перед WDT PS2 = 1; // bit 2 Настройка предделителя на 15 Гц PS1 = 1; // bit 1 Настройка предделителя на 15 Гц PS0 = 1; // bit 0 Настройка предделителя на 15 Гц TMR0 = 0; // предзагрузка TMR0, сбрасывается при переполнении. // конец настройки TMR0 на 100 Hz (сотые доли секунды). } Код void interrupt isr (void) { if (T0IF) // опрос флага прерывания по переполнению TMR0 (чтобы не сработало от другого источника) { while (45 - 1) // выполняется до тех пор, пока ( ) не равно 0. Задержка на 15 Гц * 45. Т.е. на 3 сек. { T0IF = 0; // сброс флага прерывания по переполнению TMR0 } time1 = time1 + 1; // это число выводится 7-сегментный индикатор. T0IF = 0; // сброс флага прерывания по переполнению TMR0 } } Вот только не срабатывает! Как был "0" на индикаторе, так и остался. Вариант Код ... volatile unsigned char tmp100 = 45; volatile unsigned char time1 = 0; ... void interrupt isr (void) { if (T0IF) // опрос флага прерывания по переполнению TMR0 (чтобы не сработало от другого источника) { while (tmp100) // выполняется до тех пор, пока tmp100 не равно 0. Задержка на 15 Гц * 45. Т.е. на 3 сек. { tmp100 = tmp100 - 1; // изменение переменной-задержки T0IF = 0; // сброс флага прерывания по переполнению TMR0 } time1 = time1 + 1; // это число выводится 7-сегментный индикатор. tmp100 = 45; // обнуление счетчика T0IF = 0; // сброс флага прерывания по переполнению TMR0 } } не изменяет скорость счета при изменении tmp100. Попытка написать Код void interrupt isr (void) после main() вызывает ощибку при компиляции.
Сообщение отредактировал loghir - May 30 2011, 14:33
|
|
|
|
|
May 30 2011, 15:28
|
Знающий
   
Группа: Свой
Сообщений: 812
Регистрация: 22-01-05
Из: SPb
Пользователь №: 2 119

|
Цитата(loghir @ May 30 2011, 18:32)  Вот что получилось: ... while (45 - 1) Это что такое? Бесконечный цикл? У вас проблемы не с микроконтроллером или с прерываниями. У вас проблемы с элементарным программированием.
|
|
|
|
|
May 30 2011, 15:37
|
Участник

Группа: Свой
Сообщений: 72
Регистрация: 31-01-10
Из: Минск
Пользователь №: 55 176

|
Цитата(loghir @ May 30 2011, 17:32)  while (45 - 1) офигенное условие Я же на прошлой странице привел фактически готовое решение, зачем столько хрени пихать в обработчик прерываний?
|
|
|
|
|
May 30 2011, 17:02
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Цитата У вас проблемы с элементарным программированием. я это изначально не скрывал. Цитата Я же на прошлой странице привел фактически готовое решение, зачем столько хрени пихать в обработчик прерываний? оно не работает. Как и while в обработчике прерываний. sergeeff, если вам что-то не нравится - вот и не реагируйте на здоровье. Будет меньше спама.
|
|
|
|
|
May 31 2011, 07:56
|
Участник

Группа: Участник
Сообщений: 63
Регистрация: 13-03-11
Пользователь №: 63 577

|
Всем большое спасибо!!!!!!! Получилось запустить TMR0  .
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|