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

Я поставил перед собой следующую задачу по выводу на семисегментный индикатор:
- Оформить библиотеку в отдельный класс;
- Дать возможность самостоятельного выбора базы выводимого числа;
- Дать подсветку точки;
Есть парочка недочетов в моем решении, но обо всем прошу под кат
Честно признаюсь, это мой первый класс на 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 комментарий
Ты в каждой статье их объявляешь. Пора уже вынести куда-нибудь.
Если ты уже испольуешь класс, то он не должен имет таких вот явных внешних зависимостей, особенно, дефайнов. Намного лучше при создании класса указать, сколько у индикатора будет сегментов. Правильнее всего это сделать шаблонами:
Это хоть и правильно, но сложно и не нужно. Ведь в текущей реализации нельзя использовать несколько индикаторов. В итоге, у тебя два варианта — вынести весь код, который специфичен для твоего индикатора в отдельные методы и в своей реализации переопределять их:
Либо просто забить и прописать 4 константой в классе.
Всегда радовали такие комментарии :)
Указатель на метод класса называется «делегат». Фишка в том, что чтобы вызвать метод класса нужно не просто его вызвать, но еще и передать ему указатель на данные класса. Поэтому твой хак — вполне нормальная штука.
Такого вообще лучше не делать. Совсем не понятно, что это.
Ты извини, но функция гениальна. Напиши лучше просто таблицу
Ну, это — для начала :)