Индикация 00:00 = Часы:Мнуты (Mега88V)

Обсуждаем контроллеры компании Atmel.
Ответить
Открыл глаза
Сообщения: 47
Зарегистрирован: Ср ноя 12, 2014 14:48:39

Сообщение krokodandi »

Я разбираюсь с отображением времени на индикаторе. Семисегментник у меня на 4 разряда.
Я пытаюсь вывести на индикатор часы и минуты, а выводятся секунды. Причем, если меняю программу в функции индикации, выводится какая-то муть. Мне это не нравится.
Должен выводится отсчет в обратном порядке.
Привожу рабочий код. Работает десятичный счет до 9999. В виде примера конечно же.
А что надо поправить, чтобы выводились часы.минуты?

Код: Выделить всё

//**********************************************************************************************************************************
                                              //     НАЧАЛО ФАЙЛОВОЙ ИНИЦИАЛИЗАЦИИ     //

#define F_CPU 8000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

//**********************************************************************************************************************************
                                              //     ОСНОВНЫЕ ПЕРЕМЕННЫЕ     //

// Если надо добавить точку, то пиши для ВКЛ data[1]=codes[res]^0x04;  для ВЫКЛ data[1]=codes[res]|4;
short int k = 0; 

volatile int h = 21, m = 35, s = 0, ms = 0; // тут был unsignet. Это ошибка. Поправил 

//**********************************************************************************************************************************
                                              //     ОСНОВНЫЕ МАССИВЫ ДЛЯ ВЫВОДА     //

const unsigned char codes[10]={0x14,0xD7,0x4C,0x45,0x87,0x25,0x24,0x57,0x4,0x5}; // 0-1-2-3-4-5-6-7-8-9
unsigned char counter[4]={0x08,0x04,0x02,0x01};  // 0b 0000 1111 то, есть это 8+4+2+1 // для счетчика разрядов семисегментника порта С
unsigned char data[4]={0x14,0x14,0x14,0x14};     // для значений, выводимых на дисплей... По умолчанию Err_t ТУТ {0x2C,0xEE,0xEE,0xAC}

//**********************************************************************************************************************************
                                            //    ОКОНЧАТЕЛЬНЫЙ ВАРИАНТ ПРЕРЫВАНИЯ T0    //
											  
											  
ISR(TIMER0_OVF_vect)
{
	TCNT0 = 0x7D;           // Прерывание будет вызываться через 1 мс. Стандартный вариант для 8МГц/64. 125000/125=1000 Гц (1мс)

	                        // считаем от нуля до трех. Переключаем разряд и снова ВКЛЮЧАЕМ.
	(k == 3) ? k = 0 : k++; // Если k достигла трёх, k сбрасываем, иначе k + 1 при каждом вхождении
	PORTC = counter[k];     // Просто включаем аноды сигментника по очереди.
	PORTD = data[k];        // При каждом вхождении выводим битную маску в порт по адресам портов 0,1,2,3.
	
	//   ТАЙМЕРЫ: ТИКАЕМ 1000... ВХОЖДЕНИЙ. ПОСЛЕ ПОДНИМАЕМ ФЛАГ КАЖДЫЙ ПЕРИОД    //
	
	if (++ms > 1000)    // Тактирование каждую миллисекунду и Если насчитали 1000 мс, то считаем мы получили период = 1 секунде!
	{
		ms = 0;
		s++;   
		    if (s==60)
		    {
		        s=0;
		        m--;	
		        if ( m < 0)
		            {
		                if (h>0)
		                 {
			                 h--;
					 m=59;
			                
		                 }
		            }
		
			}
				
	 }
	 
		
}

//**********************************************************************************************************************************
//**********************************************************************************************************************************
                                              //    ОСНОВНЫЕ ФУНКЦИИ ПРОГРАММЫ    //

void init_timer (void)
{
	
	TIMSK0 = (1<<TOIE0);
	TCCR0B = (0<<CS02)|(1<<CS01)|(1<<CS00) ; //8.000.000/64 = 125.000 тиков в сек. (0-1-1 - делитель на 64)
	
}

