цифровая электроника
вычислительная техника
встраиваемые системы

 



Что такое Bit Banding (бит бэндинг) в микроконтроллерах STM32

Автор: Mike(admin) от 13-10-2021, 03:55

У микроконтроллеров STM32 (ядро ARM Cortex M3) есть много интересных особенностей, одной из которых является bit banding (бит бэндинг), и она представляет собой нечто вроде атомарного доступа к отдельным битам в памяти. Хотя этот метод достаточно прост по своей концепции и довольно дружелюбен для программистов, пишущих программу на ассемблере, но при написании программы на языке C с атомарным доступом к битам довольно легко запутаться.


Что такое Bit Banding (бит бэндинг) в микроконтроллерах STM32

Но зачем нам атомарный доступ к отдельным битам? Рассмотрим единственный бит, используемый в вашей программе в качестве флага. Возможно, у вас есть буфер данных и процедура обслуживания прерывания, которая следит за тем, как он устанавливает бит в ячейке памяти, чтобы сигнализировать о том, что буфер заполнен. Функция более высокого уровня видит установленный бит и что-то с этим делает, а затем сбрасывает бит, проблема в том, что это всего лишь один бит из всего 32-битного слова памяти, и для его изменения вам нужно прочитать слово, изменить бит и записать слово обратно. Что произойдет, если какая-то другая функция прерывания изменит один из других битов в этом слове после того, как вы его прочитаете, но до того, как вы вернете измененную версию? Когда вы записываете свою версию, он вернет все биты к тому, как они были до того, тем самым уничтожая часть информации.


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


Подобные вещи постоянно происходят в регистрах периферийного управления. Возьмем, к примеру, USART. Флаг CTS становится «1», и обработчик прерывания в рамках своей работы хочет сбросить флаг. Не спрашивайте, почему, просто примите за должное. Между тем, вы только что получили байт, и установлен флаг RXNE, указывающий на наличие ожидающих данных. Но обработчик CTS находится в середине цикла чтения-изменения-записи. Он прочитал регистр состояния и находится в процессе очистки флага CTS. Когда он запишет результат обратно, флаг RXNE будет сброшен, и появление символа может остаться незамеченным.


Дело в том, что периферийным регистрам может потребоваться осторожность при установке и сбросе битов, и самый безопасный способ сделать это в обоих случаях – использовать bit banding. Почему? Потому что изменения атомарны. То есть они происходят с отдельными битами в цикле и не могут быть прерваны. Таким образом, никогда не будет случая, когда вы прочитаете бит, который может быть изменен каким-либо другим кодом, прежде чем вы сможете записать его обратно.


Итак, как это делается? У программистов микропроцессоров 8051 был богатый набор инструкций по установке/сбросу битов, которые могли делать все это очень аккуратно, но 8051 имел крошечное адресное пространство и достаточно простую архитектуру.


В STM32 некоторая магия работает внутри, так что каждый бит в заранее определенном диапазоне памяти может быть адресован как другое место в своего рода виртуальном адресном пространстве где-то еще. Так, например, 32-битное значение, хранящееся по адресу 0x20000000, также отображается как 32 последовательных ячейки памяти, начиная с 0x22000000. Есть две области памяти, которые имеют области наложения бит бандинга. Во-первых, это 1-мегабайтная область SRAM от 0x20000000 до 0x20100000, где каждый бит является псевдонимом по байтовому адресу в диапазоне 0x22000000 - 0x23FFFFFF. Затем есть периферийное пространство от 0x40000000 - 0x40100000, которое имеет наложения таким же образом в диапазоне 0x42000000 - 0x43FFFFFF.


При использовании этой схемы чтение или запись в ячейку памяти 0x22000000 совпадает с чтением или записью в младший бит ячейки SRAM 0x20000000.


Периферийная библиотека, среди других источников, предоставляет программистам на C макросы для преобразования адресов. Они выглядят следующим образом для пространства памяти SRAM:



#define RAM_BASE 0x20000000
#define RAM_BB_BASE 0x22000000
#define Var_ResetBit_BB(VarAddr, BitNumber)
(*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 0)
#define Var_SetBit_BB(VarAddr, BitNumber)
(*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 1)
#define Var_GetBit_BB(VarAddr, BitNumber)
(*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)))

Все они хороши, но не слишком интуитивно понятны в использовании. Даже если вы понимаете, что они делают. Вместо того, чтобы возиться с ними, можно определить пару дополнительных версий, которые выглядят так:



#define varSetBit(var,bit) (Var_SetBit_BB((u32)&var,bit))
#define varGetBit(var,bit) (Var_GetBit_BB((u32)&var,bit))
#define varGetBit(var,bit) (Var_GetBit_BB((u32)&var,bit))

Использовать эти макросы довольно просто. Далее приведены все разрешенные способы их использования:



uint32_t flags;
uint32_t status;
varSetBit(flags,1);
varSetBit(flags,READY_BIT);
varClrBit(flags,3);
ready = varGetBit(flags,READY_BIT);

Интересно отметить, что макрос varGetBit – это LValue, поэтому его можно использовать в таком назначении.



varGetBit(flags,4) = y;
varGetBit(flags,ARRIVED) = varGetBit(status,READY);

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


Для доступа к битам в периферийных регистрах вы можете использовать ту же технику, но с другими базовыми регистрами. Конечно, нет никаких причин, по которым вы не могли бы определить макрос, который ссылается на конкретный бит в периферийном регистре по известному адресу. Затем вы можете получить адреса, полностью предварительно рассчитанные компилятором, и наиболее эффективный код, поскольку адреса периферийных регистров известны во время компиляции.




© digitrode.ru


Теги: STM32




Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Комментарии:

Оставить комментарий