Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Эффективно и быстро управлять линией порта STM32F407
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
derun
Мне необходимо быстро менять состояние линии порта (програмно, без таймеров и прочего аппаратного). Пишу кусочек кода для проверки скорости функций управления линией порта portC.13.
Код
while (1)
{

HAL_GPIO_TogglePin(GPIOC, 1 << 13);

}

Получаю меандр с частотой порядка 2.4MHz.
Это очень медленно. Посмотрел как работает функция и переписал код так:
Код
  while (1)
{

      GPIOC->ODR ^= 1 << 13;
}

Получил меандр 8.4 MHz
Уже лучше, но все равно очень медленно. Учитывая что ядро летает на скорости 168 MHz.
Подскажите пожалуйста как сделать управление эффестивнее и быстрее.
adnega
Цитата(derun @ May 21 2014, 11:21) *
Мне необходимо быстро менять состояние линии порта (програмно, без таймеров и прочего аппаратного).
Подскажите пожалуйста как сделать управление эффестивнее и быстрее.

"Без аппаратного" и "эффективнее и быстрее" два противоречащих понятия.
Можете задачу описать подробнее?

Код
while(1)
{
  GPIOC->BSRR = (1 << 13) << 0;
  GPIOC->BSRR = (1 << 13) << 16;
}


Даст Вам максимальную частоту. Включите опримизацию по скорости. Покажите листинг.
Golikov A.
скорость ножки задали? Надо задать максимальную частоту переключения ноги, а то будет наружу будет лезть не пойми че!
scifi
Если хочется максимально быстро и именно процессором, то без ассемблера не получится.

Цитата(Golikov A. @ May 21 2014, 12:04) *
скорость ножки задали? Надо задать максимальную частоту переключения ноги, а то будет наружу будет лезть не пойми че!

Неправда. Максимум завалятся фронты, если у осциллографа ёмкость щупа неприлично большая. Частота не изменится.
ViKo
Цитата(adnega @ May 21 2014, 10:34) *
Код
while(1)
{
  GPIOC->BSRR = (1 << 13) << 0;
  GPIOC->BSRR = (1 << 13) << 16;
}

Вариант 2 (какой лучше?):
Код
while(1)
{
  GPIOC->BSRR = (1 << 13);
  GPIOC->BRR = (1 << 13);
}
demiurg_spb
Цитата(scifi @ May 21 2014, 12:06) *
Если хочется максимально быстро и именно процессором, то без ассемблера не получится.
Всё прекрасно получается и без ассемблера.
Код
static __inline volatile uint32_t* bb_bit_address(volatile uint32_t* p, uint_fast8_t bit)
{
    return (
        (volatile uint32_t*) (
            (((uint32_t)p & 0xf0000000UL) | 0x02000000UL)
          + ((((uint32_t)p & 0x000fffffUL)<<5) | (bit<<2))
        )
    );
}

#define _PIN_BITBAND_RD_PTR(XPORT, XPIN, ...)    bb_bit_address(&GPIO##XPORT->IDR, XPIN)
#define _PIN_BITBAND_WR_PTR(XPORT, XPIN, ...)    bb_bit_address(&GPIO##XPORT->ODR, XPIN)

#define _PIN_TOGGLE(XPORT, XPIN, ...)  do {*_PIN_BITBAND_WR_PTR(XPORT, XPIN) = ~*_PIN_BITBAND_RD_PTR(XPORT, XPIN);} while (0)
#define pin_toggle(PIN)            _PIN_TOGGLE(PIN)

И далее в программе:
Код
#define PIN_LED_RED           B,9,H,OUTPUT_PUSH_PULL,SPEED_10MHZ // можно и 50MHZ, но не особо нужно...

pin_init(PIN_LED_RED);

for(;;)
   pin_toggle(PIN_LED_RED);
derun
Нужно просто быстро. Порт сконфигурирован на 100 MHz.
Попробовал предложенный способ:
Код
  while (1)
{
        GPIOC->BSRRH = (1 << 13);
      GPIOC->BSRRL = (1 << 13);
}