//**********************************************************************************************************************************
//**********************************************************************************************************************************
//*************************    ДАННЫЙ КУСОК КОДА СЧИТАЕТ КОЛИЧЕСТВО СЕКУНД В ДЕСЯТИЧНОМ ФОРМАТЕ    *********************************
//**********************************************************************************************************************************  
//**********************************************************************************************************************************  

void convert_data_desimal ()                                                                                                   
{
  //Весь вывод идет через data[х]
  unsigned int temp,res;
  //++s;
  temp = s;
  
  res = temp/1000;
  data[0]=codes[res];
  temp=temp-res*1000;
  
  res = temp/100;
  if (s % 2)
  {data[1]=codes[res]^4;
  }
  else
  {data[1]=codes[res]|4;
  }
  temp=temp-res*100;
                       //маленькое вскрытие кода =))
  res = temp/10;       // res=1, temp=10
  data[2]=codes[res];  // вывод на дисплей 1 из массива codes...
  temp=temp-res*10;    // (1*10)-10=0 вот такия пирожки.
  
  data[3]=codes[temp];

}

//**********************************************************************************************************************************
                                              //    ИНИЦИАЛИЗАЦИЯ УСТРОЙСТВ ПРОГРАММЫ    //

int main (void)
{
	cli();
	DDRC = 0x0f;                // готовим порты: PortD - data PortC - anode
	DDRD = 0xff;
	PORTD = 0;
	
	init_timer ();             // готовим таймер/счетчик 0
	
	sei ();                    // Разрешить общие прерыввания. Можно еще написать по старинке SREG |= (1 << 7); 
	
//**********************************************************************************************************************************
                                              //        ГЛАВНЫЙ ЦИКЛ ПРОГРАММЫ        //	
	
	while(1)          
	{
		convert_data_desimal(s);
	}
	
}

Добавляю умножение на 100, выводится 00:21. 21 - это часы, а кажет как минуты

Код: Выделить всё

if (h>0)
         {
	  h--;
	  m=59;
	  h = (h*100)+m;
         }
Последний раз редактировалось krokodandi Пт июл 17, 2015 07:56:53, всего редактировалось 1 раз.
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

первое, что бросилось в глаза: часы, минуты и т.п. у вас unsigned, а в коде есть проверки m<0 - unsigned никогда не будет меньше 0!
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Открыл глаза
Сообщения: 47
Зарегистрирован: Ср ноя 12, 2014 14:48:39

Сообщение krokodandi »

ARV писал(а):первое, что бросилось в глаза: часы, минуты и т.п. у вас unsigned, а в коде есть проверки m<0 - unsigned никогда не будет меньше 0!
Есть такое. Спасибо. Сам бы до этого не догадался. Поправил. Мучаю код дальше.
Открыл глаза
Сообщения: 47
Зарегистрирован: Ср ноя 12, 2014 14:48:39

Сообщение krokodandi »

Я использовал данные из статьи про таймер с фриков. [url/] https://www.avrfreaks.net/forum/countdown-timer[/url]
Некоторую часть пришлось переделать и выкинуть отсчет из прерывания. Я использовал флаговый автомат. Рабочий код не выложу. Только в личку =)
Реклама
Эиком - электронные компоненты и радиодетали
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

В main вы вызываете convert_data_desimal(s). При этом сама функция написана как convert_data_desimal(void), т.е. не принимает никаких параметров. Она всегда выводит глобальные секунды (от 0 до 59), что бы в неё ни передавали. Соответственно, индикатор будет всегда выводить 00:ss.

Перепишите её как convert_data_deсimal(int16_t s), а ещё лучше (необязательно, но Вам же будет проще понять) выводите в ней не s, а обезличенное value. Типа
convert_data_decimal(int16_t value) {
...
temp = value;
...
}
А в main вызывайте что-то вроде convert_data_decimal(m + 60*s) для mm:ss, или convert_data_decimal(h + 60*m) для hh:mm.

