Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: SysTick в STM32F4xx
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
Sidoroff
Здравствуйте!
Как настроить SysTick, чтобы можно было на время, значительно меньшее периода таймера,
в фоновой программе запрещать и потом разрешать прерывания SysTick, чтобы при этом они не терялись?
Т.е. если в данный момент прерывание запрещено а событие таймера приходит, то оно должно
обработаться сразу же в момент разрешения прерываний таймера.

Ну и соответственно как разрешать/запрещать прерывания SysTick?

Инициализация сейчас стандартная: SysTick_Config(SystemCoreClock / частота);

Для запрета/разрешения пробовал такие варианты:

//такой вариант не запрещает прерывания SysTick вообще
NVIC_DisableIRQ(SysTick_IRQn); // запрет
NVIC_EnableIRQ(SysTick_IRQn); // разрешение

//тоже не запрещает прерывания SysTick
int t = RCC->CIR; RCC->CIR = 0; // запрет
RCC->CIR = t; // разрешение

// запрещает и разрешает, но прерывания теряются
SysTick->CTRL &= (~SysTick_CTRL_TICKINT_Msk); // запрет
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; // разрешение
adnega
SysTick не обычный таймер. Он генерирует исключение вместо прерывания.
Вариантов много:
1. Использовать обычный таймер.
2. Использовать приоритеты прерываний и SVC для кода, который нужно выполнить быстро.

Запрещать прерывания (и уж тем более исключения) - верный путь к блокировке.

Опишите поподробней задачу. Скорее всего решение будет другим.
Sidoroff
Задача такая. Есть внешне устройство (ПЛИС), с которым STM общается по SPI, при этом
с программно формируемым CS и дополнительной линией, определяющей, устанавливаем ли
адрес внутри ПЛИС или пишем/читаем из нее данные.
Общения должны происходить как в обработчике таймера, так и в фоне и не не мешать друг-другу.
Одна посылка SPI намного короче периода таймера. Поэтому надо просто запрещать прерывание перед
каждым обращением из фона и потом разрешать. Так всегда делал в AVR. Здесь хочется
что-то аналогичное.
jcxz
Цитата(Sidoroff @ Sep 15 2014, 16:10) *
Ну и соответственно как разрешать/запрещать прерывания SysTick?

См. SysTick Control and Status Register [0xE000E010] бит 1.
adnega
Цитата(jcxz @ Sep 15 2014, 14:57) *
См. SysTick Control and Status Register [0xE000E010] бит 1.

Будут теряться прерывания.

Правильнее сделать менеджер SPI. Завести для него очередь заданий. Из разных источников помещать задания в очередь с указанием calback-функции (когда задание выполнено и результат получен).
Sidoroff
Что за менеджер? Что-нибудь связанное с RTOS? Не пользуюсь, не знаю как. У меня stand-alone приложение.
adnega
Цитата(Sidoroff @ Sep 15 2014, 15:57) *
Что за менеджер? Что-нибудь связанное с RTOS? Не пользуюсь, не знаю как. У меня stand-alone приложение.

Конечно без RTOS.
Можно сделать простой кольцевой буфер.
Функции push() - для помещения элемента в очередь, pop() - для извлечения.
Элементом очереди будет структура, содержащая данные и указатель на callback-функцию.

При вызове push() выделяется новый элемент в очереди. В него помещается структура.
Если SPI свободен, то отправляем данные из данного элемента очереди. Если ведется передача, то в обработчике окончания передачи вызывать
указанную calback-функцию, извлекать следующий элемент из очереди и отправлять его по SPI.

Или совсем просто:
В Systick устанавливать флаг, а в фоне сбрасывать и передавать, те данные, которые нужно было отправить в Systick.
Sidoroff
Последовал первому совету: использовал обычный таймер. Все работает, прерывания не теряет.

//Инициализация
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->PSC = 0;
TIM2->ARR = SystemCoreClock/2/TIMER_FREQ - 1; // TIMER_FREQ = 10000 (10kHz)
TIM2->DIER |= TIM_DIER_UIE;
TIM2->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(TIM2_IRQn); // в первый раз разрешим прерывания

// В фоновой программе запрещаю перед обменом и разрешаю после обмена так же
NVIC_DisableIRQ(TIM2_IRQn);
.. SPI-обмен
NVIC_EnableIRQ(TIM2_IRQn);

// Обработчик
extern "C" void TIM2_IRQHandler(void) {
TIM2->SR &= ~TIM_SR_UIF; // без этого виснет, содрал просто
.. тело
}

