Кто любит RISC в жизни, заходим, не стесняемся.
Ответить

Re: STM32 новичку в ARM что к чему

Ср май 25, 2022 14:07:34

Я ж тебе с самого начала предлагал делать на IDLE, а ты сам себе геморрой устроил на ровном месте.

А ещё одна страшная тайна- это MAX13487...

Добавлено after 1 minute 40 seconds:
Не вижу твоих обработчиков прерываний. По-идее, в каждом из них должно запрещаться прерывание от другого.

Re: STM32 новичку в ARM что к чему

Ср май 25, 2022 14:21:57

Я ж тебе с самого начала предлагал делать на IDLE, а ты сам себе геморрой устроил на ровном месте.

А ещё одна страшная тайна- это MAX13487...

Добавлено after 1 minute 40 seconds:
Не вижу твоих обработчиков прерываний. По-идее, в каждом из них должно запрещаться прерывание от другого.


Если я в прерывании USART1 запрещу выполнение прерывания таймера, то он не будет запускаться вообще.

Вот мои обработчики прерываний (USART1 - приоритет 5, TIM4 - приоритет 4):
Код:
void USART1_IRQHandler(void) {

   if (SymbolCount == 0) {
              TIM4->CNT = 0;
              TIM4->CR1 |= TIM_CR1_CEN;
   }

   ReceivedText[SymbolCount++] = USART1->DR;

}


void TIM4_IRQHandler(void) {

TIM4->SR &=~ TIM_SR_UIF;

TIM4->CR1 &=~ TIM_CR1_CEN;

USART_SendStr("Time out!");

SymbolCount = 0;

}

Re: STM32 новичку в ARM что к чему

Ср май 25, 2022 15:39:28

у тебя не правильный алгоритм.
Ты должен запускать таймер в режиме OPM сразу после загрузки UART_DR, а внутри обработчиков запрещать оба прерывания и сбрасывать, например, в обработчике от таймера запрос от UART и наоборот. Тогда будет работать. Только, по-сути, так ты просто повторяешь алгоритм формирования IDLE в UART.

Re: STM32 новичку в ARM что к чему

Ср май 25, 2022 16:03:02

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

Код:
void USART1_IRQHandler(void) {

   uint32_t i = 0;

   USART1->SR   &=~  USART_SR_RXNE; // Clear USART1 RXNE Interrupt flag


      GPIOC->ODR |= GPIO_ODR_ODR12; // Enable LED

      for (i = 0; i < 65535; i++); // Delay

      GPIOC->ODR &=~ GPIO_ODR_ODR12;  // Disable LED

      for (i = 0; i < 65535; i++);  // Delay

}


Шлю один любой символ через терминал - светодиод моргает, как положено, 1 раз. Шлю его несколько раз вручную - все работает отлично. Но стоит только отправить через терминал не один символ (например, "Q"), а два или более подряд ("QE"), то светодиод начинает моргать бесконечно. То есть МК не может выйти из прерывания вообще, и гоняет его в цикле. Я так понимаю это из-за того, что следующий символ приходит раньше, чем закончилось предыдущее прерывание. Из-за чего такое может происходить?

Re: STM32 новичку в ARM что к чему

Ср май 25, 2022 16:18:11

Уберите задержки из прерывания!
Перед сбросом флага проверяйте из-за него вы туда попали или нет.
Проверяйте как настроены прерывания от UART - по каким причинам они могут возникнуть. Могут ли, например, из-за ошибок.

Re: STM32 новичку в ARM что к чему

Ср май 25, 2022 16:21:48

У тебя не правильный алгоритм.
Ессно, у тебя так будет. Тебе надо разрешать прерывание после загрузки DR, а в обработчике, после окончания передачи, его запрещать.

Re: STM32 новичку в ARM что к чему

Ср май 25, 2022 21:35:39

Есть вопрос. У меня возникает глюк и я не могу выцепить в чем причина. Поэтому пытаюсь всё проверять. Сейчас на подозрении - стек. Глюк появлялся у меня спустя около 20 секунд после начала хода. Я увеличил размер стека с 4096 до 8192 байт и один проход выполнился нормально. Второй тоже нормально, но на третьем проходе - опять появился глюк. На подозрении прерывания. Их у меня толпы.