Замечание 1: Ещё, у Вас левая цифра индикатора - это data[0], правая - data[3]. Это нелогично, логичнее нумеровать из справа налево.

Замечание 2. Смысла использовать отдельные минуты-секунды-часы особого нет. Лучше пользоваться одной переменной time (увы, типа long). В прерывании просто делать time++ (или time--) для обратного счёта, ограничив её диапазоном 0..86399 (сутки), тем самым разгрузив код прерывания. А уже при выводе времени разбирать её на часы-минуты-секунды.
Контактная информация:
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

я бы не согласился с вами, ув. WiseLord: разбор long на секунды-минуты-часы - это либо деления, либо многократные вычитания. и то и другое напрасный расход ресурсов МК. лесенка if-ов с действиями над байтами более эффективна по всем статьям. имхо.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Возможно. Но тут смысл в том, что в прерывании останется только инкремент, а он на AVR и для long не слишком сложен. А "тяжёлые" преобразования уже бы шли в основном потоке программы, где это не так критично.

А если уж делать "лесенку" в прерывании.. возможно, есть смысл делать её не для секунд-минут-часов, а разбить ещё сильнее - для отдельных разрядов (десятки часов - единицы часов -...). Тогда функции вывода вообще упростятся, деление на 10 не понадобится в принципе. Не зря же микросхемах RTC обычно двоично-десятичный код и используют для хранения и счёта времени.
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

WiseLord писал(а):есть смысл делать её не для секунд-минут-часов, а разбить ещё сильнее - для отдельных разрядов (десятки часов - единицы часов -...). Тогда функции вывода вообще упростятся, деление на 10 не понадобится в принципе.
именно так я и поступал, когда делал свой супермаленький таймер.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Открыл глаза
Сообщения: 47
Зарегистрирован: Ср ноя 12, 2014 14:48:39

Сообщение krokodandi »

ARV писал(а):
WiseLord писал(а):есть смысл делать её не для секунд-минут-часов, а разбить ещё сильнее - для отдельных разрядов (десятки часов - единицы часов -...). Тогда функции вывода вообще упростятся, деление на 10 не понадобится в принципе.
именно так я и поступал, когда делал свой супермаленький таймер.
Я не знаю как Вы делали, но решил поступить по другому. Я прорабатывал другую схему.
Для меня важно не нагружать подпрограмму прерываний. Абсолютно не важно, что за ПП. Главное, чтобы отработала быстро и вышла.

Привожу пример.

Первое: Прерывание должно считать мс и ставить флаг секунды.
Плюс - использование задержки счета мс в if( i = мс ){}

Код: Выделить всё

											  											  
ISR(TIMER0_OVF_vect) 
{
	TCNT0 = 0x7D;           // Прерывание будет вызываться через 1 мс. Стандартный вариант для 8МГц/64. 125000/125=1000 Гц (1мс)
    PORTC=0x0f; 
	                        // считаем от нуля до трех. Переключаем разряд и снова ВКЛЮЧАЕМ.
	(k == 3) ? k = 0 : k++; // Если k достигла трёх, k сбрасываем, иначе k + 1 при каждом вхождении
	PORTC = counter[k];     // Просто включаем аноды сигментника по очереди.
	PORTD = data[k];        // При каждом вхождении выводим битную маску в порт по адресам портов 0,1,2,3.
		
    //   ТАЙМЕРЫ: ТИКАЕМ 1000... ВХОЖДЕНИЙ. ПОСЛЕ ПОДНИМАЕМ ФЛАГ КАЖДЫЙ ПЕРИОД    //
	if (++ms > 1000)
	{
         ms = 0;
		 flag_sec = 1;
    }

}
Потом можно писать выполнение. Так как цикл выполняется на неделённой частоте, функция выполняется быстрее (1/8000), чем прерывание.

