Перейти к содержанию

Таймер обратного отсчета


S1mpleX

Рекомендуемые сообщения

Здравствуйте!
Помогите мне пожалуйста, я начинающий в МК и вот пишу свой первый, немножечко "серьезный" проект и никак не могу добиться работоспособности((
Суть в следующем:
Таймер после нажатия на кнопку "старт/стоп"запускает обратный отсчет (по умолчанию от 300 секунд), кнопками +10 и -10, изменяется время по 10 сек. Во время работы я хочу считывать регистр АЦП и писать его в регистр таймера (ШИМ, который управляет двигателем), оставшееся или установленное время отображается на 3х разрядном индикаторе, после достижения ноля хочу в регистр ШИМа писать ноли и останавливать двигатель.
Но сколько уже не играюсь - оно не работает в протеусе((
Очень очень сильно прошу помощи, а то уже и охота пропадает заниматься(((
Заранее спасибо! Проект протеуса и код в Atmel Studio прилагаю. MK - ATMega8

#define F_CPU 8000000
#define ADC_VREF_TYPE ((0<<REFS1) | (1<<REFS0) | (0<<ADLAR))
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned char codes[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xD8, 0x80, 0x90};
volatile int dig = 1;			  //Номер разряда
volatile int seconds = 300;       //Выводимые данные
char Timeout;
char Start;
int adc_result;

void IO_init(void){
	//_IO_Init
	// Port B initialization
	// Function: Bit7=Out Bit6=Out Bit5=Out Bit4=Out Bit3=Out Bit2=In Bit1=In Bit0=In
	DDRB=(1<<DDB7) | (1<<DDB6) | (1<<DDB5) | (1<<DDB4) | (1<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
	// State: Bit7=0 Bit6=0 Bit5=0 Bit4=0 Bit3=0 Bit2=P Bit1=P Bit0=P
	PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (1<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);

	// Port C initialization
	// Function: Bit6=Reset Bit5=Out Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=In
	DDRC=(0<<DDC6) | (1<<DDC5) | (1<<DDC4) | (1<<DDC3) | (1<<DDC2) | (1<<DDC1) | (0<<DDC0);
	// State: Bit6=0 Bit5=0 Bit4=0 Bit3=0 Bit2=0 Bit1=0 Bit0=T
	PORTC=(0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);

	// Port D initialization
	// Function: Bit7=Out Bit6=Out Bit5=Out Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=Out
	DDRD=(1<<DDD7) | (1<<DDD6) | (1<<DDD5) | (1<<DDD4) | (1<<DDD3) | (1<<DDD2) | (1<<DDD1) | (1<<DDD0);
	// State: Bit7=0 Bit6=1 Bit5=1 Bit4=1 Bit3=1 Bit2=1 Bit1=1 Bit0=1
	PORTD=(0<<PORTD7) | (1<<PORTD6) | (1<<PORTD5) | (1<<PORTD4) | (1<<PORTD3) | (1<<PORTD2) | (1<<PORTD1) | (1<<PORTD0);
		
}

unsigned int read_adc(void)
{
	unsigned int adc_result;
	ADMUX &= (~(1<<MUX0)) | (~(1<<MUX1)) | (~(1<<MUX2)) | (~(1<<MUX3)) | ADC_VREF_TYPE;
	_delay_us(10);
	ADCSRA|=(1<<ADSC);
	while ((ADCSRA & (1<<ADIF))==0);
	ADCSRA|=(1<<ADIF);
	adc_result=(ADCL|ADCH<<8);
	return adc_result;
}

void ind_timer_init(void)
	{
		TIMSK = (1 << TOIE0);       //Разрешить прерывание таймера по переполнению
		TCCR0 = (1 << CS01);        //Предделитель - 8
	}

void PWM_timer_init(void){
	// Timer/Counter 2 initialization
	// Clock source: System Clock
	// Clock value: 31,250 kHz
	// Mode: Fast PWM top=0xFF
	// OC2 output: Non-Inverted PWM
	// Timer Period: 8,192 ms
	// Output Pulse(s):
	// OC2 Period: 8,192 ms Width: 0 us
	ASSR=0<<AS2;
	TCCR2=(1<<WGM20) | (1<<COM21) | (0<<COM20) | (1<<WGM21) | (1<<CS22) | (1<<CS21) | (0<<CS20);
	TCNT2=0x00;
	OCR2=0x00;
}

void RTC_timer_init(void){
	 TCCR1A = 0;			 // настройка таймера 1, канала А
	 TCCR1B |= (1<<CS12);			 // предделитель CLK/256;
	 OCR1A = 0x7A11;		 // прерывание раз в секунду
	 TIMSK &= (~(1<<OCIE1A));	// прерывание таймера откл
}


/************************************************************************/
/*                          INTERRUPT'S PART                            */
/************************************************************************/
ISR (TIMER0_OVF_vect)
{
	PORTD = 0xFF;       //Выключаем все сегменты
	PORTC = (1 << dig);
	switch(dig)
	{
		case 1: PORTD = (codes[seconds % 1000 / 100]); break;          //Раскладываем число на разряды
		case 2: PORTD = (codes[seconds % 100 / 10]); break;
		case 3: PORTD = (codes[seconds % 10]); break;
	}
	if ((dig++) > 2) dig = 1;
}

ISR(TIMER1_COMPA_vect)
{
	Timeout = 1;
	seconds--;
	if (seconds < 1) {
		Start=0;
		TIMSK &= (~(1<<OCIE1A));
	}
}
	
int main()
{
	IO_init(); 
	ind_timer_init();
	RTC_timer_init();
	read_adc;
	sei();
			
	while(1)
	{
		if (!PINB2)
		{
			_delay_ms(50);
			if (!PINB2)
			{
				Start ^= 1;
				TCNT1 = 0;
				TIMSK |= (1<<OCIE1A);
			}
		}
		if (!PINB1)
		{
			_delay_ms(50);
			if (!PINB1)
			{
				seconds+=10;
			}
		}
		if (!PINB0)
		{
			_delay_ms(50);
			if (!PINB0)
			{
				seconds-=10;
			}
		}
		
		while(Start)
		{
			Timeout = 0;
			OCR2 = adc_result;
			while(!Timeout);
		}
						
	}
}

 

321.pdsprj


 

Ссылка на комментарий
Поделиться на другие сайты

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

6 часов назад, Alex сказал:

Что означает "не работает" ? Что конкретно не работает ?
 

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


 

Ссылка на комментарий
Поделиться на другие сайты

13 часа назад, S1mpleX сказал:

При старте сразу же начинается отсчет, при чем секунд длится дольше (с частотой все ок), либо просто вычитается по 10 секунд, не останавливается в нуле 

Заметил сразу две ошибки :

1) Флаг обрабатывающий только одно нажатие . При нажатии у Вас может произойти установка не +10сек или -10 сек за одно нажатие, а сразу +20,+30 сек и соответственно -20,-30 сек.

2) Ограничение переменных +10/-10 сек. они могут выйти за пределы допустимого .

Что может быть лучше в радиоэлектронике, чем программирование микроконтроллеров ?

Ссылка на комментарий
Поделиться на другие сайты

1 час назад, artos5 сказал:

Заметил сразу две ошибки :

1) Флаг обрабатывающий только одно нажатие . При нажатии у Вас может произойти установка не +10сек или -10 сек за одно нажатие, а сразу +20,+30 сек и соответственно -20,-30 сек.

2) Ограничение переменных +10/-10 сек. они могут выйти за пределы допустимого .

Спасибо, а где по коду это?


 

Ссылка на комментарий
Поделиться на другие сайты

Только что, Alex сказал:

Хм... Как тогда можно не понимать, как работает собственный код ? :huh:


 

я понимаю ка он должен работать, но не понимаю, почему так не происходит


 

Ссылка на комментарий
Поделиться на другие сайты

В 19.06.2017 в 21:46, S1mpleX сказал:

if (!PINB2)

А, простите, чего вы хотели добиться выражением if( ! 2 ), полностью эквивалентному процитированному? Это вам не cvavr где есть извращения вроде PINB.2

Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Ссылка на комментарий
Поделиться на другие сайты

29 минут назад, COKPOWEHEU сказал:

А, простите, чего вы хотели добиться выражением if( ! 2 ), полностью эквивалентному процитированному? Это вам не cvavr где есть извращения вроде PINB.2

Atmel Studio понимает это, на сколько я знаю, она сама предложила такую запись


 

Ссылка на комментарий
Поделиться на другие сайты

Эта запись вполне корректна с точки зрения языка. Вот только бесполезна, поскольку PINB2 это константа, равная 2.

Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Ссылка на комментарий
Поделиться на другие сайты

26 минут назад, COKPOWEHEU сказал:

Эта запись вполне корректна с точки зрения языка. Вот только бесполезна, поскольку PINB2 это константа, равная 2.

Почему? И как тогда правильно опрашивать пин? Спасибо


 

Ссылка на комментарий
Поделиться на другие сайты

Почему? С одной стороны, так оно объявлено в avr/iom8.h (наверное). С другой стороны, в Си нет прямой работы с отдельными битами переменной - только через логические операции.

Традиционно используются подобные записи:

PORTA |= (1<<0);
PORTB &=~(1<<1);
if( PINC & (1<<2) ){...}
if( ! (PIND & (1<<3)) ){...}

Рекомендую составить таблицы истинности и разобраться какая из записей что делает. Подсказка: каждая из них работает с одним битом.

Когда разберетесь, можете глянуть мои макросы (pinmacro.h в моей подписи) и использовать более наглядные и переносимые записи вроде

#define BUTTON E,4,0 //кнопка на PE4, замыкается на землю
DDR_1( BUTTON ); //включить подтяжку кнопки к питанию
if( PIN_ON( BUTTON) ){...}

Но для этого надо хорошо разбираться в битовой и макросной магии.

.

Помимо прочего, рекомендую разобраться с правильным опросом кнопок. Самое простое - просто регулярно опрашивать с частотой около 10 Гц, в бесконечном цикле или по таймеру. Другой вариант - отлавливать фронты: запоминать предыдущее состояние кнопки в переменной и сравнивать с текущим, но тут возникают проблемы с дребезгом.

Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Ссылка на комментарий
Поделиться на другие сайты

6 часов назад, COKPOWEHEU сказал:

Почему? С одной стороны, так оно объявлено в avr/iom8.h (наверное). С другой стороны, в Си нет прямой работы с отдельными битами переменной - только через логические операции.

Традиционно используются подобные записи:


PORTA |= (1<<0);
PORTB &=~(1<<1);
if( PINC & (1<<2) ){...}
if( ! (PIND & (1<<3)) ){...}

Рекомендую составить таблицы истинности и разобраться какая из записей что делает. Подсказка: каждая из них работает с одним битом.

Когда разберетесь, можете глянуть мои макросы (pinmacro.h в моей подписи) и использовать более наглядные и переносимые записи вроде


#define BUTTON E,4,0 //кнопка на PE4, замыкается на землю
DDR_1( BUTTON ); //включить подтяжку кнопки к питанию
if( PIN_ON( BUTTON) ){...}

Но для этого надо хорошо разбираться в битовой и макросной магии.

.

Помимо прочего, рекомендую разобраться с правильным опросом кнопок. Самое простое - просто регулярно опрашивать с частотой около 10 Гц, в бесконечном цикле или по таймеру. Другой вариант - отлавливать фронты: запоминать предыдущее состояние кнопки в переменной и сравнивать с текущим, но тут возникают проблемы с дребезгом.

Таймеры у меня заняты, поэтому так и хочу записать


 

Ссылка на комментарий
Поделиться на другие сайты

Только что, S1mpleX сказал:

Таймеры у меня заняты

Неужели так заняты, что опрос порта не смогут делать?

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

6 минут назад, ARV сказал:

Неужели так заняты, что опрос порта не смогут делать?

Не знаю, я еще не очень разбираюсь, поэтому и пишу сюда

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


 

Ссылка на комментарий
Поделиться на другие сайты

Уже лучше, кнопки заработали, осталась проблема с частотой, проходит явно больше секунды перед декрементом секунд, хотя таймер настроен  правильно


 

Ссылка на комментарий
Поделиться на другие сайты

20 минут назад, Alex сказал:

Частота камня не совпадает с ожидаемой ?
 

Совпадает, первая секунда идет четко, а потом все равно что замедленно в 2 раза, как будто прерывания индикации отодвигают прерывание счетчика секунд, хотя приоритет его выше


 

Ссылка на комментарий
Поделиться на другие сайты

С отсчетом времени у вас тоже накручено что-то непонятное. Я бы повесил на T2 (раз он умеет асинхронный счет) отсчет времени. А на T1 - ШИМ: у него и разрядность выше, и два канала выведены, и режимов больше.

volatile uint16_t time_s = 0;

ISR(TIMER2_COMP_vect){ //F_Timer2 = F_CPU/256 (CS22:CS20 = 0b110) ; OCR2 = 250
  static uint8_t frac_sec = 0;
  frac_sec++;
  if( frac_sec >= 125 ){
    frac_sec = 0;
    time_s ++;
  }
}

Константы (делитель на 256, аппаратный счет до 250 и программный до 125) выбраны чтобы при тактовой частоте 8 МГц переменная time_s менялась раз в секунду.

В этот же обработчик можно добавить динамическую индикацию. Только реализовав по-нормальному, а не как в коде в 1 посте. Разложение на разряды делается не при каждом выводе, а только при изменении. То есть заводится буфер кадра в котором хранятся сразу коды для вывода на индикатор/

Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Ссылка на комментарий
Поделиться на другие сайты

7 часов назад, Alex сказал:

В 8-ой Меге нет приоритетов. Все прерывания встают в очередь и будут ждать пока не обработается другое работающее.
 

Спасибо вам!

2 часа назад, COKPOWEHEU сказал:

С отсчетом времени у вас тоже накручено что-то непонятное. Я бы повесил на T2 (раз он умеет асинхронный счет) отсчет времени. А на T1 - ШИМ: у него и разрядность выше, и два канала выведены, и режимов больше.


volatile uint16_t time_s = 0;

ISR(TIMER2_COMP_vect){ //F_Timer2 = F_CPU/256 (CS22:CS20 = 0b110) ; OCR2 = 250
  static uint8_t frac_sec = 0;
  frac_sec++;
  if( frac_sec >= 125 ){
    frac_sec = 0;
    time_s ++;
  }
}

Константы (делитель на 256, аппаратный счет до 250 и программный до 125) выбраны чтобы при тактовой частоте 8 МГц переменная time_s менялась раз в секунду.

В этот же обработчик можно добавить динамическую индикацию. Только реализовав по-нормальному, а не как в коде в 1 посте. Разложение на разряды делается не при каждом выводе, а только при изменении. То есть заводится буфер кадра в котором хранятся сразу коды для вывода на индикатор/

Огромное спасибо! попробую поменять таймеры, а можете так же показать пример как корректно организовать индикацию?

И вот еще вопрос, разве тогда не будет все равно ждать обработки прерывания таймера2, после обработки прерывания индикации?


 

Ссылка на комментарий
Поделиться на другие сайты

Прерывания достаточно короткие (или должны быть короткими) и успеют завершиться за 1/125 секунды (64000 тактов). По крайней мере не придется следить за тем какой таймер запущен а какой остановлен.
 

#define SEG_A (1<<3)
#define SEG_B (1<<6)
...
const uint8_t seg[] = { //коды цифр (для примера, зажигаются при подаче лог.1)
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, //0
  SEG_B | SEG_C, //1
...
};
//для примера, пусть 3 разряда
#define DIGIT_1 (1<<2)
#define DIGIT_2 (1<<4)
#define DIGIT_3 (1<<6)
const uint8_t digits[3] = {DIGIT_1, DIGIT_2, DIGIT_3}; //коды разрядов (для примера, активный уровень - лог.0, то есть индикаторы с общим катодом)
#define PORT_SEG PORTA
#define PORT_DIGIT PORTB

volatile uint8_t videobuffer[3];

//отображение следующего разряда. Вызывать в основном цикле или по таймеру
inline void dyn_ind(){
  static uint8_t cur_digit = 0;
  PORT_SEG = 0; //гасим все сегменты чтобы на следующем не было засвета от предыдущего
  cur_digit++;
  if(cur_digit > 3)cur_digit=0;
  PORT_DIGIT |= DIGIT_1 | DIGIT_2 | DIGIT_3; //отключаем все разряды (поскольку активный уровень лог.0, то гасим записью лог.1)
  PORT_DIGIT &=~ digits[ cur_digit ]; //зажигаем (выставляем в лог.0) только текущий разряд
  PORT_SEG = videobuffer[ cur_digit ]; //зажигаем текущий символ
}

//вывод на индикатор целого числа
void out_byte(uint8_t x){
  videobuffer[2] = seg[ x % 10 ]; //сразу перевод из цифры в код символа, чтобы меньше вычислений делалось в процедуре индикации
  x /= 10;
  videobuffer[1] = seg[ x % 10 ];
  videobuffer[0] = seg[ x / 10 ];
}

Более универсальная версия есть у меня в подписи - 7seg.h

Ругался на отсутствие форматирования исходного кода (включая отсутствие осмысленных комментариев и наличие неубранного после конфигуратора мусора) не менее 15 раз.

Часть моих наработок.

Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...