-
- Я пока что в командировке, так что "решено" это условно, временно.
Я к тому так написал чтобы не ломали голову из-за меня, как бы не
тратили энергию. Но если есть какие-то мысли, конечно пишите.
Приеду домой, буду разбираться, трассировать времянки итд. "Решено"
так: сделал код прерывания как можно более коротким, просто запись
в ring buffer. Об этом писал здесь: и один из UART'ов у меня уже
был так построен. Этот (отладочную консоль) я перевел, остался еще
один, RxTx(2428 знак., 17.05.2020 10:11 - 20:16, ссылка)
- Кстати в любой функции оперирующей с указателями на данные
доступными из нескольких потоков (тот же ring buffer) можно
запросто нарваться на перестановку порядка обращения к переменным.
И volatile не поможет: gcc все эти volatile аккуратненько вынесет в
конец функции и там запишет в том же порядке. Я на когда такое
впервые увидел, чуть не рехнулся пока баг разгадывал. Поэтому после
изменения указателей или после записи данных перед продвижением
указателя нужно обязательно fk0(383 знак., 17.05.2020 13:09)
- "нарваться на перестановку порядка обращения" и "запишет в том же
порядке". Ты уж определись. - SciFi(17.05.2020 13:18)
- volatile-указатели на данные записывались в конце функции, в том
порядке, как они писались в функции. Но данные писались независимо
в теле функции. Т.е. данные писались, указатели не двигались. А
потом на выходе подвинулись указатели, ровно так как они должны
были бы подвинуться пока писались данные. Идея в том, что gcc не
пишет volatile переменные, а пишет их значения в регистры (коих у
мипса дофига). А потом перед выходом эти регистры натурально
записывает в память. - fk0(17.05.2020 14:01)
- То есть забыли добавить volatile в объявление "uint8_t fifo[];"?
Расплата вполне заслуженная. - SciFi(17.05.2020 14:05)
- Если добавлять volatile для всей памяти -- это будет треш, угар и содомия (при просмотре дизассемблера -- крайне неоптимальный код). Так делать не нужно. Потом возникает другая проблема -- вся область памяти становится несовместимой (без постоянных reinterpret_cast'ов) с обычными указателями и всеми интерфейсами в программе. Вообще volatile не нужен. Достаточно вставлять memory barrier. В случае кольцевого буфера: после записи данных и перед измененим указателя записи, и fk0(392 знак., 17.05.2020 14:20, ссылка)
- То есть забыли добавить volatile в объявление "uint8_t fifo[];"?
Расплата вполне заслуженная. - SciFi(17.05.2020 14:05)
- volatile-указатели на данные записывались в конце функции, в том
порядке, как они писались в функции. Но данные писались независимо
в теле функции. Т.е. данные писались, указатели не двигались. А
потом на выходе подвинулись указатели, ровно так как они должны
были бы подвинуться пока писались данные. Идея в том, что gcc не
пишет volatile переменные, а пишет их значения в регистры (коих у
мипса дофига). А потом перед выходом эти регистры натурально
записывает в память. - fk0(17.05.2020 14:01)
- "нарваться на перестановку порядка обращения" и "запишет в том же
порядке". Ты уж определись. - SciFi(17.05.2020 13:18)
- Приоритеты -- ещё один повод, почему блокирующихся функций в
обработчиках быть не должно. С равноприоритетными прерываниями оно
не так страшно. Про шаговый двигатель я вынес для себя на всю
жизнь: не нужно его крутить по шагам. Нужно крутить фазу с
фиксированным временным шагом. Достаточно коротким, чтоб на низких
скоростях разницы не было, а на высоких автоматически получается
пропуск микрошагов (иначе контроллер не успеет). И здесь важно,
чтоб джиттер был fk0(36 знак., 17.05.2020 12:26)
- Шаговик это вообще у меня тотальная жопа реализации. Признаюсь. Ну
лично я так считаю. Я пытался этот блок переписывать чтобы он был
"простой и тупой" как микросхема, но каждый раз оказывалась
какая-нибудь да херня. Объясню в чем дело. Аппаратура при условном
"POWER ON' калибруется по концевику. (я гоню шаговик до концевика,
который калиброочно расположен строго в определенном месте). После
этого я возвращаюсь в исходный нуль. Я при работе никогда не могу
потерять RxTx(1486 знак., 17.05.2020 12:55 - 13:11)
- Управление шаговиком -- это достаточно развесистый конечный автомат
скорей полностью умещающийся в прерываниях. И получающий от
прикладного уровня комадны вроде "приехать в такую-то позицию",
"найти начальную позицию", "безостановочное движение с такой-то
скоростью"... И отдающий на прикладной уровень текущую позицию
(когда в движении), код ошибки и т.п. При этом этот автомат должен
сам следить ещё и за концевиками или датчиками положений. Вообще
удобней такой автомат fk0(3765 знак., 17.05.2020 14:08 - 14:43, ссылка)
- У ЛИ оно там --> - Vit(17.05.2020 14:13, ссылка)
- Отсюда запрос: а нет ли у милостивых пэров ссылок на реализацию
шаговиков? Либы, статьи? - RxTx(17.05.2020 12:56)
- Во какую мурзилку нарыл, сам еще не читал LightElf(33 знак., 17.05.2020 23:39, ссылка)
- Спасибо =) Это прекрасно! - RxTx(18.05.2020 00:00)
- Есть Бoмж(19 знак., 17.05.2020 22:17, ссылка, ссылка)
- Спасибо. Бум изучать - RxTx(17.05.2020 22:38)
- Во какую мурзилку нарыл, сам еще не читал LightElf(33 знак., 17.05.2020 23:39, ссылка)
- Управление шаговиком -- это достаточно развесистый конечный автомат
скорей полностью умещающийся в прерываниях. И получающий от
прикладного уровня комадны вроде "приехать в такую-то позицию",
"найти начальную позицию", "безостановочное движение с такой-то
скоростью"... И отдающий на прикладной уровень текущую позицию
(когда в движении), код ошибки и т.п. При этом этот автомат должен
сам следить ещё и за концевиками или датчиками положений. Вообще
удобней такой автомат fk0(3765 знак., 17.05.2020 14:08 - 14:43, ссылка)
- Шаговик это вообще у меня тотальная жопа реализации. Признаюсь. Ну
лично я так считаю. Я пытался этот блок переписывать чтобы он был
"простой и тупой" как микросхема, но каждый раз оказывалась
какая-нибудь да херня. Объясню в чем дело. Аппаратура при условном
"POWER ON' калибруется по концевику. (я гоню шаговик до концевика,
который калиброочно расположен строго в определенном месте). После
этого я возвращаюсь в исходный нуль. Я при работе никогда не могу
потерять RxTx(1486 знак., 17.05.2020 12:55 - 13:11)
- Переменные status и b объявлены как volatile. Добавляем тормозов на
основании каких-то странных суеверий, видимо. - SciFi(17.05.2020 11:19)
- Я не вылизывал код, сейчас некогда и незачем этим заниматься. В данном случае это лайфхак: 1. При оптимизированном коде переменная существует в стеке и ее можно посмотреть дебаггером. 2. (теория) Некоторые реализации uart/usart требуют обязательного а. считывания статус-регистра. б. считывания гарантированно ДО чтения дата-регистра. в. Читать надо один раз. Для того чтобы обеспечить именно эти гарантии а-ля asm (а не вообще всегда) и убрать нежелательные оптимизации и RxTx(341 знак., 17.05.2020 11:38)
- "В mainloop выгребаю из ringbuffer" - видимо без критической
секции, если во время выгребания придет прерывание и запишет в
буфер данные, то кирдык? запрещать прерывания надо бы на время
чтения - NAUT(17.05.2020 10:56)
- Не надо запрещать, поскольку именно эта схема "Один Писатель - Один
Читатель" с двумя изолированными и "догоняющими" друг друга readpos
и writepos переменными сравниваемыми в одном месте LockFree. RxTx(63 знак., 17.05.2020 11:15)
- Ну, кагбэ, вопрос "че делать, если в буфере нет места" - нужно
задавать до написания реализации ring buffer. Собственно вариантов четыре: LightElf(394 знак., 17.05.2020 21:47)
- п. 2) это естественное поведение кольцевого буфера, новое при невычитанном через N=длине кольца затрет старое. Возможен п. 5) - RESET. п. 6) Подлежит обсуждению - когда стратегия записи одна, а стратегия чтения другая. Теперь смотря что понимается под "атомарным инкрементом". Прерывание и main loop это не тру потоки, прерывание и весь код в нем (если без извратов) будет "атомарным". Выполнится целиком и возвратится в основной код. - RxTx(17.05.2020 23:21)
- Пункт 1 не вариант, пункт 4 тоже -- это говнокод. Пункт 3 -- черти
что, зачем оно может быть нужно? Реальны варианты 2 и 5. 5) вывод
блокируется до появления свободного места в буфере -- требует
примитивов синхронизации. Зачем в варианте 2 атомарный инкремент, и
инкремент чего? Указатель записи двигает только писатель, а
указатель чтения двигает только читатель. Противоположная сторона в
обоих случаях (читатель и писатель соответственно) только читают не
свой указатель fk0(1028 знак., 17.05.2020 21:57)
- Пункт 1) очень часто вполне пригоден, зря ты так. Например для тех
же логов. Буфер не бесконечен, а останавливать программу "пока
логгер не прочихается" - не самая красивая идея. Соответственно
именно для логов вариант 3) может быть полезнее, чтобы хоть
"последний выдох господина ПЖ" не потерять. В варианте 2 писатель
должен двинуть и read_index и write_index. И вот для того, чтобы
корректно двинуть read_index ему и нужен atomic_inc. Вариант 4 -
самый простой и тупой, но не LightElf(116 знак., 17.05.2020 22:13)
- Вот самое забавное, когда отлаживаешься (от слова лажа :) ) таки
надо вставать, пока логгер не прочихается. А то грозит тем что
строчка "ram test failed, reset" успешно не будет записана... - RxTx(18.05.2020 00:07)
- На это я пойтить не могу. Такие баги надо обрабатывать уже после
ресета. - LightElf(18.05.2020 00:25)
- На это я могу сказать - "Кокой унтюресный у тебя RESET. Ты правда
считаешь что RESET должен так работать, оставляя что-то там в
каком-то состоянии?" RxTx(47 знак., 18.05.2020 20:13)
- Хорошим тоном является выяснять причину Reset-а. Это почти не больно. Там спецуевый регистр присопливлен. - LightElf(30.05.2020 21:50)
- На это я могу сказать - "Кокой унтюресный у тебя RESET. Ты правда
считаешь что RESET должен так работать, оставляя что-то там в
каком-то состоянии?" RxTx(47 знак., 18.05.2020 20:13)
- На это я пойтить не могу. Такие баги надо обрабатывать уже после
ресета. - LightElf(18.05.2020 00:25)
- Если останавливать не вариант -- пункт 2. Ибо в варианте 1
непонятно что делать. Останавливаться и мигать лампочкой? С
пропусками -- это варианты 2 и 3. "Последний выдох" и "новые данные
затирают последние записанные" несколько противоречиво. Что имеется
ввиду? И сколько последних записанных? Последний байт, строку?
"Последний выдох" -- это по-моему именно вариант 2 (новые данные
затирают самые старые не обработанные). В варианте 2 писатель не
должен двигать fk0(895 знак., 17.05.2020 22:52)
- Согласен, чета я все смешал в кучу. Упущен существенный вопрос, что представляют из себя "новые данные". Ежели это просто очередной символ - то набор вариантов один. Ежели это как-то форматированный блок данных (например строки лога) - то варианты другие. По поводу ковыряний с индексами - согласен, тоже вариант. Особенно на современных процах - сделал индекс типа uint32_t и лишних битов хватит на любой разумный размер буфера. - LightElf(17.05.2020 23:55)
- Вот самое забавное, когда отлаживаешься (от слова лажа :) ) таки
надо вставать, пока логгер не прочихается. А то грозит тем что
строчка "ram test failed, reset" успешно не будет записана... - RxTx(18.05.2020 00:07)
- Пункт 1) очень часто вполне пригоден, зря ты так. Например для тех
же логов. Буфер не бесконечен, а останавливать программу "пока
логгер не прочихается" - не самая красивая идея. Соответственно
именно для логов вариант 3) может быть полезнее, чтобы хоть
"последний выдох господина ПЖ" не потерять. В варианте 2 писатель
должен двинуть и read_index и write_index. И вот для того, чтобы
корректно двинуть read_index ему и нужен atomic_inc. Вариант 4 -
самый простой и тупой, но не LightElf(116 знак., 17.05.2020 22:13)
- +1. Проблемы с детектированием наезда, на необработанные данные, обгона указателя чтения, можно решить, если использовать не указатели, а индексы -- тогда освободятся старшие биты, в которых можно хранить счётчик. Ну или если есть 64-битные атомики. А если потеря данных не допустима, то увы, нужен семафор и условная переменная. fk0(1191 знак., 17.05.2020 12:53, ссылка, картинка)
- Ну, кагбэ, вопрос "че делать, если в буфере нет места" - нужно
задавать до написания реализации ring buffer. Собственно вариантов четыре: LightElf(394 знак., 17.05.2020 21:47)
- Не надо запрещать, поскольку именно эта схема "Один Писатель - Один
Читатель" с двумя изолированными и "догоняющими" друг друга readpos
и writepos переменными сравниваемыми в одном месте LockFree. RxTx(63 знак., 17.05.2020 11:15)
- Кстати в любой функции оперирующей с указателями на данные
доступными из нескольких потоков (тот же ring buffer) можно
запросто нарваться на перестановку порядка обращения к переменным.
И volatile не поможет: gcc все эти volatile аккуратненько вынесет в
конец функции и там запишет в том же порядке. Я на когда такое
впервые увидел, чуть не рехнулся пока баг разгадывал. Поэтому после
изменения указателей или после записи данных перед продвижением
указателя нужно обязательно fk0(383 знак., 17.05.2020 13:09)
- скажет только под пытками. :-) - Лaгyнoв(17.05.2020 08:44)
- Сказал же! - "Код сгенерирован CubeMX" :) - VLLV(17.05.2020 09:02)
- Я пока что в командировке, так что "решено" это условно, временно.
Я к тому так написал чтобы не ломали голову из-за меня, как бы не
тратили энергию. Но если есть какие-то мысли, конечно пишите.
Приеду домой, буду разбираться, трассировать времянки итд. "Решено"
так: сделал код прерывания как можно более коротким, просто запись
в ring buffer. Об этом писал здесь: и один из UART'ов у меня уже
был так построен. Этот (отладочную консоль) я перевел, остался еще
один, RxTx(2428 знак., 17.05.2020 10:11 - 20:16, ссылка)