И вот вопрос про прерывание PendSV - оно у меня довольно длинное. Но имеет самый низкий приоритет. Может ли быть так, что оно вызовется снова не завершив текущее. А как он знает, что текущее завершилось? Собственно, я иногда и в самом этом обработчике взвожу запрос на PendSV, чтобы обработчик перезапустился (там у меня машина состояний).


Код:
void PendSV_Handler(void) {
  profiler();
}

void profiler(void) {
...
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;  // перезапуск, чтобы начать двигаться сразу
...
}


Приоритеты распределены так:
Код:
// IRQ priorities:
#define SysTick_IRQ_Priority  2
#define I2C_IRQ_PRI           2
#define TIMER0_IRQ_PRI        3
#define TIMER1_IRQ_PRI        3
#define USART0_IRQ_PRI        4
#define USART3_TX_IRQ_PRI     4
#define LDMA_IRQ_PRI          5
#define ADC0_IRQ_Priority     5
#define LETIMER0_IRQ_Priority 5
#define PendSV_IRQ_priority   7


Память распределена следующим образом:
Код:
section              size        addr
.text               72968           0
.ARM.exidx              8       72968
.stack               8192   536870912
.data                1856   536879104
.bss               173420   536880960
.heap               13136   537054384


А можно как-нибудь узнать, что стек вылез за рамки?

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 05:26:20

Я увеличил размер стека с 4096 до 8192 байт

Это влияет только на предупреждение при компиляции. Физически во время работы МК размер стека не контролируется. Можно посмотреть только в аналитике (Static Stack Analyzer) величину максимального потребления стека функциями. В ОЗУ вот так всё это укладывается:
Изображение
Каждый вызов вложенной функции или прерывания увеличивает потребление стека. При возврате из вложенных функций и прерываний потребление стека уменьшается. А если стек увеличивается слишком сильно, он наползет на область хранения статических переменных и повредит их содержимое. В этом и будет проявляться косяк - в повреждении значений статических переменных.
Для контроля положения вершины стека есть регистр ядра SP (Stack Pointer). Регистр не имеет адреса и доступен либо через ассемблер, либо через сишную обертку __get_MSP(), возвращается текущий адрес вершины стека.
Поскольку вы используете динамическое выделение памяти, под него будет выделяться место, начиная от конца хранения статических переменных (по рисунку выше - в белой части). И вот тут возможно взаимное столкновение динамического выделения памяти (которое идет вверх по адресам) и стека (который идет вниз по адресам).
В чем именно проявляется глюк? В каком поведении? Проанализируйте, что конкретно портится.

Может ли быть так, что оно вызовется снова не завершив текущее. А как он знает, что текущее завершилось?

Нет, то же самое прерывание имеет тот же самый приоритет и вытеснить само себя не может. Поэтому, флаг запроса будет выставлен, но как только произойдет выход из текущего прерывания, МК снова войдет в это прерывание с его начала. Более подробно о работе прерываний прочтите в Programming Manual.

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 07:19:36

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

Расположение стека я показал. Причем в отличии от того, что делал Keil, у силабов стек расположен внизу памяти и данные запортить не может. Он может только наехать на область где мог бы быть флеш. Но тогда, если бы адрес возврата был бы записан в пустоту, я бы наверняка получил бы HardFault.

Ок, проведу тесты и посмотрю, может зря я на стек бочку качу.

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 08:42:22

Уберите задержки из прерывания!
Перед сбросом флага проверяйте из-за него вы туда попали или нет.
Проверяйте как настроены прерывания от UART - по каким причинам они могут возникнуть. Могут ли, например, из-за ошибок.



Специально проверил - все прерывания кроме RX отключены. Даже пошел в лоб вот так:

Код:
void USART1_IRQHandler(void) {

uint32_t i = 0;

NVIC->ICER[1] |= 0x20         ; // Disable USART1 interrupt.

USART1->SR = 0               ;


GPIOC->ODR |= GPIO_ODR_ODR12   ; // Enable LED.

for (i = 0; i < 65535; i++)      ; // Delay

GPIOC->ODR &=~ GPIO_ODR_ODR12   ; // Disable LED.

for (i = 0; i < 65535; i++)      ; // Delay

NVIC->ISER[1] |= 0x20         ; // Enable USART1 interrupt.

}


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

UPD: разобрался.

