1.把最低位置1
int byt=0x50;
写成:byt|=0x01; 或者 byt|=(0x01<<0); //要把哪位置1 就左移几位
代码:
#include
int main(void)
{
int byt=0x50; //
byt|=(0x01<<0);//要把哪位置1 就左移几位
//byt&=~(0x01<<0);//要把哪位清0 就左移几位取反 再进行与操作
printf('%x',byt);
}
2.把某位清零或置1
#include
#define SET_BIT(x,bit) (x|=(1<#define RESET_BIT(x,bit) (x&=(~(1<int main(void)
{
int bty=0x58; //0101 1000
SET_BIT(bty,0); //把第一位置1
printf('置一 %xn',bty);
RESET_BIT(bty,0);//把第一位清0
printf('清零 %xn',bty);
}
一个32位数据 字节读取操作
1.获取单字节
#include
#define GET_LOW_BYTE0(x) ((x>>0)&0x000000FF) //取第0个字节
#define GET_LOW_BYTE1(x) ((x>>8)&0x000000ff) //取第1个字节
#define GET_LOW_BYTE2(x) ((x>>16)&0x000000ff)//取第2个字节
#define GET_LOW_BYTE3(x) ((x>>24)&0x000000ff)//取第3个字节
int main(void)
{
unsigned long a =0x12345678;
printf('0x%x的第0个字节为: 0x%xn',a,GET_LOW_BYTE0(a)); //0x78
printf('0x%x的第1个字节为: 0x%xn',a,GET_LOW_BYTE1(a)); //0x56
printf('0x%x的第2个字节为: 0x%xn',a,GET_LOW_BYTE2(a)); //0x34
printf('0x%x的第3个字节为: 0x%xn',a,GET_LOW_BYTE3(a)); //0x12
}
2.获取第几位
#include
#define GET_BIT(x,bit) ((x&(1<>bit) /*获取第bit位*/
int main(void)
{
unsigned int byt=0x58; /*二进制为0101 1000 */
printf('0x%x的第0位为: %dn',byt,GET_BIT(byt,0));
printf('0x%x的第3位为: %dn',byt,GET_BIT(byt,3));
printf('0x%x的第4位为: %dn',byt,GET_BIT(byt,4));
printf('0x%x的第5位为: %dn',byt,GET_BIT(byt,5));
printf('0x%x的第6位为: %dn',byt,GET_BIT(byt,6));
printf('0x%x的第7位为: %dn',byt,GET_BIT(byt,7));
}

一个32bit数据的位,字节清零操作
1.清零某个字节
#include
//32位数据清零某个字节
#define CLEAR_LOW_BYTE0(x) (x&=0xffffff00)
#define CLEAR_LOW_BYTE1(x) (x&=0xffff00ff)
#define CLEAR_LOW_BYTE2(x) (x&=0xff00ffff)
#define CLEAR_LOW_BYTE3(x) (x&=0x00ffffff)
int main(void)
{
unsigned long a=0x11223344;
unsigned long b=0x11223344;
unsigned long c=0x11223344;
unsigned long d=0x11223344;
printf('第0个字节清空%xn',CLEAR_LOW_BYTE0(a));
printf('清空第1个字节%xn',CLEAR_LOW_BYTE1(b));
printf('第2个字节清空%xn',CLEAR_LOW_BYTE2(c));
printf('清空第3个字节%xn',CLEAR_LOW_BYTE3(d));
}

STM32寄存器配置
STM32有几套固件函数库,这些固件库函数以函数的形式进行1层或者多层封装(软件开发中很重要的思想之一:分层思想),但是到了最里面的一层就是对寄存器的配置。
我们平时都比较喜欢固件库来开发,大概是因为固件库用起来比较简单,用固件库写出来的代码比较容易阅读。
最近一段时间一直在配置寄存器,越发地发现使用寄存器来进行一些外设的配置也是很容易懂的。
使用寄存器的方式编程无非就是往寄存器的某些位置1、清零以及对寄存器一些状态位进行判断、读取寄存器的内容等。
这些基本操作在上面的例子中已经有介绍,我们依旧以实例来巩固上面的知识点(以STM32F1xx为例):
(1)寄存器配置
看一下GPIO功能的端口输出数据寄存器 (GPIOx_ODR) (x=A..E) :

