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

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

Это можно реализовать. Но я не уверен, что нужно. Это оптимизация под один частный случай использования под определенной семейство МК. В этой библиотеке есть более важные варианты использования, которые надо оптимизировать.

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

 

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

 

А снабженец закупивший MSP вместо AVR однозначно будет уволен.

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


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

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

Переводя на пример, который запросил 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, вы это хотели увидеть?

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


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

2 Andy Mozzhevilov

Я говорил про то, что управление конкретными линиями GPIO должно быть только в одном потоке. Иначе вообще странная ситуация складывается - мы пытаемся двумя разными потоками управлять одним и тем же, при этом боимся где-то пересечься. Просто программист, который будет потом разбираться в коде, должен быть в курсе того, что есть такое пересечение потоков.

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

 

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


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

2 Andy Mozzhevilov

Я говорил про то, что управление конкретными линиями GPIO должно быть только в одном потоке. Иначе вообще странная ситуация складывается - мы пытаемся двумя разными потоками управлять одним и тем же, при этом боимся где-то пересечься.

Согласен. ИМХО если есть какой-то разделяемый ресур ввода/вывода, то правильно будет ввести сущность "драйвер/менеджер" устройства, и все операции вести через него. Соответственно, вся забота о разделении доступа ложится на этот драйвер

 

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


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

ввести сущность "драйвер/менеджер" устройства, и все операции вести через него. Соответственно, вся забота о разделении доступа ложится на этот драйвер

Дык это - для синхронного вывода. Релюхами клацать :) А если у нас(Вас) случилась такая беда: используется 7 бит для LCD, и надо ж такому случиться - 1wire на последней ноге торчит... Что делать? ванварь-то требует довольно точных временных интервалов...

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


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

2 Andy Mozzhevilov

Я говорил про то, что управление конкретными линиями GPIO должно быть только в одном потоке. Иначе вообще странная ситуация складывается - мы пытаемся двумя разными потоками управлять одним и тем же, при этом боимся где-то пересечься.

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

А то что вы писали - это управление одной линией из разных потоков (или прерываний и фона) - это другая тема. И такое тоже имеет место быть при необходимости - не надо кричать, что это бред. Например, в фоне выставляется запрос внешнему устройству на каком-то пине, ответ от устройства обрабатывается по прерыванию, в котором в том числе и запрос снимается. Только в этом случае автомат состояния делается общий для такого управления, и там где нужно, изменения состояния автомата оборачиваются в критические секции.

 

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

Если это касается main - то не нужно.

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

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


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

Гость DL36
xor.w TRISB // TRISB = 0x0022 --> один бит потеряли, должно быть 0x002A

 

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

Если модифицируемые биты различны, то проблем нет.

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

 

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

 

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

 

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

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


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

Если это касается main - то не нужно.

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

Хорошо, ну а какой смысл вообще оборачивать конструкторы в критические секции? Ну сработало прерывание (не касающееся этой периферии, так как инициализация еще не завершилась), отработали его, вернулись к инициализации и поползли дальше по конструктору, что должно сломаться?

ЗЫ: я не спорю, мне просто интересно

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


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

Хорошо, ну а какой смысл вообще оборачивать конструкторы в критические секции? Ну сработало прерывание (не касающееся этой периферии, так как инициализация еще не завершилась), отработали его, вернулись к инициализации и поползли дальше по конструктору, что должно сломаться?

ЗЫ: я не спорю, мне просто интересно

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

Поясню на примере с 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 раз все дальше работает без сбоев".

 

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


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

Асм листинг:

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-файл с макросами для подсчета бит.. Они у меня, собаки, здоровенные получились, но каши не просят :)

 

 

 

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


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

Остался, кажется, один шаг - заставить Вашу библиотечку разбирать: когда выводить побитно, а когда организовать критическую секцию с "Чтением-Модификацией-Записью".

Компилятор сам решит, он умный.

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

А библиотека должна быть нейтральной.

 

Кстати, насчёт пресловутой атомарности.

В msp430 (моём любимом :) ) с этим никаких проблем.

Зря я волновался.

У него чтение-модификация-запись производится в пределах одной команды, засчёт ортогональной системы команд и адресации.

Вот пример, выцепил из рабочей прошивки.

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, и всё это за одну команду, которая не может быть прервана никаким прерыванием.

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


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

Компилятор сам решит, он умный.

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

А библиотека должна быть нейтральной.

Как хорошо иметь компилятор, умеющий без лишнего кода различить, что PORTA |= 0x07 надо делать тремя SBI, а PORTA |= 0x0f с помощью RMW в критической секции.

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


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

Остался, кажется, один шаг - заставить Вашу библиотечку разбирать: когда выводить побитно, а когда организовать критическую секцию с "Чтением-Модификацией-Записью". Это, кстати, нужно только для эффективного вывода констант (для переменных вывод побитный, там групповой вывод не работает).

Меня больше интересует именно групповой вывод для переменных.

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

 

А так-же какие есть предложения по дальнейшему развитию функционала?

- Отдельная функция для гарантированно атомарного вывода

- Управление подтяжкой линий

- Управление режимами прерываний

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


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

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

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

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

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

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

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

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

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

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

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

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

 

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

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

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


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

- лиценизия изменена на BSD.
Вот это, пожалуй, самое важное :-)

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

 

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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