Этот вариант работает быстрее получил частоту 14 MHz
Думаю этого будет вполне достаточно.
Посмотрел код дизассемблером, выглядит прилично думаю врядли смогу его сильно оптимизировать.
Спасибо всем за советы и adnega за его вариант решения.
adnega
Цитата(demiurg_spb @ May 21 2014, 12:18) *
Всё прекрасно получается и без ассемблера.

Bitband для GPIO есть не у всех процессоров STM32, так что лучше не подсаживать)
ViKo
Цитата(derun @ May 21 2014, 11:36) *
Этот вариант работает быстрее получил частоту 14 MHz
Думаю этого будет вполне достаточно.
Посмотрел код дизассемблером, выглядит прилично думаю врядли смогу его сильно оптимизировать.

12 тактов на весь цикл? Многовато. А листинг покажите.
demiurg_spb
Цитата(adnega @ May 21 2014, 12:37) *
Bitband для GPIO есть не у всех процессоров STM32, так что лучше не подсаживать)
Ну да.
Но когда есть, то почему бы не пользоваться...
ViKo
Цитата(demiurg_spb @ May 21 2014, 12:02) *
Ну да.
Но когда есть, то почему бы не пользоваться...

Потому что в данном случае вы не выиграете ничего.
derun
Цитата(ViKo @ May 21 2014, 10:54) *
12 тактов на весь цикл? Многовато. А листинг покажите.

