Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32F407 + прерывание + время реакции
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Страницы: 1, 2, 3
ШСА
Дорогие коллеги!
Сталкивался ли кто нибудь с такой тонкой и непонятной мне проблемой - значительным изменением времени входа в обработчик прерывания по внешнему событию в зависимости от команд, выполняемых в этот момент в основной программе?
Конкретная ситуация: В микроконтроллере STM32F407 в main-е крутится некая большая программа. Когда наступает внешнее событие, а именно - отрицательный фронт на EXTI_Line1, вызывается обработчик прерывания void EXTI1_IRQHandler(void). В нём обнуляется таймер TIM6, тактируемый частотой 84 МГц, и начинается вывод информации в виде набора импульсов, привязанных к значениям TIM6. Когда вывод закончен - выходим из обработчика (TIM6 оставляем молотить впустую).
Замечено следующее: Время от момента внешнего события до появления первого импульса на выходе нестабильно и меняется (по осциллографу) более чем на 0,2 мкс при тактировании ядра 168 МГц. Иными словами, время вызова обработчика может увеличиваться (или уменьшаться) аж на 34 такта! Причём меняется оно в зависимости от кода, выполняемого в данный момент в main-е. Самое большое изменение этого времени (в сторону уменьшения) происходит при выполнении (в main-е) цикла очистки области памяти:
for(i = 0; i < 60768; i++) *j++ = Сonst;

А согласно описанию на Cortex-3 время вызова обработчика прерывания составляет от 6 до 11 тактов ядра. И уж никак не должно зависеть от содержания прерываемой программы (DMA не используется)! wacko.gif

В чём причина этого явления и как с ним бороться?
Буду признателен за любые идеи или информацию.
aaarrr
Цитата(ШСА @ Jul 26 2015, 22:07) *
...никак не должно зависеть от содержания прерываемой программы (DMA не используется)! wacko.gif

В некоторой степени все же зависит.

Цитата(ШСА @ Jul 26 2015, 22:07) *
В чём причина этого явления и как с ним бороться?
Буду признателен за любые идеи или информацию.

Непредсказуемую задержку может вносить работа кэш контроллера флеш-памяти.
ViKo
Может, нестабильную задержку создает перезапуск таймера, а не вход в прерывание. Задайте в прерывании дергание ножкой порта, для проверки.
ШСА
Цитата(aaarrr @ Jul 26 2015, 22:29) *
В некоторой степени все же зависит.


Непредсказуемую задержку может вносить работа кэш контроллера флеш-памяти.


Интересная мысль - а сколько может вносить, и чем можно на эту задержку влиять?
Вообще в STM32F4, если верить описанию, время отклика на событие прерывания составляет 12 тактов - если прерывается фоновая программа, 6 тактов - если прерывается другое низкоприоритетное прерывание, 18 тактов - если прерывание произошло в момент выхода из другого прерывания (выход из прерывания занимает 12 тактов). В итоге имеем максимально возможный шум (или биения) на выходе до: 18 - 6 = 12 тактов. Для тактовой частоты 168 МГц это составит 0,072 мкс. И это максимум! А у меня в реале - 0,2 мкс. Причём даже с отключёнными другими прерываниями.
Что касается зависимости от фоновой программы. Учитывая, что в STM32F407 организован аппаратный конвейер, влияние фоновой программы на обработчик, вернее на время его вызова, не должно быть вообще! А у меня оно есть, да ещё какое сильное!
Я уж даже думал, что столь короткий цикл в фоновой программе так меняет ток потребления МК, что не выдерживает стабилизатор питания, и поэтому сбивается PLL в STM32F4. Но это не подтвердилось - обвешанный большими ёмкостями МК ведёт себя точно также, как и в стандартном включении.
Огурцов
а если умножить на 5 тактов ожидания, то получится даже хуже вашего, или ваше в среднем
ШСА
Цитата(ViKo @ Jul 27 2015, 06:03) *
Может, нестабильную задержку создает перезапуск таймера, а не вход в прерывание. Задайте в прерывании дергание ножкой порта, для проверки.


Нет, это исключено. Период внешнего события - 64 мкс, а период перезапуска таймера вдвое больше (я по нему идентифицирую исчезновение внешнего сигнала). Сброс таймера в единственном месте - вторая команда обработчика прерывания.
Да и потом - даже если таймер что-то там делает, влияние на задержку реакции оказывает именно содержание фоновой программы. Это проверено: когда я закомментировал цикл очистки памяти в фоновой программе, это явление исчезло!

Цитата(Огурцов @ Jul 27 2015, 08:53) *
а если умножить на 5 тактов ожидания, то получится даже хуже вашего, или ваше в среднем


Не понял кто кого ждёт.
Все команды фоновой программы занимают по 1 такту. DMA нет. Реакция на внешнее событие определяется укладкой в стек информации о возврате и занимает от 6 до 18 тактов. Я отключал все другие прерывания, в этом случае реакция должна быть максимально стабильной - 12-13 тактов. Т.е. нестабильность реакции - 1 такт! А у меня в реальности ничего не меняется - фоновая программа непостижимым образом изменяет время реакции на прерывания до 34 тактов!


