Недоработки порта STM32GENERIC

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Может, кому будет интересно, кто знает. Короче: по воле судеб юзаю STM32GENERIC, только по той причине, что там унутре есть хорошая либа GxTFT с поддержкой FSMC.

Но! У данного порта есть недоработки, а именно: для пары noInterrupts()/interrupts() используются вызовы __disable_irq()/__enable_irq() соответственно. При этом - внутри micros()/millis() - опрашивается системный таймер SysTick. 

Уже подозреваете, о чём я? Правильно: вот такой вот тривиальный код - наглухо вешает контроллер, и строки в порту вы никогда не увидите:

void setup()
{
	Serial.begin(57600);
	delay(1000);
	
	noInterrupts();
		delayMicroseconds(1000);
	interrupts();
	
	Serial.println("YOHOHO!!!");Serial.flush();
}

void loop()
{
	
}

Мелочь, а оооооочень неприятно, и эта сцука попила мне крови, пока обнаружил.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Почему так происходит - думаю, очевидно. Если неочевидно - могу пояснить за жисть :))

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Потому что это неправославная ардуина?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:
Потому что это неправославная ардуина?

она ж вроде каталическая, не?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

sadman41 пишет:
Потому что это неправославная ардуина?

Не, патамушта micros и millis нарисованы вот так:

inline uint32_t millis() {
    return HAL_GetTick();
}

inline uint32_t micros() {
  // by Pito 4/2017
  uint32_t m = HAL_GetTick();
  uint32_t u = SysTick->LOAD - SysTick->VAL;
  uint32_t m1 = HAL_GetTick();
  uint32_t u1 = SysTick->LOAD - SysTick->VAL;

  if (m1 > m) {
    return ( m1 * 1000 + (u1 * 1000) / SysTick->LOAD);
  } else {
    return ( m * 1000 + (u * 1000) / SysTick->LOAD);
  }
}

А когда делаем __disable_irq (вызовом noInterrupts) - что делает SysTick? Правильно - не тикает нахер. Т.е. тут либо переписывать пару noInterrupts/interrupts, либо - millis и micros. Либо - не юзать задержки после вызова noInterrupts.

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

Сейчас думаю, как это дело побороть, не лазя внутрь STM32GENERIC. Есть идеи?

b707
Онлайн
Зарегистрирован: 26.05.2017

DIYMan пишет:

Сейчас думаю, как это дело побороть, не лазя внутрь STM32GENERIC. Есть идеи?

не использовать делеи внутри прерываний? :)

sadman41
Онлайн
Зарегистрирован: 19.10.2016

В энторнетах ваших пишут, что можно запустить медленный таймер и смотреть на его TCNT периодически.

mixail844
Offline
Зарегистрирован: 30.04.2012

DIYMan пишет:

sadman41 пишет:
Потому что это неправославная ардуина?

Не, патамушта micros и millis нарисованы вот так:

inline uint32_t millis() {
    return HAL_GetTick();
}

inline uint32_t micros() {
  // by Pito 4/2017
  uint32_t m = HAL_GetTick();
  uint32_t u = SysTick->LOAD - SysTick->VAL;
  uint32_t m1 = HAL_GetTick();
  uint32_t u1 = SysTick->LOAD - SysTick->VAL;

  if (m1 > m) {
    return ( m1 * 1000 + (u1 * 1000) / SysTick->LOAD);
  } else {
    return ( m * 1000 + (u * 1000) / SysTick->LOAD);
  }
}

А когда делаем __disable_irq (вызовом noInterrupts) - что делает SysTick? Правильно - не тикает нахер. Т.е. тут либо переписывать пару noInterrupts/interrupts, либо - millis и micros. Либо - не юзать задержки после вызова noInterrupts.

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

Сейчас думаю, как это дело побороть, не лазя внутрь STM32GENERIC. Есть идеи?

 

эээмм..перевести ногодрыг на более соответствующую перефирийю?

а еще вариант : завестли HW таймер , например TIM6 (так как у него нет полезных выводов) запустить его на нужной частоте и считывать его counter register типа  TIM6->CNT

 

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

sadman41 пишет:

В энторнетах ваших пишут, что можно запустить медленный таймер и смотреть на его TCNT периодически.

