Помогите с таймером и прерываниями

Azgo
Offline
Зарегистрирован: 03.04.2022

Уважаемые участники сообщества, прошу помощи. Задача у меня такая: Хочу просто сделать таймер обратного отсчета. Взял мк авр, 7 сегментный индикатор поставил авр студию, протеус, пишу на си. По моей задаче, собственно запрограммировал 0 таймер на прерывание и отображение информации на индикаторе. Всё работает, цифры показывает, используя  _delay_ms() таймер тикает  всё работает. Но все знают что _delay_ms() это бичёвская команда. И как от неё избавится. Предложение функция millis от ардуино. Но вот вопрос. как её реализовать на си в авр студии?

Я попытался инкрементировать переменную в обработчике прерывания. После чего в основном цикле значение этой переменной присваиваю другой переменной и дальше с ней работаю. Следующим образом. Когда переменная станет равна 1000, я хочу увеличить значение на индикаторе на единицу. Итог нехрена не работает как я хочу. Код прилагаю. Объясните где и что я делаю не так. 

#define F_CPU 8000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <util/delay.h>

//Инициализация глобальных переменных
int8_t n_count=0, caunterTimer0=1, razrad1=0, razrad2=0;
int16_t timerState=0,timerCaunter=0;

void Na_ekran(int8_t razbivka_chisla); //прототип функции разбивки числа на десятки и единицы
void segment(uint8_t seg);				//Прототип функции выбора сигмента


int main(void)
{
	//Настройка портов управления 7сегментного индикатора с общим анодом (возможно катодом не помню)
	DDRB = 0b11111111; //Настраиваем порт "В" на выход для работы с 7ми сегментным индикатором
	PORTB = 0b00000000;//
	DDRD|=(1<<PD0)|(1<<PD2)|(1<<PD3)|(1<<PD5)|(1<<PD6);
	PORTD|=(1<<PD2)|(1<<PD3);
	
	//Настройка теймера0
	TIMSK|=(1<<TOIE0);//Активация режима разрешение прерывания по переполнению для таймера счётчика0
	TCCR0B|=(0<<CS02)|(1<<CS01)|(0<<CS00);// делим частоту таймера на 8 
	TCNT0=0;//Обнуление счётного резистра таймера счётчика0
	
	int16_t caunter=0;

	while (1)
	{
		cli();
		timerCaunter=timerState;
		sei();

		if (timerCaunter==1000)
		{
			Na_ekran(caunter+=1);
		}
		cli();
		if (timerCaunter>1000)
		{
			timerState=0;
		}
		sei();
	}
}


ISR (TIMER0_OVF_vect)
{
	
	if(caunterTimer0==0)
	{
		PORTD&=~(1<<PD5);
		PORTD|=(1<<PD6);
		segment(razrad1);
	}
	if(caunterTimer0==1)
	{
		PORTD&=~(1<<PD6);
		PORTD|=(1<<PD5);
		segment(razrad2);
	}
	caunterTimer0++;
	
	
	if (caunterTimer0>1)
	{
		caunterTimer0=0;
	}
	timerState++;
	if (timerState>1000)
	{
		timerState=0;
	}
	
	
	
}
void Na_ekran (int8_t razbivka_chisla) //функция разбивки числа на десятки и единицы
{
	razrad1= razbivka_chisla/10;//десятки
	razrad2= razbivka_chisla%10;//еденицы
	
}
void segment (uint8_t seg) //функция воспроизведения цыфры на 7-сегментном индикаторе
{
	switch(seg)
	{
		case 1: PORTB = 0b11111001; break;
		case 2: PORTB = 0b10100100; break;
		case 3: PORTB = 0b10110000; break;
		case 4: PORTB = 0b10011001; break;
		case 5: PORTB = 0b10010010; break;
		case 6: PORTB = 0b10000010; break;
		case 7: PORTB = 0b11111000; break;
		case 8: PORTB = 0b10000000; break;
		case 9: PORTB = 0b10010000; break;
		case 0: PORTB = 0b11000000; break;
	}
}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Это невозможно. Причинно-следственная связь с реальностью симптома "нихрена не работает как я хочу" не существует в этой тентуре вселенной.