Код: Выделить всё

												 
void countdown_timer_data_out()   // ФУНКЦИЯ ОТСЧЕТА ВРЕМЕНИ И АРИФМЕТИЧЕСКОГО ПРЕОБРРАЗОВАНИЯ С ВЫВОДОМ НА СЕМИСЕГМЕНТНИК 
{

if (flag_sec == 1)
{

s--;
if (s==0 && m==0 && h==0 )
{
	alarm_flag = 0; //время истекло и мы делаем че надо
	
}
else if (s < 0)
{
	s=59;
	m--;
	if (m < 0)
	{
		m=59;
		h--;
	}
}
flag_sec = 0;
}
//Вывод на дисплей. Арифметика...
seconds_to_go = (((h*60)+m)*60)+s;
h = seconds_to_go / (60*60);
m =(seconds_to_go-(h*60*60))/60;
s = seconds_to_go % 60;
if (h==0)
   {

data[0]=codes[m/10];
data[1]=codes[m%10];
data[2]=codes[s/10];
data[3]=codes[s%10];
   }
else 
   {
	data[0]=codes[h/10];
	data[1]=codes[h%10];
	data[2]=codes[m/10];
	data[3]=codes[m%10];
   }
}
Индикация работает отлично. Что до ресурса МК - не знаю. Остальные варианты не были рабочими. Я не видел ни одного примера индикации с подсчетом обратного времени чч:мм:сс:мсмс, чтобы он был менее 800байт и чтобы прога работала достаточно быстро без глюков.
Флаговый автомат тут решает!
Разложение long int нужен для индикации на 4х сегментах. Опять же, вариантов нет. Варианты может и есть, но кто расскажет? =) Небось за денежку.
Я делюсь, потому что задели меня фразой про ресурс МК
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

я не знаю, что вы видели, а что нет... но мне кажется, здесь if (++ms > 1000) у вас будет возникать ошибочка в 1 мс
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

И тут ошибочка, и само прерывание будет вызываться не раз в миллисекунду, как задумано. Для индикации некритично, но для времени - уже да. В самом начале прерывания TCNT0 приравнивается зачем-то к 125 (7D), и следующее прерывание возникнет через (255-125) = 130 тактов таймера.

Вообще, krokodandi, вы тут бросаетесь умными словами вроде "автомат" и прочее - хотя таймер, индикация - та ещё банальщина. Правильный код пишется за пару часов. Вот прямо сейчас попробую, в одном даже файле...
Контактная информация:
Открыл глаза
Сообщения: 47
Зарегистрирован: Ср ноя 12, 2014 14:48:39

Сообщение krokodandi »

WiseLord писал(а):И тут ошибочка, и само прерывание будет вызываться не раз в миллисекунду, как задумано. Для индикации некритично, но для времени - уже да. В самом начале прерывания TCNT0 приравнивается зачем-то к 125 (7D), и следующее прерывание возникнет через (255-125) = 130 тактов таймера.

Вообще, krokodandi, вы тут бросаетесь умными словами вроде "автомат" и прочее - хотя таймер, индикация - та ещё банальщина. Правильный код пишется за пару часов. Вот прямо сейчас попробую, в одном даже файле...
Я рад что тут увидели ошибочку в одну мс. if (++ms > 1000)
Догадывался, что что-то не то. Тогда писать if (ms++ > 1000) ?

Ну вообще нужно 125 тактов. Почему вы думаете, что таймер считает инверсно. Разве так... 0b 1111 1111 (-1), 1111 1110 ?
Я думал так 0b 0000 0000 (+1) 0b 0000 0001...

Слово автомат - это про вторую часть, не про прерывания.
...Правильный код пишется за пару часов.
Не думаю, если пишешь такое впервые в жизни. А мне еще способы управления дописывать. =)
Да и на форуме говорят, что мол за вас делать никто ничего не будет. Вот я делаю сам.
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

