Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Как ПРАВИЛЬНО программировать на С++
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2, 3, 4
halfdoom
Цитата(neiver @ Sep 27 2010, 12:00) *
Это можно реализовать. Но я не уверен, что нужно. Это оптимизация под один частный случай использования под определенной семейство МК. В этой библиотеке есть более важные варианты использования, которые надо оптимизировать.

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

Что касается атомарности, то это забота не этого класса.

А снабженец закупивший MSP вместо AVR однозначно будет уволен.
neiver
Кстати, списки линий поддерживают и манипуляцию отделными битами из списка.
Переводя на пример, который запросил Quasy это будет выглядеть так:

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

using namespace IO;

typedef PinList<Pa0, Pb0, Pd3, Pd4>  Pins;

int main()
{
              Pins::Pin<0>::Clear();
    Pins::Pin<1>::Set();
    Pins::Pin<2>::Clear();
    Pins::Pin<3>::Set();
}

Асм листинг:
Код
cbi    0x1b, 0; 27
sbi    0x18, 0; 24
cbi    0x12, 3; 18
sbi    0x12, 4; 18


Quasy, вы это хотели увидеть?
segment
2 Andy Mozzhevilov
Я говорил про то, что управление конкретными линиями GPIO должно быть только в одном потоке. Иначе вообще странная ситуация складывается - мы пытаемся двумя разными потоками управлять одним и тем же, при этом боимся где-то пересечься. Просто программист, который будет потом разбираться в коде, должен быть в курсе того, что есть такое пересечение потоков.
Кстати, Вы сказали про "приходится настройку портов оборачивать в критические секции", но если это задается вначале программы, то зачем оборачивать в критическую секцию, когда прерывания от периферии еще не разрешены?
Dima_G
Цитата(Сега @ Sep 27 2010, 22:58) *
2 Andy Mozzhevilov
Я говорил про то, что управление конкретными линиями GPIO должно быть только в одном потоке. Иначе вообще странная ситуация складывается - мы пытаемся двумя разными потоками управлять одним и тем же, при этом боимся где-то пересечься.

Согласен. ИМХО если есть какой-то разделяемый ресур ввода/вывода, то правильно будет ввести сущность "драйвер/менеджер" устройства, и все операции вести через него. Соответственно, вся забота о разделении доступа ложится на этот драйвер
_Pasha
Цитата(Dima_G @ Sep 27 2010, 20:13) *
ввести сущность "драйвер/менеджер" устройства, и все операции вести через него. Соответственно, вся забота о разделении доступа ложится на этот драйвер

Дык это - для синхронного вывода. Релюхами клацать smile.gif А если у нас(Вас) случилась такая беда: используется 7 бит для LCD, и надо ж такому случиться - 1wire на последней ноге торчит... Что делать? ванварь-то требует довольно точных временных интервалов...
Andy Mozzhevilov
Цитата(Сега @ Sep 27 2010, 19:58) *
2 Andy Mozzhevilov
Я говорил про то, что управление конкретными линиями GPIO должно быть только в одном потоке. Иначе вообще странная ситуация складывается - мы пытаемся двумя разными потоками управлять одним и тем же, при этом боимся где-то пересечься.

Значит вы не поняли суть проблемы. Она в том, что для для модификации значения одного бита порта используется команда чтения значения всего порта (8 бит для avr), модификация нужного бита в регистре, и запись 8 бит обратно в порт. То есть модификация 1 бита фактически - это модификация всего порта. Следовательно, если на этом порту есть 2 разных линии, каждая из которых управляется из разных потоков, возможны трудноуловимые баги, обусловленный неатомарными операциями с 8-битным портом. В предыдущем сообщении я написал, как это решается в ARM NXP. Для avr предлагается использовать команды манипуляции отдельными битами (насколько я понял из их мненоники и описания).
А то что вы писали - это управление одной линией из разных потоков (или прерываний и фона) - это другая тема. И такое тоже имеет место быть при необходимости - не надо кричать, что это бред. Например, в фоне выставляется запрос внешнему устройству на каком-то пине, ответ от устройства обрабатывается по прерыванию, в котором в том числе и запрос снимается. Только в этом случае автомат состояния делается общий для такого управления, и там где нужно, изменения состояния автомата оборачиваются в критические секции.

Цитата
Кстати, Вы сказали про "приходится настройку портов оборачивать в критические секции", но если это задается вначале программы, то зачем оборачивать в критическую секцию, когда прерывания от периферии еще не разрешены?

