STM32f103c8 ограничение скорости gpio - PullRequest
1 голос
/ 13 января 2020

У меня есть этот простой встроенный код сборки:

__asm__ volatile (

    ".equ GPIOA_ODR, 0x4001080C \n\t" //GPIOA base address is 0x40010800 and ODR offset is 0x0C


    //turns on PA8
    "ldr r1, =(1 << 8)     \n\t"        
    "ldr r2, =#GPIOA_ODR   \n\t"     
    "str r1, [r2]          \n\t"   

    //turn off PA8
    "ldr r1, =0            \n\t"        
    "ldr r2, =#GPIOA_ODR   \n\t"     
    "str r1, [r2]          \n\t"          

);

PA8 колеблется только на частоте 2,4 МГц, я хочу скорость 36 МГц. Я пытался использовать таймеры и достиг скорости 36 МГц раньше, но из-за некоторых ограничений я хочу избегать их использования.

Я не понимаю, почему TIMER1 Channel 1 (PA8) можно настроить на скорости переключения 36 МГц, но когда я пытаюсь сделать то же самое в сборке, я достигаю скорости 2,4 МГц на том же выводе.

Я также настраиваю вывод, используя PinMode(PA8, OUTPUT);

У меня есть пробовал другие варианты этого ассемблера и достиг максимальной частоты 2,8 МГц на PA8. У меня такой вопрос: более высокая скорость переключения, чем 2,4-2,8 МГц на выводе GPIO, невозможна на STM32f103C8?

(Это следующий вопрос после Нужна помощь в управлении регистрами во встроенной сборке (STM32F103 ") BluePill ") )

Ответы [ 3 ]

4 голосов
/ 13 января 2020

STM32F103C8 работает с максимальной тактовой частотой 72 МГц. Таким образом, 36 МГц - это максимальная частота, которая может быть сгенерирована на GPIO, поскольку для установки и очистки вывода требуется отдельный тактовый цикл. Эта частота может быть достигнута только с помощью таймера.

Если вы попробуете то же самое с кодом, вам понадобится как минимум три инструкции: два хранилища и одна ветвь. Для выполнения этих инструкций требуется около 6 тактов и, следовательно, максимальная частота составит около 12 МГц.

Чтобы добиться этого в программном обеспечении, ваш код должен выглядеть примерно так:

while (1) {
    GPIOA->ODR = 1 << 8;
    GPIOA->ODR = 0;
}

Код ассемблера не требуется, поскольку компилятор подберет оптимальный код. Это будет выглядеть так:

        ldr     r3, .L3
        movs    r1, #128
        movs    r2, #0
.L2:
        str     r1, [r3]
        str     r2, [r3]
        b       .L2
.L3:
        .word   1207959572

Обновление

Я проверил его на устройстве реального мира и получаю частоту 8 МГц. Моя оценка заключалась в том, что для этих трех инструкций необходимо 6 тактов, но, похоже, требуется 9 циклов.

Сгенерированный код более или менее соответствует ожидаемому:

7a: 60d9 str r1, [ r3, # 12] 7 c: 60da str r2, [r3, # 12] 7e: e7f c bn 7a

Область действия ясно показывает, что все три инструкции занимают одинаковое количество времени.

2 голосов
/ 24 января 2020

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

В любом случае, время, когда GPIO, где отображается память или регистры напрямую, сейчас далеко. Современные MCU имеют интерфейсы GPIO, подключенные к ядру CPU MCU через интерфейс (обычно регистры с отображением памяти), в котором вы вводите команды GPIO вместо непосредственного манипулирования битами GPIO.

Таймер обходит этот интерфейс, что обеспечивает лучшую скорость. Однако, если в этом случае есть способы, как улучшить скорость опроса GPIO с помощью MCU:

  1. Часы GPIO API

    API (интерфейс) между MCU Ядро процессора и модуль GPIO обычно управляются отдельными часами. Если установить медленную скорость, GPIO будет также медленным независимо от тактовой частоты MCU или возможностей GPIO.

    Так что постарайтесь найти его и улучшить как можно больше.

  2. группы GPIO

    выводы GPIO обычно группируются в PORTS, которые используют одни и те же регистры API. Таким образом, обычно возможно обрабатывать все выводы в одной группе одновременно с той же скоростью, как если бы вы обрабатывали только один вывод. Поэтому, если вы тщательно выбираете выводы, которые вы используете, вы можете значительно изменить частоту опроса.

    Поэтому, если возможно, используйте только одну группу ... вычислите операцию для всех выводов, а затем используйте API GPIO для установки / очистки / переключать все сразу вместо одного за другим.

  3. DMA

    некоторые MCU допускают DMA между памятью и GPIO, где вы можете обойти GPIO API и получить аналогичную скорость для таймеров. Просто создайте буфер памяти со всеми предварительно битовыми состояниями, предварительно рассчитанными с некоторой частотой дискретизации, а затем используйте DMA, чтобы «воспроизвести» его на GPIO так же, как вы воспроизводите файл wav на звуковой карте ...

  4. без использования GPIO

    некоторые микроконтроллеры просто созданы не для скоростей GPIO, а для большей вычислительной мощности или для другого назначения. В этом случае, независимо от того, что вы делаете, вы не значительно улучшите скорость GPIO , В таких случаях MCU обычно оснащены интерфейсами для соединения с различными HW, такими как внешняя память, IDE, LCD, SPI, USART и т. Д. c.

    Некоторые из них могут использоваться вместо GPIO и интерфейсы для Внешняя память обычно быстрая и поддерживает DMA, обеспечивая высокую скорость передачи, даже если GPIO слишком медленный ... Например, см .: VGA-пиксельная группировка на STM32

    Просто для сравнения я привык к AVR32 Микроконтроллеры UC3, которые на тактовой частоте процессора ~ 66 МГц имеют частоту переключения GPIO ~ 5 МГц (опрос) ... но, используя вместо этого интерфейсы, я могу иметь частоту дискретизации даже 33 МГц ...

    Проблема в том, что такой интерфейс обычно не есть много контактов в распоряжении, а также иногда они используются совместно или отображаются во времени как шины, и в этом случае вам иногда нужно добавить некоторые дополнительные вещи к вашему HW (например, диод + конденсатор или LATCH или (DE) MUX ...) для избегать сбоев

0 голосов
/ 25 января 2020

Я хочу ответить на свой вопрос, потому что получение ответа solid на это разочаровывает, и результаты НЕ очевидны, пока вы не проведете тестирование в реальном мире. Комментарии Old_timer оказались наиболее полезными.

void setup(){

#define FLASH_ACR (*(volatile uint32_t *)(0x40022000))
FLASH_ACR = 0b110010; //enable flash prefetch and wait states 

pinMode(PA8, OUTPUT);

__asm__ volatile (

"ldr r0, =(0x4001080C) \n\t" //GPIOA ODR
"ldr r1, =(1<<8) \n\t" //turn on PA8
"ldr r2, =0 \n\t" //turn off PA8
".loop: \n\t"
"str r1, [r0] \n\t" // ON and OFF commands are unrolled (repeated) about 100 times
"str r2, [r0] \n\t" // inside the loop
"b .loop \n\t"

); }

С MCU, работающим на 72 МГц, я получил очень близкую к 18 МГц частоту переключения на PA8, используя приведенный выше код. Насколько я понимаю, использование непосредственных значений или инструкции XOR может переключать вывод быстрее (помимо всего прочего, вы могли бы это сделать), потому что некоторые инструкции или определенные методы кодирования используют меньше тактов, что приводит к более высокой производительности.

Если вы также посмотрите на STM32f103 PDF:

https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=2ahUKEwjT-qaNw53nAhUZac0KHe8_Cb8QFjADegQIBBAB&url=https%3A%2F%2Fwww.st.com%2Fresource%2Fen%2Fdatasheet%2Fstm32f103c8.pdf&usg=AOvVaw0rd6I_7fuhTLdZOoycvGV5

На странице 20 в разделе 2.3.21 вы увидите, что «I / ОС на APB2 с частотой переключения до 18 МГц. " так что я думаю, что я достигну предела, если это упомянуто в документации. Если вы также посмотрите на страницу 66, вы увидите хорошую таблицу с характеристиками «I / OA C», которая показывает, что вы можете go до 50 МГц.

Так что после достижения почти 18 МГц я решил разогнать плату до 128 МГц и достичь почти 32 МГц скорости переключения на PA8 с 1,6 В C на выводе. Теперь я доволен, спасибо за все комментарии и помогите ребята. Я все еще начинающий в этом, но я думаю, что теперь я многое понимаю.

...