Дерищев 1 April 8 Posted April 8 · Report post Четверть века назад я столкнулся с проблемой, которая выражалась в том, что через несколько минут нормальной работы, портится последний байт, передаваемый по UART, всегда 0xff. Тогда я не сумел докопаться до истины. Сделал программу как-то по-другому и решил для себя, что проблему я решил. И вот недавно эта проблема мне опять выстрелила. Докопаться до сути вещей я так и не сумел. Было замечено, что проблема проявляется, когда длина передаваемых данных по обоим UART превышает некоторое значение. Когда работает только один UART, такой проблемы не возникало. Путём последовательного исключения нашёл заковырку в организации цикла for(ct = 0; ct < (Sectocol_rom[ct_Sectocol_trans].amount * 2); src.ch++, ct++) Sectocol_rom – массив во Flash. Каждый проход цикла происходило обращение ко Flash. Сделал вот так. То есть один раз присваиваю счётчику значение из Flash и произвожу его декремент с проверкой на ноль. for(ct = (Sectocol_rom[ct_Sectocol_trans].amount * 2); ct--; src.ch++) И произошло чудо! Гонял несколько часов, всё хорошо. Казалось бы, оба объявления цикла корректны. Видать, частые обращения ко Flash из разных потоков приводя к конфликтам. А каким образом этот могло повлиять на UART, я так и не понял. Мораль – красивый код, красиво работает. Не пытайтесь проблему обойти, нерешённая проблема имеет свойство возвращаться через много лет. Quote Share this post Link to post Share on other sites More sharing options...
Arlleex 346 April 8 Posted April 8 · Report post В правильно написанном коде ошибок не возникает. Про циклы сказать ничего нельзя, т.к. отсутствует куча сопутствующей информации. Quote Share this post Link to post Share on other sites More sharing options...
jcxz 364 April 8 Posted April 8 · Report post 47 минут назад, Дерищев сказал: Казалось бы, оба объявления цикла корректны. Видать, частые обращения ко Flash из разных потоков приводя к конфликтам. Нет, никакие "частые обращения к флешь" не при чём. Проблема у вас где-то в другом. Где - вы не нашли. 47 минут назад, Дерищев сказал: А каким образом этот могло повлиять на UART, я так и не понял. Ищите не там где светло. Под фонарём (как правило) искать бесполезно - давно известная истина. 47 минут назад, Дерищев сказал: Не пытайтесь проблему обойти, нерешённая проблема имеет свойство возвращаться через много лет. А вот здесь вы совершенно правы. И эта, так и не найденная проблема, к вам когда-нить снова вернётся. Quote Share this post Link to post Share on other sites More sharing options...
x893 85 April 8 Posted April 8 · Report post 1 hour ago, Дерищев said: Четверть века назад я столкнулся с проблемой, которая выражалась в том, что через несколько минут нормальной работы, портится последний байт, передаваемый по UART, всегда 0xff. Тогда я не сумел докопаться до истины. Сделал программу как-то по-другому и решил для себя, что проблему я решил. И вот недавно эта проблема мне опять выстрелила. Докопаться до сути вещей я так и не сумел. Было замечено, что проблема проявляется, когда длина передаваемых данных по обоим UART превышает некоторое значение. Когда работает только один UART, такой проблемы не возникало. Путём последовательного исключения нашёл заковырку в организации цикла for(ct = 0; ct < (Sectocol_rom[ct_Sectocol_trans].amount * 2); src.ch++, ct++) Sectocol_rom – массив во Flash. Каждый проход цикла происходило обращение ко Flash. Сделал вот так. То есть один раз присваиваю счётчику значение из Flash и произвожу его декремент с проверкой на ноль. for(ct = (Sectocol_rom[ct_Sectocol_trans].amount * 2); ct--; src.ch++) И произошло чудо! Гонял несколько часов, всё хорошо. Казалось бы, оба объявления цикла корректны. Видать, частые обращения ко Flash из разных потоков приводя к конфликтам. А каким образом этот могло повлиять на UART, я так и не понял. Мораль – красивый код, красиво работает. Не пытайтесь проблему обойти, нерешённая проблема имеет свойство возвращаться через много лет. Просто где-то наговнокодили. Делов то. Quote Share this post Link to post Share on other sites More sharing options...
Bear_ku 0 April 9 Posted April 9 · Report post Вероятно ваше "многопоточное" приложение где-то портит сами переменные, либо лезет в отведенную для них память. М.б. отключаете UART не вовремя. А код в AVR (Tiny, Mega) исполняется из Flash, поэтому в случае ошибок при "частом чтении" изменение цикла ничем бы не помогло. Quote Share this post Link to post Share on other sites More sharing options...
Sergey_Aleksandrovi4 4 April 9 Posted April 9 · Report post 17 часов назад, Дерищев сказал: for(ct = 0; ct < (Sectocol_rom[ct_Sectocol_trans].amount * 2); src.ch++, ct++) Sectocol_rom – массив во Flash. Каждый проход цикла происходило обращение ко Flash. Если член структуры .amount, массив структур Sectocol_rom[] или индексная переменная ct_Sectocol_trans объявлены без квалификатора volatile, то чтение из Flash, по идее, происходит единожды перед входом в цикл. Листинг ассемблера смотрели? Переменная ct_Sectocol_trans асинхронно по отношению к циклу где-то в коде изменяется? В прерываниях, например. Компилятор IAR EWAVR, надо полагать? Quote Share this post Link to post Share on other sites More sharing options...
HardEgor 166 April 9 Posted April 9 · Report post 17 часов назад, Дерищев сказал: Казалось бы, оба объявления цикла корректны. Видать, частые обращения ко Flash из разных потоков приводя к конфликтам. А каким образом этот могло повлиять на UART, я так и не понял. В первом приближении, для проверки, я бы побайтно писал данные в RAM, потом одним блоком скидывал во flash. Quote Share this post Link to post Share on other sites More sharing options...
jcxz 364 April 9 Posted April 9 · Report post 7 часов назад, Sergey_Aleksandrovi4 сказал: Если член структуры .amount, массив структур Sectocol_rom[] или индексная переменная ct_Sectocol_trans объявлены без квалификатора volatile, то чтение из Flash, по идее, происходит единожды перед входом в цикл. Не обязательно. Чтения из флешь может вообще не происходить. Если сonst-данные не имеют volatile, то оптимизирующий компилятор вправе создать копию таких данных и поместить их прямо в команду CPU (если таковая возможность имеется в системе команд CPU). А сами const-данные вообще удалить из программы, как неиспользуемые. PS: Вообще - угадывать что там у ТС - дело неблагодарное. Всё равно никто не угадает. Quote Share this post Link to post Share on other sites More sharing options...
Дерищев 1 April 22 Posted April 22 · Report post Приветствую! Спасибо большое за поддержку! Да, действительно, проблема была не во flash. Интереса ради, массив перенёс в RAM, эффект прежний. Было установлено, что проблема во многопоточности. Использую диспетчер кооперативной многозадачности, базирующийся на механизме псевдо программного прерывания - средний уровень приоритетности между фоновой задачей и аппаратным прерыванием. Было задумано для уменьшения времени нахождения в обработчике аппаратного прерывания. В частности, в псевдо программном прерывании осуществляется вычисление CRC UART. В обработчике аппаратного прерывания производится только сохранение регистров и инициализация псевдо программного прерывания. Механизм псевдо программного прерывания базируется на аппаратном прерывании INT0. Активация производится путём установки в ноль вывода INT0 и занесением в массив указателей адреса обработчика. Если не разрешать прерывания в диспетчере псевдо программного прерывания, всё устойчиво работает, но тогда теряется смысл в этом механизме. Вывод, что сбои происходят тогда, когда из обработчика прерывания (при разрёшённом прерывании) происходит вызов другого прерывания. Режим INT0 - при нуле (когда ноль, постоянного генерируются прерывания). Вот листинг обработчика псевдо программного прерывания: #pragma vector=SW_Int_Vector // As Soft Ware interrupt __interrupt void SWI_mng (void) { unsigned char ct; void(*ptr)(void) = NULL; // Сохранение в стек всех регистров без исключения, в дополнение к тем, которые сохраняет транслятор asm("ST -Y, R24");// Сохранение в стек SREG asm("ST -Y, R25");// Сохранение в стек RAMPZ asm("ST -Y, R4"); asm("ST -Y, R5"); asm("ST -Y, R6"); asm("ST -Y, R7"); asm("ST -Y, R8"); asm("ST -Y, R9"); asm("ST -Y, R10"); asm("ST -Y, R11"); asm("ST -Y, R12"); asm("ST -Y, R13"); asm("ST -Y, R14"); asm("ST -Y, R15"); for(ct = 0; ct < qty_SWI_executors; ct++)// Поиск в массиве указателей активированного запроса if(SWI_exec[ct])// если указатель отличен от нуля { ptr = SWI_exec[ct]; // копирование в местный указатель SWI_exec[ct] = NULL; // Deactivate further execution очистка указателя в массиве, чтобы не выполнялся постоянно SW_Int = 1; // Stop interrupt Перевод порта в единицу, чтобы не было прерываний. EIMSK &= ~(1<< SW_Int_Vect); // External interrupt disable as SoftWare interrupt Запрет аппаратного прерывания INT0 GI_enable// Запрет глобального прерывания (*ptr)(); // Индексный вызов обработчика, косвенно, через адрес в указателе GI_disable // Разрешение глобального прерывания EIMSK |= (1<< SW_Int_Vect); // External interrupt enable as SoftWare interrupt Разрешение аппаратного прерывания INT0 break; } for(ct = 0; ct < qty_SWI_executors; ct++) // Поиск в массиве указателей необработанных запросов на псевдо программное прерывание if(SWI_exec[ct]) // Если был найден { SW_Int = 0; // Start interrupt установить в ноль порт INT0 для активации прерывания. После выхода из обработчика, тут же произойдёт запрос на новое прерывание. break; } asm("LD R15, Y+"); asm("LD R14, Y+"); asm("LD R13, Y+"); asm("LD R12, Y+"); asm("LD R11, Y+"); asm("LD R10, Y+"); asm("LD R9, Y+"); asm("LD R8, Y+"); asm("LD R7, Y+"); asm("LD R6, Y+"); asm("LD R5, Y+"); asm("LD R4, Y+"); asm("LD R25, Y+"); asm("LD R24, Y+"); } Вроде бы всё правильно, но где-то в фундаменте скрылась ошибка. Заранее благодарен за любые идеи, даже те, которые кажутся абсурдными! Quote Share this post Link to post Share on other sites More sharing options...
r_dot 41 April 22 Posted April 22 · Report post 1 час назад, Дерищев сказал: Вывод, что сбои происходят тогда, когда из обработчика прерывания (при разрёшённом прерывании) происходит вызов другого прерывания. Ну так сделайте одноуровневые прерывания, раз они у вас зависимые, то есть одни и те же переменные изменяют. Настройками системы прерываний или тупо глобальным запретом прерываний на время INT0. Ну подождёт другое прерывание немного. Ничего страшного. Ничего от этого не изменится, кроме скорости реакции на прерывание. В сумме-то время вычислений всё равно то же самое будет. Только в другом порядке, не вперемешку, а по очереди. Если нужно установить какую-то зависимость вычислений в этих прерываниях, заведите нужные флаги, чтобы их синхронизировать или задавать последовательность вычислений, сигнализировать о готовности данных, и т.п. ... Quote Share this post Link to post Share on other sites More sharing options...
Дерищев 1 April 22 Posted April 22 · Report post 8 минут назад, r_dot сказал: Ну так сделайте одноуровневые прерывания. Настройками или тупо глобальным запретом прерываний на время INT0. Ну подождёт другое прерывание немного. Ничего страшного. Ничего от этого не изменится, кроме скорости реакции на прерывание. В сумме-то время вычислений всё равно то же самое будет. Только в другом порядке, не вперемешку, а по очереди. Спасибо! Так оно уже и сделано, работает. Пугает другое, что я сумел зацепиться за верхушку айсберга, а под водой скрываются фундаментальные ошибки, которые могут выстрелить опять. Пытаюсь через неработоспособность двухуровневых прерываний докопаться до предполагаемых ошибок. Quote Share this post Link to post Share on other sites More sharing options...
r_dot 41 April 22 Posted April 22 · Report post 21 минуту назад, Дерищев сказал: под водой скрываются фундаментальные ошибки Когда пишешь на ЯВУ, да с использованием чужих библиотек, ещё не то может вылезти. Когда пишу, всегда представляю, как это будет выглядеть на низком уровне. Не ленюсь и ассемблерный текст иногда посмотреть. Ну а последовательность вычислений и распределение прерываний по времени, вообще на бумажке сначала рисую. Прикинуть времена их выполнения, посмотреть, сколько и в какие моменты остаётся на основной цикл... Рекомендую. А многоуровневые прерывания - они для абсолютно независимых потоков применяются. Особого смысла в них нет - общее время счёта всё равно такое же. А вот на "затыки" и нехватку производительности наступить - запросто. В коде этого ж не видно. Типовое - пропуск прерываний. Это можно очень долго искать. Quote Share this post Link to post Share on other sites More sharing options...
Sergey_Aleksandrovi4 4 April 23 Posted April 23 · Report post GI_enable// Запрет глобального прерывания (*ptr)(); // Индексный вызов обработчика, косвенно, через адрес в указателе GI_disable // Разрешение глобального прерывания enable - запрет; disable - разрешение. Где ошибка: в тексте комментариев или в логике? GI_enable во внешнем коде как и когда вызывается? Quote Share this post Link to post Share on other sites More sharing options...
Дерищев 1 April 23 Posted April 23 · Report post 50 минут назад, Sergey_Aleksandrovi4 сказал: GI_enable// Запрет глобального прерывания (*ptr)(); // Индексный вызов обработчика, косвенно, через адрес в указателе GI_disable // Разрешение глобального прерывания enable - запрет; disable - разрешение. Где ошибка: в тексте комментариев или в логике? GI_enable во внешнем коде как и когда вызывается? Ошибка в комментарии - Разрешаем глобальное прерывание, передаём управление обработчику, запрещаем прерывание. Quote Share this post Link to post Share on other sites More sharing options...
Sergey_Aleksandrovi4 4 April 23 Posted April 23 · Report post У вас система довольно продолжительно время находится в состоянии запрета прерываний: * сохранение регистров в стек прологе обработчика прерывания INT0 (пролог формируется компилятором) * ручное сохранение регистров в стек * перебор массива указателей * повторный перебор массива указателей * ручное восстановление регистров из стека * восстановление регистров из стека в эпилоге обработчика INT0 ... спустя какое-то время ... где-то во внешнем коде вы разрешаете прерывания GI_enable Если на протяжении выполнения этого кода случится 2 и более прерываний от одного источника, обработано будет только лишь последнее. Т.е. своего рода критическая секция с большим и недетерминированным временем. Quote Share this post Link to post Share on other sites More sharing options...