krokodandi писал(а):Ну вообще нужно 125 тактов. Почему вы думаете, что таймер считает инверсно. Разве так... 0b 1111 1111 (-1), 1111 1110 ?
Да, нужно 125 тактов. Но устанавливая таймер в самом начале прерывания в число 125, вы заставляете до следующего прерывания (256 = 0) отсчитывать 131 такт. Плюс на вызов прерывания тратится некоторое количество тактов (на сброс текущего состояния регистров в стек), что тоже в коде не учтено.
Контактная информация:
Открыл глаза
Сообщения: 47
Зарегистрирован: Ср ноя 12, 2014 14:48:39

Сообщение krokodandi »

WiseLord писал(а):
krokodandi писал(а):Ну вообще нужно 125 тактов. Почему вы думаете, что таймер считает инверсно. Разве так... 0b 1111 1111 (-1), 1111 1110 ?
Да, нужно 125 тактов. Но устанавливая таймер в самом начале прерывания в число 125, вы заставляете до следующего прерывания (256 = 0) отсчитывать 131 такт. Плюс на вызов прерывания тратится некоторое количество тактов (на сброс текущего состояния регистров в стек), что тоже в коде не учтено.
Я согласен с тем,,что до 256 остается 131 такт, но разве TCNT0 не скидывает этот порог снова до 125. Таймер должен считать с 0 до 125, наоборот надо ставить 125. И может быть даже 124. Тогда в идеале будет 125 тактов.
С другой стороны, на вызов ПП тратится пару циклов. Дапустим уде будет 130, но это уже будет после преодоленного порога в 125 тактов. Получается, что через следующие 125 тактов, с момента нового отсчета, случится следующее прерывание, разве нет? Я не заставляю считать еще 131 такт, потому что я скидываю TCNT0 на новые 125 тактов.
Таков мой аргумент.
А какие у вас варианты, если оно по вашему?
Я где-то на форуме видел такой вариант.... примерно конечно же
TCNT0 = +131 Это не правильное написание, но кто-то писал, что прибавление каждые 131 такт помогает. Как-то мне верится не охотно, что это поможет.

Значит я должен ставить TCNT0=0х83?
Последний раз редактировалось krokodandi Пт июл 17, 2015 12:41:49, всего редактировалось 1 раз.
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Вот набросанный за час простой, но рабочий вариант кода с таймером. 714 байт Flash, 20 байт RAM.
Во вложении и проект с Proteus для проверки.

Код, кому лень скачивать:
Спойлер

Код: Выделить всё

#include <avr/io.h>
#include <avr/interrupt.h>

#define DIG_CNT		4

#define DIG_DDR		DDRC
#define DIG_PORT	PORTC

#define DIG_0		(1<<0) // Крайний правый разряд
#define DIG_1		(1<<1)
#define DIG_2		(1<<2)
#define DIG_3		(1<<3) // Крайний левый разряд
#define DIGITS		(DIG_0 | DIG_1 | DIG_2 | DIG_3)

#define SEGM_DDR	DDRD
#define SEGM_PORT	PORTD

#define SEG_A		(1<<0)
#define SEG_B		(1<<1)
#define SEG_C		(1<<2)
#define SEG_D		(1<<3)
#define SEG_E		(1<<4)
#define SEG_F		(1<<5)
#define SEG_G		(1<<6)
#define SEG_P		(1<<7)
#define SEGMENTS	(SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G | SEG_P)

#define CH_0		(SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F)
#define CH_1		(SEG_B | SEG_C)
#define CH_2		(SEG_A | SEG_B | SEG_D | SEG_E | SEG_G)
#define CH_3		(SEG_A | SEG_B | SEG_C | SEG_D | SEG_G)
#define CH_4		(SEG_B | SEG_C | SEG_F | SEG_G)
#define CH_5		(SEG_A | SEG_C | SEG_D | SEG_F | SEG_G)
#define CH_6		(SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G)
#define CH_7		(SEG_A | SEG_B | SEG_C)
#define CH_8		(SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G)
#define CH_9		(SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G)

