寄存器位读写,结构体位域定义,位域操作,位操作

STM8 GPIO位操作优化:通过修改头文件实现高效寄存器访问 在嵌入式系统开发中,GPIO(通用输入输出)是连接微控制器与外部世界的基础接口,其操作效率直接影响系统性能。传统库函数调用虽然可读性好,但在频繁操作场景下存在开销问题,而直接寄存器操作又降低了代码可维护性。通过C语言的结构体位域和联合体特性,可以实现一种零成本抽象层,将硬件寄存器映射为可直接访问的位成员,在编译后生成高效的位操作指令。这种技术方案在实时性要求高的嵌入式场景中价值显著,既能保持底层硬件操作的性能,又能提升代码的清晰度和安全性。本文以STM8系列MCU为例,深入解析如何通过修改官方头文件stm8 阅读详情

文章来自http://book.51cto.com/art/201012/237939.htm,个人意见:C语言结构体位域操作寄存器仅供参考,能不用则不用~


1.2.3 添加位域结构体

1)增加位域定义

我们经常需要直接访问寄存器中的某个位域。C281x C/C++头文件及外设示例所涉及的位域结构体方法,为多数片上外设寄存器提供了位域定义。例如,可以为CPU 定时器(CPU-Timer)中的每个寄存器定义一个位域结构体类型。CPU 定时器(CPU-Timer)控制寄存器的位域定义如下所示:

//*****************************************************************************  
//DSP281x_headers\include\DSP281x_CpuTimers.h CPU 定时器头文件  
//*****************************************************************************  
struct TCR_BITS //定义一个TCR_BITS 结构体类型(不是变量)  
{ Uint16 rsvd1:4; //3:0 保留,从最低位开始,顺序取位到最高位。取低4 位  
Uint16 TSS:1; //4 定时器开始/停止,取第5 位  
Uint16 TRB:1; //5 定时器重装,取第6 位  
Uint16 rsvd2:4; //9:6 保留,取第7 位到第10 位  
Uint16 SOFT:1; //10 仿真模式,取第11 位  
Uint16 FREE:1; //11 仿真模式,取第12 位  
Uint16 rsvd3:2; //12:13 保留,取第13 位到第14 位  
Uint16 TIE:1; //14 输出使能,取第15 位  
Uint16 TIF:1; //15 中断标志,取第16 位  
}; 


然后,通过共用体进行声明,以便访问位域结构体定义的各个成员或者16 位或32位寄存器的值。例如,定时器的控制寄存器共用体如下所示:

//*****************************************************************************  
//DSP281x_headers\include\DSP281x_CpuTimers.h CPU 定时器头文件  
//*****************************************************************************  
union TCR_REG //定义共用体类型TCR_REG(不是变量)  
{ Uint16 all;  
struct TCR_BITS bit; //bit 是一个具有TCR_BITS 结构体类型的变量  
};  
//all 和bit 是共用体的两个成员,它们都是16 位结构,占用内存的同一单元


一旦每个寄存器的位域结构体类型和共用体的定义都建立起来了,则在CPU 定时器(CPU-Timer)的寄存器结构体类型中,各个成员可通过采用共用体定义的形式重写:

//*****************************************************************************  
//DSP281x_headers\include\DSP281x_CpuTimers.h CPU 定时器头文件  
//*****************************************************************************  
struct CPUTIMER_REGS  
{ union TIM_GROUP TIM; //定时器计数寄存器,TIM 是一个具有 TIM_GROUP 共  
//用体类型的变量  
union PRD_GROUP PRD; //定时器周期寄存器  
union TCR_REG TCR; //定时器控制寄存器  
Uint16 rsvd1; //保留  
union TPR_REG TPR; //定时器预定标寄存器低位  
union TPRH_REG TPRH; //定时器预定标寄存器高位  
}; 

现在,既可以通过C 代码以位域的方法访问CpuTimer 寄存器中的某位,也可以对整个寄存器进行访问:

//*****************************************************************************  
//用户源文件  
//*****************************************************************************  
CpuTimer0Regs.TCR.bit.TSS = 1; //访问一个单独的位域的示例  
CpuTimer0Regs.TCR.all = TSS_MASK; //访问整个寄存器的示例 

采用位域结构体的方法具有以下优点:

(1)无须用户确定掩模值,就可对位域进行操作;

(2)可在CCS 观察窗中看到寄存器和位域的值;

(3)当使用CCS 时,编辑器会提供一张现有结构体/位域成员的列表以供选择。这一功能是CCS 自动完成的,它使编写代码变得更容易,而不必查阅寄存器和位域名文件。