Green
Offline
Зарегистрирован: 01.10.2015

А эти странные имена: caunterTimer, razrad, Na_ekran... Ужос.

Azgo
Offline
Зарегистрирован: 03.04.2022

Я выше писал что только учусь. А как правильно называть в таком случае переменные?

Azgo
Offline
Зарегистрирован: 03.04.2022

А можно конкретизировать. Почему оно не работает?

rkit
Offline
Зарегистрирован: 23.11.2016

Azgo пишет:

Я попытался инкрементировать переменную в обработчике прерывания.


ISR (TIMER0_OVF_vect)
{
	
	if(caunterTimer0==0)
	{
		PORTD&=~(1<<PD5);
		PORTD|=(1<<PD6);
		segment(razrad1);
	}
	if(caunterTimer0==1)
	{
		PORTD&=~(1<<PD6);
		PORTD|=(1<<PD5);
		segment(razrad2);
	}
	caunterTimer0++;
	
	
	if (caunterTimer0>1)
	{
		caunterTimer0=0;
	}
	timerState++;
	if (timerState>1000)
	{
		timerState=0;
	}
	
	
	
}

Вот этот фарш теперь называется "инкрементировать переменную"?

Azgo
Offline
Зарегистрирован: 03.04.2022

Окей фарш, а как надо было?

Azgo
Offline
Зарегистрирован: 03.04.2022

Я не могу понять почему переменная tamerState непойми как инкрементруется

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

Azgo пишет:

Но все знают что _delay_ms() это бичёвская команда. 

Не все. Я, например, не знаю и считаю, что это "операция как операция".

Azgo пишет:

И как от неё избавится. 

Чтобы понимать "как избавляться", для начала нужно определиться "зачем избавляться". Чем она мешает? Или цель "избавиться для того, чтобы избавиться"?

Azgo пишет:

Предложение функция millis от ардуино. ... как её реализовать на си в авр студии?

Настроить таймер на срабатывание раз в миллисекунду и инкрементировать переменную. Будет точнее, чем в Ардуино.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Azgo пишет:

Я выше писал что только учусь. А как правильно называть в таком случае переменные?

Как не странно , но по английски. Есть много языков где используется латинские буквы, но там разная грамматика. Писать по русски латинскими тоже не хорошо. Так что лучше название переменных переводить на английский. Тем более есть куча интернет переводчиков. Еще плюс-английские слова как правило короче.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Обьявляешь глобальную переменную
volatile unsigned long millis=0;
Настраиваешь прерывание по таймеру раз в 1мс.
Там инкрементируешь переменную millis++;

Можешь функцию добавить:

uint32_t millis(){
return millis;
}

Тогда отличаться от работы в ардуино не будет

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018
// 328
#define F_CPU 8000000
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint32_t TimerCounter=0;

int main(void){
	TIMSK0|=(1<<OCIE0A);
	OCR0A=124; //                              8000/64-1=124
	TCCR0A|=(1<<WGM01);
	TCCR0B|=(0<<CS02)|(1<<CS01)|(1<<CS00); //  64
	TCNT0=0;
	sei();
	while (1){
		
	}
}


ISR (TIMER0_COMPA_vect){
	TimerCounter++;
}

TimerCounter равен числу миллисекунд - как millis

Azgo
Offline
Зарегистрирован: 03.04.2022

Komandir пишет:

// 328
#define F_CPU 8000000
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint32_t TimerCounter=0;

int main(void){
	TIMSK0|=(1<<OCIE0A);
	OCR0A=124; //                              8000/64-1=124
	TCCR0A|=(1<<WGM01);
	TCCR0B|=(0<<CS02)|(1<<CS01)|(1<<CS00); //  64
	TCNT0=0;
	sei();
	while (1){
		
	}
}


ISR (TIMER0_COMPA_vect){
	TimerCounter++;
}

TimerCounter равен числу миллисекунд - как millis


Я тоже так думаю, но при передаче в основной цикл и выводе значений на экран получается хрень. Вместо единицы за 1000 прерываний у меня вылетает к примеру число 15, потом 30 и т.д. Вот я и не пойму что я делаю не так!?