Цитата(Огурцов @ Jul 27 2015, 08:53) *
а если умножить на 5 тактов ожидания, то получится даже хуже вашего, или ваше в среднем


Не понял кто кого ждёт.
Все команды фоновой программы занимают по 1 такту. DMA нет. Реакция на внешнее событие определяется укладкой в стек информации о возврате и занимает от 6 до 18 тактов. Я отключал все другие прерывания, в этом случае реакция должна быть максимально стабильной - 12-13 тактов. Т.е. нестабильность реакции - 1 такт! А у меня в реальности ничего не меняется - фоновая программа непостижимым образом изменяет время реакции на прерывания до 34 тактов!
aaarrr
Попробуйте сделать следующее:
1. Внешнее прерывание замените на прерывание от таймера с высокой тактовой частотой (84МГц). При входе в прерывание считывайте CNT. Так получите значительно более точное измерение времени входа.
2. Перенесите обработчик в RAM, сравните результаты.
ШСА
Цитата(aaarrr @ Jul 27 2015, 09:27) *
Попробуйте сделать следующее:
1. Внешнее прерывание замените на прерывание от таймера с высокой тактовой частотой (84МГц). При входе в прерывание считывайте CNT. Так получите значительно более точное измерение времени входа.
2. Перенесите обработчик в RAM, сравните результаты.


Маленькое уточнение - эти два пункта надо сделать одновременно или по одному?
Пункт 1, я понял, нужен, чтобы исключить биения внешней частоты и источника тактирования МК. Я это попробую сегодня сделать. А вот пункт 2 - поясните. Я такого никогда не делал и не знаю (пока) как это сделать. Но главное - зачем, ведь в описании на Cortex, насколько я помню, программа из RAM выполняется медленнее, чем из FLASH. А у меня и так МК едва успевает выполнять нужную работу, причём только после оптимизации компилятором. Если замедлить работу обработчика, работа программы развалится и уже ничего невозможно будет измерить.
scifi
Вот тут расписано про задержку на входе в обработчик прерывания у Cortex-M3 и Cortex-M4: "between eight and 32 cycles if the floating point context in a Cortex-M4 with FPU is stacked immediately".
Кстати, у вас нигде прерывания не запрещаются? Атомарные операции и всё такое.
aaarrr
Интересно сравнить результаты при работе из флеш и RAM.
Про пункты:
1. Чтобы исключить осциллограф прежде всего sm.gif
2. Программа из RAM выполняется примерно с той же скоростью, что из "ускоренной" флеш. Немного медленнее, зато гораздо более предсказуемо.
scifi
Цитата(aaarrr @ Jul 27 2015, 09:52) *
2. Программа из RAM выполняется примерно с той же скоростью, что из "ускоренной" флеш. Немного медленнее, зато гораздо более предсказуемо.

Всё не так просто. Можно разогнать программу в ОЗУ. Для этого код должен сидеть в куске ОЗУ 112 КБ (начинается с адреса 0x20000000), подключенном к I-bus, ОЗУ надо замапить на адрес 0, а данные расположить в CCM (64 КБ). В этом случае всё должно работать с нулевыми задержками.
aaarrr
Цитата(scifi @ Jul 27 2015, 10:00) *
Можно разогнать программу в ОЗУ.

Можно, просто для проверки предположения о влиянии "ускорителя" флеш это не нужно.
ШСА
Цитата(scifi @ Jul 27 2015, 09:49) *
Вот тут расписано про задержку на входе в обработчик прерывания у Cortex-M3 и Cortex-M4: "between eight and 32 cycles if the floating point context in a Cortex-M4 with FPU is stacked immediately".
Кстати, у вас нигде прерывания не запрещаются? Атомарные операции и всё такое.


Спасибо за ссылочку: на первый же взгляд вижу намёк на причину - на возможные такты ожидания при обращении к слову по частям. А ведь у меня этого много. Буду разбираться.
Прерывания не запрещаются. Флаги очищаются в конце обработчика, так что вложенные прерывания исключены.
Атомарные операции... Наверное, вы правы! Для экономии памяти я сплошь работаю с невыровненными данными в ОЗУ, а ведь это может приводить к аппаратным задержкам! Это похоже на правду, ведь цикл очистки массива у меня идёт по байтовому указателю.
Timmy
Цитата(ШСА @ Jul 26 2015, 22:07) *
Дорогие коллеги!
Сталкивался ли кто нибудь с такой тонкой и непонятной мне проблемой - значительным изменением времени входа в обработчик прерывания по внешнему событию в зависимости от команд, выполняемых в этот момент в основной программе?
Конкретная ситуация: В микроконтроллере STM32F407 в main-е крутится некая большая программа. Когда наступает внешнее событие, а именно - отрицательный фронт на EXTI_Line1, вызывается обработчик прерывания void EXTI1_IRQHandler(void). В нём обнуляется таймер TIM6, тактируемый частотой 84 МГц, и начинается вывод информации в виде набора импульсов, привязанных к значениям TIM6. Когда вывод закончен - выходим из обработчика (TIM6 оставляем молотить впустую).

