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

Кто любит RISC в жизни, заходим, не стесняемся.
tonyk
Это не хвост, это антенна
Сообщения: 1305
Зарегистрирован: Вт ноя 19, 2019 06:10:18

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

Сообщение tonyk »

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

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

Добавлено after 1 minute 40 seconds:
Не вижу твоих обработчиков прерываний. По-идее, в каждом из них должно запрещаться прерывание от другого.
Lum1noFor
Открыл глаза
Сообщения: 65
Зарегистрирован: Ср сен 24, 2014 12:30:09

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

Сообщение Lum1noFor »

Я ж тебе с самого начала предлагал делать на 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;

}
tonyk
Это не хвост, это антенна
Сообщения: 1305
Зарегистрирован: Вт ноя 19, 2019 06:10:18

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

Сообщение tonyk »

у тебя не правильный алгоритм.
Ты должен запускать таймер в режиме OPM сразу после загрузки UART_DR, а внутри обработчиков запрещать оба прерывания и сбрасывать, например, в обработчике от таймера запрос от UART и наоборот. Тогда будет работать. Только, по-сути, так ты просто повторяешь алгоритм формирования IDLE в UART.
Lum1noFor
Открыл глаза
Сообщения: 65
Зарегистрирован: Ср сен 24, 2014 12:30:09

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

Сообщение Lum1noFor »

Весь день проковырялся с этой штукой, выяснил кое-что странное. Оказывается, таймер вообще тут не при чем - зря его обвиняли. Вешаю светодиод на любую ногу МК и беру простейшее прерывание 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"), то светодиод начинает моргать бесконечно. То есть МК не может выйти из прерывания вообще, и гоняет его в цикле. Я так понимаю это из-за того, что следующий символ приходит раньше, чем закончилось предыдущее прерывание. Из-за чего такое может происходить?
Аватара пользователя
COKPOWEHEU
Говорящий с текстолитом
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

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

Сообщение COKPOWEHEU »

Уберите задержки из прерывания!
Перед сбросом флага проверяйте из-за него вы туда попали или нет.
Проверяйте как настроены прерывания от UART - по каким причинам они могут возникнуть. Могут ли, например, из-за ошибок.
tonyk
Это не хвост, это антенна
Сообщения: 1305
Зарегистрирован: Вт ноя 19, 2019 06:10:18

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

Сообщение tonyk »

У тебя не правильный алгоритм.
Ессно, у тебя так будет. Тебе надо разрешать прерывание после загрузки DR, а в обработчике, после окончания передачи, его запрещать.
Аватара пользователя
uldemir
Друг Кота
Сообщения: 7356
Зарегистрирован: Пт авг 28, 2009 21:34:30
Откуда: 845-й км.

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

Сообщение uldemir »

Есть вопрос. У меня возникает глюк и я не могу выцепить в чем причина. Поэтому пытаюсь всё проверять. Сейчас на подозрении - стек. Глюк появлялся у меня спустя около 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


А можно как-нибудь узнать, что стек вылез за рамки?
Аватара пользователя
НовыйДень
Потрогал лапой паяльник
Сообщения: 362
Зарегистрирован: Вс апр 03, 2022 07:01:29

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

Сообщение НовыйДень »

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

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

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

Нет, то же самое прерывание имеет тот же самый приоритет и вытеснить само себя не может. Поэтому, флаг запроса будет выставлен, но как только произойдет выход из текущего прерывания, МК снова войдет в это прерывание с его начала. Более подробно о работе прерываний прочтите в Programming Manual.
Аватара пользователя
uldemir
Друг Кота
Сообщения: 7356
Зарегистрирован: Пт авг 28, 2009 21:34:30
Откуда: 845-й км.

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

Сообщение uldemir »

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

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

Ок, проведу тесты и посмотрю, может зря я на стек бочку качу.
Lum1noFor
Открыл глаза
Сообщения: 65
Зарегистрирован: Ср сен 24, 2014 12:30:09

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

Сообщение Lum1noFor »

Уберите задержки из прерывания!
Перед сбросом флага проверяйте из-за него вы туда попали или нет.
Проверяйте как настроены прерывания от 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, то просто сбрасываем все флаги и ничего не делаем. Ну, либо, выдаем ошибку.
tonyk
Это не хвост, это антенна
Сообщения: 1305
Зарегистрирован: Вт ноя 19, 2019 06:10:18

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

Сообщение tonyk »

Мне страшно представить, когда ты начнёшь осваивать UART+DMA... А ведь там без IDLE или RTO никак...
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

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

Сообщение ARV »

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

Мой уютный бложик... заходите!
Lum1noFor
Открыл глаза
Сообщения: 65
Зарегистрирован: Ср сен 24, 2014 12:30:09

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

Сообщение Lum1noFor »

Проблема была в том, что на одном бите разрешения прерывания 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.
Viktorkin
Первый раз сказал Мяу!
Сообщения: 26
Зарегистрирован: Вт май 17, 2022 09:49:48

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