Всем спасибо.
adnega
Цитата(Sidoroff @ Sep 16 2014, 14:04) *
Последовал первому совету: использовал обычный таймер. Все работает, прерывания не теряет.

Т.к. у обычных прерываний есть pending-бит в NVIC. Типа, отложенные прерывания - идеально для вас.
Цитата(Sidoroff @ Sep 16 2014, 14:04) *
TIM2->SR &= ~TIM_SR_UIF; // без этого виснет, содрал просто

Лучше так:
Код
TIM2->SR = ~TIM_SR_UIF;

Почему? Много раз обсуждалось тут: чтобы сбросить только UIF.
jcxz
Цитата(adnega @ Sep 15 2014, 17:51) *
Будут теряться прерывания.
Правильнее сделать менеджер SPI. Завести для него очередь заданий. Из разных источников помещать задания в очередь с указанием calback-функции (когда задание выполнено и результат получен).

Если учесть, что как пишет ТС, период запрета много короче периода прерываний, и думать что и как делаешь, то не будут теряться.

Но правильней конечно пересмотреть структуру программы, чтобы обмен по SPI не вызывался изнутри обработчика прерывания и снаружи.
Сделать нормальный менеджер службы SPI и API доступа к нему.
adnega
Цитата(jcxz @ Sep 16 2014, 19:55) *
Сделать нормальный менеджер службы SPI и API доступа к нему.

Предлагал. Но для доступа к очереди все равно придется городить критические секции (запрещать прерывания).
То, что было нужно ТС - это отложенные прерывания. Но я бы подумал над архитектурой. Cortex-M это не AVR))) Возможности богаче.
jcxz
Цитата(adnega @ Sep 16 2014, 22:35) *
Предлагал. Но для доступа к очереди все равно придется городить критические секции (запрещать прерывания).

Во-первых: очереди возможно и не нужны. Если доступ к службе делать с блокировкой (задачи), до достаточно для каждой задачи иметь флаг и задаче ждать
на этом флаге снятия его ISR-ом. ISR знает все флаги.
Во-вторых: очереди легко строятся без всяких критических секций, если писатель (в очередь) только один (задача или ISR) и читатель - только один.
Так даже можно очереди синхронизации между ядрами CPU строить, где невозможно сделать критическую секцию другому ядру.

Цитата(adnega @ Sep 16 2014, 22:35) *
То, что было нужно ТС - это отложенные прерывания. Но я бы подумал над архитектурой. Cortex-M это не AVR))) Возможности богаче.

Да, у ТС естественно детский, АВР-ский подход. Думаю он ещё не скоро вырастет из детских штанишек wink.gif
adnega
Цитата(jcxz @ Sep 18 2014, 15:26) *
Во-первых: очереди возможно и не нужны. Если доступ к службе делать с блокировкой (задачи), до достаточно для каждой задачи иметь флаг и задаче ждать
на этом флаге снятия его ISR-ом. ISR знает все флаги.

Deadlock: в фоне сделали блокировку, в Systick ждем освобождения.
Цитата(jcxz @ Sep 18 2014, 15:26) *
Во-вторых: очереди легко строятся без всяких критических секций, если писатель (в очередь) только один (задача или ISR) и читатель - только один.

Сам так пользуюсь. Но к сожалению, писателей у ТС два(
jcxz
Цитата(adnega @ Sep 18 2014, 18:05) *
Deadlock: в фоне сделали блокировку, в Systick ждем освобождения.

Какой дидлок? В ISR-ах никогда не должно быть никаких ожиданий!
WitFed
Я вот тоже в детских штанишках без callback-ов предлагаю пожить, если жизнь очень не требует выпрыгнуть до извра-высочайших уровней:
2 признака в программе: 1: из "фона" в ISR "сейчас уже исполняется команда SPI" и 2: из ISR в "фон" -- "в прерывании таймера было нужно вывести команду в SPI".
Без запретов прерываний просто нужно установить "1:" перед посылом обычной команды, сбросить в конце -- это сигнал для обработчика, можно/нельзя работать с девайсом.
Если обработчик видит этот флаг -- устанавливает "2:" и сохраняет свои данные для "фона", "фон" тогда после своей посылки проверит "2:" и пошлёт данные "из ISR", потом обнуляет "1:" и "2:".
Ну и если ISR не видит "1:" -- шлёт в SPI сразу без вопросов, что хочется, в этот уровень "фон" встрять уже не может.
Логики на 5 строк, но железной !
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.