Если вам надо обеспечить постоянное время реакции на фронт входного сигнала, то следует защёлкнуть этот фронт в capture unit, и привязать выходные сигналы к защёлкнутому времени таким образом, чтобы процессор всегда успевал среагировать. Правда, время реакции получится по-максимуму, зато стабильно. Если нужно совсем быструю реакцию, лучше использовать FPGA.
Время входа в прерывание можно сократить, если использовать FIQ, а не IRQ, но его на большинстве кортексов нет.
Golikov A.
Цитата
Флаги очищаются в конце обработчика, так что вложенные прерывания исключены.

Вроде это как раз гарантия создания повторного прерыванияsm.gif Вложенные прерывания у вас исключает NVIC, а вот очищенный в конце флаг может вызвать прерывание повторно из-за конвейеров, нужны будут барьеры на выходе.

Опять же ускоритель флешь, если вы попадаете не на 16 на 32 битные команды то ему больше 4 команд не выбрать, а частота выборки команд у него в почти 8 раз меньше, так что скакнув прерыванием в не закишированную область, да еще и попав на 32 битную команду, у вас все здорово притормозится, а в след раз вы можете попасть уже в область выбранную и сохраненную в ускорителе, а потом вы ее можете почистить и так по кругу, так что у вас там есть где набрать нестабильность ИМХО...

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

Я могу ошибаться, но вроде как в кортексах уже NVIC, и потому деления на быстрое и медленное уже нет ни в большинстве, а во всех кортексах...
ШСА
Цитата(Timmy @ Jul 27 2015, 10:30) *
Если вам надо обеспечить постоянное время реакции на фронт входного сигнала, то следует защёлкнуть этот фронт в capture unit, и привязать выходные сигналы к защёлкнутому времени таким образом, чтобы процессор всегда успевал среагировать. Правда, время реакции получится по-максимуму, зато стабильно.


Ну, собственно, мой обработчик IRQ это и делает, только без использования capture unit. По наступлении события таймер сбрасываем, а потом, следя за его изменением, пишем в выходной порт данные из ОЗУ. Время между началом события и первым выходным импульсом = 10,5 мкс. За это время я успеваю многое пересчитать и даже запустить АЦП. Во всяком случае две трети времени от этих 10,5 мкс занимает цикл ожидания.
scifi
Цитата(ШСА @ Jul 27 2015, 11:04) *
Ну, собственно, мой обработчик IRQ это и делает, только без использования capture unit. По наступлении события таймер сбрасываем, а потом, следя за его изменением, пишем в выходной порт данные из ОЗУ. Время между началом события и первым выходным импульсом = 10,5 мкс. За это время я успеваю многое пересчитать и даже запустить АЦП. Во всяком случае две трети времени от этих 10,5 мкс занимает цикл ожидания.

А нельзя ли по событию сделать DMA для сброса таймера и запуск обработки прерывания для вычислений? У DMA джиттер поменьше должен быть.
ШСА
[quote name='Golikov A.' date='Jul 27 2015, 10:49' post='1354043']
Вроде это как раз гарантия создания повторного прерыванияsm.gif Вложенные прерывания у вас исключает NVIC, а вот очищенный в конце флаг может вызвать прерывание повторно из-за конвейеров,

Это для меня новость. NVIC не будет вызывать прерывания, пока я в обработчике не сброшу флаг своего вектора. А я это делаю в конце. Как может быть вложенный вызов?

Опять же ускоритель флешь, если вы попадаете не на 16 на 32 битные команды то ему больше 4 команд не выбрать, а частота выборки команд у него в почти 8 раз меньше, так что скакнув прерыванием в не закишированную область, да еще и попав на 32 битную команду, у вас все здорово притормозится,

Так прерывание, как непредсказуемое событие, в принципе не может быть закэшировано. Так что конвейер должен быть перезагружен. Потому у STM32F4 время реакции = 6-12 тактов. Вернее должно быть, а вот у меня бывает более 34-х!
scifi
Цитата(ШСА @ Jul 27 2015, 11:18) *
Так прерывание, как непредсказуемое событие, в принципе не может быть закэшировано.

Неправда. Если между двумя прерываниями процессор крутится в маленьком цикле (как у вас при очистке памяти), то он может не успеть выбросить из кэша код обработчика прерывания. Тогда вход в обработчик прерывания будет быстрее. Кстати, это легко проверить: на выходе из прерывания нужно сбрасывать кэш, есть для этого соответствующий регистр.
LightElf
QUOTE (ШСА @ Jul 27 2015, 11:18) *
Так прерывание, как непредсказуемое событие, в принципе не может быть закэшировано. Так что конвейер должен быть перезагружен. Потому у STM32F4 время реакции = 6-12 тактов. Вернее должно быть, а вот у меня бывает более 34-х!

