STM32入门系列-使用C语言封装寄存器

发布者:mlgb999最新更新时间:2022-11-18 来源: zhihu关键字:STM32  入门系列  C语言 手机看文章 扫描二维码
随时随地手机看文章

前面介绍了存储器映射、寄存器和寄存器映射,这些都是为了介绍使用 C语言封装寄存器做铺垫。这里我们通过一个实例来对 C 语言封装寄存器进行介绍。


具体实例:控制 GPIOC 端口的第 0 管脚输出一个低电平。首先我们需要知道GPIOC 端口外设是挂接在哪个总线上的,然后根据总线基地址和本身的偏移地址得到 GPIOC 外设基地址,最后通过这个外设基地址得到里面各种寄存器基地址。


总线和外设基地址封装
根据寄存器的概念,我们可以使用 C 语言中的宏定义对寄存器进行定义。具体代码如下:
//定义外设基地址
#define PERIPH_BASE ((unsigned int)0x40000000) 1)
//定义 APB2 总线基地址
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000) 2)
//定义 GPIOC 外设基地址
#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800) 3)
//定义寄存器基地址 这里以 GPIOC 为例
#define GPIOC_CRL *(unsigned int*)(GPIOC_BASE+0x00) 4)
#define GPIOC_CRH *(unsigned int*)(GPIOC_BASE+0x04)
#define GPIOC_IDR *(unsigned int*)(GPIOC_BASE+0x08)
#define GPIOC_ODR *(unsigned int*)(GPIOC_BASE+0x0C)
#define GPIOC_BSRR *(unsigned int*)(GPIOC_BASE+0x10)
#define GPIOC_BRR *(unsigned int*)(GPIOC_BASE+0x14)
#define GPIOC_LCKR *(unsigned int*)(GPIOC_BASE+0x18)
上述代码中我们在后面备注了数字,下面对其进行简单介绍下其功能:

  • 定义外设的基地址,这个地址也是 Block2 的基地址。


  • 定义 APB2 总线基地址,因为 Block2 的第一个总线是 APB1,而 APB2 总线地址只需要加上对应的地址偏移量即可。


  • 定义 GPIO 外设基地址,因为 GPIOC 是挂接在 APB2 总线上的,所以找到对应的端口地址偏移量即可知道 GPIOC 端口基地址。


  • 定义 GPIO 外设寄存器基地址,这里以 GPIOC 端口为例,因GPIOC_CRL是 GPIOC 外设的第一个寄存器,所以基地址就是 GPIOC 地址,其他寄存器地址只需要在 GPIOC 基地址上加上相应的偏移量即可。

我们得到了寄存器具体的地址,那么就可以使用 C 语言指针来操作读写。例如我们需要 GPIOC0 输出一个低电平或者高电平,可以使用下面语句来操作。
//控制 GPIOC 第 0 管脚输出一个低电平
GPIOC_BSRR = (0x01<<(16+0));
//控制 GPIOC 第 0 管脚输出一个高电平
GPIOC_BSRR = (0x01<<0);


我们知道 GPIOC_BSRR 的值是这个寄存器的地址,但是编译器不知道它是地址,而是把它当做立即数,所以我们必须要强制转换为(unsigned int *)指针类型才可以对其操作,这一点特别要注意。然后再在前面加上一个“*”作取指针操作,表示对该地址内内容进行写,读操作也同样使用“*”取指针操作。如下:
unsigned int temp;
temp =GPIOC_IDR;
将寄存器内的数据保存在变量 temp 中,使用到变量时一定要进行定义。


寄存器封装
通过前面讲解,我们已经可以对寄存器进行操作,但是还稍有不足,因为STM32的GPIO比较多, 我们不可能每使用一个GPIO都做前面一样的一大堆定义。根据GPIO寄存器的特点,我们知道不论GPIOA还是GPIOB等都拥有一组功能相同的寄存器,如GPIOA_ODR/GPIOB_ODR/GPIOC_ODR等等,它们只是地址不一样。
为了更方便地访问寄存器,我们引入C语言中的结构体对寄存器进行封装,具体代码如下:
typedef unsigned int uint32_t; /*无符号 32 位变量*/
typedef unsigned short int uint16_t; /*无符号 16 位变量*/
/* GPIO 寄存器列表 */
typedef struct
{
uint32_t CRL; /*GPIO 端口配置低寄存器 地址偏移: 0x00 */
uint32_t CRH; /*GPIO 端口配置高寄存器 地址偏移: 0x04 */
uint32_t IDR; /*GPIO 数据输入寄存器 地址偏移: 0x08 */
uint32_t ODR; /*GPIO 数据输出寄存器 地址偏移: 0x0C */
uint32_t BSRR; /*GPIO 位设置/清除寄存器 地址偏移: 0x10 */
uint32_t BRR; /*GPIO 端口位清除寄存器 地址偏移: 0x14 */
uint16_t LCKR; /*GPIO 端口配置锁定寄存器 地址偏移: 0x18 */
}GPIO_TypeDef;
这段代码用 typedef 关键字声明了名为GPIO_TypeDef的结构体类型,结构体内有7 个成员变量,变量名正好对应寄存器的名字。C 语言的语法规定,结构体内变量的存储空间是连续的,其中32位的变量占用4个字节,16 位的变量占用2个字节。


于是,我们定义的GPIO_TypeDef,假如这个结构体的首地址为0x4001 1000(这也是第一个成员变量 CRL的地址),那么结构体中第二个成员变量CRH的地址即为0x4001 1000 +0x04,加上的这个0x04,正是代表CRH所占用的4个字节地址的偏移量,其它成员变量相对于结构体首地址的偏移,在上述代码右侧注释已给出。


这样的地址偏移与STM32 GPIO外设定义的寄存器地址偏移一一对应,只要给结构体设置好首地址,就能把结构体内成员的地址确定下来,然后就能以结构体的形式访问寄存器了,比如我们还是将GPIOC0输出低电平,具体代码如下:
GPIO_TypeDef * GPIOx; //定义一个GPIO_TypeDef型结构体指针GPIOx
GPIOx = GPIOC_BASE; //把指针地址设置为宏 GPIOC_BASE 地址
GPIOx->BSRR =(1<<(16+0)); //通过指针访问并修改 GPIOC_BSRR 寄存器
这段代码先用GPIO_TypeDef类型定义一个结构体指针GPIOx,并让指针指向GPIOC基地址GPIOC_BASE,地址确定下来,然后根据C语言访问结构体的内容,用GPIOx->BSRR写寄存器。为了操作更简便灵活,我们直接使用宏定义好GPIO_TypeDef类型的指针,而且指针指向各个GPIO端口的首地址,使用时我们直接用该宏访问寄存器即可。具体代码如下:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
GPIOC->BSRR = (1<<(16+0));
我们这里仅仅以GPIO这个外设为例,给大家讲解了如何使用C语言对寄存器封装,对于其他的外设也是使用同样方法。其实到了后面的实验程序的编写,我们都是使用ST公司提供的固件库,他们把STM32所有外设都已经封装好了,我们只需要调用即可。 我们这里分析这个封装过程只是想让大家更加清楚理解如何使用C来封装寄存器的。

关键字:STM32  入门系列  C语言 引用地址:STM32入门系列-使用C语言封装寄存器

上一篇:STM32入门系列-STM32外设地址映射
下一篇:STM32入门系列-创建寄存器模板

推荐阅读最新更新时间:2024-11-13 13:12