掩模值是指位掩码(位屏蔽码),在下面的代码段中,常数TCR_MASK 是位掩码是用于置位或清除较大字段中的一个特殊位的常数值。

#define TCR_MASK 0x0010  
…  
CpuTimer0Regs.TCR.all = TCR_MASK; 

2)使用位域时,“读—修改—写”的注意事项当对寄存器中的单个位域进行写操作时,硬件将执行一个读—修改—写的操作,即读出寄存器中的内容,修改单个位域的值及回写整个寄存器。上述操作在F28x 上的单个周期内完成。当发生回写操作时,寄存器内的其他位将被写入读出时所读到的同一个数值。有些寄存器没有采用共用体定义,是因为不推荐采用这种方式访问,也存在一些例外情况,包括:

(1)具有写1 清除位的寄存器,如事件管理标志寄存器;

(2)无论在什么时候访问寄存器,都必须用特殊方式对位进行写入操作的寄存器,如看门狗控制寄存器。

没有位域结构体和共用体定义的寄存器,不使用*.bit 或*.all 名称进行访问,例如:

//*****************************************************************************  
//用户源文件  
//*****************************************************************************  
SysCtrlRegs.WDCR = 0x0068; 

3)代码长度考量

采用位域定义访问寄存器,可使代码变得易读、易修改和易维护。当需要对寄存器中单独某位域进行访问或者查询时,使用这种方法也非常有效。然而,值得注意的是:当对一个寄存器进行一定数量的访问时,使用*.bit 位域定义形式进行访问将导致比使用*.all 形式对寄存器进行写操作需要更多的代码,例如:

//*****************************************************************************  
//用户源文件  
//*****************************************************************************  
CpuTimer0Regs.TCR.bit.TSS = 1; //1 = 停止定时器  
CpuTimer0Regs.TCR.bit.TRB = 1; //1 = 重装定时器  
CpuTimer0Regs.TCR.bit.SOFT = 1; //当SOFT=1 且FREE=1 时,定时器自由运行  
CpuTimer2Regs.TCR.bit.FREE = 1;  
CpuTimer2Regs.TCR.bit.TIE = 1; //1 = 使能定时器中断
采用上述的方法,可以得到可读性非常强并且易于修改的代码。不足是代码有些长。如果用户更加关心代码的长度,可使用*.all 结构对寄存器进行一次性的写操作。

CpuTimer0Regs.TCR.all = TCR_MASK; //TCR_MASK 可在文件头部用#define 定义 

1.2.4 共用体结构体位域的应用实例

【例】设count 是一个16 位的无符号整型计数器,最大计数为十六进制0xffff,要求将这个计数值以十六进制半字节的形式分解出来。

对于上述实例通常采用移位的方法求解,而采用共用体结构体位域的方法不需要通过移位运算。以下,对CCS 在头文件中大量使用的共用体结构体位域进行注解。

先定义一个共用体结构体位域:

Uint16 cont,g,s,b,q; //16 位无符号整型变量定义  
cont=0xfedc; //对cont 赋值  
…  
union //共用体类型定义  
{ Uint16 i; //定义i 为16 位无符号整型变量  
struct //结构体类型定义  
{  
Uint16 low:4; //最低4 位在前。从最低4 位开始,取每4 位构成半字节  
Uint16 mid0:4;  
Uint16 mid1:4;  
Uint16 high:4; //最高4 位在后  
}HalfByte; //HalfByte 为具有所定义的结构体类型的变量  
}Count; //Count为具有所定义的共用体类型的变量

union 定义一个共用体类型,它包含两个成员:一个是16 位无符号整型变量i,另一个是包含4 个半字节变量(low,mid0,mid1,high)的结构体类型。它们占用同一个内存单元,通过对i(Count.i)进行赋值,可以完成对结构体4 个变量的赋值。

上面的程序,在定义共用体类型和结构体类型的同时,直接完成了这两个类型变量的定义,而未定义共用体和结构体类型名。即HalfByte 是一个具有所定义的结构体类型的变量,Count 是一个具有所定义的共用体类型的变量。理解了共用体与结构体之间的关系,下面的赋值指令就清楚了。

Count.i = cont; //对共用体类型成员i 进行赋值

g = Count.HalfByte.low; //将cont 的0~3 位赋值给g,g=0x000c 
s = Count.HalfByte.mid0; //将cont 的4~7 位赋值给s,s=0x000d 
b = Count.HalfByte.mid1; //将cont 的8~11 位赋值给b,b=0x000e 
q = Count.HalfByte.high; //将cont 的12~15 位赋值给q,q=0x000f 

