|
|
  |
STM32F407 + прерывание + время реакции, Меняется время реакции на внешнее событие |
|
|
|
Jul 29 2015, 15:54
|
Местный
  
Группа: Участник
Сообщений: 291
Регистрация: 11-04-14
Из: Саратов
Пользователь №: 81 335

|
Цитата(Golikov A. @ Jul 28 2015, 18:45)  вы в какой среде пишите? Если в Keil, то для переноса функций в РАМ, вам надо правой кнопкой в С файл с функциями тыкнуть, и сказать чтобы они запустились из РАМ, все остальное сделает кеил за вас сам. Регистры FPU надо сохранять в стэк, только если в прерываниях вы будите использовать модуль FPU, если нет, то пусть они лежат где лежать, и вход будет быстрее в прерывание и проблем не будет. Идея с ДМА вам видится не верно. Простите за долгое отсутствие - текучка и изучение мануала по наводке jcxz. Оказывается, я многое упустил в своё время... Среда у меня, к сожалению, CooCox. Keil прекрасная штука, но мне не по карману, а программа слишком велика для бесплатной версии. В прерываниях FPU нигде не используется, только в фоне. Ищу способ запретить сохранять FPU регистры в стек - а их там 32, если не ошибаюсь. Если даже по 1 такту на каждого - это уже масштаб моих флуктуаций! Идея с DMA мною не отброшена. Я её проработаю, но позже. А пока мне надо научиться отключать Lazy context save - я даже это ещё не могу заставить сделать свой кокос. Цитата(bugdesigner @ Jul 28 2015, 19:26)  А можно одно уточнение? У Вас таймер считает импульсы 84 МГц без прескалера? Я правильно понял? Не могли бы Вы показать в студию код обработчика прерывания и код инициализации таймера? Может что-то прояснится... И последнее, я присоединюсь к мнению коллег насчёт дернуть ногой при входе в обработчик. Может всё же какая-то логическая ошибка закралась. Да, таймер считает без прескалера. Инициализируеется так: Код /*------------------Инициализация TIM6 - строчный таймер-*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); //включаем тактирование //Частота шины APB1 42 МГц, на таймер - 84 Мгц TIM6->PSC = 0; //Настройка предделителя на 84 "такта" в микросекунду TIM6->ARR = 10080; //10080 период - почти две строки 120 мкс 5600 TIM6->DIER &= ~TIM_DIER_UIE; //запрет прерывания от таймера TIM6->CR1 |= TIM_CR1_CEN; //включаем таймер - пусть молотит непрерывно // прерывания от TIM6 не будет //TIM6_CNT — текущее значение счётчика для чтения/записи; Описал максимально подробно. Из обработчика приведу фрагмент, который, собственно и занимается выводом телевизионной строки Код Tim6_Count = TIM6->CNT; //длительность предыдущей строки TIM6->CNT = 0; //начало измерения строк - сбрасываем таймер //подготовимся к скоростному выводу данных Td = Tdelt; //1 шаг = 10 тактов TIM6 - шаг Ts = T_Nath - Td;//начальная задержка 10,5 мкс Kt = KolBytStr; //число байт в строке //Начали вывод активной строки. Пока не кончится активная строка, не выйдем отсюда if (Zerkalo) { //вывод "зеркально" - со старшего полубайта последнего байта массива строки Video_Adres_Th0 = (Nstr - NomerNath) * KolBytStr + KolBytStr + Video_Adres; //адрес первого байта //это адрес текущих 2 точек вывода - последний байт строки while (TIM6->CNT < Ts)continue;//ждём момента для вывода 1 точки GPIO_SetBits(PORT_OUT1_ON_OSD, OUT1_ON_OSD);//вкл. сигнала видео Vt = (uint32_t)(*--Video_Adres_Th0) << SDWIG_RGBT; //первые две точки while (Kt--) { Vt1 = Vt; Vt = Vt >> 4; //спустим старший полубайт Ts += Td; //время следующей точки while (TIM6->CNT < Ts) continue; //ждём момента для вывода точки PORT_RGBT->ODR = Vt; //вывод старшего полубайта Ts += Td; //время следующей четной точки Vt = (uint32_t)(*--Video_Adres_Th0) << SDWIG_RGBT; //следующий байт на разложение на точки while (TIM6->CNT < Ts)continue; //ждём момента для вывода точки PORT_RGBT->ODR = Vt1; //вывод младшего полубайта } } else { //вывод "прямо" Поясню, что строка, где "//ждём момента для вывода 1 точки" это и есть интервал от начала прерывания до начала вывода. Вот он то и "плавает". Кстати следующая строка и есть ваше "дернуть ногой".
Сообщение отредактировал IgorKossak - Jul 29 2015, 17:16
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!
|
|
|
|
|
Jul 29 2015, 17:03
|
Местный
  
Группа: Участник
Сообщений: 291
Регистрация: 11-04-14
Из: Саратов
Пользователь №: 81 335

|
Цитата(Сергей Борщ @ Jul 29 2015, 10:38)  Ой, не смешите. Завести массив в ОЗУ, неспешно прописать в него состояния порта через одинаковые промежутки времени, таймер настроить... Прекрасные времянки получаются. Конечно! Если удастся уменьшить флуктуации до 4 тактов, их уже никто не заметит при 168 МГц. А вот с ОЗУ напряжёнка: Там две страницы видеополя находятся - формируемая и отображаемая. Так что особо не разбежишься.
|
|
|
|
|
Jul 30 2015, 03:15
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(ШСА @ Jul 29 2015, 21:54)  В прерываниях FPU нигде не используется, только в фоне. Ищу способ запретить сохранять FPU регистры в стек - а их там 32, если не ошибаюсь. Если даже по 1 такту на каждого - это уже масштаб моих флуктуаций! Идея с DMA мною не отброшена. Я её проработаю, но позже. А пока мне надо научиться отключать Lazy context save - я даже это ещё не могу заставить сделать свой кокос. Вы как-то странно мануал читаете.... Lazy context save Вам отключать не надо, он никак не увеличивает время входа, а наоборот - предназначен для уменьшения времени входа в ISR (резервирует место в стеке, но не пишет). Но так как у Вас FPU в фоне используется, а где-то не используется, то из-за Lazy context save у Вас в этих местах и будет разница во времени входа (но место в стеке при стэкинге всё равно будет выделяться одинаково!). А вот если Вы, как я советовал, вообще отключите автоматическое сохранение контекста FPU при входе в ISR, то всегда на стек будет сохраняться как в M3 только 8 регистров. Это можно сделать, если в ISR-ах у Вас не используется FPU. Но если есть операционка и FPU используется в разных задачах, то в переключателе задач ОС (программном) надо сделать сохранение контектса FPU ручками. У нас в наших изделиях я так и делаю, но не для уменьшения времени входа в ISR, а для экономии стеков задач (делаю FPCCR = 0, а потом сохраняю или не сохраняю контекст FPU в переключателе задач ручками, в зависимости от того - использует данная задача FPU или нет). И как это сделать - я Вам уже выше писал: FPCCR = 0. Вы как-то читаете выборочно  И от компилятора это никак не зависит, это Вам надо открыть мануал на ядро M4 и найти там FPCCR.
|
|
|
|
|
Jul 30 2015, 05:09
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(ШСА @ Jul 29 2015, 23:03)  А вот с ОЗУ напряжёнка: Там две страницы видеополя находятся - формируемая и отображаемая. Так что особо не разбежишься. Советую всё-таки открыть мануал на используемый процессор и поискать там что-нить типа Quad-SPI или SDIO. Они позволяют до 4 линий параллельно выводить. Тогда возможно каждый байт памяти будет использоваться полностью, а не одна тетрада. Да и загрузка шины при DMA-передаче на такой интерфейс будет ниже чем при передаче DMA->GPIO. По крайней мере на Tiva (TM4C129), который я сейчас использую, связка Quad-SSI+DMA идеальна для Вашей задачи. Даже если Вы не будете использовать DMA, то лучше использовать Quad-SPI чем тот ногодрыг, что Вы сейчас пытаетесь сделать. О ногодрыге вообще стоит забывать сразу после перехода на ARM. Оставить его AVR-щикам. SPI Вам жёстко даст ту времянку, которую Вы запрограммируете ему, вместо болтанки с программным таймером и ногодрыгом.
|
|
|
|
|
Jul 30 2015, 17:13
|
Местный
  
Группа: Участник
Сообщений: 291
Регистрация: 11-04-14
Из: Саратов
Пользователь №: 81 335

|
Наконец сегодня дошли руки проверить хоть какие-то гипотезы. Собственно, рабочих гипотез осталось две. Начну с первой. jcxz предположил, что причиной влияния фоновой программы на время реакции обработчика и является сохранение в стеке регистров FPU и предложил его запретить. Такой запрет не помешал бы работе системы, поскольку FPU в обработчиках прерываний не используется, а значит и состояние FPU от прерывания не изменилось бы. В STM32F4 это делается просто: FPU->FPCCR &= ~((1U << FPU_FPCCR_ASPEN_Pos) | (1U << FPU_FPCCR_LSPEN_Pos)); В результате явление не исчезло, ну существенно уменьшилось. Т.е влияние фоновой программы на прерывания осталось, но флуктуации времени запуска обработчика прерываний снизились не менее чем в два раза. Мораль: Количество сохраняемых в стеке регистров ЦП при прерывании зависит не от того, выполняло или нет в этот момент FPU какую нибудь команду, а от того, что записано в регистре CONTROL с помощью FPU->FPCCR. Поэтому запрещая сохранение регистров FPU можно изменить среднее время реакции обработчиков на внешние события и изменить интенсивность использования стека. Но к флуктуациям времени вызова обработчика в зависимости от команды, выполняемой в этот момент фоновой программой, это не имеет отношения. Осталась вторая гипотеза. scifi полагал, что время реакции обработчика зависит от длины кэша команд МК. Т.е. механизм такой: если фоновая программа выполняет длительный короткий цикл, в течение которого происходит несколько прерываний, то после первого прерывания из кэша не вытесняется начало обработчика прерывания и все последующие прерывания происходят быстрее. Чтобы проверить эту идею нужно было очистить кэш команд перед выходом из обработчика. Я не нашёл (да пока и не искал) такой команды, но чтобы проверить эту идею в фоновой программе внутри короткого цикла вставил вызов небольшой функции, которая ничего полезного не делала, но и ничего не портила. Фактически, цикл перестал быть коротким. Результат - эти флуктуации исчезли! Мораль сногсшибательная: кэш команд в STM32F4, который так здорово "разгоняет" ЦП, может быть причиной непредсказуемых флуктуаций времени реакции обработчиков на внешние события. Причём флуктуаций весьма чувствительных - до 400 нс, когда используется FPU и включено сохранение его регистров в стеке (это происходит по умолчанию при включении FPU), и когда различные прерывания конкурируют между собой. А управлять этой непредсказуемостью будет фоновая программа! Так что мой обработчик придётся выбросить. Всем, кто принял участие в этом мозговом штурме ОГРОМНОЕ СПАСИБО! И, конечно, jcxz и scifi от меня особая благодарность, за активизацию моих мозгов. Цитата(jcxz @ Jul 30 2015, 08:09)  Советую поискать там что-нить типа Quad-SPI или SDIO. Они позволяют до 4 линий параллельно выводить. У STM32F407 есть одна неприятная особенность - пины SDIO пересекаются с пинами видеокамеры DCMI. Придётся выбирать. А Quad-SPI там нет.
Сообщение отредактировал ШСА - Jul 30 2015, 19:02
|
|
|
|
|
Jul 31 2015, 04:46
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(ШСА @ Jul 30 2015, 20:13)  Мораль: Количество сохраняемых в стеке регистров ЦП при прерывании зависит не от того, выполняло или нет в этот момент FPU какую нибудь команду, а от того, что записано в регистре CONTROL с помощью FPU->FPCCR. Вы хотя бы задумались. А как может быть по другому?! Какая разница где именно в фоновой задече используется FPU? Если имеется шанс запортить используемые регистры, то их надо сохранять! Здесь на крайняк, может компилятор грамотно обсчитывать имеется ли в Ваших прерываниях работа с FPU. Но учитывая как работает компилятор, и этого не получается. Например Вы можете вызвать из прерывания ф-цию, которая использует FPU. Или Вы можете написать обработчик на ассемблере и компилятор даже знать не будет о том, что Вы в прерывании используете FPU. Поэтому действия разработчиков процессора единственно верны. Цитата Мораль сногсшибательная: кэш команд в STM32F4, который так здорово "разгоняет" ЦП, может быть причиной непредсказуемых флуктуаций времени реакции обработчиков на внешние события. Причём флуктуаций весьма чувствительных - до 400 нс, когда используется FPU и включено сохранение его регистров в стеке (это происходит по умолчанию при включении FPU), и когда различные прерывания конкурируют между собой. А управлять этой непредсказуемостью будет фоновая программа! А здесь Вы просто капитан очевидность! Именно за счёт этого и получен выигрыш в быстродействии. Я вообще не понимаю что Вас удивляет? Так работают все процы CISC, в частности. И совершенно очевидно, что в Cortex-M7 джитер будет ещё выше. Он будет в любом процессоре. В процах без кэша он будет меняться на разницу в времени исполнения команд, которые Вы используете в фоновой задаче. Например в AVR, при применении умножения +1 такт. Поэтому Вам несколько раз настойчиво советуют использовать аппаратные средства либо самого процессора либо внешнего устройства.
|
|
|
|
|
Jul 31 2015, 07:04
|
Местный
  
Группа: Участник
Сообщений: 291
Регистрация: 11-04-14
Из: Саратов
Пользователь №: 81 335

|
Цитата(SasaVitebsk @ Jul 31 2015, 07:46)  Поэтому действия разработчиков процессора единственно верны. Именно за счёт этого и получен выигрыш в быстродействии. Я вообще не понимаю что Вас удивляет? А кто говорит, что разработчики из ST что-то сделали неправильно? STM32F4 - серия шикарных микроконтроллеров! Но, как всё шикарное, требуют аккуратного обращения. А удивляет меня не задержка вызова прерываний, и не джиттер при разборках обработчиков между собой. Это всё общеизвестно. А вот то, что на время вызова обработчика может влиять содержимое фоновой программы - для меня открытие. Я с таким никогда не встречался и не читал о таком. Результат проведённого исследования - понимание механизма взаимовлияния фона на реактивность системы, которое позволит осознаннее выбирать программные средства. Правильно заметил jcxz, что от AVR-ные подходы для ARM-ов не всегда годятся. И понять это явление помогло именно включённое FPU, которое на порядок увеличило этот эффект, и заставило разбираться до конца.
|
|
|
|
|
Jul 31 2015, 10:05
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(scifi @ Jul 31 2015, 14:05)  Возьмите многоядерный МК типа LPC4370. Там 3 процессора. Один из них можно натравить на узкую задачу с жёсткой времянкой - и дело в шляпе :-) Кстати - всё хотелось узнать поподробнее про эти многоядерные LPC: а как там память программ и данных организована? Общая или для каждого ядра своя? И как шины к памяти проложены? Можно-ли написать ПО так, чтобы каждое ядро работало со своим набором регионов памяти/шин доступа к ним, оставив только один общий регион для межпроцессорного обмена? Если всё-таки там ядра конкурируют за какие-либо ресурсы (регионы памяти, шины), или программа так написана, что имеется много конфликтов по ресурсам, то ядра будут влиять друг на друга (на скорость выполнения кода друг друга).
|
|
|
|
|
Jul 31 2015, 19:00
|
Местный
  
Группа: Участник
Сообщений: 291
Регистрация: 11-04-14
Из: Саратов
Пользователь №: 81 335

|
Цитата(Golikov A. @ Jul 31 2015, 11:33)  А обдумав этот факт становится понятно что вид программы с точки зрения переходов, должен влиять на времянку выполнения программы. Согласен. И хотя вы все будете смеяться, но я опять чего-то недопонимаю. Чтобы не связываться с DMA, решил попытаться использовать захват сигнала через таймер. Но перед этим, чтобы быть уверенным в конечном результате, решил проверить экспериментально верность нашей "гениальной теории". Способ мне пришёл в голову очень простой: Вызов обработчика происходит по переднему фронту синхроимпульса, и пусть флуктуации вызова обработчика достигают 0,2 мкс. Поскольку длительность этого внешнего синхроимпульса равна 4,7 мкс, то очевидно, что его задний фронт будет гарантированно в то время, когда обработчик уже будет вовсю работать. Значит, если вставить цикл ожидания окончания синхроимпульса и сбрасывать таймер после этого, я должен получить гарантированное избавление от зависимости от времени вызова обработчика, а значит, и от содержимого фоновой программы. Но эксперимент (критерий истины) показал, что эффект влияния фоновой программы остался! Хотя, величина флуктуаций фазы первого выводимого импульса стала значительно меньше, чем раньше (на глазок - в 2,5 - 3 раза). Цитата(scifi @ Jul 31 2015, 13:26)  Конкретно в LPC4370 нет наботной флеши, А что это такое "наботная?"
Сообщение отредактировал ШСА - Jul 31 2015, 18:31
|
|
|
|
|
  |
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|