Azgo
Offline
Зарегистрирован: 03.04.2022

rkit пишет:

Azgo пишет:

Я попытался инкрементировать переменную в обработчике прерывания.


ISR (TIMER0_OVF_vect)
{
	
	if(caunterTimer0==0)
	{
		PORTD&=~(1<<PD5);
		PORTD|=(1<<PD6);
		segment(razrad1);
	}
	if(caunterTimer0==1)
	{
		PORTD&=~(1<<PD6);
		PORTD|=(1<<PD5);
		segment(razrad2);
	}
	caunterTimer0++;
	
	
	if (caunterTimer0>1)
	{
		caunterTimer0=0;
	}
	timerState++;
	if (timerState>1000)
	{
		timerState=0;
	}
	
	
	
}

Вот этот фарш теперь называется "инкрементировать переменную"?


Покажите пожалуйста как должно быть, чтобы это небыло фаршем.

b707
Offline
Зарегистрирован: 26.05.2017

Azgo][quote=Komandir пишет:

при передаче в основной цикл и выводе значений на экран получается хрень. Вместо единицы за 1000 прерываний у меня вылетает к примеру число 15, потом 30 и т.д.

Вот я и не пойму что я делаю не так!?

чтобы такого не было, все переменные, изменяемые в прерывании, должны быть обьявлены volatile

 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Azgo пишет:
Вот я и не пойму что я делаю не так!?

ВСЁ !!!

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018
#define F_CPU 8000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

volatile uint32_t TimerCounter=0;

uint32_t millis(void){
	uint32_t r;
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
		r=TimerCounter;
	}
	return r;
}

int main(void){
	TIMSK0|=(1<<OCIE0A);
	OCR0A=F_CPU/1000/64-1;                  // 8000/64-1=124
	TCCR0A|=(1<<WGM01);
	TCCR0B|=(0<<CS02)|(1<<CS01)|(1<<CS00);  //      64
	TCNT0=0;
	sei();
	uint32_t s=0;
	uint32_t om=millis();
	while (1){
		if((millis()-om)>=1000){
			om=millis();
			s++;
			//выводим s - секунды
		}
	}
}


ISR (TIMER0_COMPA_vect){
	TimerCounter++;
}

Время в прерывании ~60 тактов на каждые 8000 тактов (<1%).

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

Kakmyc пишет:

// Обьявляешь глобальную переменную

volatile unsigned long millis=0;

// Настраиваешь прерывание по таймеру раз в 1мс.
// Там инкрементируешь переменную millis++;

// Можешь функцию добавить:

uint32_t millis(){
    return millis;
}

// Тогда отличаться от работы в ардуино не будет

Так точно можно?

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

ЕвгенийП чего это компилятор не видит тут return ? (control reaches end of non-void function)

uint32_t millis(){
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
		return TimerCounter;
	}
}

P.S. Пока туда сюда копировал - ругаться перестал ...

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

Komandir пишет:

чего это компилятор не видит тут return ?

Потому, что его там нет.

Ваша функция разворачивается вот в такое (это легко посмотреть в папке buil IDE):

uint32_t  millis(){
  for ( uint8_t sreg_save __attribute__((__cleanup__(__iRestore))) = (*(volatile uint8_t *)((0x3F) + 0x20)), __ToDo = __iCliRetVal(); __ToDo ; __ToDo = 0 )
     return TimerCounter;
  }
}

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

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

ЕвгенийП   странная вещь ... если сменить уровень оптимизации, то ошибка при первой сборке появляется и при второй уже нет ...

(ничего кроме уровня оптимизации не меняется) (AtmelStudio 7)

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

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

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Использую Rebuild Solution...

Редактирование самого кода к появлению ошибки не приводит ...

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

Ну, не знаю как и что они там делают, но в том, что там действительно не на всех ветках есть return, компилятор прав, Вы же сами видите.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Мошт проще так:

// Обьявляешь глобальную переменную

volatile unsigned long millis=0;

// Настраиваешь прерывание по таймеру раз в 1мс.
// Там инкрементируешь переменную millis++;