Чтобы отработать такую ситуацию, есть флаг ORE - "Overrun error". Прерывание по этому флагу то же самое, что и для RX - RXNEIE. Поэтому оно и вылезает у меня всегда. Проблема была в том, что этот флаг невозможно сбросить простым обнулением, нужно выполнить сначала чтение из USART1->DR, а затем чтение из USART1->SR. Только тогда он сбрасывается.

В итоге, эта проблема решилась вот так:

Код:
void USART1_IRQHandler(void) {

if ((((USART1->SR & USART_SR_RXNE) >> USART_SR_RXNE_Pos) == 1) & (((USART1->SR & USART_SR_ORE) >> USART_SR_ORE_Pos) != 1)) {

ReceivedText[SymbolCount++] = USART1->DR;

} else {

(void)USART1->DR;
(void)USART1->SR;

}


Здесь проверяется флаг RXNE (хотя, в любом случае мы попали в это прерывание именно через него, но если в друг включены другие прерывания, то проверять его нужно), и флаг ORE не должен быть равен единице. Если все нормально, читаем данные. Если Overrun, то просто сбрасываем все флаги и ничего не делаем. Ну, либо, выдаем ошибку.

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 09:29:23

Мне страшно представить, когда ты начнёшь осваивать UART+DMA... А ведь там без IDLE или RTO никак...

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 09:33:45

как я понял, истинная причина проблемы в отсутствии считывания регистра принятых данных

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 10:34:17

Проблема была в том, что на одном бите разрешения прерывания RXNEIE висело два флага от разных событий - RXNE и ORE. И чтобы сбросить один из этих флагов, ORE, нужно не просто считать регистр, а выполнить определенную последовательность действий. В итоге, я получаю 10 символов по USART1 вот таким кодом:

Код:
void USART1_IRQHandler(void) {

uint8_t i = 0;

if ((((USART1->SR & USART_SR_RXNE) >> USART_SR_RXNE_Pos) == 1) & (((USART1->SR & USART_SR_ORE) >> USART_SR_ORE_Pos) != 1) & (((USART1->SR & USART_SR_IDLE) >> USART_SR_IDLE_Pos) != 1)) {

   ReceivedText[SymbolCount++] = USART1->DR;
   (void)USART1->SR;

} else {

   if (((USART1->SR & USART_SR_ORE) >> USART_SR_ORE_Pos) == 1) {

      USART_SendStr("Overrun!");

      (void)USART1->DR;
      (void)USART1->SR;

      memset(ReceivedText, 0, sizeof ReceivedText);

      SymbolCount = 0;

   }

   if (((USART1->SR & USART_SR_IDLE) >> USART_SR_IDLE_Pos) == 1) {
      if (SymbolCount != 10) {

         USART_SendStr("Time out!");
         memset(ReceivedText, 0, sizeof ReceivedText);
         SymbolCount = 0;

         (void)USART1->DR;
         (void)USART1->SR;

      } else {

         SymbolCount = 0;

            for (i = 0; i < 11; i++) {
               ReceivedCommand[i] = ReceivedText[i];
            }

         ReceivedCommand[11] = 0;

         (void)USART1->DR;
         (void)USART1->SR;

      }
   }
}

}


Вроде все работает как надо, но после приема 10 символов почему-то еще один лишний раз срабатывает прерывание и выдается одна ошибка Time out. Как будто принимается один лишний символ. В счетчике SymbolCount на тот момент оказывается число 1, а должно быть 0.

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 10:50:51

В ОЗУ вот так всё это укладывается:
Изображение
l.

Ооо, спасибо, наглядно изображено.

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 13:01:42

нужно выполнить сначала чтение из USART1->DR, а затем чтение из USART1->SR.
Даташиты то когда будем читать?
Изображение
SR дополнительно читать не надо, так как вы делает это при проверке статуса (правильная последовательность чтения - сначала SR, затем DR). А в вашем случае даже затрудняюсь посчитать сколько раз SR прочитан. Я приводил вот в этом сообщении скелет прерывания. Больше там ничего не нужно.

Код:
if ((((USART1->SR & USART_SR_RXNE) >> USART_SR_RXNE_Pos) == 1) & (((USART1->SR & USART_SR_ORE) >> USART_SR_ORE_Pos) != 1) & (((USART1->SR & USART_SR_IDLE) >> USART_SR_IDLE_Pos) != 1))
Код получился %цензор%. Ну что это? Что ещё за "&"?

Уберите всё. Сделайте приём с минимальным количеством кода без таймаутов. После этого добавьте обнуление и запуск таймера при приёме каждого байта. В прерывании от таймера реакция на таймаут. Это всё.


ore.png
(27.04 KiB) Скачиваний: 464


Добавлено after 1 minute 59 seconds:
как я понял, истинная причина проблемы в отсутствии считывания регистра принятых данных
Нет, в непрочтении документации и переусложнении алгоритма.
Последний раз редактировалось VladislavS Чт май 26, 2022 13:37:05, всего редактировалось 2 раз(а).

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 13:14:19

Код получился %цензор%. Ну что это? Что ещё ща "&"?
Но, как ни странно, работать будет. Потому что остальные условия тоже по-дурацки написаны и выставляют только младший бит.
Это не отменяет, разумеется, того, что переписать по-человечески надо.
Lum1noFor, проверка отдельного бита делается так:
Код:
(USART1->SR & USART_SR_RXNE)

И все - никаких сдвигов не нужно.
Даташиты то когда будем читать?
Где ж вы раньше были? Сейчас-то, когда ТС сам нашел упоминание этого бита (который он вообще не трогал!), легко тыкать в документацию.

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 13:28:51

Код:
if (((USART1->SR & USART_SR_ORE) >> USART_SR_ORE_Pos) == 1)
Прекратите писать лишние буквы. Это пишеся просто вот так
Код:
if (USART1->SR & USART_SR_ORE)
Работает так же, писать менешь - быстрее и меньше ошибок. А результат ещё и быстрее может оказаться.


Добавлено after 4 minutes 5 seconds:
Даташиты то когда будем читать?
Где ж вы раньше были? Сейчас-то, когда ТС сам нашел упоминание этого бита (который он вообще не трогал!), легко тыкать в документацию.
Я где раньше был? Я уже давно скелет прерывания показал. В нём ORE автоматом скидывается. Да и откуда ему возникнуть, если прерывание короткое как полёт пули. Разве что приоритет задушить и сидеть в каком-нибудь другом прерывании. Ну так это ССЗБ.

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 16:56:20

Ну уж извините, туповат я, я и не спорю. Не в том я уже возрасте, чтобы все сходу понимать. Был бы крутым программистом - не обращался бы в тему для новичков. Спасибо Вам за критику и помощь. Учту все. Но, согласитесь, если просто взять чужое решение, ничего в голове не останется. Надо вникать, почему именно так. Что я и пытаюсь сделать.

Добавлено after 5 minutes 50 seconds:
Код:
if (((USART1->SR & USART_SR_ORE) >> USART_SR_ORE_Pos) == 1)
Прекратите писать лишние буквы. Это пишеся просто вот так
Код:
if (USART1->SR & USART_SR_ORE)
Работает так же, писать менешь - быстрее и меньше ошибок. А результат ещё и быстрее может оказаться.


Добавлено after 4 minutes 5 seconds:
Даташиты то когда будем читать?
Где ж вы раньше были? Сейчас-то, когда ТС сам нашел упоминание этого бита (который он вообще не трогал!), легко тыкать в документацию.
Я где раньше был? Я уже давно скелет прерывания показал. В нём ORE автоматом скидывается. Да и откуда ему возникнуть, если прерывание короткое как полёт пули. Разве что приоритет задушить и сидеть в каком-нибудь другом прерывании. Ну так это ССЗБ.



А раз уж Вы решили сразу код мне привести готовый, можно Вас попросить написать все это вместе с таймером таймаута? Тогда уж буду анализировать все вместе и думать, почему так и где я был не прав. Сегодня 6 часов просидел уже - все равно не работает нормально.

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 17:00:22

Быть может, стоило бы прочесть Reference Manual, раздел USART? Потому что там очень доходчиво, даже с диаграммами, расписано всё. Можете через гугл-переводчик, если проблемы с английским

Re: STM32 новичку в ARM что к чему

Чт май 26, 2022 17:02:47

Мне страшно представить, когда ты начнёшь осваивать UART+DMA... А ведь там без IDLE или RTO никак...

У меня UART работает через DMA: где-то только передача, где-то - и передача, и прием. И я ни разу в жизни ни IDLE, ни RTO не использовал. ЧЯДНТ?
Ну и код приема в прерывании с контролем таймаутов я уже приводил, если кому интересно. Прием через DMA с таймаутами тоже несложно реализуется: достаточно в поллинге время проверять.
Ответить