// Массив, хранящий коды цифр выводимого числа
static volatile uint8_t ind[DIG_CNT];
// Массив, хранящий коды десятичных цифр
static const uint8_t num[] = {CH_0, CH_1, CH_2, CH_3, CH_4, CH_5, CH_6, CH_7, CH_8, CH_9};
// Позиция активного символа
static uint8_t pos = 0;
// Время
static volatile int8_t hour, min, sec;

// Прерывание вызывается 1000 раз в секунду
ISR (TIMER2_COMP_vect)
{
	static volatile int16_t ms; // миллисекунды

	// Гасим все сегменты и разряды
	SEGM_PORT &= ~SEGMENTS;
	DIG_PORT |= DIGITS;
	// Переключаемся на следующий разряд
	if (++pos >= DIG_CNT)
		pos = 0;
	// Устанавливаем текущее число и секундную точку на сегменты и включаем нужный разряд
	SEGM_PORT |= ind[pos];
	if (pos == 2 && sec & 0x01)
		SEGM_PORT |= SEG_P;
	DIG_PORT &= ~(1<<pos);

	// Отсчёт таймера
	ms++;
	if (ms >= 1000) {
		ms = 0;
		if (--sec < 0) {
			sec = 59;
			if (--min < 0) {
				min = 59;
				if (--hour < 0)
					hour = 23;
			}
		}
	}

	return;
}

void segmInit(void)
{
	// Выводы МК, отвещающие за разряды и сегменты - на выход
	DIG_DDR |= (DIG_0 | DIG_1 | DIG_2 | DIG_3);
	SEGM_DDR |= 0xFF;
	// Включаем прерывание по совпадению
	TIMSK |= (1<<OCIE2);
	// Устанавливаем предделитель таймера на 64 (125кГц) и частоту прерываний 1кГц
	TCCR2 |= (1<<CS22) | (0<<CS21) | (0<<CS20) | (1<<WGM21) | (0<<WGM20);
	OCR2 = 125;

	sei(); // Глобально включаем прерывания

	return;
}

// Перевод числа в binary-decimal формат
int8_t dec2bindec(int8_t num)
{
	return ((num / 10) << 4) + (num % 10);
}

void show(int8_t dig32, int8_t dig10)
{
	dig32 = dec2bindec (dig32);
	dig10 = dec2bindec (dig10);

	ind[0] = num[dig10 & 0x0F];
	ind[1] = num[dig10 >> 4];

	ind[2] = num[dig32 & 0x0F];
	ind[3] = num[dig32 >> 4];

	return;
}

int main(void)
{
	segmInit ();

	// Счёт с 01:00:08
	hour = 1;
	min = 0;
	sec = 8;

	while (1) {
		if (hour > 0) {
			show(hour, min);
		} else {
			show(min, sec);
		}
	}

	return 0;
}
krokodandi писал(а):Я согласен с тем,,что до 256 остается 131 такт, но разве TCNT0 не скидывает этот порог снова до 125.
Скидывает. И дальше таймер считает 126, 127, 128 и так дальше вплоть до следующего прерывания на 256 = 0. Итого между прерываниями будет 131 такт (вместо дефолтных 256, если TCNT0 не трогать вообще). Лучше использовать прерывание по совпадению, как у меня в коде.

Если бы таймер считал вниз - то да, от 125 до 0 было бы 125 тактов, а так как он считает вверх, нужно TCNT0 устанавливать в такое число, чтобы ДО следующего прерывания (256) он считал 125 раз.
Вложения
timer.7z
(13.64 КБ) 170 скачиваний
Контактная информация:
Открыл глаза
Сообщения: 47
Зарегистрирован: Ср ноя 12, 2014 14:48:39

Сообщение krokodandi »

WiseLord писал(а):
#define...
А зачем использовать #define, да и еще так часто? Я их вообще не использую. Могу наловить ошибок

тогда TCNT0=83? Тогда до 256ти будет 125 тактов, а дальше все на оборот? Тогда это фарш.
Тогда лучше сброс по совпадению включать и задействовать второй таймер. Я его ни разу не юзал. Надо пробовать и вновь открывать док на 88й
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Для удобства.