Если это касается main - то не нужно.
На я использую драйвера периферии на С++, где в конструкторе производится инициализация и настройка альтернативных режимов пинов. Все конструкторы закрыты в критические секции, поскольку объекты могут быть созданы не только глобально (конструкторы их будут вызваны до main), но и локально в функции (задаче). В этом случае конструктор будет вызван в месте создания, когда прерывания уже во всю имеют место быть.
DL36
Цитата(neiver @ Sep 27 2010, 14:29) *
xor.w TRISB // TRISB = 0x0022 --> один бит потеряли, должно быть 0x002A

Так, что и тут есть подводные камни. Но только в случае, если модифицируется пересекающееся множество бит.
Если модифицируемые биты различны, то проблем нет.

Дело не в подводных камнях просто пример совершенно не корректный, скажу более он в принципе не имеет решения.

С нашим устройством связаны биты о маска 0х000F и они должны меняться из одного места. Меняйте любые другие и все будет работать.

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

Макросы прекрасно работают в реальных проектах под TNKernel и позволяют отказаться во многих местах от использования критических секций.
segment
Цитата(Andy Mozzhevilov @ Sep 28 2010, 08:42) *
Если это касается main - то не нужно.
На я использую драйвера периферии на С++, где в конструкторе производится инициализация и настройка альтернативных режимов пинов. Все конструкторы закрыты в критические секции, поскольку объекты могут быть созданы не только глобально (конструкторы их будут вызваны до main), но и локально в функции (задаче). В этом случае конструктор будет вызван в месте создания, когда прерывания уже во всю имеют место быть.

Хорошо, ну а какой смысл вообще оборачивать конструкторы в критические секции? Ну сработало прерывание (не касающееся этой периферии, так как инициализация еще не завершилась), отработали его, вернулись к инициализации и поползли дальше по конструктору, что должно сломаться?
ЗЫ: я не спорю, мне просто интересно
Andy Mozzhevilov
Цитата(Сега @ Sep 28 2010, 10:03) *
Хорошо, ну а какой смысл вообще оборачивать конструкторы в критические секции? Ну сработало прерывание (не касающееся этой периферии, так как инициализация еще не завершилась), отработали его, вернулись к инициализации и поползли дальше по конструктору, что должно сломаться?
ЗЫ: я не спорю, мне просто интересно

Так смысл абсолютно тот же, что и обсуждается сейчас здесь про неатомарные команды модификации битов порта.
Поясню на примере с ARM NXP.
Eсть регистр ввода-вывода (32 битный). В этом регистре настраиваются 16 линий порта на некоторые альтернативные функции, по 2 бита на пин. То есть, чтобы перенастроить некоторую линию (или группу линий) на другую функцию - нужно:
1. Считать значение IO регистра
2. Модифицировать значение нужным образом.
3. Записать новое значение в IO регистр.
При этом в конкретном драйвере мне нужно (предположим) перенастроить только 2 IO линии из 16-ти, за которые отвечает этот регистр.

Теперь - у меня RTOS с вытеснением (аналогичные рассуждения для прерываний).
В другой задаче у меня так же есть код, который оперирует настройкой линий IO в пределах тех же 16-ти линий того же порта.
Проследите, что будет, если где-то между 1. и 3. произойдет переключение контекста на другую задачу, которая решит также перенастроить линии порта (другие линии, но находящиеся в пределах этих 16-ти).

1a. Считать значение IO регистра
--- контекст a->b
1б. Считать значение IO регистра
2б. Модифицировать значение нужным образом.
3б. Записать новое значение в IO регистр.
--- контекст b->a
2a. Модифицировать значение нужным образом.
3a. Записать новое значение в IO регистр.

В результате операции, выполненные в 1б, 2б, 3б - будут похерены.

ЗЫ: Проблема в том, что подобные баги - трудноуловимы. Их проявление - стечение обстоятельств. То есть как правило - это нечто вроде: "У меня софт прекрасно работает месяц, но потом иногда затыкается обмен данными".
Или: "У меня 1 раз из 100 устройство не включается, но после включения все 99 раз все дальше работает без сбоев".
Quasy
Цитата(neiver @ Sep 27 2010, 18:48) *
Асм листинг:
Код
cbi    0x1b, 0; 27
sbi    0x18, 0; 24
cbi    0x12, 3; 18
sbi    0x12, 4; 18