Тут дело больше в совместимости с Wiring. Идеальным вариантом было бы включать SysTick после запрещения всех прерываний, но я что-то пока не нашёл, можно ли это сделать. Смотреть периодически TCNT таймера - те же яйца, только в профиль: с таким же успехом с приемлемой точностью я в самописном delay прокручу цикл, и всё. Но это будет - малопереносимый код, а хочется - системных решений, обвязка Wiring и так небольшая.

Все остальные методы - таймера, не использование delay - лишний секас, которого и так хватает. А вот быстро переносимый код - это важно. Меня бы всё устраивало, если бы не этот гадкий нюанс. В принципе, оно работает и без запрещения прерываний - проц быстрый, и тайминги даже на 1-Wire не бьются. Но для успокоения души - надо иметь простое решение.

Если его нет и __disable_irq() наглухо глушит все прерывания - то тады ой, просто буду иметь в виду эту тонкость реализации STM32GENERIC, и, на худой конец - своими delay, тупыми циклами, обойдусь.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

mixail844 пишет:

а еще вариант : завестли HW таймер , например TIM6 (так как у него нет полезных выводов) запустить его на нужной частоте и считывать его counter register типа  TIM6->CNT

Просто смотреть его регистр периодически? Так это тот же самописный delay на цикле, которым можно обойтись, при желании - точности хватит и без таймера. В моём случае - использование таймера избыточно.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707 пишет:

не использовать делеи внутри прерываний? :)

А кто использует делеи внутри прерываний - ткни пальцем, пж. Перечитай проблему, и увидишь, что ВНУТРИ прерываний - никто делеи не использует. Проблема в том, что SysTick перестаёт тикать, когда вызываешь noInterrupts - особенность реализации STM32GENERIC. И - гадкая особенность.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Можно имитировать delay_ms(), тогда останется совместимость с avr.

b707
Онлайн
Зарегистрирован: 26.05.2017

DIYMan пишет:

А кто использует делеи внутри прерываний - ткни пальцем, пж.

Не горячись, это ж шутка была.

Я бы наверно полез смотреть, как запрещать прерывания не скопом, а селективно, чтобы миллисы продолжали тикать.  Но, с другой стороны - оставление в работе прерывания таймера противоречит ТЗ - "чтобы тайминги не съезжали"... так что не знаю.

Да и с совместимостью в этом случае будет грустно.

nik182
Offline
Зарегистрирован: 04.05.2015

Это просто не правильный стиль программирования. Ни разу не встречал, что бы прерывания глобально были запрещены более чем на на несколько операторов, а тем более на цикл. Обычно ими обеспечивают атомарность. Я так и не понял, в чём смысл запрета на такой огромный срок?

b707
Онлайн
Зарегистрирован: 26.05.2017

Совсем чуть-чуть погуглил - во-первых, эта ситуация - отнюдь не "тонкость реализации STM32GENERIC", а растет из HAL - то есть относится не только к аддону ардуино, а вообще к большинству АРМ-овских тулчейнов.

И второе - как я и предполагал - идти надо через приоритеты прерываний. Вот, нашел в Stack-Exchange ровно такой же вопрос - "Как на СТМ32 запретить все прерывания, кроме одного?"

https://stackoverflow.com/questions/49135275/disable-irq-on-stm32

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

nik182 пишет:
Это просто не правильный стиль программирования. Ни разу не встречал, что бы прерывания глобально были запрещены более чем на на несколько операторов, а тем более на цикл. Обычно ими обеспечивают атомарность. Я так и не понял, в чём смысл запрета на такой огромный срок?

Срок - не огромный, это раз. Два - во всех других портах - счётчик при вызове delayMicroseconds продолжает тикать после вызова noInterrupts, можете попробовать это дело для плат AVR. Собственно, само предназначение Wiring - дать минимальный набор инструментов, чтобы не париться  теми же самописными delay. Удивлён, что приходится объяснять столь очевидные вещи.

Что касается неправильного стиля программирования - скажите это автору библиотеки OneWire, например - там для непобития таймингов в коде всё очевидным образом видно. И эта библиотека, кстати - вызывает зависание МК в бесконечном цикле, если использовать её под STM32GENERIC.

Всё, что я пытаюсь донести в этом топике, это тот факт, что в STM32GENERIC бездумно сделаны define для noInterrupts/interrupts и эти define замаплены на пару __disable_irq/__enable_irq, и вся прозрачная и строгая концепция Wiring разбивается об эту стену. В порту STM32Duino подобного, кстати, не наблюдал.