Во-первых, код читабельнее становится. Смотреть на SEG_PORT |= SEG_D приятно, потому что понятно, что при этом происходит, в отличие от PORTD |= (1<<3);

Во-вторых, при разводке платы, если вдруг станет удобнее развести порт на индикатор по-другому (скажем, в обратном порядке, или, учитывая не самое удобное расположение выводов у счетверённых семисегментников, вообще в некоем произвольном порядке), только в одном месте кода надо будет поменять привязку сегмента к номеру порта без необходимости выискивать это (1<<3) по всему коду.
Контактная информация:
Открыл глаза
Сообщения: 47
Зарегистрирован: Ср ноя 12, 2014 14:48:39

Сообщение krokodandi »

WiseLord писал(а):Для удобства.

Во-первых, код читабельнее ...
Интересно будет попробовать так работать. Но пока я знакомился с текстом программы я нашел для себя интересный кусочек.

Код: Выделить всё

// Перевод числа в binary-decimal формат
int8_t dec2bindec(int8_t num)
{
	return ((num / 10) << 4) + (num % 10);
}

void show(int8_t dig32, int8_t dig10)
{
	dig32 = dec2bindec (dig32);
	dig10 = dec2bindec (dig10);

	ind[0] = num[dig10 & 0x0F];
	ind[1] = num[dig10 >> 4];

	ind[2] = num[dig32 & 0x0F];
	ind[3] = num[dig32 >> 4];
Это ведь сдвиг на 4 разряда. Я прежде такова не видел. Разве так стандартно поступать? Сначала сдвиг влево, потом вправо... Это для меня вновинку =)
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Честно, не понял, о чём речь. Какие сначала влево, потом вправо?

Функция dec2bindec(), как нетрудно догадаться по названию, переводит число из десятичного вида (например, 56) в двоично-десятичный (0x56). Выделяя цифру 5 (num/10), ставя её в левую тетраду, а цифру 6 (num%10) оставляя в правой.

Дальше уже подготовленное число выводится на индикатор - младшая цифра (& 0x0F) на 0-1 разряды, старшая (>>4) на 3-2.

Можно было этот кусок и проще сделать (680 байт Flash, 22 байта RAM):
Спойлер

Код: Выделить всё

// Перевод числа в binary-decimal формат
static uint8_t left, right;
void dec2bindec(int8_t num)
{
	left = num / 10;
	right = num % 10;

	return;
}

void show(int8_t dig32, int8_t dig10)
{
	dec2bindec (dig10);
	ind[0] = num[right];
	ind[1] = num[left];

	dec2bindec (dig32);
	ind[2] = num[right];
	ind[3] = num[left];

	return;
}
P.S. Или ещё проще, без доп. функции(556 байт Flash, 20 байт RAM):
Спойлер

Код: Выделить всё

void show(int8_t dig32, int8_t dig10)
{
	ind[0] = num[dig10 % 10];
	ind[1] = num[dig10 / 10];

	ind[2] = num[dig32 % 10];
	ind[3] = num[dig32 / 10];

	return;
}
P.P.S. А если часы-минуты-секунды разложить каждое ещё на два отдельных числа, то и деление на 10 не понадобится.
Последний раз редактировалось WiseLord Пт июл 17, 2015 13:38:39, всего редактировалось 2 раза.
Контактная информация:
Открыл глаза
Сообщения: 47
Зарегистрирован: Ср ноя 12, 2014 14:48:39

Сообщение krokodandi »

Дальше уже подготовленное число выводится на индикатор - младшая цифра (& 0x0F) на 0-1 разряды, старшая (>>4) на 3-2...

Я про это http://lib.rus.ec/i/84/165884/i_002.png
Логическим сдвигом я никогда не пользовался, хотя читал, что этот оператор сокращает размер кода. Конечно если грамотно его использовать.
Вижу, что код получился уже в разы меньше.
:shock:

Дальше - круче :beer:
Ответить

Вернуться в «AVR»