假设我们要让PA10引脚输出高、输出低,可以这么做:
方法一:
GPIOA->ODR |=1<<10;/* PA10输出高(置1操作) */
GPIOA->ODR &= ~(1<<10);/* PA10输出低(清0操作) */
也可用我们上面的置位、清零的宏定义:
SET_BIT(GPIOA->ODR,10);/* PA10输出高(置1操作) */
CLEAR_BIT(GPIOA->ODR,10);/* PA10输出低(清0操作) */
方法二:
GPIOA->ODR |= (uint16_t)0x0400;/* PA10输出高(置1操作) */
GPIOA->ODR &= ~(uint16_t)0x0400;/* PA10输出低(清0操作) */
貌似第二种方法更麻烦?还得去细心地去构造一个数据。
但是,其实第二种方法其实是ST推荐我们用的方法,为什么这么说呢?因为ST官方已经把这些我们要用到的值给我们配好了,在stm32f10x.h中:

这个头文件中存放的就是外设寄存器的一些位配置。
所以我们的方法二等价于:
GPIOA->ODR |= GPIO_ODR_ODR10;/* PA10输出高(置1操作) */
GPIOA->ODR &= ~GPIO_ODR_ODR10;/* PA10输出低(清0操作) */
两种方法都是很好的方法,但方法一似乎更好理解。
配置连续几位的方法也是一样的,就不介绍了。简单介绍配置不连续位的方法,以TIM1的CR1寄存器为例:

设置CEN位为1、设置CMS[1:0]位为01、设置CKD[1:0]位为10:
TIM1->CR1 |= (0x1<<1)| (0x1<<5) |(0x2<<8);
这是组合的写法。当然,像上面一样拆开来写也是可以的。
(2)判断标志位
以状态寄存器(USART_SR) 为例:

判断RXNE是否被置位:
/* 数据寄存器非空,RXNE标志置位 */
if(USART1->SR & (1<<5))
{
/* 其它代码 */
USART1->SR &= ~(1<<5);/* 清零RXNE标志 */
}
或者:
/* 数据寄存器非空,RXNE标志置位 */
if(USART1->SR & USART_SR_RXNE)
{
/* 其它代码 */
USART1->SR &= ~USART_SR_RXNE;/* 清零RXNE标志 */
}
四、总结
以上就是本次关于位操作的一点总结笔记,有必要掌握。虽然说在用STM32的时候有库函数可以用,但是最接近芯片内部原理的还是寄存器。有可能之后有用到其它芯片没有像ST这样把寄存器相关配置封装得那么好,那就不得不直接操控寄存器了。
此外,使用库函数的方式代码占用空间大,用寄存器的话,代码占用空间小。之前有个需求,我能用的Flash的空间大小只有4KB,遇到类似这样的情况就不能那么随性的用库函数了。
i<<3 相当于把所有2进制左移3位 右边补零
左移n位相当于 乘以2的n次方 前提是数据不能丢失
i>>3 相当于把所有2进制右移3位
右移n位 相当于除以2的n次方
#define RESET_BIT(x,bit) (x&=(~(1<int main(void)
{
int bty=0x58; //0101 1000
SET_BIT(bty,0); //把第一位置1
printf('置一 %xn',bty);
RESET_BIT(bty,0);//把第一位清0
printf('清零 %xn',bty);
}
一个32位数据 字节读取操作
1.获取单字节
#include
#define GET_LOW_BYTE0(x) ((x>>0)&0x000000FF) //取第0个字节
#define GET_LOW_BYTE1(x) ((x>>8)&0x000000ff) //取第1个字节
#define GET_LOW_BYTE2(x) ((x>>16)&0x000000ff)//取第2个字节
#define GET_LOW_BYTE3(x) ((x>>24)&0x000000ff)//取第3个字节
int main(void)
{
unsigned long a =0x12345678;
printf('0x%x的第0个字节为: 0x%xn',a,GET_LOW_BYTE0(a)); //0x78
printf('0x%x的第1个字节为: 0x%xn',a,GET_LOW_BYTE1(a)); //0x56
printf('0x%x的第2个字节为: 0x%xn',a,GET_LOW_BYTE2(a)); //0x34
printf('0x%x的第3个字节为: 0x%xn',a,GET_LOW_BYTE3(a)); //0x12
}
2.获取第几位
#include
#define GET_BIT(x,bit) ((x&(1<>bit) /*获取第bit位*/
int main(void)
{
unsigned int byt=0x58; /*二进制为0101 1000 */
printf('0x%x的第0位为: %dn',byt,GET_BIT(byt,0));
printf('0x%x的第3位为: %dn',byt,GET_BIT(byt,3));
printf('0x%x的第4位为: %dn',byt,GET_BIT(byt,4));
printf('0x%x的第5位为: %dn',byt,GET_BIT(byt,5));
printf('0x%x的第6位为: %dn',byt,GET_BIT(byt,6));
printf('0x%x的第7位为: %dn',byt,GET_BIT(byt,7));
}

