Сергей Борщ 140 13 августа Опубликовано 13 августа · Жалоба 17 минут назад, EdgeAligned сказал: Напишите обработчик прерывания void HardFault_Handler(void){ while(1) ; } и поставьте на нем брейкпоинт, запустите отладку. void HardFault_Handler(void) { volatile int i = 0; while(!i) ; } Попав в него отладчиком ставим i = 1 и шагая по ассемблерным командам выходим из этого обработчика. Попадаем на следующую инструкцию за вызвавшей исключение. Смотрим на нее (вызвавшую исключение), на содержимое задействованных в ней регистров, медитируем. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
kpv 12 13 августа Опубликовано 13 августа · Жалоба ну так смотрите по регистрам кто туда полез по этому адресу и разматывайте в обратную сторону ход программы Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Priest_89 8 13 августа Опубликовано 13 августа · Жалоба 1 hour ago, koluna said: там два устройства, второе устройство обменивается по USART с глючащим устройством... В функции приема по usart есть ограничение диапазона памяти для записи принимаемых данных? Встречался с такой проблемой - шум на линии, либо второе устройство глючит и выдает посылку длиннее чем надо - и контроллер пишет принимаемые байты по инкрементируемому указателю, попадая в итоге в несуществующую память и уходя в hardfault. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 81 13 августа Опубликовано 13 августа · Жалоба Это не относится именно к UART-у. Это - программная ошибка в построении кода на приеме байтов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
koluna 0 13 августа Опубликовано 13 августа · Жалоба On 8/13/2024 at 11:41 AM, Priest_89 said: В функции приема по usart есть ограничение диапазона памяти для записи принимаемых данных? Встречался с такой проблемой - шум на линии, либо второе устройство глючит и выдает посылку длиннее чем надо - и контроллер пишет принимаемые байты по инкрементируемому указателю, попадая в итоге в несуществующую память и уходя в hardfault. Данные пишутся в массив 2 кБ, но на самом деле данных меньше гораздо. Устройства соединены кабелем 0.5 м, на столе все лежит... В принимаемом пакете есть информация о длине пакета. Если значение больше размера буфера - пакет игнорируется, сообщается об ошибке. Иначе - обработчик считает принятые байты (одновременно их записывая в массив) и как только примет необходимое количество байт - прием пакета заканчивается. Дальше проверка контрольной суммы и разбор пакета. Обработчик прерывания: void USART2_IRQHandler(void) { uint16_t status = USART2->SR; // Получаем флаги. if(status & USART_SR_RXNE) { // Прерывание: регистр данных приемника не пуст. // RXNE сбрасывается автоматически после чтения USART_DR или записью нуля. exchRXNEHandler(); } else if(status & USART_SR_TXE) { // Прерывание: регистр данных передатчика пуст. // TXE сбрасывается автоматически при записи в USART_DR. exchTXEHandler(); } } Функция, вызываемая из него для обработки прерывания: void exchRXNEHandler(void) { static uint16_t packetLength; static uint32_t startTime; static enum { RX_ISR_START_MARKER1, RX_ISR_START_MARKER2, RX_ISR_START_MARKER3, RX_ISR_START_LENGTH1, RX_ISR_START_LENGTH2, RX_ISR_RECEIVE_DATA } rxIsrState = RX_ISR_START_MARKER1; uint8_t data = exchReadByte(); // Считываем принятый байт. switch(rxIsrState) { case RX_ISR_START_MARKER1: // Ожидаем первый символ МНП. if(EXCH_MARKER1 == data) { rxIsrState = RX_ISR_START_MARKER2; exchBuff[rxCnt++] = data; } break; case RX_ISR_START_MARKER2: // Ожидаем второй символ МНП. if(EXCH_MARKER2 == data) { rxIsrState = RX_ISR_START_MARKER3; exchBuff[rxCnt++] = data; } else { // Принят другой символ - начинаем снова. rxIsrState = RX_ISR_START_MARKER1; rxCnt = 0; } break; case RX_ISR_START_MARKER3: // Ожидаем третий символ МНП. if(EXCH_MARKER3 == data) { rxIsrState = RX_ISR_START_LENGTH1; exchBuff[rxCnt++] = data; } else { // Принят другой символ - начинаем снова. rxIsrState = RX_ISR_START_MARKER1; rxCnt = 0; } break; case RX_ISR_START_LENGTH1: // Ожидаем младший байт длины пакета. rxIsrState = RX_ISR_START_LENGTH2; exchBuff[rxCnt++] = data; packetLength = data; break; case RX_ISR_START_LENGTH2: // Ожидаем старший байт длины пакета. exchBuff[rxCnt++] = data; packetLength |= (uint16_t) data << 8; if(packetLength > EXCH_BUFF_SIZE) { // Длина больше допустимой, прием завершаем! rxComplete = true; rxOverflow = true; exchRxneDisable(); rxCnt = 0; rxIsrState = RX_ISR_START_MARKER1; } else { rxIsrState = RX_ISR_RECEIVE_DATA; startTime = getTickCount(); // Инициализируем таймер для обнаружения таймаута приема оставшихся данных. } break; case RX_ISR_RECEIVE_DATA: if(calcTicks(startTime) > EXCH_RECEIVE_TIMEOUT) { // Таймаут при приеме, прием завершаем! rxComplete = true; rxTimeout = true; exchRxneDisable(); rxCnt = 0; rxIsrState = RX_ISR_START_MARKER1; } else { // Укладываемся во временные рамки при приеме. exchBuff[rxCnt++] = data; if(rxCnt == packetLength) { // Пакет полностью принят. rxComplete = true; exchRxneDisable(); rxCnt = 0; rxIsrState = RX_ISR_START_MARKER1; } } break; default: break; } } А вот так запускается прием: static PT_THREAD(waitForRequest(PT* pt, EXCH_Result* exchRes)) { PT_BEGIN(pt); memset(exchBuff, 0, sizeof(exchBuff)); *exchRes = EXCH_UNEXPECTED_RES; rxCnt = 0; rxComplete = false; rxOverflow = false; rxTimeout = false; exchReadByte(); exchRxneEnable(); dbgout(LOG_DEBUG, "[%s()] Wait for packet... ", __func__); static uint32_t dbgTm; dbgStartTime(&dbgTm); PT_WAIT_UNTIL(pt, rxComplete); rxComplete = false; dbgout(LOG_DEBUG, "%u\n", dbgStopTime(&dbgTm)); if(rxOverflow) { // Переполнение приемного буфера! rxOverflow = false; *exchRes = EXCH_OVERFLOW; } else if(rxTimeout) { // Таймаут при приеме пакета! rxTimeout = false; *exchRes = EXCH_TIMEOUT; } else if(!checkCrc()) { *exchRes = EXCH_CRC_ERROR; } else { // Пакет принят нормально. *exchRes = EXCH_OK; } uint16_t packetLength = getPacketLength(); dbgout(LOG_DEBUG, "\n[%s()] Result: %u, Received packet length: %u\n", __func__, *exchRes, packetLength); PT_END(pt); } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 81 13 августа Опубликовано 13 августа · Жалоба Чтож, повторю еще раз анекдот: --- Ты чего тут по полу ползаешь? --- Да я вон там ключи потерял, ищу... --- Так и ищи там, где потерял --- Так здесь светлее же! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
kpv 12 13 августа Опубликовано 13 августа · Жалоба rxCnt нигде не проверяется. используйте assert. вы приняли неизвестно что rxIsrState = RX_ISR_START_LENGTH2; exchBuff[rxCnt++] = data; packetLength = data; exchBuff[rxCnt++] = data; if(rxCnt == packetLength) { // Пакет полностью принят. rxComplete = true; exchRxneDisable(); rxCnt = 0; rxIsrState = RX_ISR_START_MARKER1; } и пытаетесь неизвестно куда это записать. Все такие вещи надо проверять на выход за пределы массива Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 239 13 августа Опубликовано 13 августа · Жалоба 14 часов назад, Arlleex сказал: Считайте BFAR и в нем будет адрес инструкции, которая вызвала ошибку. Не обязательно. Только если соответствующий бит валидности установлен. МК ТС-а имеет буфер записи? Такие простейшие - имеют его? Не работал, не в курсе... Но если имеет, то запросто может быть "неточная ошибка при обращении к данным" (imprecisible bus error) в случае записи куда не следует. А при ней нет валидного адреса fault-а. ТС-у: Открыть мануал на ядро Cortex-M и прочитать про обработку исключений. Там всё разжёвано предельно ясно. 13 часов назад, koluna сказал: [HardFault_Handler()] BFAR=x20002000 Да, это Bus Fault, я уже понял. И адрес RAM. В просессоре 8 кБ, как раз на конец указывает, только вот что это значит... Вроде как очевидная высокая вероятность, что какой-то цикл у вас начал писать с автоинкрементом в память без берегов. Пока не дошёл до границы ОЗУ. Где и вылетел в fault. даже не заглядывая в код... 1 час назад, EdgeAligned сказал: Напишите обработчик прерывания void HardFault_Handler(void){ while(1) ; } и поставьте на нем брейкпоинт, запустите отладку. При остановке посмотрите отладочную инфу с указанием адреса инструкции, вызвавшей "тяжелый сбой". Откройте листинг кода и по адресу найдите, какая инструкция и к чему (какому куску кода) она принадлежит. Далее будет видно. Гораздо проще. Раз выяснено, что имеется bus fault по обращению к конкретному адресу, то просто ставим data-бряк на обращение к 0x20002000 по записи или чтению. И получаем останов в точке бага. 2-й совет ТС-у: Изучить возможности собственных средств отладки. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 239 13 августа Опубликовано 13 августа · Жалоба 57 минут назад, koluna сказал: Данные пишутся в массив 2 кБ "Массив 2 кБ" - это 2 килобита или 2 килобайта? 59 минут назад, koluna сказал: В принимаемом пакете есть информация о длине пакета. Если значение больше размера буфера - пакет игнорируется, сообщается об ошибке. Что есть неправильное действие. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
esaulenka 7 13 августа Опубликовано 13 августа · Жалоба 1 hour ago, koluna said: Иначе - обработчик считает принятые байты (одновременно их записывая в массив) и как только примет необходимое количество байт - прием пакета заканчивается. Дальше проверка контрольной суммы и разбор пакета. А что будет, если "необходимое количество байт" в переменной packetLenght будет меньше пяти? И вообще, было б крайне полезно отладчиком посмотреть на содержимое приемного буфера (и следующих за ним байт) после того, как всё грохнулось. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
koluna 0 13 августа Опубликовано 13 августа · Жалоба On 8/13/2024 at 12:34 PM, jcxz said: Не обязательно. Только если соответствующий бит валидности установлен. Установлен. On 8/13/2024 at 12:34 PM, jcxz said: МК ТС-а имеет буфер записи? exchBuf в коде, в него данные записываются при приеме, а потом ответ в него записывается при передаче. On 8/13/2024 at 12:34 PM, jcxz said: ТС-у: Открыть мануал на ядро Cortex-M и прочитать про обработку исключений. Там всё разжёвано предельно ясно. Читал Джозефа Ю, смотрел даташит... On 8/13/2024 at 12:34 PM, jcxz said: Вроде как очевидная высокая вероятность, что какой-то цикл у вас начал писать с автоинкрементом в память без берегов. Да вот не найду я этот цикл никак... On 8/13/2024 at 12:34 PM, jcxz said: то просто ставим data-бряк на обращение к 0x20002000 по записи или чтению. И получаем останов в точке бага. Что Вы имеете в виду? Настройка отладчика или что-то еще? Так-то по стеку вызовов в отладчике видно где оно падает, но вот почему оно там падает - пока не понял... On 8/13/2024 at 12:51 PM, jcxz said: "Массив 2 кБ" - это 2 килобита или 2 килобайта? 2 килобайта. On 8/13/2024 at 12:51 PM, jcxz said: Что есть неправильное действие. Как было бы правильнее? assert не помогает с rxCnt. Да я и до этого его мониторил... On 8/13/2024 at 11:06 AM, Сергей Борщ said: void HardFault_Handler(void) { volatile int i = 0; while(!i) ; } Попав в него отладчиком ставим i = 1 и шагая по ассемблерным командам выходим из этого обработчика. Попадаем на следующую инструкцию за вызвавшей исключение. Смотрим на нее (вызвавшую исключение), на содержимое задействованных в ней регистров, медитируем. Делал так, не выходит из исключения и ведет себя как-то странно... как бы подвисает чтоли... On 8/13/2024 at 12:59 PM, esaulenka said: А что будет, если "необходимое количество байт" в переменной packetLenght будет меньше пяти? Да, будет какая-то такая ошибка 🙂 Спасибо, учту. Но у меня логи пишутся со второго устройства, я вижу, что уходит то, что нужно, менее 8 не бывает 🙂 И логи с глючного устройства тоже вижу - там видно, что принимается оно нормально... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 239 13 августа Опубликовано 13 августа · Жалоба А вот и баг: 1 час назад, koluna сказал: if(packetLength > EXCH_BUFF_SIZE) { // Длина больше допустимой, прием завершаем! rxComplete = true; rxOverflow = true; exchRxneDisable(); rxCnt = 0; rxIsrState = RX_ISR_START_MARKER1; } else { rxIsrState = RX_ISR_RECEIVE_DATA; startTime = getTickCount(); // Инициализируем таймер для обнаружения таймаута приема оставшихся данных. } break; case RX_ISR_RECEIVE_DATA: if(calcTicks(startTime) > EXCH_RECEIVE_TIMEOUT) { // Таймаут при приеме, прием завершаем! rxComplete = true; rxTimeout = true; exchRxneDisable(); rxCnt = 0; rxIsrState = RX_ISR_START_MARKER1; } else { // Укладываемся во временные рамки при приеме. exchBuff[rxCnt++] = data; if(rxCnt == packetLength) При приёме нулевой длины пакета получите как раз переполнение ОЗУ. Что и наблюдается. И здесь: 1 час назад, koluna сказал: case RX_ISR_RECEIVE_DATA: if(calcTicks(startTime) > EXCH_RECEIVE_TIMEOUT) { Что за дикий способ контроля таймаута??? Если пакет принимался, принимался, а потом хвост его пропал (обрыв, таймаут), то вместо регистрации таймаута по факту самого таймаута, будет зарегистрирован ложный таймаут в начале следующего валидного кадра. С потерей его. PS: Совет: До того как лепить собственные деревянные лисапеды-"протоколы" с квадратными колёсами, следует изучить существующие стандартные. SLIP, COBS, ... Чего и вам советую. Они на голову выше вашего поделия. А всё что выше - выкинуть в мусорку. 29 минут назад, koluna сказал: exchBuf в коде, в него данные записываются при приеме, а потом ответ в него записывается при передаче. Речь шла о буфере записи в ядре (CPU). См. мануал на ядро. При наличии такого буфера, записи в недопустимые адреса часто возбуждают "imprecisible bus error" без адреса. 29 минут назад, koluna сказал: Да вот не найду я этот цикл никак... Я его уже нашёл. См. выше. 29 минут назад, koluna сказал: Что Вы имеете в виду? Настройка отладчика или что-то еще? Нормальные отладчики (типа J-Link) имеют возможность установки не только code-breakpoints, но и data-breakpoints. Речь шла о них. Если ваш их умеет, то это иногда позволяет быстро находить баги с записью по неверному известному адресу. 29 минут назад, koluna сказал: Как было бы правильнее? При обнаружении превышения пакетом максимально допустимого размера, правильное действие: Осуществить полный приём всего пакета, без его сохранения в буфер. Т.е. - нормальный пропуск пакета. А в вашем случае при переполнении вы просто раскладываете себе грабли: Если в теле данных пакета случится наличие маркеров, то будет ложная сработка обнаружения пакета. Со всеми вытекающими. Ведь "протокол" у вас не кодонезависимый. В отличие от нормальных протоколов (COBS, SLIP). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
kpv 12 13 августа Опубликовано 13 августа · Жалоба 24 minutes ago, koluna said: я вижу, что уходит то RXNE это не только правильный приём, но и overrun error туда может проскочить, если разрешено. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
kpv 12 13 августа Опубликовано 13 августа · Жалоба 34 minutes ago, koluna said: Что Вы имеете в виду? Настройка отладчика или что-то еще? https://community.silabs.com/s/article/how-do-i-trigger-a-breakpoint-when-a-value-in-memory-is-read-and-or-written-x Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
koluna 0 13 августа Опубликовано 13 августа · Жалоба On 8/13/2024 at 1:21 PM, jcxz said: Нормальные отладчики (типа J-Link) имеют возможность установки не только code-breakpoints, но и data-breakpoints. Речь шла о них. Если ваш их умеет, то это иногда позволяет быстро находить баги с записью по неверному известному адресу. У меня TE-ARM-Link + OpenOCD. С data-breakpoints не знаком пока... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться