Не могу настроить таймер1 attiny85

papakaplo
Offline
Зарегистрирован: 18.04.2016

Не получается настроить таймер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)
 {
}
}

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

может поменять 6 строку на #define TIMER_TICKS_IN_ONE_SECOND 8

papakaplo
Offline
Зарегистрирован: 18.04.2016

получется что 8*125*512 = 512000, а должно 8000000

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

papakaplo пишет:

получется что 8*125*512 = 512000, а должно 8000000

да я и не спорю :)

но тут два варианта: или фьюзы криво стоят, или в программе ошибка - меняем 6 строку на 8 и срабатывает аккурат в 16 раз быстрее.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

когда над своей attiny тренировался - простейший метод узнать правильно ли скорость фьюзами выставлена,

мигаем светодиодом с задержкой delay(1000) - если ровно секунда - значит скорось выставлена верно

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

papakaplo, смотрю все наступают на одни и те-же грабли. Как-то раз уже разъяснял, у вас полностью аналогичный случай.

papakaplo
Offline
Зарегистрирован: 18.04.2016

andycat пишет:

papakaplo пишет:

получется что 8*125*512 = 512000, а должно 8000000

да я и не спорю :)

но тут два варианта: или фьюзы криво стоят, или в программе ошибка - меняем 6 строку на 8 и срабатывает аккурат в 16 раз быстрее.

CLKDIV8 выставил в протеусе теперь раз в 2 секунды смена состояния идет. 

dimax пишет:

papakaplo, смотрю все наступают на одни и те-же грабли. Как-то раз уже разъяснял, у вас полностью аналогичный случай.

смотрел и не раз, скажите в чем именно косяк? в режиме CTC (сравнения)? меняю значения регистра OCR1A как считал так и считает без изменений.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

papakaplo, йошкин кот! Ответ #1 в той теме. Читайте до прояснения.

papakaplo
Offline
Зарегистрирован: 18.04.2016

dimax пишет:

papakaplo, йошкин кот! Ответ #1 в той теме. Читайте до прояснения.

Читал, в общем сделал вот так 

OCR1A = 5;   <---тут может быть любое число лишь бы не 0
OCR1C = 125; 
 
странная конечно настройка, OCR1A можно указывать любым лишь бы не ноль был. А вот таймер считает до OCR1C = 125; все таки битом управления является OCR1A, как сказано в даташите если он очищен то таймер не будет считать до OCR1C, а чисто до переполнения.
 
Спасибо! надеюсь все правильно понял
ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Я не dimax, но, если не возражаете, помогу ему

papakaplo пишет:

надеюсь все правильно понял

Боюсь, что нет.

papakaplo пишет:

OCR1A = 5;   <---тут может быть любое число лишь бы не 0

Почему? С 0 тоже отлично работает. Здесь может быть число <=125, а вот при любом >125 работать перестаёт (по крайней мере в реальной жизни - насчёт протеуса не знаю).

Дело, конечно Ваше (локальную проблему Вы решили), но вот я бы на Вашем месте, читал бы даташит до дыр и экспериментировал бы с микросхемой до тех пор, пока не понял бы до конца и не убедился бы, что реальная микросхема ведёт себя в точном соответствии с моим пониманием и моей картиной мира.

 

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

ЕвгенийП пишет:

экспериментировал бы с микросхемой до тех пор, пока не понял бы до конца и не убедился бы, что реальная микросхема ведёт себя в точном соответствии .

 

Протеус дает не правильные задержки, только реальная микросхема.

papakaplo
Offline
Зарегистрирован: 18.04.2016

ЕвгенийП пишет:

Почему? С 0 тоже отлично работает. Здесь может быть число <=125, а вот при любом >125 работать перестаёт (по крайней мере в реальной жизни - насчёт протеуса не знаю).

Дело, конечно Ваше (локальную проблему Вы решили), но вот я бы на Вашем месте, читал бы даташит до дыр и экспериментировал бы с микросхемой до тех пор, пока не понял бы до конца и не убедился бы, что реальная микросхема ведёт себя в точном соответствии с моим пониманием и моей картиной мира.

только что проверил в протеусе, работает только в этом интервале, 0 < OCR1A <=OCR1C . 

ДШ открыт постоянно, но вот познания в английском не позволяют полностью понять весь смысл заложенный инженерами АВР. Тем более там технический язык. Но со временем я думаю проще станет. попробую в микросхему загрузить интервал 0 < OCR1A <=125. 

 

 

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

papakaplo пишет:

только что проверил в протеусе, работает только в этом интервале, 0 < OCR1A <=OCR1C . 

Ну, я сразу сказал, что про протеус не знаю. А в микросхеме у меня работает и с 0 тоже.

sedoy
Offline
Зарегистрирован: 02.08.2019

Всем здравия... Подскажите плиз почему у меня "лыжи не едут". Пытаюсь эмулировать в протеусе таймер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, но смысл тот же.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

А где Вы устанавливаете делитель частоты? Константу вижу TC1_DIV_16384, а где она используется?

sedoy
Offline
Зарегистрирован: 02.08.2019

ЕвгенийП пишет:

А где Вы устанавливаете делитель частоты? Константу вижу TC1_DIV_16384, а где она используется?

В строке, в которой я запускаю таймер. Она вне этой функции.

  //Запускаем таймер с делителем на 16384 и автоматическим сбросом в 0
  TCCR1 |= TC1_COMPARE_MATCH_CLEAR|TC1_DIV_16384;

 

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

sedoy пишет:

В строке, в которой я запускаю таймер. Она вне этой функции.

Вот именно поэтому, я терпеть не могу, когда выкладывают код не полностью.

Вот смотрите, я уже (1)  скопировал Ваш код, (2) добавил к нему необходимый минимум, чтобы запустить, (3) запустил, посмотрел. Теперь я узнаю, что оказывается, это не полная конфигурация таймера, а есть ещё какая-то строка "вне этой функции". Простите, а что ещё у Вас там есть "вне этой функции"?

Вам не кажется, что многовато усилий для случайного совета незнакомому человеку?

Давайте так, если Вам нужна помощь, сделайте полный пример, демонстрирующий Вашу проблему. Полный, чтобы я мог его просто запустить не дописывая отсебятину и не догадываясь, что там есть ещё "вне этой функции". Тогда, я его посмотрю.

И, кстати, что во фьюзах? Какова частота F_CPU?

sedoy
Offline
Зарегистрирован: 02.08.2019

ЕвгенийП пишет:

Вот именно поэтому, я терпеть не могу, когда выкладывают код не полностью.

Вот смотрите, я уже (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.

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

sedoy пишет:
Я все таки новенький тут у вас...:)

Вот только поэтому я всё ещё здесь (а также потому, что я сегодня необычайно размякший (с бодуна, наверное)). Вы ставьте себя на место человека, который решил Вам помочь и никогда не создавайте ему лишних проблем. То, что Вы выложили нечто, что я не могу просто запустить – это проблема. То, что Вы потом вывалили код с нафиг не нужными потрохами от 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 уже установлен. Я выбросил Ваш сброс делителя, т.к. мы его и так уже сбросили, зачем лишние сущности?

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

sedoy
Offline
Зарегистрирован: 02.08.2019

ЕвгенийП пишет:

Смотрите, вот это нормально работает и в железе и в протеусе (у меня AVR-студия, потому инклюды немного другие)

Спасибо, все очень доходчиво.

Цитата:
Что здесь важно? В Errata к даташиту написано, что настройки COM1B1 нормально работают только если COM1A1 настроен точно также. Там же написано, что вроде они это уже исправили с какой-то версии чипа, но в протеусе именно так. В железе - как повезёт.

Каюсь... В Errata я как-то и не додумался глянуть. Грешил тупо на протеус... А оно вон оно как... Оказывается модель в протеусе писалась с глючного камня со всеми глюками. :) Однако...

Цитата:
Также важно, чтобы настройки делались, когда CTC1 уже установлен.

Поигрался я с этим 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);
}

 

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

sedoy пишет:

Вобщем вариант с той же настройкой в COM1A0 меня не устроил, т.к. при этом жестко перекрывается еще один из выводов, аккурат задействованный для моей HC595. 

А может их можно просто поменять местами? Это "B" нельзя настроить на СТС/PWM без "А", а "А" без "В" отлично настраивается. Будет у Вас меандр на ноге PB1, а освободившийся PB4 используйте для 595

sedoy
Offline
Зарегистрирован: 02.08.2019

ЕвгенийП пишет:

А может их можно просто поменять местами? Это "B" нельзя настроить на СТС/PWM без "А", а "А" без "В" отлично настраивается. Будет у Вас меандр на ноге PB1, а освободившийся PB4 используйте для 595

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