Выборка 1 команды из флеш-памяти занимает 6 тактов процессора (для частоты 168МГц). При выполнении линейного куска кода флеш-акселератор скрывает эту задержку (потому что считывает по 128 бит). Когда происходит прерывание, флеш-акселератор помочь ничем не может и вы попадаете на максимальную задержку.
Для начала попробуйте перенести в ОЗУ таблицу векторов - получите экономию 5 тактов минимум.
ШСА
Цитата(scifi @ Jul 27 2015, 11:08) *
А нельзя ли по событию сделать DMA для сброса таймера и запуск обработки прерывания для вычислений? У DMA джиттер поменьше должен быть.


Интересная мысль насчёт DMA. Как-то не думал о таком варианте. Вообще я DMA как-то побаиваюсь, потому что процессор может "уснуть" неизвестно когда. Но впрочем, это мои фобии. А джиттер в реальности слишком велик (34 такта - не кот чихнул), и блохи от DMA здесь не играют роли.
Ведь главное - понять необычность ситуации: фоновая программа влияет на скорость вызова обработчика. Ведь она не привязана к внешним событиям, чего-то там себе считает и считает. А у обработчика при этом плавает время реакции! Да плавает-то как - в три раза превышает допуск!
AHTOXA
Мне кажется, что самый здравый совет был вот этот:
Цитата(ViKo @ Jul 27 2015, 08:03) *
Задайте в прерывании дергание ножкой порта, для проверки.

Сначала надо уточнить, где именно возникает задержка, а уже потом выяснять её причину.
Огурцов
Цитата(ШСА @ Jul 27 2015, 06:09) *
Не понял кто кого ждёт

флеш и кэш
выше правильное предложение - выполнять из озу, работать будет медленнее, но гораздо стабильнее
а ну да, можно загнать тактовую на 24 мгц с нулевым ожиданием и посмотреть на результат
ШСА
Цитата(scifi @ Jul 27 2015, 11:29) *
Неправда. Если между двумя прерываниями процессор крутится в маленьком цикле (как у вас при очистке памяти), то он может не успеть выбросить из кэша код обработчика прерывания. Тогда вход в обработчик прерывания будет быстрее. Кстати, это легко проверить: на выходе из прерывания нужно сбрасывать кэш, есть для этого соответствующий регистр.


Интересная мысль!!! А ведь у меня при очистке памяти время реакции именно уменьшается!
Чтобы мне не копаться, подскажите, как очистить кэш (желательно на языке СИ).
scifi
Цитата(ШСА @ Jul 27 2015, 11:40) *
Интересная мысль!!! А ведь у меня при очистке памяти время реакции именно уменьшается!
Чтобы мне не копаться, подскажите, как очистить кэш (желательно на языке СИ).

Я углядел нужные биты в регистре FLASH_ACR. А дальше сами читайте, там вроде бы понятно написано.
ШСА
Цитата(Огурцов @ Jul 27 2015, 11:36) *
флеш и кэш
выше правильное предложение - выполнять из озу, работать будет медленнее, но гораздо стабильнее
а ну да, можно загнать тактовую на 24 мгц с нулевым ожиданием и посмотреть на результат


24 МГц можно попробовать, чтобы не переделывать программу. С единственной целью убедиться в исчезновении этого явления. Жаль только, что будет много паяльных работ, т.к. тактовая частота МК в моей системе имеет критическое значение.
Golikov A.
1. Можно отключить флэщ акселератор и тогда время будет стабильное, медленное, но стабильное.

2. По поводу
Цитата
так что конвейер должен быть перезагружен. Потому у STM32F4 время реакции = 6-12 тактов.
Там не только в конвейере дело, а еще в сохранении контекста

Цитата
Это для меня новость. NVIC не будет вызывать прерывания, пока я в обработчике не сброшу флаг своего вектора. А я это делаю в конце. Как может быть вложенный вызов?

3.1 НВИК не будет повторно вызывать прерывание даже если вы сбросите в начале и оно опять случиться, пока не выйдете из этого прерывания повторный вызов того же приоритета не проихойдет
3.2. А вот сбрасывая флаг в конце вы имеете шанс выйти из прерывания до того как флаг реально сброситься, там что-то с конвейерами и перестановкой команд, и выйдя из прерывания НВИК ловит не сброшенный флаг и повторно входит в прерывание.
Так что вложенного вызова не будет, а вот повторный вполне может быть.
ШСА
Цитата(Golikov A. @ Jul 27 2015, 12:15) *
1. Можно отключить флэщ акселератор и тогда время будет стабильное, медленное, но стабильное.

2. По поводу Там не только в конвейере дело, а еще в сохранении контекста


3.1 НВИК не будет повторно вызывать прерывание даже если вы сбросите в начале и оно опять случиться, пока не выйдете из этого прерывания повторный вызов того же приоритета не проихойдет
3.2. А вот сбрасывая флаг в конце вы имеете шанс выйти из прерывания до того как флаг реально сброситься, там что-то с конвейерами и перестановкой команд, и выйдя из прерывания НВИК ловит не сброшенный флаг и повторно входит в прерывание.
Так что вложенного вызова не будет, а вот повторный вполне может быть.


Это важно, я это изучу. Спасибо.
jcxz
Выключите сохранение контекста FPU при стэкинге (и резервирование места для него): FPCCR = 0;

Цитата(ШСА @ Jul 27 2015, 14:40) *
Интересная мысль!!! А ведь у меня при очистке памяти время реакции именно уменьшается!
Чтобы мне не копаться, подскажите, как очистить кэш (желательно на языке СИ).

Зачем его чистить? Перенесите ISR в ОЗУ как уже советовали. Причём начало его выровняйте на адрес, кратный размеру строки выборки команд CPU
(вроде STM407 выбирает команды строками по 128байт). И таблицу векторов - тоже в ОЗУ. Лучше - в CCM.

Но самое правильное решение - конечно использовать DMA, выставив ему приоритет доступа к шине выше чем CPU.
aaarrr
Цитата(jcxz @ Jul 28 2015, 05:43) *
Перенесите ISR в ОЗУ как уже советовали. Причём начало его выровняйте на адрес, кратный размеру строки выборки команд CPU
(вроде STM407 выбирает команды строками по 128байт).

Для ОЗУ выравнивать ничего не нужно - выборка строками по 128 бит идет только из флеш.
zltigo
QUOTE (Golikov A. @ Jul 27 2015, 10:49) *
потому деления на быстрое и медленное уже нет ни в большинстве, а во всех кортексах...

Подсказка - "все кортексы" НЕ кончаются на букве М sm.gif sm.gif

Golikov A.
Справедливо...
ШСА
Цитата(jcxz @ Jul 28 2015, 05:43) *
Выключите сохранение контекста FPU при стэкинге (и резервирование места для него): FPCCR = 0;

Зачем его чистить? Перенесите ISR в ОЗУ как уже советовали. Причём начало его выровняйте на адрес, кратный размеру строки выборки команд CPU
(вроде STM407 выбирает команды строками по 128байт). И таблицу векторов - тоже в ОЗУ. Лучше - в CCM.

Но самое правильное решение - конечно использовать DMA, выставив ему приоритет доступа к шине выше чем CPU.


Вы меня своим заявлением слегка контузили.
Я не знаю что такое контекст FPU и стэкинг. Аппаратный FPU (Floating Point Unit) у меня включён, и выключать его нельзя. Это всё, что я знаю про FPU.
Использовать DMA - что я выиграю, и главное - сколько? Ведь фаза выходного импульса плавает аж на 34 такта!
Перенести ISR в ОЗУ мне кажется здравой идеей, сейчас я продумываю как её осуществить, чтобы себе не сильно напортить.

Ребята! Давайте договоримся - если у кого есть идея, излагайте в таком порядке:
1. Гипотеза, объясняющая природу этого явления, а именно - каким образом фоновая программа может влиять на время реакции на внешнее событие, да ещё в таких масштабах;
2. Способ или механизм исключения или компенсации этих задержек;
3. Конкретные рекомендации;
В противном случае я вынужден по вашим рекомендациям гадать, в чём же заключается идея. А это плохо потому, что я не настолько глубоко знаю STM32F4, чтобы по намёку понять всё, что имелось в виду.
Кстати, если есть примеры как оформить ISR в ОЗУ, а тем более таблицу векторов в CCM, дайте ссылочку, чтобы мне поменьше пришлось наступать на грабли.
scifi
Цитата(ШСА @ Jul 28 2015, 13:09) *
Ведь фаза выходного импульса плавает аж на 34 такта!

А это хорошо или плохо? Сколько надо? 10 хватит? А 30?
Телепаты в отпуске.

Цитата(ШСА @ Jul 28 2015, 13:09) *
Использовать DMA - что я выиграю, и главное - сколько?

Не очень понятно, что вы там ваяете, но суть предложения такова: сделать так, чтобы фазу формировал контроллер DMA, а не процессор с его неопределённым временем входа в обработчик прерывания. В общем, надеемся, и не без оснований, что у DMA джиттер поменьше, чем 34 такта.
Предвидя вопрос "как?", отвечаю: только вам ведомо, как работает эта ваша адская машина.
ViKo
Цитата(ШСА @ Jul 28 2015, 13:09) *
Ребята! Давайте договоримся - если у кого есть идея, излагайте в таком порядке:
1. Гипотеза, объясняющая природу этого явления...

А вы так и не попробовали дергать ногой в прерывании? Гипотеза в том, что у таймера есть предделитель и счетчик, которые (оба или один из них - вам читать в руководстве) могут перегружаться сразу, а могут, только, когда досчитают до конца. У вас как?
ШСА
Цитата(scifi @ Jul 28 2015, 13:37) *
А это хорошо или плохо? Сколько надо? 10 хватит? А 30?


