Метод, который я придумал, - сдвинуть маску, инвертировать ее,
логически И это с существующим значением регистра, логически ИЛИ
результат с немного сдвинутым новым значением.
Это или эквивалентный способ сделать это.
Я хочу объединить маску и новое значение, чтобы уменьшить количество
логические операции, операции сдвига битов. Цель также сохранить
процесс достаточно общий, так что я могу использовать для битовых операций 1,2,3
или шириной 4 бита.
Есть ли лучший способ?
Вы должны выполнить две основные цели:
- гарантирует, что биты, которые должны быть выключены в затронутом диапазоне, фактически выключены, и
- убедитесь, что биты, которые должны быть включены в затронутом диапазоне, действительно включены.
В общем случае для них требуются две отдельные операции: побитовое И для принудительного отключения битов и побитовое ИЛИ (или XOR, если биты сначала очищаются) для включения желаемых битов. Могут быть способы сокращения для определенных случаев исходных и целевых значений, но если вы хотите что-то общего назначения, как вы говорите, тогда ваши возможности ограничены.
Лично я думаю, что был бы склонен построить его из нескольких частей, отделяя выбор GPIO от фактических вычислений. Как минимум, вы можете выделить общий макрос для установки диапазона битов:
#define SETBITS32(x,bits,offset,mask) ((((uint32_t)(x)) & ~(((uint32_t)(mask)) << (offset))) | (((uint32_t)(bits)) << (offset)))
#define GPIOxMODE(gpio,mode,port) (GPIO##gpio->MODER = SETBITS32(GPIO##gpio->MODER, mode, port * 2, GPIO2BITMASK)
Но обратите внимание, что, похоже, нет хорошего способа избежать такого макроса, который оценивает некоторые из своих аргументов более одного раза. Поэтому было бы безопаснее вместо этого написать SETBITS32
как функцию. Компилятор, вероятно, встроит такую функцию в любом случае, но вы можете максимизировать вероятность этого, объявив ее static
и inline
:
static inline uint32_t SETBITS32(uint32_t x, uint32_t bits, unsigned offset, uint32_t mask) {
return x & ~(mask << offset) | (bits << offset);
}
Это также легче читать, хотя он, как и макрос, предполагает, что bits
не имеет установленных битов вне области маски.
Конечно, есть и другие, похожие формулировки. Например, если вам не нужно поддерживать прерывистые битовые диапазоны, вы можете указать битовый счет вместо битовой маски. Эта альтернатива делает это, защищает от предоставления пользователем битов за пределами указанного диапазона, а также имеет проверку некоторых параметров:
static inline uint32_t set_bitrange_32(uint32_t x, uint32_t bits, unsigned width,
unsigned offset) {
if (width + offset > 32) {
// error: invalid parameters
return x;
} else if (width == 0) {
return x;
}
uint32_t mask = ~(uint32_t)0 >> (32 - width);
return x & ~(mask << offset) | ((bits & mask) << offset);
}