Задача: получить импульсы с частотой 5Гц.
Исходный материал: PIC16f628a и простенькая devboard.
Таймер TMR0 применяется во многих программах и имеет следующие полезные опции:
- 8 битный таймер/счетчик
- Программируемый выбор внешнее/внутреннее тактирование
- По переполнению генерирует прерывание
В этом эксперименте пока что обойдемся функциями самого таймера без прерываний.
За опции таймера TMR0 отвечает регистр OPTION. Понять работу таймера помогает очень наглядная картинка из ресурса mikroe:
А вот собственно биты регистра OPTION:
-
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; } }
В результате работы программы получаем такую симпатичную картинку:
25*8 = 200 мс
Исходники можно взять здесь.
А какой версии у Вас используется компилятор?
У меня на OPTION ругается 🙁
Hi-tech PICC Compiler
В тексте слова “Тактирование от внешнего источника через пин RA4” лучше подправить на “Тактирование от внешнего источника через пин T0CKI (RA4*)” и добавить сноску что RA4 справедливо для вашего МК, так как для других это может быть другой пин (например для PIC16F630 это будет RA2) – читать даташит.
Что делает строчка
while (TMR0 < 250) { cnt++; }
?
Фактически просто ожидает, когда значение регистра TMR0 станет равным 250, cnt там в принципе не нужно.
Здравствуйте.
Почему-то у меня не такой результат как у Вас.
Я только изменил OPTION на OPTION_REG , а то ошибку выдавало и убрал строку с __CONFIG, а то ошибку на нее выдавало.
Компилятор как у Вас.
В чем отличие:
У Вас в длину 8 клеток импульс, а у меня 10. ( настройки осцилографа выставил как у Вас)
И еще у Вас прямые линии, а у меня они кривые.
Если компилятор такой как у меня (hi-tech picc compiler), то ругаться не должно ни на OPTION, ни на __CONFIG.
Проверьте еще свойства мк в протеусе, частота кварца должна совпадать с нужной.
Да, стоит там 4MHz.
Вот такой: HI-TECH Software\PICC\9.83\bin\picc.exe
Как и у Вас.
ругался не на сам __CONFIG, а на то, что внутри: сторожевой теймер, сборос….
Ну вот, у меня 9.71. Но на время импульсов это никак не должно было повлиять.
Не видя кода и результата работы сложно что-то сказать, показывайте исходники и файлы симуляции.
Вот, пожалуйста.
Залил проект на файлообменник.
http://dropmefiles.com/6wrQC
Не понял, что вас не устраивает, все замечательно работает, ширина импульса ровно 200мс.
Можно скрин осциллограмы, где все неправильно работает (убедитесь что подгружен правильный хекс, т.к. у вас используется абсолютный путь в тех файлах, которые вы мне прислали)?
Вот фото: http://itmages.ru/image/view/2370362/71f23883
10 клеток.
Ну, отлично 10 клеток * 20мс = 200 мс.
У меня 8 клеток *25мс = 200 мс, а на скошенный сигнал не обращайте внимания, в данном случае нам амплитуда абсолютно не важна.
А почему он скошен? У Вас нет. Если я хочу как положено, чтобы было, то что для этого надо, не знаете?
В моем протеусе все ровно, скорее всего дело в настройках протеуса вашего, я бы забил и не обращал внимания, это все равно цифровой сигнал.
А, да, это возможно…. Не впервой такое уже. В разных версиях Proteus, разный результат…
Можете пожалуйста мне пояснить программу?
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 мс
Еще раз: никаких прерываний тут нет, все линейно.
В даташите написано, что прерывание происходит при переполнении.
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;
Прерывания флаг устанавливается в любом случае, но чтобы его дальше обрабатывать должен стоять флаг 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.
И да, по смыслу это выражение делает тоже самой что делэй.
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.
это не команда, а цикл while(), она ждет пока в таймере не будет значения >= 250, как только там появляется число больше выполняется следующая строчка программы.
О! Я так и думал изначально, но как заметил знак <250, то этож наоборот и не так понимал.
Все, большое спасибо теперь все понял кроме
во-вторых можно про переменную можно забыть, а рассматривать это как изменение состояния пина на противоположное.
Не знаю как ваша переменная понимает какой длины импульс должен быть(сколько клеток). Мы ведь во время выполнения цикла в нее ничего не заносим….
Да не надо ей ничего понимать, весь этот код можно свести к макросу delay:
while(1)
{
__delay_ms(200);
RB7 = !RB7;
}
Все, этот код будет делать точно такое же действие с некоторой погрешностью.
Подправьте пожалуйста код как Вы имеете ввиду. А то я не понимаю как так сделать, чтобы было еще и при этом прерывание.
Смотрите в таймере1 http://diymicro.ru/pic-mk-eksperiment-8-ispolzovanie-tajmera-tmr1.html
Здравствуйте.
Под 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
Даташит на ваш чип, пункт 4.1
Нельзя просто так взять и использовать порт А в качестве IO порта.
Я не понимаю на английском.
Заметил приписку что-то про ANSEL в пункте про таймер. Сделал порт цифровым и все заработало.
Осталось посчитать теперь для 8МГц. 8МГц как и 4МГц делится на 4?
Т.е. 8МГЦ\4=2МГц?
Что это вообще за 4 берется?
Это встроенный предделитель, ваш клок всегда делится на 4.
Чертовщина какая-то…
Значит программа которая делает таймер с частотой в 1с — и делает 1с для 4МГц, а если взять эту же программу для микроконтроллера с 8Мгц, то таймер с 0ю5с длительностью получается….
Почему так? Я думал наоборот 2 с будет вместо одной….
А зачем предделитель на 4 нужен? Тогда бы делали уж с нужной частотой без предделителей. Микроконтроллеры.
Где тут чертовщина, вы повышаете частоту в два раза, соответственно период в два раза становится меньше, все логично. (T = 1/F)
Зачем встроенный предделитель на 4 – вопрос интересный, но я к сожалению не могу дать вам на него ответ 🙂
Уведомление: PIC Lab, PIC16, Experiment #3: Timer TMR0 | diymicro.org