当前位置:首页>Linux>Linux中的“位操作”

Linux中的“位操作”

  • 2026-08-25 20:32:29
Linux中的“位操作”

Linux驱动入门课程已更新完结!提供一对一指导!

https://www.bilibili.com/cheese/play/ss635583370

前言

很多同学在操作寄存器的时候,要设置或清除某个位时,都是手动使用左移(<<)或右移(>>)来进行操作,但其实内核中已经为我们提供了一些宏,今天我们来看一下这些宏。

位操作宏

  • BIT

#define BIT(nr)      (UL(1) << (nr))

生成第 nr 位为 1,其余位为 0 的整数

表达式

二进制结果

BIT(0)

0000 0001

BIT(1)

0000 0010

BIT(3)

0000 1000

BIT(7)

1000 0000

使用场景

#define CTRL_ENABLE     BIT(0)#define CTRL_RESET      BIT(1)#define CTRL_IRQ_EN     BIT(5)reg |= CTRL_ENABLE;reg &= ~CTRL_RESET;
#define FLAG_RUNNING    BIT(0)#define FLAG_SUSPEND    BIT(1)#define FLAG_ERROR      BIT(2)unsigned long flags;//判断标志位flags |= FLAG_RUNNING;if (flags & FLAG_ERROR) {    ...}
  • GENMASK

#define GENMASK(h, l) \  (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))#define __GENMASK(h, l) \  (((~UL(0)) - (UL(1) << (l)) + 1) & \   (~UL(0) >> (BITS_PER_LONG - 1 - (h))))

从第 h 位到第 l 位(包含 h 和 l),全部置 1,其余为 0

比如:

GENMASK(7, 4)位号: 76543210结果: 11110000

即:

0b11110000// 0xF0
  • FIELD_GET

#define FIELD_GET(_mask, _reg)            \  ({                \    (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask));  \  })

从寄存器值 reg 中提取由 mask 定义的位,并将其转换为原始数值(自动右移)。

在没有宏之前,我们需要这样写代码才能获取到寄存器中的值:

// 假设我们要设置寄存器的 [7:4] 位#define MY_FIELD_MASK  0xF0#define MY_FIELD_SHIFT 4// 提取数据val = (reg & MY_FIELD_MASK) >> MY_FIELD_SHIFT;

有了宏之后,我们就不需要关心shift (偏移)了。

比如:

有一个 8 位的寄存器,我们要获取中间 [5:2] 位的值(掩码为 0x3C,即二进制 00111100):

  1. 输入寄存器值: 10110110

  2. 应用 Mask: (10110110 & 00111100) -> 00110100

  3. 自动右移: 宏会自动计算出需要右移 2 位,结果得到 00001101 (十进制 13)。

  • FIELD_PREP

#define FIELD_PREP(_mask, _val)            \  ({                \    ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask);  \  })

将数值 val 放入 mask 定义的对应位置中(自动左移)

在没有宏之前,我们需要这样写代码才能设置寄存器值:

// 假设我们要设置寄存器的 [7:4] 位#define MY_FIELD_MASK  0xF0#define MY_FIELD_SHIFT 4// 准备写入reg |= (val << MY_FIELD_SHIFT) & MY_FIELD_MASK;

有了宏之后,和上面一样,不需要再关心shift (偏移)了。

比如:

如果要将数值 5 (二进制 101) 放入上述寄存器的 [5:2] 位:

  1. 输入数值: 00000101

  2. 自动左移: 宏根据 Mask 0x3C 自动左移 2 位 -> 00010100

举例

#include<linux/bitfield.h>/* 1. 定义掩码 (通常使用 GENMASK 宏) */#define REG_CONTROL_SPEED_MASK  GENMASK(7, 4) // 定义位 [7:4]voidupdate_speed(void __iomem *reg_addr, u32 new_speed){    u32 reg_val = readl(reg_addr);/* 2. 使用 FIELD_GET 提取当前速度 */    u32 current_speed = FIELD_GET(REG_CONTROL_SPEED_MASK, reg_val);/* 3. 使用 FIELD_PREP 准备新的寄存器值 */// 清除旧位并填入新位    reg_val &= ~REG_CONTROL_SPEED_MASK;    reg_val |= FIELD_PREP(REG_CONTROL_SPEED_MASK, new_speed);    writel(reg_val, reg_addr);}

往

期推荐

放弃全局变量!这才是 Linux 内核驱动数据管理的“正确姿势” ➠

Linux驱动之RTC驱动(PCF8563) ➠

Linux驱动之RTC子系统 ➠

最新文章

随机文章