通过共用体结构体定义,当对共用体类型成员i 进行赋值时,由于结构体类型变量HalfByte 与i 占用同一个内存单元,因此,也就完成了对HalfByte 的各成员的赋值。

C 语言的共用体结构体位域定义,可以完成对寄存器位域的访问。至于被访问的位域在内存中的具体位置则由编译器安排,编程者可以不必关注。

下面是一个访问寄存器位域的例子,供读者参考。

先建立一个共用体结构体位域定义,将某个寄存器的16 位,从最低位到最高位分别

定义为Bit1,Bit2,…,Bit16。

union //共用体类型定义  
{ Uint16 all; //定义all 为16 位无符号整型变量  
struct //结构体类型定义  
{  
Uint16 Bit1:1; //0 位Bit1 取寄存器最低位0 位,以下顺序取1 位直到最高位  
Uint16 Bit2:1; //1  
Uint16 Bit3:1; //2  
Uint16 Bit4:1; //3  
Uint16 Bit5:1; //4  
Uint16 Bit6:1; //5  
Uint16 Bit7:1; //6  
Uint16 Bit8:1; //7  
Uint16 Bit9:1; //8  
Uint16 Bit10:1; //9  
Uint16 Bit11:1; //10  
Uint16 Bit12:1; //11  
Uint16 Bit13:1; //12  
Uint16 Bit14:1; //13  
Uint16 Bit15:1; //14  
Uint16 Bit16:1; //15  
}bit; //bit为具有所定义的结构体类型的变量  
}CtrlBit; //CtrlBit 为具有所定义的共用体类型的变量

有了上面的定义之后,要访问某一个位或某些位就很容易了。比如要置Bit4,Bit8,Bit12 及Bit16 为1,可用两种方法进行:

方法一:

CtrlBit.bit.Bit4 = 1;  
CtrlBit.bit.Bit8 = 1;  
CtrlBit.bit.Bit12 = 1;  
CtrlBit.bit.Bit16 = 1;
方法二:

CtrlBit.all = 0x8888; 

















MCU寄存器配置实战指南:从位操作到外设驱动 本文详细介绍了MCU寄存器配置的核心原理与实践技巧,涵盖内存映射、位操作和GPIO外设驱动开发。通过具体代码示例解析如何通过寄存器直接控制硬件,提升嵌入式开发效率与底层编程能力,是MCU寄存器配置的实用指南。 阅读详情

相关推荐

直接用位域映射 MCU 寄存器

本文解析了嵌入式编程中利用C语言位段(bit-field)实现寄存器位映射的核心方法。通过定义位段结构体,将32位硬件寄存器按功能拆分为命名成员(如EN、DIR等),替代传统的位运算操作,显著提升代码可读性和可维护性。文章详细说明了位段结构体的定义规则、寄存器映射原理,并对比了传统位运算写法的劣势。同时强调了位序依赖架构、保留位处理、原子性操作等关键注意事项。这种寄存器抽象方法是嵌入式编程的最佳实践之一。

qq_37999850的博客 509

记录Verilog的一些关键点

记录Verilog的一些关键点

技术考古员的博客 2050

点餐系统数据库设计--SQL Server

友情链接 1、学生成绩管理系统数据库设计–MySQL 学生成绩管理系统数据库设计–MySQL 2、医疗信息管理系统数据库–MySQL 医疗信息管理系统数据库–MySQL 3、邮件管理数据库设计–MySQL 邮件管理数据库设计–MySQL 4、SQL Server医疗信息管理系统数据库【英文版-源码】–(Medical Management System Database) SQL Server医疗信息管理系统数据库【英文版-源码】–(Medical Management System Database) 5

若如初见 8万+

共用体结构体位域的应用

1)增加位域定义 我们经常需要直接访问寄存器中的某个位域。C281x C/C++头文件及外设示例所涉及的位域结构体方法,为多数片上外设寄存器提供了位域定义。例如,可以为CPU 定时器(CPU-Timer)中的每个寄存器定义一个位域结构体类型。CPU 定时器(CPU-Timer)控制寄存器的位域定义如下所示: 1. //************************************

newnewman 2413

FFT中的精度博弈:定点数缩放、块浮点与溢出处理在Vivado IP核中的实践权衡