Сообщение Viktorkin »

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

Ооо, спасибо, наглядно изображено.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

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

Сообщение VladislavS »

нужно выполнить сначала чтение из 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 КБ) 919 скачиваний


Добавлено after 1 minute 59 seconds:
как я понял, истинная причина проблемы в отсутствии считывания регистра принятых данных
Нет, в непрочтении документации и переусложнении алгоритма.
Последний раз редактировалось VladislavS Чт май 26, 2022 13:37:05, всего редактировалось 2 раза.
Аватара пользователя
COKPOWEHEU
Говорящий с текстолитом
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

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

Сообщение COKPOWEHEU »

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

Код: Выделить всё

(USART1->SR & USART_SR_RXNE)

И все - никаких сдвигов не нужно.
Даташиты то когда будем читать?
Где ж вы раньше были? Сейчас-то, когда ТС сам нашел упоминание этого бита (который он вообще не трогал!), легко тыкать в документацию.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

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

Сообщение VladislavS »

Код: Выделить всё

if (((USART1->SR & USART_SR_ORE) >> USART_SR_ORE_Pos) == 1)
Прекратите писать лишние буквы. Это пишеся просто вот так

Код: Выделить всё

if (USART1->SR & USART_SR_ORE)
Работает так же, писать менешь - быстрее и меньше ошибок. А результат ещё и быстрее может оказаться.
СпойлерСравниваем

Код: Выделить всё

void foo1()
{
  if (((USART1->ISR & USART_ISR_ORE) >> USART_ISR_ORE_Pos) == 1)
    __NOP();
}

void foo2()
{
  if (USART1->ISR & USART_ISR_ORE)
    __NOP();
}


_Z4foo1v:
        LDR.N    R0,??DataTable2_2
        LDR      R1,[R0, #+0]   
        UBFX     R0,R1,#+3,#+1 
        CBZ.N    R0,??foo1_0   
        Nop                     
??foo1_0:
        BX       LR             

_Z4foo2v:
        LDR.N    R0,??DataTable2_2
        LDR      R1,[R0, #+0]   
        LSLS     R2,R1,#+28     
        IT       MI               
        NopMI                   
        BX       LR


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

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

Сообщение Lum1noFor »

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

Добавлено after 5 minutes 50 seconds:
[uquote="Lum1noFor",url="/forum/viewtopic.php?p=4236833#p4236833"]

Код: Выделить всё

if (((USART1->SR & USART_SR_ORE) >> USART_SR_ORE_Pos) == 1)
Прекратите писать лишние буквы. Это пишеся просто вот так

Код: Выделить всё

if (USART1->SR & USART_SR_ORE)
Работает так же, писать менешь - быстрее и меньше ошибок. А результат ещё и быстрее может оказаться.
СпойлерСравниваем

Код: Выделить всё

void foo1()
{
  if (((USART1->ISR & USART_ISR_ORE) >> USART_ISR_ORE_Pos) == 1)
    __NOP();
}

void foo2()
{
  if (USART1->ISR & USART_ISR_ORE)
    __NOP();
}


_Z4foo1v:
        LDR.N    R0,??DataTable2_2
        LDR      R1,[R0, #+0]   
        UBFX     R0,R1,#+3,#+1 
        CBZ.N    R0,??foo1_0   
        Nop                     
??foo1_0:
        BX       LR             

_Z4foo2v:
        LDR.N    R0,??DataTable2_2
        LDR      R1,[R0, #+0]   
        LSLS     R2,R1,#+28     
        IT       MI               
        NopMI                   
        BX       LR


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


А раз уж Вы решили сразу код мне привести готовый, можно Вас попросить написать все это вместе с таймером таймаута? Тогда уж буду анализировать все вместе и думать, почему так и где я был не прав. Сегодня 6 часов просидел уже - все равно не работает нормально.
Аватара пользователя
НовыйДень
Потрогал лапой паяльник
Сообщения: 362
Зарегистрирован: Вс апр 03, 2022 07:01:29

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

Сообщение НовыйДень »

Быть может, стоило бы прочесть Reference Manual, раздел USART? Потому что там очень доходчиво, даже с диаграммами, расписано всё. Можете через гугл-переводчик, если проблемы с английским
Аватара пользователя
Eddy_Em
Собутыльник Кота
Сообщения: 2516
Зарегистрирован: Пт июл 12, 2019 22:52:01
Контактная информация:

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

Сообщение Eddy_Em »

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

У меня UART работает через DMA: где-то только передача, где-то - и передача, и прием. И я ни разу в жизни ни IDLE, ни RTO не использовал. ЧЯДНТ?
Ну и код приема в прерывании с контролем таймаутов я уже приводил, если кому интересно. Прием через DMA с таймаутами тоже несложно реализуется: достаточно в поллинге время проверять.
Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда.
Я на гитхабе, в ЖЖ
Ответить

Вернуться в «ARM»