Чтобы у вас не рисовались монстры в голове, расскажу, что это за машина. Эта штучка получает изображение от видеокамеры. Фоновая программа, никуда не спеша, рассматривает его, выявляет в нём разные особенности, а потом формирует своё изображение, которое, по её мнению, и должно появиться на экране. А выводом изображения как раз и занимается мой обработчик. И он очень спешит - его работа запускается телевизионными синхроимпульсами. Сама по себе начальная задержка не критична, а вот её "плавание" недопустимо. Допуск на фазовый шум планировался на уровне 4 тактов. По результатам испытаний стало ясно, что 10, от силы 12 тактов ещё можно терпеть. В процессе борьбы с шумами мы и выявили эту странную взаимосвязь фоновой программы и обработчика. Кстати, стало ясно, что и происхождение других мелких шумов, скорее всего имеют ту же природу. А вот с природой-то и пошли непонятки.


Цитата(ViKo @ Jul 28 2015, 14:33) *
А вы так и не попробовали дергать ногой в прерывании? Гипотеза в том, что у таймера есть предделитель и счетчик, которые (оба или один из них - вам читать в руководстве) могут перегружаться сразу, а могут, только, когда досчитают до конца. У вас как?


Мой обработчик, сообразуясь со таймером, пересылает данные на выходной порт. Так что дёрганья ног здесь более чем достаточно. Таймер сбрасывается в самом начале прерывания, чтобы "синхронизироваться" с внешним событием (в этом-то и ахиллесова пята алгоритма). До следующего события таймер перезагрузиться не успеет, а если это случилось, значит входной сигнал исчез, и можно "уснуть". Так что во время работы таймер никогда не может перезагружаться сам.
scifi
Цитата(ViKo @ Jul 28 2015, 14:33) *
А вы так и не попробовали...

У меня тоже некоторый осадок остался по поводу "не пробовали". Тут уже несколько предложений было, которые можно было бы проверить, но, похоже, аффтар просто мотает на ус и ждёт, когда волшебник в голубом вертолёте привезёт готовое решение...
А если пробовали, но результат отрицательный, всё равно озвучьте - интересно ведь.
jcxz
Цитата(ШСА @ Jul 28 2015, 16:09) *
Я не знаю что такое контекст FPU и стэкинг. Аппаратный FPU (Floating Point Unit) у меня включён, и выключать его нельзя. Это всё, что я знаю про FPU.

Откройте мануал на CortexM, там есть описание что такое stacking. Это собственно - сохранение некоторых регистров CPU при входе в прерывание.
M4, в отличие от M3, умеет сохранять (или резервировать место для сохранения) также регистры FPU. Если у Вас выключено Lazy context save или МК посчитает что по каким-то причинам контекст
FPU тоже надо сохранить, то в стек вместо 8 слов запишется гораздо больше.
Т.е. - если как я понимаю Вы где-то в фоне использовали FPU, то это и произойдёт.

Цитата(ШСА @ Jul 28 2015, 16:09) *
Использовать DMA - что я выиграю, и главное - сколько? Ведь фаза выходного импульса плавает аж на 34 такта!

Не знаю какой выходной импульс Вы имеете в виду, но Вашу задачу - прерывание на входной импульс и сброс таймера в обработчике этого прерывания, может сделать DMA:
делаете запуск DMA-транзакции по этому самому импульсу, этой транзакцией обнуляете таймер. Задержка запуска DMA-транзакции будет неизмеримо меньше времени реакции на прерывание.
Не знаю правда - поддерживает ли такое DMA-контроллер STM32F407? Но в LPC17xx такое возможно. STM32F407 думаю тоже должен уметь.

Цитата(ШСА @ Jul 28 2015, 18:17) *
Мой обработчик, сообразуясь со таймером, пересылает данные на выходной порт. Так что дёрганья ног здесь более чем достаточно. Таймер сбрасывается в самом начале прерывания, чтобы "синхронизироваться" с внешним событием (в этом-то и ахиллесова пята алгоритма). До следующего события таймер перезагрузиться не успеет, а если это случилось, значит входной сигнал исчез, и можно "уснуть". Так что во время работы таймер никогда не может перезагружаться сам.

Имхо - в таком случае вообще программно ничего делать не нужно. Всё должен делать DMA, получающий синхрособытия от таймера, который по этим событиям будет класть данные в порт.
ШСА
Цитата(jcxz @ Jul 28 2015, 15:33) *
Имхо - в таком случае вообще программно ничего делать не нужно. Всё должен делать DMA, получающий синхрособытия от таймера, который по этим событиям будет класть данные в порт.


Интересная идея, но её надо обсчитать: вывод должен происходить каждые 20 тактов ( для таймера от 84 МГц - 10 тактов). Но выводиться должна только половина байта по очереди старший - младший, или младший - старший.
Т.о. за двадцать тактов нужно успеть: "достать" нужный байт, "перевернуть" его как надо, выделить нужную половинку и отправить на выходной порт. Да, ещё следить, когда включить-выключить таймер.