Quasy, вы это хотели увидеть?

Для данной конфигурации - да. Остался, кажется, один шаг - заставить Вашу библиотечку разбирать: когда выводить побитно, а когда организовать критическую секцию с "Чтением-Модификацией-Записью". Это, кстати, нужно только для эффективного вывода констант (для переменных вывод побитный, там групповой вывод не работает).
Если интересно, то скажу, что в моем тупом варианте это делается весьма легко: в препроцессоре макрос считает для константы биты на каждом порту, и препроцессор же делает выбор.
Цитата
#if (GROUP_0_COUNT_BITS_OF_PORTA < 4) && (PORTA_HAVE_BITS_ACCESS)
Вот тут побитно SBI/CBI для порта А
#else
ENTER_ATOMIC Ну, для XMEGA критическая секция не нужна
RMW PORTA
RELEASE_ATONIC
#endif


... и так для всех портов.

Что для этого потребовалось? Ну, где-то сказать, что ПортА на битовом пространстве, ПортG - нет.
(для ХМЕГИ это означает - сказать на какой виртуальный порт отображен PORTn)
И макрос соорудить для подсчета бит. Это совсем легко.
Негатив такого варианта: для каждой Группы нужен h-файл с макросами для подсчета бит.. Они у меня, собаки, здоровенные получились, но каши не просят smile.gif


MrYuran
Цитата(Quasy @ Sep 28 2010, 13:13) *
Остался, кажется, один шаг - заставить Вашу библиотечку разбирать: когда выводить побитно, а когда организовать критическую секцию с "Чтением-Модификацией-Записью".

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

Кстати, насчёт пресловутой атомарности.
В msp430 (моём любимом smile.gif ) с этим никаких проблем.
Зря я волновался.
У него чтение-модификация-запись производится в пределах одной команды, засчёт ортогональной системы команд и адресации.
Вот пример, выцепил из рабочей прошивки.
Код
static __inline__ void SwPortToSend(void)
{
    LINE_DIR.out.LINE_DIR_PIN = LINE_DIR_TRANSMIT;
    7a70:    f2 f0 bf ff     and.b    #-65,    &0x001d;#0xffbf
    7a74:    1d 00

На порт по адресу 001d накладывается маска 0xffbf, и всё это за одну команду, которая не может быть прервана никаким прерыванием.
Quasy
Цитата(MrYuran @ Sep 28 2010, 13:41) *
Компилятор сам решит, он умный.
Посчитает количество тактов либо количество занятых под программу байт - в зависимости от ключей оптимизации.
А библиотека должна быть нейтральной.

Как хорошо иметь компилятор, умеющий без лишнего кода различить, что PORTA |= 0x07 надо делать тремя SBI, а PORTA |= 0x0f с помощью RMW в критической секции.
neiver
Цитата(Quasy @ Sep 28 2010, 13:13) *
Остался, кажется, один шаг - заставить Вашу библиотечку разбирать: когда выводить побитно, а когда организовать критическую секцию с "Чтением-Модификацией-Записью". Это, кстати, нужно только для эффективного вывода констант (для переменных вывод побитный, там групповой вывод не работает).

Меня больше интересует именно групповой вывод для переменных.
Есть у кого нибудь предложеня по дальнейшей оптимизации этой операции? Какие ещё приёмы отображения битов из входного значения в соответствующие биты портов можно придумать? Эффективные приемы для частных случаев?

А так-же какие есть предложения по дальнейшему развитию функционала?
- Отдельная функция для гарантированно атомарного вывода
- Управление подтяжкой линий
- Управление режимами прерываний
neiver
Доброго всем времени суток.
В моей библиотеке произошло довольно много изменений:
- поддержка МК Texas Instruments MSP430;
- поддержка STM32;
- поддерживаются компиляторы GCC, IAR, Keil;
- новый подход к конфигурации портов;
- убраны предупреждения компиляторов;
- лучшая оптимизация;
- исправлены ошибки;
- автоматические тесты;
- лиценизия изменена на BSD.

Найти ее можно по старому адресу:
https://github.com/KonstantinChizhov/AvrPro...e/master/mcucpp
ReAl
Цитата(neiver @ Dec 30 2010, 17:13) *
- лиценизия изменена на BSD.
Вот это, пожалуй, самое важное :-)
В отличие от, к примеру, портирования на STM32, это самостоятельно сделать возможности никакой не было.
Quasy
Цитата(neiver @ Dec 30 2010, 18:13) *
Доброго всем времени суток.
В моей библиотеке произошло довольно много изменений:
- поддержка МК Texas Instruments MSP430;
- поддержка STM32;
- поддерживаются компиляторы GCC, IAR, Keil;
- новый подход к конфигурации портов;
- убраны предупреждения компиляторов;
- лучшая оптимизация;
- исправлены ошибки;
- автоматические тесты;
- лиценизия изменена на BSD.

