Перейти к содержанию
    

Работа таймера TMR0 (PIC16)

На данный момент проблема в смутном описании бита T0CS регистра OPTION:

бит 5: T0CS: Выбор тактового сигнала для TMR0

1 = внешний тактовый сигнал с вывода RA4/T0CKI

0 = внутренний тактовый сигнал CLKOUT

внутренний тактовый сигнал - это Fosc/4?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На данный момент проблема в смутном описании бита T0CS регистра OPTION:

 

внутренний тактовый сигнал - это Fosc/4?

Да. На блок-схеме TMR0 всё разрисовано.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо.

Но лучше бы авторы даташита делали его в одном стиле, с большим числом информации.

Пробую получить с помощью 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;
    }
} 
}

вроде все правильно , а в итоге "фиг вам". :smile3046:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

программка жесткая какая-то, в частности выносы в функции меня в данном случае только больше запутывают :)

 

ну а по теме: если вы хотите использовать прерывания то добавьте вот это после main() { }

interrupt isr() {

if (T0IF) {
чего то делаем
T0IF = 0;
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Обработчик прерываний для мелких пиков должен выглядеть

interrupt void isr(void)
{
   if(T0IF)
// или if(T0IF && T0IE)
   {
      ...
   }

   if(TMR1IF)
   {
      ...
   }
}

Имя функции обработчика прерывания не важно, но слово interrupt в её объявлении должно присутствовать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

пишите в конце своей программы вот это:

interrupt isr() {

if (T0IF) {
tmp100 = tmp100 + 1;
TMR0 = TMR0 + 100;
T0IF = 0;
}

 

Вызывать ничего не надо, это прерывание, оно автоматически вызывается в случае T0IF = 1

 

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если

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;
}

то программа просто не работает: цифры не меняются.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

писать надо после main() { } я ведь это уже два раза написал

 

зы. по поводу программы, естественно ее еще отлаживать нужно, я просто указал как правильно обработку прерывания оформлять

 

ззы. рекомендую вот тут FAQ почитать

Изменено пользователем sargein

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

xemul,

при чем тут

TMR1IF

?

Я забыл добавить ... после if(TMR1IF) {...}, т.к. описывал общий случай.

У мелких пиков одноуровневая система прерываний, и все возможные прерывания обрабатываются в одной функции.

Больше наивных предположений и вставок на подумать делать не буду.

Если

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).

А если ...

то программа просто не работает: цифры не меняются.

Наверное, так прога написана. Предлагаете нам догадаться, как именно?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо за подсказку.

Буквари ценны тем, что быстро узнаешь структуру простейшей программы. А вот реализация у них, мягко говоря, хромает.

Прога не работает оттого, что c TMR0 проблема. TMR1 в этом же коде прекрасно работает без функции обработчика прерываний. Вся беда в том, что в программе мне надо 3 временных промежутка...

Основное достоинство самоучителей - что в одном не упомянут, в другом есть.

Изменено пользователем loghir

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вся беда в том, что в программе мне надо 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;
      }
   }
}

Это, естесно, "рыба" на подумать, хоть я и обещал больше так не делать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

все требуемые интервалы сделайте на программных счётчиках.

Спасибо, принцип мне понятен. И таймер удалось запустить. Как и сказано в #8, обработчик прервываний вызывается при появлении флага.

Но вот связать настройку предделителя с частотой на выходе... При кварце 4 МГц Fosc/4 = 1 МГц. Как считать частоту прерываний при коэфф. деления предделителя 2, 4... 256 ?

Изменено пользователем loghir

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Уважаемый loghir,

 

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

 

Прерывание по таймеру работает? Да. Кто мешает в эту процедуру вставить переменную, инкрементироать ее и ваводить на свободный pin. Смотрим осциллографом частоту прерываний. Дальше играем с прескалером. Это что, фантастически сложно? Десять минут. Зато вы сами в чем-то разберетесь.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...