PIC мк. Эксперимент №3. Использование таймера TMR0.

Задача: получить импульсы с частотой 5Гц.

Исходный материал: PIC16f628a и простенькая devboard.

Таймер TMR0 применяется во многих программах и имеет следующие полезные опции:

  • 8 битный таймер/счетчик
  • Программируемый выбор внешнее/внутреннее тактирование
  • По переполнению генерирует прерывание

В этом эксперименте пока что обойдемся функциями самого таймера без прерываний.

За опции таймера TMR0 отвечает регистр OPTION.  Понять работу таймера помогает очень наглядная картинка из ресурса mikroe:
pic

А вот собственно биты регистра OPTION:
bits

  • RBPU – PORTB подтяжки

    • 1 – выключены

    • 0 – включены

  • INTEDG – Прерывание по спадающему/возрастающему импульсу тактирования
    • 1 – По возрастающему импульсу

    • 0 – По спадающему импульсу

  • T0CS – Режим таймер/счетчик
    • 1 – Тактирование от внешнего источника через пин RA4.

    • 0 – Таймер использует внутренний осциллятор (Fosc/4).

  • T0SE – TMR0 Срабатывание по спадающему/возрастающему импульсу тактирования

    • 0 – Срабатывание по спадающему импульсу

    • 1 – Срабатывание по возрастающему импульсу

  • PSA – Подключение предделителя
    • 1 -Предделитель подключен к сторожевому таймеру

    • 0 – Предделитель подключен к таймеру

  • PS2, PS1, PS0 – Биты, которые определяют на сколько мы будем делить нашу частоту тактирования

PS2 PS1 PS0 TMR0 WDT
0 0 0 1:2 1:1
0 0 1 1:4 1:2
0 1 0 1:8 1:4
0 1 1 1:16 1:8
1 0 0 1:32 1:16
1 0 1 1:64 1:32
1 1 0 1:128 1:64
1 1 1 1:256 1:128

Теперь все что нужно знать у нас есть. Исходя из задачи нам нужно получить импульсы длиной 200 мс, от кварца мы получаем 4/4 =  1 МГц ~ 1 мкс. А значит за один прогон таймера мы можем получить максимум 256 мкс, маловато… Но ничего, введем переменную


#include <htc.h>
#define _XTAL_FREQ 4000000

__CONFIG(WDTDIS & UNPROTECT & MCLREN);

unsigned char cnt = 0;
unsigned char i = 0;
unsigned char srb = 0;
void main() {
TRISB = 0b01111111;
RB7 = 0;
for (;;) {
 OPTION = 0b11010111; // считаем по 256 мкс
 for (i=1;i<4;i++) {                // 192 мс
 TMR0 = 0;
 while (TMR0 < 250)  { cnt++; }
 }
 OPTION = 0b11010100; // считаем по 32 мкс
 TMR0 = 0; cnt = 0;
 while (TMR0 < 250) {cnt++;}  // 8 мс
 srb = !srb;
 RB7 = srb;
}

}

В результате работы программы получаем такую симпатичную картинку:
oscillograma

25*8 = 200 мс

Исходники можно взять здесь.

