Не могу настроить таймер1 attiny85
- Войдите на сайт для отправки комментариев
Пт, 27/10/2017 - 16:33
Не получается настроить таймер1 для аттини, хчоу чтоб раз в секунду инвертировался выход, по расчетам вроде не ошибся, но порт инвертируется раз в 16 секунд гдето, судя по протеусу. Ещё меняю значение регистра OCR1A; и тоже ничего не происходит. Где я прокосячил?
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define TIMER_TICKS_IN_ONE_SECOND 125 //тики таймера в секунду при делителе 512. OCR1A=125. (8000000/512/125 = 125)
unsigned char count_to_second; //число тиков TIMER1
//инициализация таймера1
void timer1_ini (void){
TCCR1 |= (1 << CTC1) //режим CTC
|(0<<COM1A1)|(0<<COM1A0) //отключаем OC1A
|(1 << CS13)|(0 << CS12)|(1 << CS11)|(0 << CS10); //Делитель 512
TCNT1 = 0x00; //сброс счетчика
OCR1A = 125; //регистр сравнения
TIMSK |=(1<<OCIE1A); //включаем прерывания по сравнению с OCR1A
}
//------------------------------------------------------------------
void port_ini (void){
DDRB=0b00000001;//PB0 - выход, PB1 - , PB2 - , PB3 - , PB4 - входы,
PORTB=0b00000000;
}
//обработка прерывания таймера1
ISR(TIMER1_COMPA_vect){
count_to_second++;
if (count_to_second >= TIMER_TICKS_IN_ONE_SECOND) { // Отсчитываем прерывание 125 раз получаем 1 секунду
PORTB ^= _BV(PB0); // инвертируем состояние порта PB0
count_to_second=0;
}
}
int main(void)
{
port_ini();
timer1_ini();
sei();
while (1)
{
}
}
может поменять 6 строку на #define TIMER_TICKS_IN_ONE_SECOND 8
получется что 8*125*512 = 512000, а должно 8000000
получется что 8*125*512 = 512000, а должно 8000000
да я и не спорю :)
но тут два варианта: или фьюзы криво стоят, или в программе ошибка - меняем 6 строку на 8 и срабатывает аккурат в 16 раз быстрее.
когда над своей attiny тренировался - простейший метод узнать правильно ли скорость фьюзами выставлена,
мигаем светодиодом с задержкой delay(1000) - если ровно секунда - значит скорось выставлена верно
papakaplo, смотрю все наступают на одни и те-же грабли. Как-то раз уже разъяснял, у вас полностью аналогичный случай.
получется что 8*125*512 = 512000, а должно 8000000
да я и не спорю :)
но тут два варианта: или фьюзы криво стоят, или в программе ошибка - меняем 6 строку на 8 и срабатывает аккурат в 16 раз быстрее.
CLKDIV8 выставил в протеусе теперь раз в 2 секунды смена состояния идет.
papakaplo, смотрю все наступают на одни и те-же грабли. Как-то раз уже разъяснял, у вас полностью аналогичный случай.
смотрел и не раз, скажите в чем именно косяк? в режиме CTC (сравнения)? меняю значения регистра OCR1A как считал так и считает без изменений.
papakaplo, йошкин кот! Ответ #1 в той теме. Читайте до прояснения.
papakaplo, йошкин кот! Ответ #1 в той теме. Читайте до прояснения.
Читал, в общем сделал вот так
Я не dimax, но, если не возражаете, помогу ему
надеюсь все правильно понял
Боюсь, что нет.
OCR1A = 5; <---тут может быть любое число лишь бы не 0
Почему? С 0 тоже отлично работает. Здесь может быть число <=125, а вот при любом >125 работать перестаёт (по крайней мере в реальной жизни - насчёт протеуса не знаю).
Дело, конечно Ваше (локальную проблему Вы решили), но вот я бы на Вашем месте, читал бы даташит до дыр и экспериментировал бы с микросхемой до тех пор, пока не понял бы до конца и не убедился бы, что реальная микросхема ведёт себя в точном соответствии с моим пониманием и моей картиной мира.
экспериментировал бы с микросхемой до тех пор, пока не понял бы до конца и не убедился бы, что реальная микросхема ведёт себя в точном соответствии .
Протеус дает не правильные задержки, только реальная микросхема.
Почему? С 0 тоже отлично работает. Здесь может быть число <=125, а вот при любом >125 работать перестаёт (по крайней мере в реальной жизни - насчёт протеуса не знаю).
Дело, конечно Ваше (локальную проблему Вы решили), но вот я бы на Вашем месте, читал бы даташит до дыр и экспериментировал бы с микросхемой до тех пор, пока не понял бы до конца и не убедился бы, что реальная микросхема ведёт себя в точном соответствии с моим пониманием и моей картиной мира.
только что проверил в протеусе, работает только в этом интервале, 0 < OCR1A <=OCR1C .
ДШ открыт постоянно, но вот познания в английском не позволяют полностью понять весь смысл заложенный инженерами АВР. Тем более там технический язык. Но со временем я думаю проще станет. попробую в микросхему загрузить интервал 0 < OCR1A <=125.
только что проверил в протеусе, работает только в этом интервале, 0 < OCR1A <=OCR1C .
Ну, я сразу сказал, что про протеус не знаю. А в микросхеме у меня работает и с 0 тоже.
Всем здравия... Подскажите плиз почему у меня "лыжи не едут". Пытаюсь эмулировать в протеусе таймер1 в Tiny45. Вроде все работает, но задержка получается почему-то слишком длинной. Ожидается в 1 сек, а в протеусе показывает около 4-х сек. Подскажите это я что-то делаю не так или это протеус мне голову морочит.
#define F_CPU 1000000UL #define TC1_MODE_TOGGLE ((0<<COM1B1)|(1<<COM1B0)) #define TC1_COMPARE_MATCH_CLEAR ((1<<CTC1)) #define TC1_DIV_16384 ((1<<CS13)|(1<<CS12)|(1<<CS11)|(1<<CS10)) #define TC1_PRESCALLER_RESET ((1<<PSR1)) void TC1_Init(void) { //Останавливаем таймер TCCR1 = 0; //Обнуляем содержимое таймера TCNT1 = 0; //Настраиваем 3-й вывод порта для контроля работы прерывания PORTB_Bit3 = 0; //Инициализируем вывод DDRB_Bit3 = 1; //Делаем его выходом //Настраиваем 4-й вывод порта на выход от таймера PORTB_Bit4 = 0; //Инициализируем вывод DDRB_Bit4 = 1; //Делаем его выходом //Подключаем к нему таймер и сбрасываем предделитель GTCCR |= TC1_MODE_TOGGLE|TC1_PRESCALLER_RESET; //Инициализируем регистры значениями для сравнения OCR1B = F_CPU / 16384UL; OCR1C = F_CPU / 16384UL; //Разрешаем прерывание от таймера TIMSK |= (1<<OCIE1B); }Код конечно не ардуиновский, использую IAR, но смысл тот же.
А где Вы устанавливаете делитель частоты? Константу вижу TC1_DIV_16384, а где она используется?
А где Вы устанавливаете делитель частоты? Константу вижу TC1_DIV_16384, а где она используется?
В строке, в которой я запускаю таймер. Она вне этой функции.
В строке, в которой я запускаю таймер. Она вне этой функции.
Вот именно поэтому, я терпеть не могу, когда выкладывают код не полностью.
Вот смотрите, я уже (1) скопировал Ваш код, (2) добавил к нему необходимый минимум, чтобы запустить, (3) запустил, посмотрел. Теперь я узнаю, что оказывается, это не полная конфигурация таймера, а есть ещё какая-то строка "вне этой функции". Простите, а что ещё у Вас там есть "вне этой функции"?
Вам не кажется, что многовато усилий для случайного совета незнакомому человеку?
Давайте так, если Вам нужна помощь, сделайте полный пример, демонстрирующий Вашу проблему. Полный, чтобы я мог его просто запустить не дописывая отсебятину и не догадываясь, что там есть ещё "вне этой функции". Тогда, я его посмотрю.
И, кстати, что во фьюзах? Какова частота F_CPU?
Вот именно поэтому, я терпеть не могу, когда выкладывают код не полностью.
Вот смотрите, я уже (1) скопировал Ваш код, (2) добавил к нему необходимый минимум, чтобы запустить, (3) запустил, посмотрел. Теперь я узнаю, что оказывается, это не полная конфигурация таймера, а есть ещё какая-то строка "вне этой функции". Простите, а что ещё у Вас там есть "вне этой функции"?
Да практически больше ничего, что касается таймера. Единственное, что еще есть это инверсия 3-го пина порта в прерывании. Вобщем суть такая... Я в протеусе собрал схемку для управления регистром HC595. При этом задержку сделал на встроенной в компилятор функции __delay_cycles(). Все заработало, как положено. После этого я решил сделать задержку по таймеру, чтоб не тупить в цикле, а заслать контроллер в слип и чтоб по прерыванию он просыпался и слал в HC595 нужный байт. Но прежде чем усыплять проц, я решил проверить работу таймера светодиодами паралельно с работой HC595. Навесил светодиоды на 3-й и 4-й пин порта. Где 3-й пин управляется из прерывания, а 4-й автоматом, таймером, по совпадению. И вот получается, что при симуляции HC595 работает правильно, а светодиоды от таймера работают с задержками порядка 4-х сек, а ожидалось, что они будут моргать с задержкой около 1 сек.
Не нужно нервничать... Я все таки новенький тут у вас...:) Просто я предполагал, что у меня затык именно в функции инициализации таймера.
Не вопрос... Вот весь код.
#include <ioavr.h> #include <intrinsics.h> #include <stdbool.h> //#include <avr_macros.h> #define F_CPU 1000000UL #define DELAY_US(us) __delay_cycles((F_CPU / 1000000UL) * (us)); #define DELAY_MS(ms) __delay_cycles((F_CPU / 1000UL) * (ms)); //Описание выводов для HC595 #define HC595_DATA PORTB_Bit0 #define HC595_LATCH PORTB_Bit1 #define HC595_CLK PORTB_Bit2 //Comparator output B in Normal mode #define TC1_MODE_DISCONNECTED ((0<<COM1B1)|(0<<COM1B0)) #define TC1_MODE_TOGGLE ((0<<COM1B1)|(1<<COM1B0)) #define TC1_MODE_CLEAR ((1<<COM1B1)|(0<<COM1B0)) #define TC1_MODE_SET ((1<<COM1B1)|(1<<COM1B0)) #define TC1_COMPARE_MATCH_CLEAR ((1<<CTC1)) #define TC1_DIV_16384 ((1<<CS13)|(1<<CS12)|(1<<CS11)|(1<<CS10)) #define TC1_PRESCALLER_RESET ((1<<PSR1)) void Send_HC595(char); void TC1_Init(void); //--------------------------------------------------------------- #pragma vector = TIMER1_COMPB_vect __interrupt void ISR_TC1_COMPB(void) { PORTB_Bit3 ^= 1; } //--------------------------------------------------------------- void main(void) { //__disable_interrupt(); ACSR_ACD = 1; //Выключаем аналоговый компаратор MCUCR_PUD = 1; //Отключаем резисторы подтяжки PORTB = 0; //Очищаем содержимое порта //Настраиваем лини 0,1 и 2 порта на вывод DDRB |= (1<<DDB2)|(1<<DDB1)|(1<<DDB0); //Инициализируем таймер TC1_Init(); __enable_interrupt(); //Запускаем таймер с делителем на 16384 и автоматическим сбросом в 0 TCCR1 |= TC1_COMPARE_MATCH_CLEAR|TC1_DIV_16384; while(true) { __no_operation(); Send_HC595(0x55); DELAY_MS(500); Send_HC595(0xAA); DELAY_MS(500); } } //--------------------------------------------------------------- void Send_HC595(char data) { //Побитно засылаем байт в регистр HC595 for(char i = 0; i < 8; i++) { //Выставляем бит данных в порт if(data & (1 << 7)) HC595_DATA = 1; //Устанвка бита else HC595_DATA = 0; //Сброс бита __delay_cycles(2); //Пропихиваем бит в регистр HC595_CLK = 1; __delay_cycles(2); HC595_CLK = 0; //Готовим следующий бит данных data = (data << 1); } //Защелкиваем данные в регистре HC595_LATCH = 1; __delay_cycles(2); HC595_LATCH = 0; } //--------------------------------------------------------------- void TC1_Init(void) { //Останавливаем таймер TCCR1 = 0; //Обнуляем содержимое таймера TCNT1 = 0; //Настраиваем 3-й вывод порта для контроля работы прерывания PORTB_Bit3 = 0; //Инициализируем вывод DDRB_Bit3 = 1; //Делаем его выходом //Настраиваем 4-й вывод порта на выход от таймера PORTB_Bit4 = 0; //Инициализируем вывод DDRB_Bit4 = 1; //Делаем его выходом //Подключаем к нему таймер и сбрасываем предделитель GTCCR |= TC1_MODE_TOGGLE|TC1_PRESCALLER_RESET; //Инициализируем регистры значениями для сравнения //Тут я уже подставлял разные значения, но эффекта от них не получил OCR1B = 0x79; //F_CPU / 16384UL; OCR1C = 0x79; //F_CPU / 16384UL; /* Clear Output Compare Flag 1B */ TIFR = 1<<OCF1B; //Разрешаем прерывание от таймера TIMSK |= (1<<OCIE1B); }И, кстати, что во фьюзах? Какова частота F_CPU?
Фьюзы по умолчанию. Такт от внутреннего генератора через CLKDIV8. В итоге имеем 1МГц F_CPU.
Вот только поэтому я всё ещё здесь (а также потому, что я сегодня необычайно размякший (с бодуна, наверное)). Вы ставьте себя на место человека, который решил Вам помочь и никогда не создавайте ему лишних проблем. То, что Вы выложили нечто, что я не могу просто запустить – это проблема. То, что Вы потом вывалили код с нафиг не нужными потрохами от 595 – это проблема. Зачем? Если Вы хотите получить ответ на свой вопрос – сделайте специальный, короткий код, показывающий Ваш затык и именно его и выкладывайте.
Сейчас я приведу Вам код правильной инициализации таймера. Заодно считайте его учебным пособием по подготовке кода для вопроса на форуме – полный, работающий, и ничего лишнего, чтобы не пудрить людям мозги.
Смотрите, вот это нормально работает и в железе и в протеусе (у меня AVR-студия, потому инклюды немного другие)
#define F_CPU 1000000UL #include <avr/io.h> #include <avr/interrupt.h> #define TC1_MODE_TOGGLE ((0<<COM1B1)|(1<<COM1B0)) #define TC1_COMPARE_MATCH_CLEAR ((1<<CTC1)) #define TC1_DIV_16384 ((1<<CS13)|(1<<CS12)|(1<<CS11)|(1<<CS10)) #define TC1_PRESCALLER_RESET ((1<<PSR1)) void TC1_Init(void) { cli(); DDRB |= (1 << PB4); // Пин OC1B в OUTPUT TCCR1 = TC1_COMPARE_MATCH_CLEAR; //Останавливаем таймер + "сброс по сравнению" TCNT1 = 0; // Обнуляем счётчик GTCCR = TC1_MODE_TOGGLE; // инвертировать пин по сравнению OCR1B = F_CPU / 16384UL - 1; // "-1" нужно т.к. счёт с нуля OCR1C = F_CPU / 16384UL; // Здесь что угодно, больше чем в предыдущей строке TIMSK = (1<<OCIE1B); //Разрешаем прерывание от таймера sei(); } int main(void) { PRR &= ~(1 << PRTIM1); // Питание таймера TC1_Init(); TCCR1 |= (1 << COM1A0) | TC1_DIV_16384; // Запуск таймера. COM1A0 нужен! см. Errata while(true); // любуемся на осциллографе } ISR(TIMER1_COMPB_vect) {}Что здесь важно? В Errata к даташиту написано, что настройки COM1B1 нормально работают только если COM1A1 настроен точно также. Там же написано, что вроде они это уже исправили с какой-то версии чипа, но в протеусе именно так. В железе - как повезёт. Также важно, чтобы настройки делались, когда CTC1 уже установлен. Я выбросил Ваш сброс делителя, т.к. мы его и так уже сбросили, зачем лишние сущности?
Вот примерно такого размера код Вы и должны были выложить с Вашим вопросом. Проблема проявляется, ничего лишнего и можно запускать без доработок.
Смотрите, вот это нормально работает и в железе и в протеусе (у меня AVR-студия, потому инклюды немного другие)
Спасибо, все очень доходчиво.
Каюсь... В Errata я как-то и не додумался глянуть. Грешил тупо на протеус... А оно вон оно как... Оказывается модель в протеусе писалась с глючного камня со всеми глюками. :) Однако...
Поигрался я с этим CTC1... Скажем так, не совсем критично, чтобы он был сразу установлен. Но желательно. Если инициализацию делать при сброшенном CTC1, то при запуске таймера значение на выводе, контролируемом таймером аппаратно, будет инверсным, что несколько неожиданно, но в моем случае не критично. А если инициализацию делать при установленном CTC1, то все получается правильно и ожидаемо.
Вы наверное ошиблись. :) Сбросили мы только содержимое самого таймера, а вот предделитель - это отдельная песня. Потому для него даже отдельный бит организовали, чтоб его по нужде можно было дергать независимо.
Вобщем вариант с той же настройкой в COM1A0 меня не устроил, т.к. при этом жестко перекрывается еще один из выводов, аккурат задействованный для моей HC595. Тут либо надеятся, что железный камень окажется без глюков, либо дергать нужные ноги чисто в прерывании, либо менять камень. Я поменял модельку в протеусе на Tiny25 и все заработало даже с моим кодом(с оговоркой про CTC1). В конечном итоге остановился на таком варианте функции...
void TC1_Init(void) { //Питание таймера PRR &= ~(1 << PRTIM1); //Настраиваем 3-й и 4-й вывод порта для таймера //3-й вывод - управляется из прерывания //4-й вывод - управляется таймером PORTB &= ~((1<<PB4)|(1<<PB3)); DDRB |= (1<<PB4)|(1<<PB3); //Останавливаем таймер + "сброс по сравнению" TCCR1 = TC1_COMPARE_MATCH_CLEAR; //Обнуляем содержимое таймера TCNT1 = 0; //Сбрасываем флаг прерывания TIFR = 1<<OCF1B; //Подключаем к 4-му выводу таймер и сбрасываем предделитель GTCCR |= TC1_MODE_TOGGLE|TC1_PRESCALLER_RESET; //Инициализируем регистры значениями для сравнения OCR1B = F_CPU / 16384UL - 1; OCR1C = F_CPU / 16384UL; //Разрешаем прерывание от таймера TIMSK |= (1<<OCIE1B); }Вобщем вариант с той же настройкой в COM1A0 меня не устроил, т.к. при этом жестко перекрывается еще один из выводов, аккурат задействованный для моей HC595.
А может их можно просто поменять местами? Это "B" нельзя настроить на СТС/PWM без "А", а "А" без "В" отлично настраивается. Будет у Вас меандр на ноге PB1, а освободившийся PB4 используйте для 595
А может их можно просто поменять местами? Это "B" нельзя настроить на СТС/PWM без "А", а "А" без "В" отлично настраивается. Будет у Вас меандр на ноге PB1, а освободившийся PB4 используйте для 595
В принципе это тоже вариант. Однако я планировал использовать оба канала почти независимо, с отличающимися настройками. В этом случае все равно то на то и получится. Пока покумекаю над работой в прерываниях, чтоб не зависеть от аппаратных причуд, а там видно будет. Еще раз спасибо.