STM32 новичку в ARM что к чему
Re: STM32 новичку в ARM что к чему
Я ж тебе с самого начала предлагал делать на IDLE, а ты сам себе геморрой устроил на ровном месте.
А ещё одна страшная тайна- это MAX13487...
Добавлено after 1 minute 40 seconds:
Не вижу твоих обработчиков прерываний. По-идее, в каждом из них должно запрещаться прерывание от другого.
А ещё одна страшная тайна- это MAX13487...
Добавлено after 1 minute 40 seconds:
Не вижу твоих обработчиков прерываний. По-идее, в каждом из них должно запрещаться прерывание от другого.
Re: STM32 новичку в ARM что к чему
Я ж тебе с самого начала предлагал делать на IDLE, а ты сам себе геморрой устроил на ровном месте.
А ещё одна страшная тайна- это MAX13487...
Добавлено after 1 minute 40 seconds:
Не вижу твоих обработчиков прерываний. По-идее, в каждом из них должно запрещаться прерывание от другого.
А ещё одна страшная тайна- это 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 что к чему
у тебя не правильный алгоритм.
Ты должен запускать таймер в режиме OPM сразу после загрузки UART_DR, а внутри обработчиков запрещать оба прерывания и сбрасывать, например, в обработчике от таймера запрос от UART и наоборот. Тогда будет работать. Только, по-сути, так ты просто повторяешь алгоритм формирования IDLE в UART.
Ты должен запускать таймер в режиме OPM сразу после загрузки UART_DR, а внутри обработчиков запрещать оба прерывания и сбрасывать, например, в обработчике от таймера запрос от UART и наоборот. Тогда будет работать. Только, по-сути, так ты просто повторяешь алгоритм формирования IDLE в UART.
Re: STM32 новичку в ARM что к чему
Весь день проковырялся с этой штукой, выяснил кое-что странное. Оказывается, таймер вообще тут не при чем - зря его обвиняли. Вешаю светодиод на любую ногу МК и беру простейшее прерывание USART1 (для исключения влияния чего-либо даже задержку делаю на цикле):
Шлю один любой символ через терминал - светодиод моргает, как положено, 1 раз. Шлю его несколько раз вручную - все работает отлично. Но стоит только отправить через терминал не один символ (например, "Q"), а два или более подряд ("QE"), то светодиод начинает моргать бесконечно. То есть МК не может выйти из прерывания вообще, и гоняет его в цикле. Я так понимаю это из-за того, что следующий символ приходит раньше, чем закончилось предыдущее прерывание. Из-за чего такое может происходить?
Код: Выделить всё
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 что к чему
Уберите задержки из прерывания!
Перед сбросом флага проверяйте из-за него вы туда попали или нет.
Проверяйте как настроены прерывания от UART - по каким причинам они могут возникнуть. Могут ли, например, из-за ошибок.
Перед сбросом флага проверяйте из-за него вы туда попали или нет.
Проверяйте как настроены прерывания от UART - по каким причинам они могут возникнуть. Могут ли, например, из-за ошибок.
Re: STM32 новичку в ARM что к чему
У тебя не правильный алгоритм.
Ессно, у тебя так будет. Тебе надо разрешать прерывание после загрузки DR, а в обработчике, после окончания передачи, его запрещать.
Ессно, у тебя так будет. Тебе надо разрешать прерывание после загрузки DR, а в обработчике, после окончания передачи, его запрещать.
Re: STM32 новичку в ARM что к чему
Есть вопрос. У меня возникает глюк и я не могу выцепить в чем причина. Поэтому пытаюсь всё проверять. Сейчас на подозрении - стек. Глюк появлялся у меня спустя около 20 секунд после начала хода. Я увеличил размер стека с 4096 до 8192 байт и один проход выполнился нормально. Второй тоже нормально, но на третьем проходе - опять появился глюк. На подозрении прерывания. Их у меня толпы.
И вот вопрос про прерывание PendSV - оно у меня довольно длинное. Но имеет самый низкий приоритет. Может ли быть так, что оно вызовется снова не завершив текущее. А как он знает, что текущее завершилось? Собственно, я иногда и в самом этом обработчике взвожу запрос на PendSV, чтобы обработчик перезапустился (там у меня машина состояний).
Приоритеты распределены так:
Память распределена следующим образом:
А можно как-нибудь узнать, что стек вылез за рамки?
И вот вопрос про прерывание 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 что к чему
Я увеличил размер стека с 4096 до 8192 байт
Это влияет только на предупреждение при компиляции. Физически во время работы МК размер стека не контролируется. Можно посмотреть только в аналитике (Static Stack Analyzer) величину максимального потребления стека функциями. В ОЗУ вот так всё это укладывается:
Каждый вызов вложенной функции или прерывания увеличивает потребление стека. При возврате из вложенных функций и прерываний потребление стека уменьшается. А если стек увеличивается слишком сильно, он наползет на область хранения статических переменных и повредит их содержимое. В этом и будет проявляться косяк - в повреждении значений статических переменных.
Для контроля положения вершины стека есть регистр ядра SP (Stack Pointer). Регистр не имеет адреса и доступен либо через ассемблер, либо через сишную обертку __get_MSP(), возвращается текущий адрес вершины стека.
Поскольку вы используете динамическое выделение памяти, под него будет выделяться место, начиная от конца хранения статических переменных (по рисунку выше - в белой части). И вот тут возможно взаимное столкновение динамического выделения памяти (которое идет вверх по адресам) и стека (который идет вниз по адресам).
В чем именно проявляется глюк? В каком поведении? Проанализируйте, что конкретно портится.
Может ли быть так, что оно вызовется снова не завершив текущее. А как он знает, что текущее завершилось?
Нет, то же самое прерывание имеет тот же самый приоритет и вытеснить само себя не может. Поэтому, флаг запроса будет выставлен, но как только произойдет выход из текущего прерывания, МК снова войдет в это прерывание с его начала. Более подробно о работе прерываний прочтите в Programming Manual.
Re: STM32 новичку в ARM что к чему
Спасибо за подсказку. попробую вставить контроль значений вершины стека в журнал, чтобы узнать в этом ли проблема.
Расположение стека я показал. Причем в отличии от того, что делал Keil, у силабов стек расположен внизу памяти и данные запортить не может. Он может только наехать на область где мог бы быть флеш. Но тогда, если бы адрес возврата был бы записан в пустоту, я бы наверняка получил бы HardFault.
Ок, проведу тесты и посмотрю, может зря я на стек бочку качу.
Расположение стека я показал. Причем в отличии от того, что делал Keil, у силабов стек расположен внизу памяти и данные запортить не может. Он может только наехать на область где мог бы быть флеш. Но тогда, если бы адрес возврата был бы записан в пустоту, я бы наверняка получил бы HardFault.
Ок, проведу тесты и посмотрю, может зря я на стек бочку качу.
Re: STM32 новичку в ARM что к чему
Уберите задержки из прерывания!
Перед сбросом флага проверяйте из-за него вы туда попали или нет.
Проверяйте как настроены прерывания от UART - по каким причинам они могут возникнуть. Могут ли, например, из-за ошибок.
Перед сбросом флага проверяйте из-за него вы туда попали или нет.
Проверяйте как настроены прерывания от 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 что к чему
Мне страшно представить, когда ты начнёшь осваивать UART+DMA... А ведь там без IDLE или RTO никак...
- ARV
- Ум, честь и совесть. И скромность.
- Сообщения: 18544
- Зарегистрирован: Чт дек 28, 2006 08:19:56
- Откуда: Новочеркасск
- Контактная информация:
Re: STM32 новичку в ARM что к чему
как я понял, истинная причина проблемы в отсутствии считывания регистра принятых данных
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
Re: STM32 новичку в ARM что к чему
Проблема была в том, что на одном бите разрешения прерывания RXNEIE висело два флага от разных событий - RXNE и ORE. И чтобы сбросить один из этих флагов, ORE, нужно не просто считать регистр, а выполнить определенную последовательность действий. В итоге, я получаю 10 символов по USART1 вот таким кодом:
Вроде все работает как надо, но после приема 10 символов почему-то еще один лишний раз срабатывает прерывание и выдается одна ошибка Time out. Как будто принимается один лишний символ. В счетчике SymbolCount на тот момент оказывается число 1, а должно быть 0.
Код: Выделить всё
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.
- VladislavS
- Собутыльник Кота
- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
Re: STM32 новичку в ARM что к чему
нужно выполнить сначала чтение из 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))Уберите всё. Сделайте приём с минимальным количеством кода без таймаутов. После этого добавьте обнуление и запуск таймера при приёме каждого байта. В прерывании от таймера реакция на таймаут. Это всё.
Добавлено after 1 minute 59 seconds:
как я понял, истинная причина проблемы в отсутствии считывания регистра принятых данных
Нет, в непрочтении документации и переусложнении алгоритма.
Последний раз редактировалось VladislavS Чт май 26, 2022 13:37:05, всего редактировалось 2 раза.
- COKPOWEHEU
- Говорящий с текстолитом
- Сообщения: 1525
- Зарегистрирован: Чт июн 10, 2010 20:11:19
Re: STM32 новичку в ARM что к чему
Но, как ни странно, работать будет. Потому что остальные условия тоже по-дурацки написаны и выставляют только младший бит.Код получился %цензор%. Ну что это? Что ещё ща "&"?
Это не отменяет, разумеется, того, что переписать по-человечески надо.
Lum1noFor, проверка отдельного бита делается так:
Код: Выделить всё
(USART1->SR & USART_SR_RXNE)И все - никаких сдвигов не нужно.
Где ж вы раньше были? Сейчас-то, когда ТС сам нашел упоминание этого бита (который он вообще не трогал!), легко тыкать в документацию.Даташиты то когда будем читать?
- VladislavS
- Собутыльник Кота
- Сообщения: 2562
- Зарегистрирован: Вт май 01, 2018 19:44:47
Re: STM32 новичку в ARM что к чему
Код: Выделить всё
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:
Где ж вы раньше были? Сейчас-то, когда ТС сам нашел упоминание этого бита (который он вообще не трогал!), легко тыкать в документацию.Даташиты то когда будем читать?
Re: STM32 новичку в ARM что к чему
Ну уж извините, туповат я, я и не спорю. Не в том я уже возрасте, чтобы все сходу понимать. Был бы крутым программистом - не обращался бы в тему для новичков. Спасибо Вам за критику и помощь. Учту все. Но, согласитесь, если просто взять чужое решение, ничего в голове не останется. Надо вникать, почему именно так. Что я и пытаюсь сделать.
Добавлено after 5 minutes 50 seconds:
Работает так же, писать менешь - быстрее и меньше ошибок. А результат ещё и быстрее может оказаться.
Добавлено after 4 minutes 5 seconds:
Я где раньше был? Я уже давно скелет прерывания показал. В нём ORE автоматом скидывается. Да и откуда ему возникнуть, если прерывание короткое как полёт пули. Разве что приоритет задушить и сидеть в каком-нибудь другом прерывании. Ну так это ССЗБ.[/uquote]
А раз уж Вы решили сразу код мне привести готовый, можно Вас попросить написать все это вместе с таймером таймаута? Тогда уж буду анализировать все вместе и думать, почему так и где я был не прав. Сегодня 6 часов просидел уже - все равно не работает нормально.
Добавлено 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:
Где ж вы раньше были? Сейчас-то, когда ТС сам нашел упоминание этого бита (который он вообще не трогал!), легко тыкать в документацию.Даташиты то когда будем читать?
А раз уж Вы решили сразу код мне привести готовый, можно Вас попросить написать все это вместе с таймером таймаута? Тогда уж буду анализировать все вместе и думать, почему так и где я был не прав. Сегодня 6 часов просидел уже - все равно не работает нормально.
Re: STM32 новичку в ARM что к чему
Быть может, стоило бы прочесть Reference Manual, раздел USART? Потому что там очень доходчиво, даже с диаграммами, расписано всё. Можете через гугл-переводчик, если проблемы с английским
- Eddy_Em
- Собутыльник Кота
- Сообщения: 2516
- Зарегистрирован: Пт июл 12, 2019 22:52:01
- Контактная информация:
Re: STM32 новичку в ARM что к чему
Мне страшно представить, когда ты начнёшь осваивать UART+DMA... А ведь там без IDLE или RTO никак...
У меня UART работает через DMA: где-то только передача, где-то - и передача, и прием. И я ни разу в жизни ни IDLE, ни RTO не использовал. ЧЯДНТ?
Ну и код приема в прерывании с контролем таймаутов я уже приводил, если кому интересно. Прием через DMA с таймаутами тоже несложно реализуется: достаточно в поллинге время проверять.