// Можешь функцию добавить:

uint32_t millis(){
  uint32_t temp = millis;
  return temp;
}

// Тогда отличаться от работы в ардуино не будет

 

juf@mail.ru
Offline
Зарегистрирован: 09.12.2019

#include <avr/io.h>

  #include <avr/interrupt.h>

Подскажите пожалуйста, не смог победить эти две строки. Как надо подставлять библиотеку? Скачал avr-libc-master.zip, разархивировал и положил в C:\Program Files (x86)\Arduino\libraries\ , где все остальные лежат. Не видит. Вытащил папку Avr наверх, ругается опять что внутри не может найти файлы. Как надо то?

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Это стандартные библиотеки и они уже есть в Arduino IDE.

...\arduino-1.8.13\hardware\tools\avr\avr\include\avr

juf@mail.ru
Offline
Зарегистрирован: 09.12.2019

Спасибо. Тупо скопировал скетч из инета, пример для работы с прерываниями. Не подскажете почему тогда ругается на строку  -

sketch_apr13a:32:5: error: expected constructor, destructor, or type conversion before '(' token
 ISR(TIMER1_OVF_vect)                    // процедура обработки прерывания переполнения счетчика
?
Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

ХЗ что там за скетч у вас ... хрустальный шар разбил давно ...

Просто где то лишняя скобка или наоборот скобки не хватает ....

juf@mail.ru
Offline
Зарегистрирован: 09.12.2019
#include<LiquidCrystal.h>            //подключение библиотеки для работы с ЖК дисплеем
#define ledPin 7
LiquidCrystal lcd(8,9,10,11,12,13);
float value = 3035;                   //Preload timer value (3035 for 4 seconds)
void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(0,0);
  lcd.print("ARDUINO TIMERS");
  delay(2000);
  lcd.clear();
 
  pinMode(ledPin, OUTPUT);
  pinMode(2,INPUT);
  pinMode(4,INPUT);
  
  noInterrupts();                       // отключаем все прерывания
  
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = value;                        // preload timer
  TCCR1B |= (1 << CS10)|(1 << CS12);    // 1024 prescaler (коэффициент деления предделителя)
  TIMSK1 |= (1 << TOIE1);               // enable timer overflow interrupt ISR (разрешаем вызов процедуры обработки прерывания переполнения счетчика)
  interrupts();                         // разрешаем все прерывания
}
ISR(TIMER1_OVF_vect)                    // процедура обработки прерывания переполнения счетчика
{
  TCNT1 = value;                                // preload timer
  digitalWrite(ledPin, digitalRead(ledPin) ^ 1);  //включаем и выключаем светодиод
}
void loop()
{
  if(digitalRead(2) == HIGH)
  {
    value = value+10;             //Incement preload value
  }
  if(digitalRead(4)== HIGH)
  {
    value = value-10;            //Decrement preload value
  }
  lcd.setCursor(0,0);
  lcd.print(value);
}

 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018
Скетч использует 4006 байт (12%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 77 байт (3%) динамической памяти, оставляя 1971 байт для локальных переменных. Максимум: 2048 байт.
 
Всё чётко !
juf@mail.ru
Offline
Зарегистрирован: 09.12.2019

А от платы это не зависит? У меня Wemos D1. Может надо библиотеки дополнительные подключать?

sadman41
Offline
Зарегистрирован: 19.10.2016

Этот скетч не для ESP, а для AVR.

juf@mail.ru
Offline
Зарегистрирован: 09.12.2019

sadman41 пишет:

Этот скетч не для ESP, а для AVR.

Понял,спасибо огромное. А где бы почитать про прерывания для ESP?

sadman41
Offline
Зарегистрирован: 19.10.2016
SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

juf@mail.ru пишет:

Понял,спасибо огромное. А где бы почитать про прерывания для ESP?

Для ESP8266 есть библиотечка Ticker. 

Вопросы с прерыванием от таймера ей решаются.

b707
Offline
Зарегистрирован: 26.05.2017

juf@mail.ru пишет:

Понял,спасибо огромное. А где бы почитать про прерывания для ESP?

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