一个32bit数据的位,字节清零操作
1.清零某个字节
#include
//32位数据清零某个字节
#define CLEAR_LOW_BYTE0(x) (x&=0xffffff00)
#define CLEAR_LOW_BYTE1(x) (x&=0xffff00ff)
#define CLEAR_LOW_BYTE2(x) (x&=0xff00ffff)
#define CLEAR_LOW_BYTE3(x) (x&=0x00ffffff)
int main(void)
{
unsigned long a=0x11223344;
unsigned long b=0x11223344;
unsigned long c=0x11223344;
unsigned long d=0x11223344;
printf('第0个字节清空%xn',CLEAR_LOW_BYTE0(a));
printf('清空第1个字节%xn',CLEAR_LOW_BYTE1(b));
printf('第2个字节清空%xn',CLEAR_LOW_BYTE2(c));
printf('清空第3个字节%xn',CLEAR_LOW_BYTE3(d));
}

STM32寄存器配置
STM32有几套固件函数库,这些固件库函数以函数的形式进行1层或者多层封装(软件开发中很重要的思想之一:分层思想),但是到了最里面的一层就是对寄存器的配置。
我们平时都比较喜欢固件库来开发,大概是因为固件库用起来比较简单,用固件库写出来的代码比较容易阅读。
最近一段时间一直在配置寄存器,越发地发现使用寄存器来进行一些外设的配置也是很容易懂的。
使用寄存器的方式编程无非就是往寄存器的某些位置1、清零以及对寄存器一些状态位进行判断、读取寄存器的内容等。
这些基本操作在上面的例子中已经有介绍,我们依旧以实例来巩固上面的知识点(以STM32F1xx为例):
(1)寄存器配置
看一下GPIO功能的端口输出数据寄存器 (GPIOx_ODR) (x=A..E) :

假设我们要让PA10引脚输出高、输出低,可以这么做:
方法一:
GPIOA->ODR |=1<<10;/* PA10输出高(置1操作) */
GPIOA->ODR &= ~(1<<10);/* PA10输出低(清0操作) */
也可用我们上面的置位、清零的宏定义:
SET_BIT(GPIOA->ODR,10);/* PA10输出高(置1操作) */
CLEAR_BIT(GPIOA->ODR,10);/* PA10输出低(清0操作) */
方法二:
GPIOA->ODR |= (uint16_t)0x0400;/* PA10输出高(置1操作) */
GPIOA->ODR &= ~(uint16_t)0x0400;/* PA10输出低(清0操作) */
貌似第二种方法更麻烦?还得去细心地去构造一个数据。
但是,其实第二种方法其实是ST推荐我们用的方法,为什么这么说呢?因为ST官方已经把这些我们要用到的值给我们配好了,在stm32f10x.h中:

这个头文件中存放的就是外设寄存器的一些位配置。
所以我们的方法二等价于:
GPIOA->ODR |= GPIO_ODR_ODR10;/* PA10输出高(置1操作) */
GPIOA->ODR &= ~GPIO_ODR_ODR10;/* PA10输出低(清0操作) */
两种方法都是很好的方法,但方法一似乎更好理解。
配置连续几位的方法也是一样的,就不介绍了。简单介绍配置不连续位的方法,以TIM1的CR1寄存器为例:

设置CEN位为1、设置CMS[1:0]位为01、设置CKD[1:0]位为10:
TIM1->CR1 |= (0x1<<1)| (0x1<<5) |(0x2<<8);
这是组合的写法。当然,像上面一样拆开来写也是可以的。
(2)判断标志位
以状态寄存器(USART_SR) 为例:

