kolhozing.ru
В началоАвторемонтМоделизмОргтехникаСад и огородЭлектроникаФорум

STM32 и libopencm3, ложечка дегтя.

Поговорим немного о библиотеке libopencm3 и ее применении в целях разработки ПО для МК stm32f103. Библиотека выпускается под лицензией LGPL, доступна на гитхабе и предназначена для МК на базе ARM Cortex-M0(+)/M3/M4, к которым относятся не только МК семейства stm32, но и МК других производителей. В моем случае для Cortext-М7 библиотека не собралась по причине unknown cpu `cortex-m7', но это не беда по причине отсутствия у меня МК с подобным ядром, а для stm32f0 и stm32f1 собирается без каких-либо трудностей. Для библиотеки доступна какая-никакая документация и примеры, что, впрочем, не отменяет необходимости прочитать документацию на МК, что бы понимать что и когда из библиотеки использовать.

Как минимум, библиотеку можно использовать ради заголовочных файлов, что бы не определять все регистры и биты самостоятельно. Но можно использовать и библиотечные функции, если немного закрыть глаза на оптимальность и скорость. Для примера одна из самых ходовых операций, дерганье ногой МК. Как вы знаете, STM32 имеет специальный регистр для каждого порта, запись единичного бита в который, соответствует изменению соответствующего выхода, т.е. одна операция записи вместо традиционных в таких случаях read-modify-write. Применительно к языку Си это выглядит так:

*(volatile uint32_t *)(0x40011010) = 0x800;
В данном случае устанавливается C11.
0x40011010 это адрес регистра BSRR для GPIOC, а 0x800 бит соответствующий выводу C11.
Хотя для читаемости лучше записать через константы, т.е.
GPIO_BSRR(GPIOC) = GPIO11;
Раскручиваем макросы из заголовочных файлов библиотеки:
#define GPIO11                          (1 << 11)
#define PERIPH_BASE			(0x40000000U)
#define PERIPH_BASE_APB2		(PERIPH_BASE + 0x10000)
#define GPIO_PORT_C_BASE		(PERIPH_BASE_APB2 + 0x1000)
#define GPIOC				GPIO_PORT_C_BASE
#define MMIO32(addr)		        (*(volatile uint32_t *)(addr))
#define GPIO_BSRR(port)			MMIO32((port) + 0x10)
И в итоге получим как раз первый вариант. Компилируется что первый, что второй вариант, в одинаковый код
        ldr     r3, .L18
        mov     r2, #2048
        str     r2, [r3]
По адресу .L18 находится константа 0x40011010. Увы, но сразу записать содержимое регистра по адресу 0x40011010 невозможно, смещение команд LDR/STR позволяет задать либо 12-битное значение относительно текущего адреса, либо 8-битное относительно значения регистра, а 12 бит не хватает что бы «дотянуться» из области кода до адресов регистров периферии.

Теперь посмотрим, как установка бита реализована в библиотеке. Для этого предлагается воспользоваться следующей функцией:

void gpio_set(uint32_t gpioport, uint16_t gpios)
{
	GPIO_BSRR(gpioport) = gpios;
}
Функция не встраиваемая, поэтому ее вызов
gpio_set(GPIOC, GPIO11);
компилируется в следующий ассемблерный код:
        mov     r1, #2048
        ldr     r0, .L19
        bl      gpio_set
...
gpio_set:
        str     r1, [r0, #16]
        bx      lr
Добавляются две команды перехода, а это лишние такты исполнения и лишние байты памяти. На каждую операцию установки/сброса выхода, потому что функция gpio_clear устроена точно так же. Так что, возможно, имеет смысл немного переписать библиотечные функции или иногда использовать прямые обращения к регистрам, благо что архитектура ARM позволяет это делать на Си без использования ассемблерных вставок.


рубрика: электроника
Комментарии
Антон
2016.11.24
 
Я полностью согласен с автором идеологически, но не пойму в чём смысл писать подобного рода статьи! Ты нашёл опенсорсную либу, потестил, нашёл косячёк - так будь добр пришли патч или напиши предложение в рассылке.
Николай
2017.01.19
 
Так там же статическая библиотека, по-любому функции будут вызываться а не встраиваться
Ваше имя:

Ваше мнение:

введите код:
Пользовательский поиск
 
RSS-канал
Подписка по эл.почте
 
Реклама
 
Рубрики
 
Популярные метки