Вывод информации на семисегментный индикатор

Задача вывода на семисегментный индикатор относительно нетривиальна.
Я поставил перед собой следующую задачу по выводу на семисегментный индикатор:
  • Оформить библиотеку в отдельный класс;
  • Дать возможность самостоятельного выбора базы выводимого числа;
  • Дать подсветку точки;

Есть парочка недочетов в моем решении, но обо всем прошу под кат

Честно признаюсь, это мой первый класс на AVR :) Раньше как-то без них обходилось. Я и так всегда рад замечаниям и дополнениям, а тут и вовсе буду рад вдвойне.
Объявляем библиотеки и данные:
#include <avr/io.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/pgmspace.h>
#include "taskmanager.h"
#include <avr/pgmspace.h>
//представление HEX-числа для 7-сегментного индикатора
static uint8_t hexnumber_x[] PROGMEM ={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67,0x77,0x7C,0x39,0x5E,0x79,0x71};
//общее количество разрядов индикатора
#define LEDDIGITS 4

Объявляем класс:
class LedOut {
	public:
		uint16_t DataLog;//Выводимое число
		uint8_t DataDigit;//Подсвечиваемая точка разряда
		void Init(uint8_t _Base);//функция инициализации
		void Disp(void);//функция для динамического вывода данных
	private:
		uint8_t DataDigits[LEDDIGITS];//цифры, разбитые по базе
		uint8_t number_x;//текущий разряд
		uint8_t Base;//база
};

Инициализация дисплея должна включать в себя запоминание базы дисплея, и активацию ножек, к которым подключен индикатор.
void LedOut::Init(uint8_t _Base){
	DDRA|=0xF0;//индикатор. разряды
	DDRC|=0xFF;//индикатор. сегменты
	number_x=0;//текущий разряд
	Base = _Base;//текущая база
}

Поскольку у меня имеется ЭТОТ диспетчер, то динамика висит на нем, лишь бы функцию вызывал. т.е. нужна функция обновления, разбивающая число на разряды, изменяющая текущий разряд и выводящая его. Про точку не забываем.
void LedOut::Disp(){//функция выводит число на индикатор.
	int16_t DataUnsignedLog=abs(DataLog);//гасим знак.
	for (uint8_t digits_count=0;digits_count<LEDDIGITS;digits_count++)
	{//разделяем число на разряды по базе
      if (DataUnsignedLog>0){
	    // Копируем очередную цифру в массив
        DataDigits[digits_count] = DataUnsignedLog % Base;
        // Переходим к следующей цифре
        DataUnsignedLog /= Base;
	  }
	  else//если в разряде нуль, то нуль(гашение старого числа).
	  {
		DataDigits[digits_count]=0;		  		  
	  }
	}
		PORTC=pgm_read_byte(&hexnumber_x[DataDigits[number_x]]);//выводим в порт представление числа
		if (number_x==DataDigit)//Разряд подсвечиваем
			PORTC|=0x80;
		PORTA=(1<<(4+number_x));//выбираем разряд
		number_x++;
		if (number_x>(LEDDIGITS-1)) number_x=0;
}

Единственная проблема — мне не удалось передать указатель на эту функцию ни изнутри класса, ни снаружи, посему пришлось написать небольшой костыль. Если кто подскажет, как лучше сделать — буду только рад
void foo(){//костыль
	led.Disp();
}	
int main(void)
{
	led.Init(BASE);
	DDRL|=(1<<PL3)|(1<<PL4)|(1<<PL5);
	BUTTONSSETUP;
	TSetup();//настройка таймера
	 TReload(0,*foo,1);//запускаем функцию обновления дисплея
	TReload(1,*ConfigChange,20);//запускаем фуцнкцию опроса кнопок
  TStart();//запуск счета
   sei();
   while(1)
    {
    }
}

Там промелькнул опрос кнопок. Он скорее для проверки. Его задача — изменять число. С этим пришлось повозиться. Задача какова — кнопки влево/вправо меняют номер редактируемого разряда, а ± — изменяют сам разряд. Чтобы изменить какой-то разряд, нужно прибавлять/отнимать BASE^DIGIT. Да незадача — функция возведения в степень имеется только для float. Пишем свою, для uint16_t:
uint16_t UIntPow(uint16_t _data, uint8_t _pow){//Возведение целого числа в степень
	uint16_t _result=1;
	if (!_pow)// в нулевой степени, чо
	return 1;
	while (_pow){
		_result*=_data;
		_pow--;
	}
	return _result;		
}

Сама функция обработчика кнопок. Вызываю ее каждые 0,2 секунды, что обеспечивает полный оборот разряда за 3,2 секунды при 16 базе числа:
void ConfigChange(){
    uint8_t button=(~BUTTONSPORT)&BUTTONSMASK;
	 if (button){//если кнопка была нажата:
	 switch (button)
	 {
	 case BUTTONUP:
		led.DataLog+=UIntPow(BASE,led.DataDigit);
	 break;
	 case BUTTONDOWN:
		led.DataLog-=UIntPow(BASE,led.DataDigit);		
	 break;
	 case BUTTONLEFT:
		if ((LEDDIGITS-1)<(++led.DataDigit)) led.DataDigit=0;//если разряд больше числа разрядов дисплея
	 break;
	  case BUTTONRIGHT:
		if (0>(--led.DataDigit)) led.DataDigit=(LEDDIGITS-1);//если разряд больше числа разрядов дисплея
	 break;
	 }
	 }
}

И напоследок видео всего процесса, с изменением выводимой базы числа:

1 комментарий

avatar
Хотел замечаний, получи:
Объявляем библиотеки и данные:
Ты в каждой статье их объявляешь. Пора уже вынести куда-нибудь.

//общее количество разрядов индикатора
#define LEDDIGITS 4

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

template <uint8 SegmentCount> class SegmentDisplay
// а потом сделать тайпдеф для своего индикатора:
typedef SegmentDisplay<4> RlnSegmentDisplay


Это хоть и правильно, но сложно и не нужно. Ведь в текущей реализации нельзя использовать несколько индикаторов. В итоге, у тебя два варианта — вынести весь код, который специфичен для твоего индикатора в отдельные методы и в своей реализации переопределять их:

class SegmentIndicatorBase
{
    protected:
        virtual int segmentCount() = 0 const;
        ....
};

class RlnSegmentIndicator : protected SegmentIndicatorBase
{
    protected:
        virtual int segmentCount() { return 4; };
        ....
};


Либо просто забить и прописать 4 константой в классе.

void Init(uint8_t _Base);//функция инициализации
Всегда радовали такие комментарии :)

мне не удалось передать указатель на эту функцию ни изнутри класса, ни снаружи
Указатель на метод класса называется «делегат». Фишка в том, что чтобы вызвать метод класса нужно не просто его вызвать, но еще и передать ему указатель на данные класса. Поэтому твой хак — вполне нормальная штука.

BUTTONSSETUP;

Такого вообще лучше не делать. Совсем не понятно, что это.

uint16_t UIntPow(uint16_t _data, uint8_t _pow){//Возведение целого числа в степень

Ты извини, но функция гениальна. Напиши лучше просто таблицу

Ну, это — для начала :)
Комментарий отредактирован 2013-01-07 20:51:45 пользователем bsvi
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.