GPIOC->BSRRL = (1 << 13);
080002d8: ldr r3, [pc, #16] ; (0x80002ec <main+116>)
080002da: mov.w r2, #8192 ; 0x2000
080002de: strh r2, [r3, #24]
117 GPIOC->BSRRH = (1 << 13);
080002e0: ldr r3, [pc, #8] ; (0x80002ec <main+116>)
080002e2: mov.w r2, #8192 ; 0x2000
080002e6: strh r2, [r3, #26]
120 }
080002e8: b.n 0x80002d8 <main+96>

Как-то так
demiurg_spb
Цитата(ViKo @ May 21 2014, 13:07) *
Потому что в данном случае вы не выиграете ничего.
Ну давайте померяемся...
Напишите макрос, инвертирующий состояние линии GPIO без BB и посмотрите, что получится...
На STM32F103 разница очевидна.

Код
8000bb2:    6823          ldr    r3, [r4, #0]
8000bb4:    ea6f 0203     mvn.w    r2, r3
8000bb8:    602a          str    r2, [r5, #0]
8000bba:    e7fa          b.n    8000bb2 <Reset_Handler+0xc2>

Итого по предварительной оценке 18МГц при тактовой 72МГц.
ViKo
Цитата(demiurg_spb @ May 21 2014, 12:18) *
Ну давайте померяемся...
Напишите макрос, инвертирующий состояние линии GPIO без BB и посмотрите, что получится...
На STM32F103 разница очевидна.

Давайте по-другому. rolleyes.gif Будем не инвертировать предыдущее состояние, а конкретно задавать то 0, то 1. Мой "макрос" уже выдан чуть выше.
Отдельно обращу внимание, что в обычном варианте можно одновременно изменить состояние не одного бита, а целой группы. Причем, некоторые можно установить, а некоторые сбросить.

Цитата(derun @ May 21 2014, 12:10) *
Как-то так

А оптимизация не задана?
scifi
Цитата(derun @ May 21 2014, 13:10) *
Как-то так

Плохенький листинг. Четыре инструкции просятся, чтобы их вынесли за цикл. Оптимизация по скорости не была включена, видимо.
derun
Цитата(ViKo @ May 21 2014, 11:24) *
А оптимизация не задана?

Задавал : и без оптимизации и максимальная. На код не влияет.
scifi
Цитата(derun @ May 21 2014, 13:30) *
Задавал : и без оптимизации и максимальная. На код не влияет.

Потому и говорю: оптимальный по скорости результат гарантированно можно получить только при помощи ассемблера.
derun
Цитата(scifi @ May 21 2014, 11:26) *
Плохенький листинг. Четыре инструкции просятся, чтобы их вынесли за цикл. Оптимизация по скорости не была включена, видимо.

На самом деле мне цикл и не нужен , нужно просто устанавливать порт в нужное состояние. Я использую цикл для наглядности,
для проверки сколько времени нужно чтобы порт в нужное состояние установить. Хoтя с оптимизацией надо бы разобраться. У меня Eclipse и GNU Tools ARM Embedded
Тут такой опции нет "Speed optimize" есть "Optimize most"

Цитата(scifi @ May 21 2014, 11:34) *
Потому и говорю: оптимальный по скорости результат гарантированно можно получить только при помощи ассемблера.

По видимому дальше можно улучшать только ассемблером.

Подскажите пожалуйста как правильно оформить ассемблерную вставку, например для этой программы. Спасибо.
demiurg_spb
Цитата(ViKo @ May 21 2014, 13:24) *
Давайте по-другому.

Давайте:
Код
for (;;)
{
    pin_set(PIN_LED_RED);
    pin_clr(PIN_LED_RED);
}

Код
8000bae:    6123          str    r3, [r4, #16]
8000bb0:    6163          str    r3, [r4, #20]
8000bb2:    e7fc          b.n    8000bae <Reset_Handler+0xbe>

Да, так на четверть быстрее. Но это не интересно...
scifi
Там ещё одна проблема есть: даже если не нужна точная времянка, а нужно просто успеть за какое-то время, нет никакой гарантии, что при изменении версии компилятора или его настроек код будет продолжать успевать.
demiurg_spb
Цитата(scifi @ May 21 2014, 13:34) *
Потому и говорю: оптимальный по скорости результат гарантированно можно получить только при помощи ассемблера.
По-прежнему не согласен (если убрать слово гарантированно)sm.gif
В обычной жизни на asm разве что процедура delay_cycles написана.
Во всех остальных случаях с вероятностью 99% его использование не оправдано.
scifi
Цитата(demiurg_spb @ May 21 2014, 13:49) *
По-прежнему не согласен (если убрать слово гарантированно)sm.gif

Отож. Я старался формулировать аккуратно :-)

Цитата(demiurg_spb @ May 21 2014, 13:49) *
В обычной жизни на asm разве что процедура delay_cycles написана.
Во всех остальных случаях с вероятностью 99% его использование не оправдано.

Поддерживаю.
ViKo
Цитата(demiurg_spb @ May 21 2014, 12:18) *
Итого по предварительной оценке 18МГц при тактовой 72МГц.

Команда B выполнится за 1 + P тактов.
Цитата
P The number of cycles required for a pipeline refill. This ranges from 1 to 3
depending on the alignment and width of the target instruction, and whether the
processor manages to speculate the address early.

По моим экспериментам, всегда выполнялась за 3 такта. (Так же работает и условный переход, если он есть. Если нет, то чтобы перейти к следующей команде нужен один такт.)
Итого, в лучшем случае, непрерывно дрыгать ножкой можно за 5 тактов.
adnega
Но всегда нужно помнить, что в любой момент может произойти прерывание...
megajohn
и чё тут меряются строками кода, если нигде не написано про PCLK, и только CCLK упоминается. И кстати, можно даже умудрится биения получить.
scifi
Цитата(megajohn @ May 21 2014, 16:23) *
и чё тут меряются строками кода, если нигде не написано про PCLK, и только CCLK упоминается. И кстати, можно даже умудрится биения получить.

Для справки: в семействе STM32F4xx GPIO сидит на шине AHB и тактируется на частоте процессора. Обращения к регистрам GPIO происходят без задержек (с точностью до возможных, но маловероятных задержек внутри Multilayer Bus Matrix).
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.