判断RXNE是否被置位:
/* 数据寄存器非空,RXNE标志置位 */
if(USART1->SR & (1<<5))
{
/* 其它代码 */
USART1->SR &= ~(1<<5);/* 清零RXNE标志 */
}
或者:
/* 数据寄存器非空,RXNE标志置位 */
if(USART1->SR & USART_SR_RXNE)
{
/* 其它代码 */
USART1->SR &= ~USART_SR_RXNE;/* 清零RXNE标志 */
}
四、总结
以上就是本次关于位操作的一点总结笔记,有必要掌握。虽然说在用STM32的时候有库函数可以用,但是最接近芯片内部原理的还是寄存器。有可能之后有用到其它芯片没有像ST这样把寄存器相关配置封装得那么好,那就不得不直接操控寄存器了。
此外,使用库函数的方式代码占用空间大,用寄存器的话,代码占用空间小。之前有个需求,我能用的Flash的空间大小只有4KB,遇到类似这样的情况就不能那么随性的用库函数了。
i<<3 相当于把所有2进制左移3位 右边补零
左移n位相当于 乘以2的n次方 前提是数据不能丢失
i>>3 相当于把所有2进制右移3位
右移n位 相当于除以2的n次方
int main(void)
{
int bty=0x58; //0101 1000
SET_BIT(bty,0); //把第一位置1
printf('置一 %xn',bty);
RESET_BIT(bty,0);//把第一位清0
printf('清零 %xn',bty);
}
一个32位数据 字节读取操作
1.获取单字节
#include
#define GET_LOW_BYTE0(x) ((x>>0)&0x000000FF) //取第0个字节
#define GET_LOW_BYTE1(x) ((x>>8)&0x000000ff) //取第1个字节
#define GET_LOW_BYTE2(x) ((x>>16)&0x000000ff)//取第2个字节
#define GET_LOW_BYTE3(x) ((x>>24)&0x000000ff)//取第3个字节
int main(void)
{
unsigned long a =0x12345678;
printf('0x%x的第0个字节为: 0x%xn',a,GET_LOW_BYTE0(a)); //0x78
printf('0x%x的第1个字节为: 0x%xn',a,GET_LOW_BYTE1(a)); //0x56
printf('0x%x的第2个字节为: 0x%xn',a,GET_LOW_BYTE2(a)); //0x34
printf('0x%x的第3个字节为: 0x%xn',a,GET_LOW_BYTE3(a)); //0x12
}
2.获取第几位
#include
#define GET_BIT(x,bit) ((x&(1<>bit) /*获取第bit位*/
int main(void)
{
unsigned int byt=0x58; /*二进制为0101 1000 */
printf('0x%x的第0位为: %dn',byt,GET_BIT(byt,0));
printf('0x%x的第3位为: %dn',byt,GET_BIT(byt,3));
printf('0x%x的第4位为: %dn',byt,GET_BIT(byt,4));
printf('0x%x的第5位为: %dn',byt,GET_BIT(byt,5));
printf('0x%x的第6位为: %dn',byt,GET_BIT(byt,6));
printf('0x%x的第7位为: %dn',byt,GET_BIT(byt,7));
}

一个32bit数据的位,字节清零操作
1.清零某个字节
#include
//32位数据清零某个字节
#define CLEAR_LOW_BYTE0(x) (x&=0xffffff00)
#define CLEAR_LOW_BYTE1(x) (x&=0xffff00ff)
#define CLEAR_LOW_BYTE2(x) (x&=0xff00ffff)
#define CLEAR_LOW_BYTE3(x) (x&=0x00ffffff)
int main(void)
{
unsigned long a=0x11223344;
unsigned long b=0x11223344;
unsigned long c=0x11223344;
unsigned long d=0x11223344;
printf('第0个字节清空%xn',CLEAR_LOW_BYTE0(a));
printf('清空第1个字节%xn',CLEAR_LOW_BYTE1(b));
printf('第2个字节清空%xn',CLEAR_LOW_BYTE2(c));
printf('清空第3个字节%xn',CLEAR_LOW_BYTE3(d));
}

STM32寄存器配置
STM32有几套固件函数库,这些固件库函数以函数的形式进行1层或者多层封装(软件开发中很重要的思想之一:分层思想),但是到了最里面的一层就是对寄存器的配置。
我们平时都比较喜欢固件库来开发,大概是因为固件库用起来比较简单,用固件库写出来的代码比较容易阅读。
最近一段时间一直在配置寄存器,越发地发现使用寄存器来进行一些外设的配置也是很容易懂的。
使用寄存器的方式编程无非就是往寄存器的某些位置1、清零以及对寄存器一些状态位进行判断、读取寄存器的内容等。
这些基本操作在上面的例子中已经有介绍,我们依旧以实例来巩固上面的知识点(以STM32F1xx为例):
(1)寄存器配置
看一下GPIO功能的端口输出数据寄存器 (GPIOx_ODR) (x=A..E) :