Вот, собственно, и всё. И не надо никаких выводов о неправильном стиле программирования и прочих высокопарных речей.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

b707 пишет:

Совсем чуть-чуть погуглил - во-первых, эта ситуация - отнюдь не "тонкость реализации STM32GENERIC", а растет из HAL - то есть относится не только к аддону ардуино, а вообще к большинству АРМ-овских тулчейнов.

И второе - как я и предполагал - идти надо через приоритеты прерываний. Вот, нашел в Stack-Exchange ровно такой же вопрос - "Как на СТМ32 запретить все прерывания, кроме одного?"

https://stackoverflow.com/questions/49135275/disable-irq-on-stm32

Это именно косяк реализации STM32GENERIC, патамушта - читай пост выше. Если бы noInterrupts в этом порту была бы сделано нормально, как раз через HAL и запрещала бы все прерывания, не трогая SysTick - то проблемы бы не было. Это - определённо косяк, т.к. порт - очень спорный и небезгрешный, к сожалению.

Вследствие этого ЛЮБОЙ код, который вызывает noInterrupts, а затем делает любой вид delay* из Wiring - вызывает зависание МК в бесконечном цикле. Скажи мне ещё, что так и было задумано. И - мой пост чуть выше ;)

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

sadman41 пишет:
Можно имитировать delay_ms(), тогда останется совместимость с avr.

Ну да, это одно из решений, лежащих на поверхности. Печально, конечно, что приходится отходить от концепции Wiring, но, по ходу - иногда придётся. Кстати, известная всем OneWire без переделок - виснет под STM32GENERIC. Если закомментировать вызовы noInterrupts в её коде - всё работает, но уже нет уверенности в том, что при случайном стечении обстоятельств тайминги на шине не побьются. Так и живём.

nik182
Offline
Зарегистрирован: 04.05.2015

DIYMan пишет:
... Два - во всех других портах - счётчик при вызове delayMicroseconds продолжает тикать после вызова noInterrupts, можете попробовать это дело для плат AVR. Собственно, само предназначение Wiring - дать минимальный набор инструментов, чтобы не париться  теми же самописными delay. Удивлён, что приходится объяснять столь очевидные вещи...

Ну так и есть. Для avr delayMicroseconds полностью блокирующая, организованная через вращение цикла единичных задержек функция. "Собственно, само предназначение Wiring - дать минимальный набор инструментов, чтобы не париться  теми же самописными delay" ДЛЯ ПОДДЕРЖИВАЕМЫХ ОФИЦИАЛЬНО СРЕДОЙ АРДУИНО ПЛАТ.  Ожидать, что сторонние тулчейны, не сертифицированные разработчиками АРДУИНО, будут работать точно так же как исходные для AVR, по крайней мере наивно. Многие библиотеки, специально не доработанные для STM или ESP не работают на сторонних платах. "Удивлён, что приходится объяснять столь очевидные вещи."  Но это не отменяет благодарность Вам за проделанную работу и полезное предупреждение. 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

nik182 пишет:

 Ожидать, что сторонние тулчейны, не сертифицированные разработчиками АРДУИНО, будут работать точно так же как исходные для AVR, по крайней мере наивно. 

Так я и не ожидал, собственно. И именно поэтому - предупредил вот о такой особенности STM32GENERIC. Понятно, что этот частный случай - решаем, и вполне решаем, просто - неприятен тем фактом, что допущена вот такая вот оплошность. Скажем так, я до сих пор в смятении по поводу выбранного решения использовать __disable_irq, тогда как есть тот же NVIC, где и приоритеты, и прочая лабуда, и можно было бы дизаблить прерывания с приоритетом 1 и выше цифрами.

К слову сказать, тому же SysTick при конфигурировании назначена группа 0, т.е. складывается ощущение, что сделать-то хотели нормально, а вот когда дошло до noInterrupts - забыли, чего хотели :)

Лезть же в кишки STM32GENERIC - ну такое себе, не особо хочется хотя бы потому, что это однозначно станет необходимостью тянуть за собой исправленную его версию, и в таком случае - уж лучше реально переползти на самописный delay_ms, например.

Итог: тема - нужна как информация о конкретной особенности конкретного пакета поддержки плат STM32, в условиях, когда под Arduino IDE ставят поддержку всего, чего только можно, без оглядки на официальность плат. Се ля ви, так сказать.