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

Как ПРАВИЛЬНО программировать на С++

Доброго всем времени суток.

В моей библиотеке произошло довольно много изменений:

- поддержка МК Texas Instruments MSP430;

- поддержка STM32;

- поддерживаются компиляторы GCC, IAR, Keil;

- новый подход к конфигурации портов;

- убраны предупреждения компиляторов;

- лучшая оптимизация;

- исправлены ошибки;

- автоматические тесты;

- лиценизия изменена на BSD.

 

Найти ее можно по старому адресу:

https://github.com/KonstantinChizhov/AvrPro...e/master/mcucpp

 

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

Поделиться сообщением


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

Добрый день.

Есть вопрос по С++.

В этом топике выше подымалась тема библиотеки статических классов по управлению портами.

При работе с этой библиотекой возник вопрос.

Можно ли различные списки пинов привести к какой либо ссылке что бы в классе который реализует одинаковую логику но для разных списков использовать просто указатель на список.

Пример чего хочу:

typedef PinList <Pb6, Pb7> TestList1;
typedef PinList <Pb4, Pb5> TestList2;

template <class lst>
class A
{
public:
    unsigned char Read(void)
    {
    lst::Read();
    }
}

class B
{
public:
    B(A *ptrA)(pA = ptrA;)
    unsigned char Calc(void)
    {
    unsigned char data;
    data = pA->Read();
    // преобразования data;
    return data;
    }
private:
    A *pA;
}

Но ничего не выходит потому что указатель класса B объявлен без списка параметров.

Пока реализовал класс B с шаблоном по списку пинов, но не устраивает что несколько членов-методов которые работают с данными из шаблона дублируются столько раз сколько раз я определяю шаблон.

Есть ли выход из данной ситуации. Или это оптимальное решение.

Все компилирую на IAR.

 

Поделиться сообщением


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

Есть два пути решения этого вопроса:

1. Если требуется всего одна-две функции из интерфейса списка линий, то можно передавать просто указатель на функцию:

typedef PinList<Pa4, Pa1, Pa6, Pa3>  Pins1;
...
typedef void (* WriteFuncPtr)(uint8_t value);

class Bar
{
    public:
    Bar(WriteFuncPtr func)
    :_func(func)
    {}
    
    void Foo()
    {
        _func(0x55);    
    }

    WriteFuncPtr _func;
};
...
    Bar bar(Pins1::Write);
    bar.Foo();

Затраты на вызов одной функции по указателю в худшем случае тактов 8, может и меньше.

 

2. Написать вируальный базовый класс с интерфейсом списка линий и шаблон для генерации классов наследников с реализацией.

 

#include <avr/io.h>
#include "iopins.h"
#include "pinlist.h"


using namespace IO;

typedef PinList<Pa4, Pa1, Pa6, Pa3>  Pins1;
typedef PinList<Pa2, Pa5, Pa0, Pa7>  Pins2;

class ListBase
{
    public:
    typedef uint8_t DataT;
    typedef NativePortBase::Configuration Configuration;
    virtual void Write(DataT value)=0;
    virtual void Set(DataT value)=0;
    virtual void Clear(DataT value)=0;
    virtual void SetConfiguration(Configuration value, DataT mask)=0;
    virtual DataT Read()=0;
    virtual DataT PinRead()=0;
};

template<class List>
class ListOfPins : public ListBase
{
    public:
    virtual void Write(DataT value)
    {
        List::Write(value);
    }
    virtual void Set(DataT value)
    {    
        List::Set(value);
    }
    virtual void Clear(DataT value)
    {
        List::Clear(value);
    }
    virtual void SetConfiguration(Configuration value, DataT mask)
    {
        List::SetConfiguration(value, mask);
    }
    virtual DataT Read()
    {
        return List::Read();
    }
    virtual DataT PinRead()
    {
        return List::PinRead();
    }
};


class B
{
    public:
    B(ListBase *list)
    :_list(list)
    {}
    
    void Foo()
    {
        _list->Write(0x55);    
    }

    ListBase *_list;
};

__attribute((OS_main))
int main()
{
    ListOfPins<Pins1> list1;
    ListOfPins<Pins2> list2;

    B b1(&list1);
    B b2(&list2);

    b1.Foo();
    b2.Foo();

    while(1)
    {}
}

 

Здесь надо иметь ввиду, что вызов виртуальной функции будет дороже даже вызова функции по указателю. Плюс к тому для каждого класса сгенерированного из шаблона ListOfPins будет создана своя табица виртуальных функций и она занимает место, втом числе и в оперативке.

Поделиться сообщением


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

Решил все сделать через указатель на функцию. Огромное спасибо.

Только вопрос в чем разница между кодом как привели Вы:

class Bar
{
    public:
    Bar(WriteFuncPtr func)
    :_func(func)
    {}

 

и кодом который работает аналогично только занимает на 10 байт больше:

 

class Bar
{
    public:
    Bar(WriteFuncPtr func)
    {
    _func = func;
    }

 

И если я передаю в конструктор два указателя на функцию. Как это описать?

Поделиться сообщением


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

typedef void (* WriteFuncPtr)(uint8_t value);
typedef uint8_t (* ReadFuncPtr)();

class Bar
{
    public:
    Bar(WriteFuncPtr writeFunc, ReadFuncPtr readFunc)
    :_writefunc(writeFunc), _readFunc(readFunc)
    {}
    
    WriteFuncPtr _writefunc;
    ReadFuncPtr _readFunc;
};

 

Только вопрос в чем разница между кодом как привели Вы:

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

В с++ надо отличать присваивание и инициализацию, например:

1)
A a;
a = something;

2.
A a = something;

3.
A a(something);

Вариант 1 - инициализация значением по умолчанию потом присваивание.

Вариант 2 - иничиализация копированием (вызов копирующего конструктора).

Вариант 3 - явный вазов конструктора. Наиболее предпочтительный.

 

Поделиться сообщением


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

to neiver

Добрый день.

Можешь посоветовать как описать набор из n регистров 74HC595(выходы) и 74HC597(входы) для работы с произвольными pin этого набора.

Если в связке будет до 8 регистров или 64 входа/выхода в зависимости от конкретной схемы. В статье описано как работать с одним регистром.

А вот как описать пины для большей разрядности не понятно.

Заранее спасибо.

 

....

Кажется понял

template<class ClockPin, class DataPin, class LatchPin, unsigned ID, class T = uint8_t>
class ThreePinLatch
{
    public:

    typedef T DataT;
    // нужен для сортировки портов в списках линий
    enum{Id = ID};
    //разрядность регистра
    enum{Width=sizeof(DataT)*8};
    // запись значения
    static void Write(T value)
    {    
        _currentValue = value;
        for(uint8_t i=0; i < Width; ++i)
        {
            DataPin::Set(value & 1);
            ClockPin::Set();
            value >>= 1;
            ClockPin::Clear();
        }
        LatchPin::Set();
        LatchPin::Clear();
    }
    static DataT Read()
    {
        return _currentValue;
    }
    static void ClearAndSet(DataT clearMask, DataT value)
    {
        Write(_currentValue = (_currentValue & ~clearMask) | value);
    }
    static void Set(DataT value)
    {
        Write(_currentValue |= value);
    }
    static void Clear(DataT value)
    {
        Write(_currentValue &= ~value);
    }
    static void Togle(DataT value)
    {
        Write(_currentValue ^= value);
    }
    static void DirWrite(DataT value)
    { /*не можем менять направление*/ }
    static DataT DirRead()
    {
        return 0xff; //всегда выход
    }
    static void DirSet(DataT value)
    {/* не можем менять направление */}
    static void DirClear(DataT value)
    {/* не можем менять направление */    }
    static void DirTogle(DataT value)
    {/* не можем менять направление */}
    protected:
        // текущее значение в регистре
        static DataT _currentValue; 
    };

    template<class ClockPin, class DataPin, class LatchPin, unsigned ID, class T>
    T ThreePinLatch<ClockPin, DataPin, LatchPin, ID, T>::_currentValue = 0;

и объявление из статьи

typedef ThreePinLatch<Pa0, Pa1, Pa3, 'R1'> ShiftReg1;

typedef TPin<ShiftReg1, 0> Rg0;
typedef TPin<ShiftReg1, 1> Rg1;
typedef TPin<ShiftReg1, 2> Rg2;
typedef TPin<ShiftReg1, 3> Rg3;
typedef TPin<ShiftReg1, 4> Rg4;
typedef TPin<ShiftReg1, 5> Rg5;
typedef TPin<ShiftReg1, 6> Rg6;
typedef TPin<ShiftReg1, 7> Rg7;

но можно же определять сдвиговый регистр как

typedef ThreePinLatch<Pa0, Pa1, Pa3, 'R1', unsigned long> ShiftReg1;

typedef TPin<ShiftReg1, 0> Rg0;
typedef TPin<ShiftReg1, 1> Rg1;
typedef TPin<ShiftReg1, 2> Rg2;
typedef TPin<ShiftReg1, 3> Rg3;
typedef TPin<ShiftReg1, 4> Rg4;
typedef TPin<ShiftReg1, 5> Rg5;
typedef TPin<ShiftReg1, 6> Rg6;
typedef TPin<ShiftReg1, 7> Rg7;
...
typedef TPin<ShiftReg1, 27> Rg27;
typedef TPin<ShiftReg1, 28> Rg28;
typedef TPin<ShiftReg1, 29> Rg29;
typedef TPin<ShiftReg1, 30> Rg30;
typedef TPin<ShiftReg1, 31> Rg31;

И немного переписать методы DataT Read() и void Write(T value) что бы обновлялись регистры значениями из переменной _currentValue и наоборот.

Но тогда не вижу как можно сделать больше чем 32 пина?

 

Поделиться сообщением


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

Вобщем-то все правильно. А зачем больше 32 пинов использовать? Два списка по 32 пина и все. Хотя если очень хочется можно и тип uint64_t (он-же unsigned long long), но тогда еще и реализацию спискак линий прийдётся расширять до 64 линий...

Лучше 2 списка по 32 линии.

Поделиться сообщением


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

Вобщем-то все правильно. А зачем больше 32 пинов использовать? Два списка по 32 пина и все. Хотя если очень хочется можно и тип uint64_t (он-же unsigned long long), но тогда еще и реализацию спискак линий прийдётся расширять до 64 линий...

Лучше 2 списка по 32 линии.

 

Можно и два списка по 32. Вопрос не в этом. Обычно на spi все регистры висят последовательно. Как два списка будут работать с одной колбаской из 8-ми регистров?

скажем даже так есть два регистра IN и два OUT. Можно ли описать их двумя списками по 16 бит, но при этом что бы списки по SPI работали каждый со своими регистрами?

Поделиться сообщением


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

С точки зрения оптимальности, выыодить значение в цепочку регистров нужно за один проход. И если 8 регистров включены последовательно, то в любом прийдётся выводить сразу все 64 бита. И соответственно текущее значение хранить в переменной типа uint64_t. Однако, к одному модулю SPI можно подключить несколько цепочек регистров, например, по 4 штуки, разделив их линии защёлкивания данных(ST_CP для 74HC595). Я думаю, что задачу всё-таки нужно свести к 32 битным значениям.

Поделиться сообщением


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

Хотя если очень хочется можно и тип uint64_t (он-же unsigned long long), но тогда еще и реализацию спискак линий прийдётся расширять до 64 линий...

Или уж совсем радикально - массив unsigned int. И ни в чём себе не отказывать :-)