假设我们要让PA10引脚输出高、输出低,可以这么做:
方法一:
GPIOA->ODR |=1<<10;/* PA10输出高(置1操作) */
GPIOA->ODR &= ~(1<<10);/* PA10输出低(清0操作) */
也可用我们上面的置位、清零的宏定义:
SET_BIT(GPIOA->ODR,10);/* PA10输出高(置1操作) */
CLEAR_BIT(GPIOA->ODR,10);/* PA10输出低(清0操作) */
方法二:
GPIOA->ODR |= (uint16_t)0x0400;/* PA10输出高(置1操作) */
GPIOA->ODR &= ~(uint16_t)0x0400;/* PA10输出低(清0操作) */
貌似第二种方法更麻烦?还得去细心地去构造一个数据。
但是,其实第二种方法其实是ST推荐我们用的方法,为什么这么说呢?因为ST官方已经把这些我们要用到的值给我们配好了,在stm32f10x.h中:

这个头文件中存放的就是外设寄存器的一些位配置。
所以我们的方法二等价于:
GPIOA->ODR |= GPIO_ODR_ODR10;/* PA10输出高(置1操作) */
GPIOA->ODR &= ~GPIO_ODR_ODR10;/* PA10输出低(清0操作) */
两种方法都是很好的方法,但方法一似乎更好理解。
配置连续几位的方法也是一样的,就不介绍了。简单介绍配置不连续位的方法,以TIM1的CR1寄存器为例:

设置CEN位为1、设置CMS[1:0]位为01、设置CKD[1:0]位为10:
TIM1->CR1 |= (0x1<<1)| (0x1<<5) |(0x2<<8);
这是组合的写法。当然,像上面一样拆开来写也是可以的。
(2)判断标志位
以状态寄存器(USART_SR) 为例:

判断RXNE是否被置位:
/* 数据寄存器非空,RXNE标志置位 */
if(USART1->SR & (1<<5))
{
/* 其它代码 */
USART1->SR &= ~(1<<5);/* 清零RXNE标志 */
}
或者:
/* 数据寄存器非空,RXNE标志置位 */
if(USART1->SR & USART_SR_RXNE)
{
/* 其它代码 */
USART1->SR &= ~USART_SR_RXNE;/* 清零RXNE标志 */
}
四、总结
以上就是本次关于位操作的一点总结笔记,有必要掌握。虽然说在用STM32的时候有库函数可以用,但是最接近芯片内部原理的还是寄存器。有可能之后有用到其它芯片没有像ST这样把寄存器相关配置封装得那么好,那就不得不直接操控寄存器了。
此外,使用库函数的方式代码占用空间大,用寄存器的话,代码占用空间小。之前有个需求,我能用的Flash的空间大小只有4KB,遇到类似这样的情况就不能那么随性的用库函数了。
i<<3 相当于把所有2进制左移3位 右边补零
左移n位相当于 乘以2的n次方 前提是数据不能丢失
i>>3 相当于把所有2进制右移3位
右移n位 相当于除以2的n次方
上一篇:[课程-014位带操作——GPIO的输入和输出控制]
下一篇:SPI通信四大模式
推荐阅读最新更新时间:2026-03-25 12:05
- 用于 7VIN 至 16VIN、1.5V 和 1.2V 输出的 LTM4628EV DC/DC 模块稳压器的典型应用电路
- 使用 Analog Devices 的 LTC3728LIGN 的参考设计
- DER-406 - 适用于 A19 灯的 5.76 W 高 PF 非隔离降压-升压型 TRIAC 调光 LED 驱动器
- ADR5045B 5V 输出精密微功率并联模式电压基准的典型应用
- LT3970EDDB-3.42 2.5V 降压转换器的典型应用
- MC78M08BDTG 8V 电流调节器的典型应用
- LT1021DCN8-5 精密电压基准的典型应用
- DER-282 - 100W, 扁平(11 mm), LLC DC-DC转换器
- REF193 低压差开尔文连接电压基准的典型应用电路
- LT3088EM 线性稳压器用于添加软启动的典型应用

STC8H全系列单片机应用手册
使用PIC单片机控制心电前端ADS1192的实现单导心电测量
Follow me第三季第4期任务
现代雷达系统的信号设计
BFR340T






京公网安备 11010802033920号