Найти ее можно по старому адресу:
https://github.com/KonstantinChizhov/AvrPro...e/master/mcucpp


Вот они, проклятые макросы-то, и появились. Вот она, мешанина-то. Вот он, как говорили вынос мозга-то... Рад, что натолкнул на правильные мысли. Вы уже на правильном пути.
Serega Doc
Добрый день.
Есть вопрос по С++.
В этом топике выше подымалась тема библиотеки статических классов по управлению портами.
При работе с этой библиотекой возник вопрос.
Можно ли различные списки пинов привести к какой либо ссылке что бы в классе который реализует одинаковую логику но для разных списков использовать просто указатель на список.
Пример чего хочу:
Код
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.
neiver
Есть два пути решения этого вопроса:
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 будет создана своя табица виртуальных функций и она занимает место, втом числе и в оперативке.
Serega Doc
Решил все сделать через указатель на функцию. Огромное спасибо.
Только вопрос в чем разница между кодом как привели Вы:
Код
class Bar
{
    public:
    Bar(WriteFuncPtr func)
    :_func(func)
    {}


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

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


И если я передаю в конструктор два указателя на функцию. Как это описать?
neiver
Код
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 - явный вазов конструктора. Наиболее предпочтительный.
Serega Doc
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 пина?
neiver
Вобщем-то все правильно. А зачем больше 32 пинов использовать? Два списка по 32 пина и все. Хотя если очень хочется можно и тип uint64_t (он-же unsigned long long), но тогда еще и реализацию спискак линий прийдётся расширять до 64 линий...
Лучше 2 списка по 32 линии.
Serega Doc
Цитата(neiver @ Feb 15 2011, 18:38) *
Вобщем-то все правильно. А зачем больше 32 пинов использовать? Два списка по 32 пина и все. Хотя если очень хочется можно и тип uint64_t (он-же unsigned long long), но тогда еще и реализацию спискак линий прийдётся расширять до 64 линий...
Лучше 2 списка по 32 линии.


Можно и два списка по 32. Вопрос не в этом. Обычно на spi все регистры висят последовательно. Как два списка будут работать с одной колбаской из 8-ми регистров?
скажем даже так есть два регистра IN и два OUT. Можно ли описать их двумя списками по 16 бит, но при этом что бы списки по SPI работали каждый со своими регистрами?
neiver
С точки зрения оптимальности, выыодить значение в цепочку регистров нужно за один проход. И если 8 регистров включены последовательно, то в любом прийдётся выводить сразу все 64 бита. И соответственно текущее значение хранить в переменной типа uint64_t. Однако, к одному модулю SPI можно подключить несколько цепочек регистров, например, по 4 штуки, разделив их линии защёлкивания данных(ST_CP для 74HC595). Я думаю, что задачу всё-таки нужно свести к 32 битным значениям.
AHTOXA
Цитата(neiver @ Feb 15 2011, 19:38) *
Хотя если очень хочется можно и тип uint64_t (он-же unsigned long long), но тогда еще и реализацию спискак линий прийдётся расширять до 64 линий...

Или уж совсем радикально - массив unsigned int. И ни в чём себе не отказывать :-)
ar__systems
Цитата(Mahagam @ Aug 3 2010, 07:50) *
во налетели. sm.gif
ещё раз. чисто наблюдения показывают, что вопросы касаемые конструкций и возможностей языка у программистов Си практически не обсуждаются. зато активно муссируются у плюсистов.


У неграмотных плюсистов.
Waso
Попытался адаптировать библиотеку Чижова под 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);}
            }
Это часть класса, связанная с конфигурацией.
В итоге все эти условия так и лезут в кристалл. Что я делаю не так?
Прикрепил весь проект в ИАР. Посмотрите пожалуйста.
neiver
В общем всё правильно. При вызове не шаблонной 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 портов.Нажмите для просмотра прикрепленного файла
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.