PIC мк. Эксперимент №3. Использование таймера TMR0.: 38 комментариев

  1. В тексте слова “Тактирование от внешнего источника через пин RA4” лучше подправить на “Тактирование от внешнего источника через пин T0CKI (RA4*)” и добавить сноску что RA4 справедливо для вашего МК, так как для других это может быть другой пин (например для PIC16F630 это будет RA2) – читать даташит.

  2. Здравствуйте.
    Почему-то у меня не такой результат как у Вас.
    Я только изменил OPTION на OPTION_REG , а то ошибку выдавало и убрал строку с __CONFIG, а то ошибку на нее выдавало.
    Компилятор как у Вас.
    В чем отличие:
    У Вас в длину 8 клеток импульс, а у меня 10. ( настройки осцилографа выставил как у Вас)
    И еще у Вас прямые линии, а у меня они кривые.

    • Если компилятор такой как у меня (hi-tech picc compiler), то ругаться не должно ни на OPTION, ни на __CONFIG.

      Проверьте еще свойства мк в протеусе, частота кварца должна совпадать с нужной.

  3. Да, стоит там 4MHz.
    Вот такой: HI-TECH Software\PICC\9.83\bin\picc.exe
    Как и у Вас.
    ругался не на сам __CONFIG, а на то, что внутри: сторожевой теймер, сборос….

    • Ну вот, у меня 9.71. Но на время импульсов это никак не должно было повлиять.

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

  4. Можете пожалуйста мне пояснить программу?
    TMR0 = 0;
    while (TMR0 < 250) { cnt++; }
    Вот зачем это?
    ___________
    Таймер 8 битный, после перехода 256 в 0 происходит прерывание так? А если ставить делитель на 256 к примеру, как у Вас. То получается прерывание счета таймера должно произойти через 256*256 мкс. Я правильно понимаю?
    ___________
    Вы хотите сделать счет до 500 мкс и так 4 цикла. Получается 20000 мкс. мк = 10^-6, а задача сделать в м = 10^-3.
    Получается 20000мкс = 20 мс, а не 200мс.
    ___________
    Как получается, что 8 клеток высокий уровень, а потом 8 клеток низкий. Ведь каждый раз инвертируется сигнал и по идеи должно же вроде быть, что 1 клетка высокий уровень, следующая низкий, потом опять высокий.
    srb = !srb;
    RB7 = srb;
    ___________
    Не понимаю где тут происходит прерывание, по окончанию счета таймера…. Или же TMR0=0 это и если сброс, чтобы прерывания не было….

    Заранее спасибо!

    • В данном примере прерывания не используются вообще.

      Таймер сам по себе имеет 8 бит, то есть 256 тактов может отщелкать. Теперь по-порядку:
      1. Частота кварца 4 МГц, после встроенного и работающего всегда делителя имеем 1 МГц.
      2. Выставляем предделитель 256, то есть значение в регистре таймера будет увеличиваться каждые 256 мкс (Fin = 1MHz)
      3. Там не 4, а 3 цикла -> 3*256*250 = 192 мс
      4. Затем нам нужно набрать еще 8 мс -> Предделитель на 32 -> 32*250 = 8 мс

      192 + 8 = 200 мс

      Еще раз: никаких прерываний тут нет, все линейно.

  5. В даташите написано, что прерывание происходит при переполнении.
    for (;;) {
    OPTION_REG = 0b11010111;
    for (i=1;i<17;i++) {
    srb = !srb;
    RB3 = srb;
    }
    }
    Получилась ерунда… Хотел сделать. чтобы 1с.
    Вот берем мы делитель 256, 16 циклов, получаем 256*256 (не знаю почему вы писали 250, если 8 бит)*16 = 1 048 576мкс, что соответствует 1с.
    А у меня 25 мкс получаются импульсы….
    Вы не пояснили по поводу:
    TMR0 = 0;
    while (TMR0 < 250) { cnt++; }
    srb = !srb;
    RB7 = srb;

  6. Прерывания флаг устанавливается в любом случае, но чтобы его дальше обрабатывать должен стоять флаг TMR0IE = 1, и дальше должна быть процедура обработки прерываний, я имел ввиду что прерывания не используются в этом коде.

    Естественно ерунда, вы не сбрасываете значение регистра TMR0, да еще и меняете выход каждый цикл. Этот код просто меняет состояние RB3 16 раз, без всяких задержек и всего прочего.

    250 писал как раз потому что while (TMR0 < 250) { cnt++; }, я не ждал пока таймер полностью заполнится, а сбрасывал его по достижении 250 каунтов. Хотите сделать одну секунду, тогда это все будет выглядеть как-то так:
    #include
    #define _XTAL_FREQ 4000000

    __CONFIG(WDTDIS & UNPROTECT & MCLREN);

    unsigned char cnt = 0;
    unsigned char i = 0;
    unsigned char srb = 0;
    void main() {
    TRISB = 0b01111111;
    RB7 = 0;
    OPTION = 0b11010111; // считаем по 256 мкс
    for (;;) {

    for (i=1;i<17;i++)
    {
    TMR0 = 0;
    while (TMR0 < 255) { }; //задержка пока тикает таймер } srb = !srb; RB7 = srb; } }

    • Правильно ли я Вас понял:

      OPTION = 0b11010111; // с этой строки начинает работать таймер
      for (;;) {

      for (i=1;i<17;i++)
      {
      TMR0 = 0; // обнуляем регистр в который записывается значение таймера
      while (TMR0 < 255) { }; //задержка пока тикает таймер. Всмысле? тут же условие и как я понял когда таймер будет 256, то все, дальше никуда не пойдет.
      }
      //вот я не понимаю, объясните пожалуйста еще как так получается что непрерывно идет импульс высокий, а потом низкий такой же длины. ведь чередоваться как я понял должно. маленький импульс высокого уровня, маленький низкого….
      srb = !srb;
      RB7 = srb;

      • Я если честно не знаю уже как тут объяснить, вроде все проще некуда.

        У вас есть 16 циклов с задержкой по 256*256 мкс в течении которых ничего не меняется, прошли эти 16 циклов изменился выходной уровень, программа переместилась в начало глобального цикла и снова 16 циклов по 256*256 мкс.

        Если еще непонятно – рисуйте диаграмму и будет разбираться где у вас пробелы.

        • Да вот это я понять не могу
          for (i=1;i<17;i++)
          {
          TMR0 = 0;
          while (TMR0 < 250) { };
          }
          Как в TMR0 может быть какое-либо число, если он постоянно обнуляется.
          while (TMR0 < 250) { }; — и это разве задержка? Просто проверка условия, такое выполняется как и все программа очень быстро. Это же не __delay_ms()

          • Что значит постоянно? Один раз в цикл, который длится 250 мкс.

            По поводу второго, вы не понимаете как работает оператор while() судя по всему. Это не if, здесь ваш цикл будет выполняться пока условие в скобках true.

            И да, по смыслу это выражение делает тоже самой что делэй.

  7. for (i=1;i<17;i++)
    {//за каждый проход 250 происходит циклов
    TMR0 = 0; // таймер считает после этой команды?
    while (TMR0 < 250) { }; // эта команда делает, чтобы таймер считал только до 250, а когда число становится больше 250, то как я понял он дальше не идет, пока не обнулится и тогда попадает на строку TMR0 = 0 не знаю как он обеспечивает так задержку
    }
    после выполнения этих 17 циклов идет вывод переменной (не знаю как записывается что-то в эту переменную)

    Все так? Что не понял – уточните пожалуйста. Вроде что-то более-менее стал понимать.
    Так же не ясно как сделать, чтобы наоборот таймер считал поступающие сигналы, а не выдавал их.

    • for (i=1;i<17;i++)
      {//за каждый проход 250 происходит циклов
      TMR0 = 0; // таймер считает после этой команды?
      --таймер считает вообще всегда, пока не заполниться до конца
      while (TMR0 < 250) { }; // эта команда делает, чтобы таймер считал только до 250, а когда число становится больше 250, то как я понял он дальше не идет, пока не обнулится и тогда попадает на строку TMR0 = 0 не знаю как он обеспечивает так задержку ---это не команда, а цикл while(), она ждет пока в таймере не будет значения >= 250, как только там появляется число больше выполняется следующая строчка программы.

      после выполнения этих 17 циклов идет вывод переменной (не знаю как записывается что-то в эту переменную)

      –во-первых 16 циклов, а не 17, во-вторых можно про переменную можно забыть, а рассматривать это как изменение состояния пина на противоположное.

      Чтобы работать в режиме счетчика необходимо T0CS поставить в 1, и завести ваш источник на RA4.

  8. это не команда, а цикл while(), она ждет пока в таймере не будет значения >= 250, как только там появляется число больше выполняется следующая строчка программы.

    О! Я так и думал изначально, но как заметил знак <250, то этож наоборот и не так понимал.

    Все, большое спасибо теперь все понял кроме
    во-вторых можно про переменную можно забыть, а рассматривать это как изменение состояния пина на противоположное.
    Не знаю как ваша переменная понимает какой длины импульс должен быть(сколько клеток). Мы ведь во время выполнения цикла в нее ничего не заносим….

    • Да не надо ей ничего понимать, весь этот код можно свести к макросу delay:

      while(1)
      {
      __delay_ms(200);
      RB7 = !RB7;
      }

      Все, этот код будет делать точно такое же действие с некоторой погрешностью.

  9. Подправьте пожалуйста код как Вы имеете ввиду. А то я не понимаю как так сделать, чтобы было еще и при этом прерывание.

  10. Здравствуйте.
    Под PIc16F688 не фурычит:
    #include
    #include
    #include
    #define _XTAL_FREQ 8000000 //4MHz
    #pragma config MCLRE = OFF, FOSC = INTOSCIO, WDTE = OFF

    unsigned char cnt = 0;
    unsigned char i = 0;
    unsigned char srb = 0;

    void main() {
    TRISA = 0b01111111;
    RA0 = 0;
    for (;;) {
    OPTION_REG = 0b11010111; // считаем по 256 мкс
    for (i=1;i<4;i++) { // 192 мс
    TMR0 = 0;
    while (TMR0 < 250) { cnt++; }
    }
    OPTION_REG = 0b11010100; // считаем по 32 мкс
    TMR0 = 0; cnt = 0;
    while (TMR0 < 250) {cnt++;} // 8 мс
    srb = !srb;
    RA0 = srb;
    }

    }
    регистр OPTION_REG та же последовательность что и у PIC16F628

      • Я не понимаю на английском.
        Заметил приписку что-то про ANSEL в пункте про таймер. Сделал порт цифровым и все заработало.
        Осталось посчитать теперь для 8МГц. 8МГц как и 4МГц делится на 4?
        Т.е. 8МГЦ\4=2МГц?

            • Чертовщина какая-то…
              Значит программа которая делает таймер с частотой в 1с — и делает 1с для 4МГц, а если взять эту же программу для микроконтроллера с 8Мгц, то таймер с 0ю5с длительностью получается….
              Почему так? Я думал наоборот 2 с будет вместо одной….

              А зачем предделитель на 4 нужен? Тогда бы делали уж с нужной частотой без предделителей. Микроконтроллеры.

              • Где тут чертовщина, вы повышаете частоту в два раза, соответственно период в два раза становится меньше, все логично. (T = 1/F)

                Зачем встроенный предделитель на 4 – вопрос интересный, но я к сожалению не могу дать вам на него ответ 🙂

  11. Уведомление: PIC Lab, PIC16, Experiment #3: Timer TMR0 | diymicro.org

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.