本文探讨了在Vivado FFT IP核中实现高精度快速傅里叶变换的关键技术,包括定点数缩放策略、块浮点模式及溢出处理机制。通过分析不同精度控制方案的资源开销与性能权衡,为工程师在医疗成像、雷达信号处理等高动态范围应用中优化FFT实现提供实践指导。

melon的博客 1097

手把手教 - VSCode 开发 GD32F450 单片机

系统版本:rt-thread v5.2.2单片机型号:GD32F450ZK基于文章进行开发。

weixin_46158019的博客 841

存储器的类型

存储器分为内部存储器和外部存储器两类。 1、内部存储器速度快,但断电后存储的数据丢失。如内存。 内存储器有很多类型。随机存取存储器( RAM)在计算期间被用作高速暂存记忆区。数据可以在RAM中存储、读取和用新的数据代替。当计算机在运行时RAM是可得到的。它包含了放置在计算机此刻所处理的问题处的信息。大多数RAM是“不稳定的”,这意味着当关闭计算机时信息将会丢失。只读存储器(ROM)是稳定的。它被用于存储计算机在必要时需要的指令集。存储在ROM内的信息是硬接线的”(即,它是电子元件的一个物理组

m0_73482761的博客 467

基于Matlab-Simulink的大林算法控制系统设计与振铃消除实验

本文详细介绍了基于Matlab-Simulink的大林算法控制系统设计与振铃消除实验。通过分析纯滞后系统的控制难题,探讨大林算法在工业控制中的应用优势,并提供Matlab-Simulink环境搭建、控制器D(z)设计、振铃消除及仿真实验的完整流程。文章还对比了大林算法与PID控制的性能差异,为工程实践提供参考。

dropout6artist的博客 796

Verilog学习笔记

这是我在学习Verilog语言过程中做的一些笔记,用的参考书是《EDA技术与Verilog设计 第二版》。笔记中提取了一些书中的内容,并加入了自己的理解。

qq_60205969的博客 1507

opencv实现“找茬”

// 111.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include #include #include using namespace std; #define CVX_RED CV_RGB(0xff,0x00,0x00) #define CVX_BLUE CV_RGB(0x00,0x00,0xff)//定义宏,轮廓边框颜色 in

yanHa007的专栏 2094

BUSCO评估基因组的完整性

因为数据库收集的时候找的就是在不同的物种间都是单拷贝的基因,所以说在评估的物种里面应该也是单拷贝的状态才对。基因组层面评估:先去基因组上预测这些基因,再去做评估,如果说给他是一个注释好的基因集,这个时候提供的是一个蛋白集合,它就直接是去进行同源搜索,再去评估,看BUSCO数据库里面的这些单拷贝基因有或者无;首先,tblastn也是同源搜索,同源搜索之后,去对转录本进行读码框预测,预测完的结果还是去进行hmmsearch的搜索(hmmsearch是做蛋白质结构域搜索的),最终拿到BUSCO评估结果。

qq_64400864的博客 2959

@3.verilog数据类型

verilog 数据类型

sdl0358的博客 1402

【Audio driver】深入解析mixer_paths.xml中的音频通路配置

本文深入解析了Android音频驱动开发中的核心配置文件mixer_paths.xml。文章以高通平台为例,详细阐述了音频通路(FE、BE、DSP、Device)的概念与连接逻辑,并通过实战案例拆解了扬声器与麦克风通路的配置方法、关键控件作用及调试技巧,是理解与定制Android音频系统的实用指南。

ik6789012的博客 357

c语言读寄存器某一位的值,寄存器位读写,结构体位域定义,位域操作,位操作...

文章来自http://book.51cto.com/art/201012/237939.htm,个人意见:C语言结构体位域操作寄存器仅供参考,能不用则不用~1.2.3 添加位域结构体1)增加位域定义我们经常需要直接访问寄存器中的某个位域。C281x C/C++头文件及外设示例所涉及的位域结构体方法,为多数片上外设寄存器提供了位域定义。例如,可以为CPU 定时器(CPU-Timer)中的每个寄存器定...

weixin_39946266的博客 3708

ComfyUI报错ModuleNotFoundError: No module named ‘comfy.options‘

好家伙,我一想我刚刚好像用了pip安装了comfy模块,不会是和ComfyUI自带的comfy模块产生冲突了吧。(3)还有的说是项目不是最新版,叫更新到最新版本的,好家伙,我报错之前根本没更新也没错,突然就报错的。(4)还有的说没有comfy.options这部分代码,叫自己去拉一个,我一看也有啊。我也不知道啥时候脑抽风去装这么个东西的,幸好我聪明机智,一眼就知道了。看把我气的,然后网上搜集各种资料,一个一个去试。

满目星辰 5158

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

寄存器操作

1.#define方法 1 寄存器地址的定义:     #define UART_BASE_ADRS (0x10000000)     /* 串口的基地址 */     #define UART_RHR *(volatile unsigned char *)(UART_BASE_ADRS + 0)  /* 数据接受寄存器 */     #define UART_THR *(

lzh445096的博客 2155

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

C语言位域在驱动编写中的应用

场景 比如有以下寄存器0111 0110-》1011 0110 想把func1(2bit)赋值成10,一般这样写: data = reg; data |= (1<<7); data &= ~(1<<6); reg = data; 位域操作 有位域就可以这样操作 1.在h文件定义寄存器 #define REG_ADDR 0x15U typedef struct { uint8_t func1 : 2; uint8_t func2 : 1;

jiang_2018的博客 285

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

学习所有用c语言定义stm32的语句

每个外设都有一段连续的地址空间,首先需要定义其起始地址。// 外设总线基地址(以 STM32F1 系列为例)uint32_t0x40000000// 外设总线基地址#define APB1PERIPH_BASE PERIPH_BASE // APB1 总线。

2501_91812691的博客 190

深入解析TMS320F28379D寄存器操作:以ADC寄存器为例的C语言实践指南

本文以TMS320F28379D的ADC模块为例,深入解析了DSP开发中寄存器操作的核心原理与实践方法。通过剖析位域定义、联合体应用及寄存器结构体组织,提供了从理解到实战的C语言编程指南,帮助开发者掌握底层硬件控制能力,实现精细化的时序与功耗管理。

tensorflowjs6的博客 440

Linux 传感器驱动1:寄存器读写访问与位操作简易总结

本文介绍了嵌入式开发中寄存器操作的核心技术与实践方法,重点围绕Linux内核的Regmap框架展开。主要内容包括:1. Regmap框架的使用方法,包括初始化配置和基本API调用;2. 寄存器位操作技巧,如掩码生成和位域处理;3. 多字节寄存器读写的大小端处理;4. 寄存器值的拆分与合并;5. 寄存器间接访问的Bank机制及其在ICM-42670陀螺仪中的应用。文章通过具体代码示例,详细讲解了如何利用Linux内核提供的工具高效地进行寄存器操作,特别强调了跨总线兼容性和位域处理等实用技巧,为嵌入式开发提供了

qq_52763245的博客 1498

STM32F0利用C语言位域实现仿位带操作,编写STM32通用的sys.h,同正点原子的sys.h函数名和功能一致,方面程序移植

一、前言 平常使用STM32F1和F4,程序不少参考的正点原子的教程,代码里都包含头文件他们的sys.h,这里面主要是实现了stm32的位带操作,位带是啥,博主也没深入研究,但是就是能直接读写GPIO口的某一位,例如: PAout(1) = 1;//GPIOA Pin1 输出高电平 if(PAin(2)==1);//判断GPIOA Pin2 是否为高电平 用起来清晰直观,但是最近使用ST...

yxy244的博客 1万+

寄存器位级访问的完美解决方案:位域结构体

​高效可读性差​。

m0_56508437的博客 938

C语言结构体位域(位段)使用

C语言中的结构体位域(也称为位段)是一种特殊的数据结构,它允许程序员以位为单位来定义和操作结构体中的成员变量。位域的使用主要目的是为了节省存储空间和实现对硬件寄存器的直接访问,尤其是在嵌入式系统编程中非常有用。位域的声明允许你定义一个整数类型的数据成员,但只占用你指定的几位,而不是整个字节或更大的存储单元。

1455

C语言 位域——《跟老吕学C》

位域的定义通常是在结构体内部,通过指定数据类型和位数来完成的。struct {// 1位标志位// 2位标志位// 4位计数器// 填充位,没有名字// 正常大小的整型在上面的示例中,flag1是一个1位的无符号整数,flag2是一个2位的无符号整数,count是一个4位的无符号整数。: 2是一个没有名字的2位填充位,它通常用于确保后续成员在内存中的对齐。// 注意这里只能赋值为0、1、2或3// 注意这里只能赋值为0-15之间的值。

Python老吕的博客 1211
上一篇: 视频编码的守望者——专访x264核心开发者“Dark Shikari”詹森·盖瑞特-格拉泽
下一篇: 软件实现PWM信号,LED闪烁
#Page#
博客等级 码龄23年 1691粉丝 360原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值