解读STM32位带操作, 让你快速了解并掌握它的用途
今天重温一下经典的位带操作,主要结合Cortex-M3内核(STM32)来讲述,相信许多朋友在初学的时候都被绕晕过。 关于位带操作,它的难点其实在于需要理解或掌握较多基础知识。当你掌握这些基础知识,它其实就不难了。 接下来带领大家掌握关于Cortex-M3的位带操作,顺便让大家回顾一下这些基础知识。 1初识位带操作 Bit-banding简称位带,有人也叫位段。支持位带操作后,可以使用普通的加载/存储指令来对单一的比特进行读写。 很多朋友是从学习51单片机过来的,都知道P1.1这个引脚可以单独控制,我们操作的这个引脚就是一个Bit位。 我们都知道在STM32中不能直接操作寄存器的某一个Bit位,比如单独控制PA端口输出数据寄存
[单片机]
解读<font color='red'>STM32</font>位带操作, 让你快速了解并掌握它的用途
STM32 通用定时器与滴答定时器
STM32 通用定时器与滴答定时器 前言:STM32包括TIM1/TIM8两个高级定时器,TIM2~TIM5四个通用定时器,TIM6/TIM7两个基本定时器,还有使用非常方便的系统滴答定时器(systick),基本操作为:配置(使能时钟/设置工作方式及初值/配置中断/开启中断和时钟/使能定时器) 计数 产生中断。 一、通用定时器使用 void TimerConfig(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 使能时钟/复位/自动重装载设置/时钟预分频设置/采样分频/up/清除中断/开启时钟 RCC_APB1PeriphClock
[单片机]
STM32的四种IO输出模式
1、普通推挽输出(GPIO_Mode_Out_PP): 使用场合:一般用在0V和3.3V的场合。线路经过两个P_MOS 和N_MOS 管,负责上拉和下拉电流。 使用方法:直接使用 输出电平:推挽输出的低电平是0V,高电平是3.3V。 2、普通开漏输出(GPIO_Mode_Out_OD): 使用场合:一般用在电平不匹配的场合,如需要输出5V的高电平。 使用方法:就需要再外部接一个上拉电阻,电源为5V,把GPIO设置为开漏模式, 当输出高组态时,由上拉电阻和电源向外输出5V的电压。 输出电平:在开漏输出模式时,如果输出为0,低电平,则使N_MOS 导通,使输 出接地。若控制输出为1(无法直接输出高电平),则既不输出高电平 也不输出
[单片机]
带有LED警示灯的骑行夜服设计
近年来,夜间骑车或跑步遭遇不幸的事件时有发生,这往往是因为在转弯或者高速行驶的过程中,汽车司机受限夜间视场,来不及避让而造成的。传统的夜行服大多采用被动方式,即由反光材料制作而成,在暗光条件下工作的人员广泛使用,由于利用的是反光原理,只有在灯光照射下才能进行反光,而且不同灯光照射产生的反光效果不同,因此大大限制了夜光服的使用场合和使用人群。有些主动发光的LED灯,发光面很小,强度较弱,无法起到好的警示作用。 本文设计了一套带有LED警示功能的骑行夜服,能够在夜晚自主发光,该夜行服可为需要在夜间出行的骑行者或跑步者起到好的保护作用。 1、系统方案总体设计 系统总体框图如图1所示,以STM32单片机为主控制核心,包括LED
[电源管理]
STM32-一文搞懂通用定时器捕获/比较通道
捕获和比较 捕获 什么是捕获 所谓捕获就是通过检测捕获通道上的边沿信号。在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCR)里面,完成一次捕获。 捕获的应用 STM32支持一下捕获模式: 输入捕获模式 PWM输入模式 输入捕获模式可以用来测量脉冲宽度或者测量频率。下图是输入捕获测量高电平脉宽的原理,假定定时器工作在向上计数模式,图中 t1~t2 时间,就是需要测量的高电平时间。 测量方法如下:首先设置定时器通道 x 为上升沿捕获,这样,t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x为下降沿捕获,这样到
[单片机]
STM32-一文搞懂通用定时器捕获/比较通道
windows下用J-link J-Flash下载STM32程序
打开“Option”—》“Project settings…”,进行烧写前的必要设置,如下图: 在“General”页,选择“USB”,如下图: 在“Target Interface”页,选择“JTAG”,JTAG速度设置为“Auto selection”,如下图 在“CPU”页,选择 开发板 的CPU型号:ST STM32F103VB,如下图: “FLASH”页不用设置,默认设置就可以了。 在“Production”页,我们把“Start application”选上,则在下载成功之后,程序会自动运行。 在设置完成后,我们点击“应用”和“确定”退出。在JLINK软件界面左边,将显示烧写
[单片机]
windows下用J-link J-Flash下载<font color='red'>STM32</font>程序
STM32定时器不准确启动时钟的问题
用STM32 F407IG 开发一硬件控制器,需要精准的定时器。我使用了IIM2,可上电启动。有时候准确,有时候开机 定时器,很慢,比如定时500MS 闪烁一次,可能3秒才闪烁一次。源代码如下。 int main(void) { GPIO_Configuration(); TIM_Config(); NVIC_TIM_Config(); while(1) { } } void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(LED1_RCC_AHBPeriph|LED2_RC
[单片机]
STM32 | 硬件SPI主从通信
例子说明及框图 本例子基于STM32F103ZET6芯片,实现SPI1与SPI2的主从通信。其中SPI1配置为主机,SPI2配置为从机,均配置为全双工模式。硬件连接图: 其中,我们需要注意的是,SPI的从机不能主动发送数据,只能应答数据。本例子的数据交互过程: 主机使用查询方式发送数据给从机。 从机使用中断接收方式接收数据,把接收到的数据加上0x05再发送给主机。 从机总是在收到主机的数据时,才会发送数据给从机。即从机被动发送数据,也即主机主动申请数据。 代码细节 主函数: int main(void) { uint8_t i = 0; //---------------------------
[单片机]
<font color='red'>STM32</font> | 硬件SPI主从通信
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
更多往期活动
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved