|
|
  |
Работа таймера 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
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|