Поделиться сообщением


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

во налетели. :)

ещё раз. чисто наблюдения показывают, что вопросы касаемые конструкций и возможностей языка у программистов Си практически не обсуждаются. зато активно муссируются у плюсистов.

 

У неграмотных плюсистов.

Поделиться сообщением


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

Попытался адаптировать библиотеку Чижова под STM8. Изменения коснулись только файлов ports.h и atomic.h. Работа с битами в выходном регистре оптимизируется компилятором хорошо, а вот конфигурирование ведет себя так, будто не все известно при компиляции.

            static void SetConfiguration(DataT mask, Configuration cfg)
            {
                          if((cfg==Out)||(cfg==OpenDrainOut)
                           ||(cfg==Out2Mhz)||(cfg==OpenDrainOut2Mhz)
                           ||(cfg==Out10Mhz)||(cfg==OpenDrainOut10Mhz))
                              {ATOMIC DDR::Or(mask);}
                          else
                              {ATOMIC DDR::And(~mask);}

                          if((cfg==InPU)||(cfg==InPUIRQ)
                           ||(cfg==Out)||(cfg==Out2Mhz)||(cfg==Out10Mhz))
                              {ATOMIC CR1::Or(mask);}
                          else
                              {ATOMIC CR1::And(~mask);}

                          if((cfg==InIRQ)||(cfg==InPUIRQ)
                           ||(cfg==Out)||(cfg==OpenDrainOut10Mhz)||(cfg==Out10Mhz))
                              {ATOMIC CR2::Or(mask);}
                          else
                              {ATOMIC CR2::And(~mask);}
            }

Это часть класса, связанная с конфигурацией.

В итоге все эти условия так и лезут в кристалл. Что я делаю не так?

Прикрепил весь проект в ИАР. Посмотрите пожалуйста.

testpult.zip

Поделиться сообщением


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

В общем всё правильно. При вызове не шаблонной SetConfiguration компилятор не обязан всё инлайнить и вычислять во время компиляции. Для этого лучше использовать шаблонный вариант:

Pd0::SetConfiguration<Portd::Out>();

Он гарантированно только три машинных инструкции.

Ну и код можно существенно оптимизировать:

 

static void SetConfiguration(DataT mask, Configuration cfg)
{
    if(cfg & DdrMask)
        {ATOMIC DDR::Or(mask);}
    else
        {ATOMIC DDR::And(~mask);}
    
    if(cfg & Cr1Mask)
        {ATOMIC CR1::Or(mask);}
    else
        {ATOMIC CR1::And(~mask);}
    
    if(cfg & Cr2Mask)
        {ATOMIC CR2::Or(mask);}
    else
        {ATOMIC CR2::And(~mask);}
}

В реализации портов для MSP430 такой прием был использован. MSP430 гораздо ближе по структуре портов к STM8. Можно было взять реализацию их портов за основу, было-бы проще чем кусками брать код из AVR и STM32 портов.

testpult_reviewed.zip

Изменено пользователем neiver

Поделиться сообщением


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

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

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

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

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

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

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

×
×
  • Создать...