А вот ваша идея насчёт FPU меня потрясла. Действительно, во время короткого цикла очистки памяти в фоне FPU не используется и время реакции на прерывание уменьшается. А в остальных местах программы FPU занято на всю катушку, и время реакции увеличивается. Я посмотрю свои настройки, надеюсь, уж если не решить проблему, то хотя бы уменьшить.
Golikov A.
вы в какой среде пишите? Если в Keil, то для переноса функций в РАМ, вам надо правой кнопкой в С файл с функциями тыкнуть, и сказать чтобы они запустились из РАМ, все остальное сделает кеил за вас сам.

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

Идея с ДМА вам видится не верно. Вам надо подготовить массив для вывода, так чтобы ДМА просто перекладывало данные на порт, с заданной частотой. Пусть ваша фоновая неспешная программа подготавливая файл делает его как раз того вида, чтобы ДМА осталось его только выплюнуть. ДМА сможет спокойно запускаться по таймеру с заданными интервалами и без каких либо плаваний времянки (если ему поставить приоритет повыше) все делать. А ядро в это время сможет продолжать заниматься своими делами....
bugdesigner
А можно одно уточнение? У Вас таймер считает импульсы 84 МГц без прескалера? Я правильно понял?
Не могли бы Вы показать в студию код обработчика прерывания и код инициализации таймера? Может что-то прояснится...
И последнее, я присоединюсь к мнению коллег насчёт дернуть ногой при входе в обработчик. Может всё же какая-то логическая ошибка закралась. Тогда уж точно можно будет утверждать, что имеет место джиттер входа в обработчик или нет.
jcxz
Цитата(ШСА @ Jul 28 2015, 19:03) *
Интересная идея, но её надо обсчитать: вывод должен происходить каждые 20 тактов ( для таймера от 84 МГц - 10 тактов). Но выводиться должна только половина байта по очереди старший - младший, или младший - старший.
Т.о. за двадцать тактов нужно успеть: "достать" нужный байт, "перевернуть" его как надо, выделить нужную половинку и отправить на выходной порт. Да, ещё следить, когда включить-выключить таймер.

Вы видно плохо представляете как работает DMA...
Я не очень хорошо знаю периферию STM32, но для LPC17xx сделал бы так:
Таймер1 программирую в режим счётчика импульсов на ноге МК (Ваши стартовые импульсы подаю на эту ногу). Предел счёта для него ==1. По этому пределу программирую стоп таймера1 и выдачу события на DMA-канал1. DMA-канал1 программирует Таймер2, запускает его с периодом ==10 тактов. Таймер2 по достижении величины периода (10) перезапускается и выдаёт событие на DMA-канал2.
DMA-канал2 выполняет пересылки из заранее подготовленного массива тетрад в порт каждые 10 тактов таймера2 (по каждому его перезапуску). Это - первый блок пересылки DMA-канала2
(он находится в режиме передачи свЯзных списков (linked list)). Размер первого блока linked list равен размеру пересылаемого массива тетрад. После завершения первого блока, DMA-канал2 вторым
блоком пересылки выключает таймер2. Третьим блоком пересылки DMA-канал2 включает и программрует таймер1 на ожидание след. входного импульса.
После этого DMA-канал2 переходит на начало связного списка своих блоков передач (готов к обработке след. входного импульса).
Если STM32 позволяет запускать DMA от перепада на внешнем пине, то можно обойтись без таймера, а запускать DMA-канал1 от пина.

Цитата(ШСА @ Jul 28 2015, 19:03) *
А вот ваша идея насчёт FPU меня потрясла. Действительно, во время короткого цикла очистки памяти в фоне FPU не используется и время реакции на прерывание уменьшается. А в остальных местах программы FPU занято на всю катушку, и время реакции увеличивается. Я посмотрю свои настройки, надеюсь, уж если не решить проблему, то хотя бы уменьшить.

Достаточно просто выключить это сохранение контекста FPU раз у Вас FPU не используется в ISR. Это надо делать всегда, хотя-бы для того, чтобы уменьшить расход стека.

Но по уму надо делать через DMA, раз у Вас такие требования к временным интервалам, что всё в тактах считаете. DMA более детерминирован по времени чем выполнение кода в CPU.
SasaVitebsk
Цитата(ШСА @ Jul 28 2015, 16:03) *
вывод должен происходить каждые 20 тактов ( для таймера от 84 МГц - 10 тактов).

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

Но если по теме, то раз речь идёт о 20 тактах, то всё же задачу требуется решать аппаратно. То есть фоновая задача может отсчитываться процессором, а, этот Ваш вывод, должен выполняться аппаратно. Аппаратно либо средствами процессора, либо внешними аппаратными средствами.

