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.: 37 комментариев

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

    Ответить

  2. Что делает строчка
    while (TMR0 < 250) { cnt++; }
    ?

    Ответить

    sarge Ответил:

    Фактически просто ожидает, когда значение регистра TMR0 станет равным 250, cnt там в принципе не нужно.

    Ответить

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

    Ответить

    sarge Ответил:

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

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

    Ответить

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

    Ответить

    sarge Ответил:

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

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

    Ответить

    Василий Ответил:

    Вот, пожалуйста.
    Залил проект на файлообменник.
    http://dropmefiles.com/6wrQC

    Ответить

    sarge Ответил:

    Не понял, что вас не устраивает, все замечательно работает, ширина импульса ровно 200мс.

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

    Ответить

    Василий Ответил:

    Вот фото: http://itmages.ru/image/view/2370362/71f23883
    10 клеток.

    Ответить

    sarge Ответил:

    Ну, отлично 10 клеток * 20мс = 200 мс.
    У меня 8 клеток *25мс = 200 мс, а на скошенный сигнал не обращайте внимания, в данном случае нам амплитуда абсолютно не важна.

    Ответить

    Василий Ответил:

    А почему он скошен? У Вас нет. Если я хочу как положено, чтобы было, то что для этого надо, не знаете?

    Ответить

    sarge Ответил:

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

    Ответить

  5. А, да, это возможно…. Не впервой такое уже. В разных версиях Proteus, разный результат…

    Ответить

  6. Можете пожалуйста мне пояснить программу?
    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 это и если сброс, чтобы прерывания не было….

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

    Ответить

    sarge Ответил:

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

    Таймер сам по себе имеет 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 мс

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

    Ответить

  7. В даташите написано, что прерывание происходит при переполнении.
    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;

    Ответить

  8. Прерывания флаг устанавливается в любом случае, но чтобы его дальше обрабатывать должен стоять флаг 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;

    Ответить

    sarge Ответил:

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

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

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

    Ответить

    Василий Ответил:

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

    Ответить

    sarge Ответил:

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

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

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

    Ответить

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

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

    Ответить

    sarge Ответил:

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

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

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

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

    Ответить

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

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

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

    Ответить

    sarge Ответил:

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

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

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

    Ответить

  11. Здравствуйте.
    Под 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

    Ответить

    sarge Ответил:

    Даташит на ваш чип, пункт 4.1
    Нельзя просто так взять и использовать порт А в качестве IO порта.

    Ответить

    Василий Ответил:

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

    Ответить

    Василий Ответил:

    Что это вообще за 4 берется?

    Ответить

    sarge Ответил:

    Это встроенный предделитель, ваш клок всегда делится на 4.

    Ответить

    Василий Ответил:

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

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

    Ответить

    sarge Ответил:

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

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

    Ответить

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

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

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">