На мой взгляд, это очевидно. Данный процессор, как раз не предназначен для формирования временных диаграмм. Для этих целей требуются процессора с чётко определённым временем выполнения команд, то есть без кэш, без механизмов предвыборки и так далее. Типа AVR, PIC, а так как они по быстродействию не укладываются, то плисину какую-нибудь применить. А процессор должен выполнять свою задачу асинхронно к данному событию. Чем раньше Вы это осмыслите, переделаете железо, тем быстрее решите задачу. Иначе всё равно к этому вернётесь. Моё мнение.
Сергей Борщ
Цитата(SasaVitebsk @ Jul 29 2015, 08:50) *
На мой взгляд, это очевидно. Данный процессор, как раз не предназначен для формирования временных диаграмм.
Ой, не смешите. Завести массив в ОЗУ, неспешно прописать в него состояния порта через одинаковые промежутки времени, таймер настроить на эти промежутки и пусть DMA от этого таймера циклически пихает этот массив в порт (или через bitband в ногу/несколько соседних ног порта). Прекрасные времянки получаются.
jcxz
Цитата(Сергей Борщ @ Jul 29 2015, 13:38) *
Ой, не смешите. Завести массив в ОЗУ, неспешно прописать в него состояния порта через одинаковые промежутки времени, таймер настроить на эти промежутки и пусть DMA от этого таймера циклически пихает этот массив в порт (или через bitband в ногу/несколько соседних ног порта). Прекрасные времянки получаются.

Не забыть ещё приоритет доступа к шине для DMA поставить повыше чем у CPU.
Tanya
Цитата(Сергей Борщ @ Jul 29 2015, 10:38) *
. Прекрасные времянки получаются.

Либо записать в массив параметры таймера и отдать процесс записи DMA.
ШСА
Цитата(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 точки" это и есть интервал от начала прерывания до начала вывода. Вот он то и "плавает". Кстати следующая строка и есть ваше "дернуть ногой".
ШСА
Цитата(Сергей Борщ @ Jul 29 2015, 10:38) *
Ой, не смешите. Завести массив в ОЗУ, неспешно прописать в него состояния порта через одинаковые промежутки времени, таймер настроить... Прекрасные времянки получаются.


Конечно! Если удастся уменьшить флуктуации до 4 тактов, их уже никто не заметит при 168 МГц. А вот с ОЗУ напряжёнка: Там две страницы видеополя находятся - формируемая и отображаемая. Так что особо не разбежишься.
jcxz
Цитата(ШСА @ Jul 29 2015, 21:54) *
В прерываниях FPU нигде не используется, только в фоне. Ищу способ запретить сохранять FPU регистры в стек - а их там 32, если не ошибаюсь. Если даже по 1 такту на каждого - это уже масштаб моих флуктуаций!
Идея с DMA мною не отброшена. Я её проработаю, но позже. А пока мне надо научиться отключать Lazy context save - я даже это ещё не могу заставить сделать свой кокос.

Вы как-то странно мануал читаете.... wacko.gif
Lazy context save Вам отключать не надо, он никак не увеличивает время входа, а наоборот - предназначен для уменьшения времени входа в ISR (резервирует место в стеке, но не пишет).
Но так как у Вас FPU в фоне используется, а где-то не используется, то из-за Lazy context save у Вас в этих местах и будет разница во времени входа (но место в стеке при стэкинге всё равно
будет выделяться одинаково!). А вот если Вы, как я советовал, вообще отключите автоматическое сохранение контекста FPU при входе в ISR, то всегда на стек будет сохраняться как в M3 только
8 регистров. Это можно сделать, если в ISR-ах у Вас не используется FPU. Но если есть операционка и FPU используется в разных задачах, то в переключателе задач ОС (программном) надо сделать
сохранение контектса FPU ручками.
У нас в наших изделиях я так и делаю, но не для уменьшения времени входа в ISR, а для экономии стеков задач (делаю FPCCR = 0, а потом сохраняю или не сохраняю контекст FPU в
переключателе задач ручками, в зависимости от того - использует данная задача FPU или нет).

И как это сделать - я Вам уже выше писал: FPCCR = 0. Вы как-то читаете выборочно sm.gif
И от компилятора это никак не зависит, это Вам надо открыть мануал на ядро M4 и найти там FPCCR.
jcxz
Цитата(ШСА @ Jul 29 2015, 23:03) *
А вот с ОЗУ напряжёнка: Там две страницы видеополя находятся - формируемая и отображаемая. Так что особо не разбежишься.

Советую всё-таки открыть мануал на используемый процессор и поискать там что-нить типа Quad-SPI или SDIO. Они позволяют до 4 линий параллельно выводить.
Тогда возможно каждый байт памяти будет использоваться полностью, а не одна тетрада. Да и загрузка шины при DMA-передаче на такой интерфейс
будет ниже чем при передаче DMA->GPIO.
По крайней мере на Tiva (TM4C129), который я сейчас использую, связка Quad-SSI+DMA идеальна для Вашей задачи.
Даже если Вы не будете использовать DMA, то лучше использовать Quad-SPI чем тот ногодрыг, что Вы сейчас пытаетесь сделать.
О ногодрыге вообще стоит забывать сразу после перехода на ARM. Оставить его AVR-щикам.
SPI Вам жёстко даст ту времянку, которую Вы запрограммируете ему, вместо болтанки с программным таймером и ногодрыгом.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.