https://m.toutiao.com/is/SHQwfBw/ 我们平时所常用的STM32芯片主要由内核和片上外设组成,内核通过各种总线与外设连接,芯片最重要的作用有两个,一个是根据输入在一定条件下进行运算,而另一个重要的作用就是操作这些外设对外部硬件进行控制。 ![]() 而这些可以控制外部硬件的操作就分别存储在一个个内存空间中,这些内存空间可以形象的理解为一个个“小房子”, 内核想利用“房子”中的指令就要通过地址去访问,这个地址就如同我们生活中的门牌号,你要通过门牌号找对地方,才能找到要找的人。 但是这个门牌号不是很好记忆,很容易搞错,位数越多的芯片越不方便,于是我们给这些地址重新起了别名,这就是我们常说的寄存器。 ![]() STM32外设资源丰富,一个GPIO端口下有很多控制寄存器,比如配置输入输出模式与速度的GPIO_CRL和GPIO_CRH,控制输入输出数据的GPIO_IDR和GPIO_ODR,进行端口位设置清除的GPIO_BSRR和GPIO_BRR。 寄存器可以用指针来操作,它将寄存器和指向的地址关联起来,然后对寄存器直接进行赋值操作就可以控制输入输出满足我们的开发需求。 那指针是什么?我们先简要说明下。 ![]() 指针的应用最主要就是“*”号和“&”的符号,“&”的符号表示取地址,“&a”就表示变量a的存储地址; 而“*”号要注意区分,定义时星号表示指针变量,所以定义时直接赋值需要取地址,而不是取具体值; 而在计算时,星号表示取值,“*p”就表示p指向地址中的值,所以图中第二段代码最后计算出来b就与a的数值相等了。 当内存空间较小,单片机资源有限时,比如8位51单片机,我们一般可以直接对寄存器进行赋值操作, 这就相当于我直接把房子挑出来,然后将一个数值丢进去存起来,但当单片机资源庞大,寄存器众多,操作位数变为32位或者64位,这种方式就不合适了, 一个是每次都要去查寄存器详细定义,效率低下还容易出错,再一个是程序可读性差后期维护困难,除了编写原作者其他人很难看懂。 ![]() 所以现在很多芯片厂商都推出了固件库,工程师在应用时也会根据公司产品的特点开发私有库,但无论形式如何这些都是为了减轻我们开发负担的手段。 以STM32为例,ST公司将几乎所有常用寄存器进行封装,这样绝大部分应用场合我们就只需要关心功能的开发,而不必去知道底层硬件资源都是怎么被调用的。 而这些寄存器操作函数封装中大量使用指针和结构体指针,比如单片机GPIO作输入输出时都要使用的IO口初始化函数GPIO_Init中就定义了结构体指针变量GPIO_InitStruct。 void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) GPIO_InitStruct有三个变量成员,分别是PIN脚定义、IO口通信速率、输入输出模式:
而这些变量成员可以选择的值都在特定的头文件中用宏定义或枚举的方式,将大串数字替换成了清晰易懂的英文单词,这样在用户后续进行赋值操作的时候就不用去查芯片手册再敲上一长串数字了,直接使用替代名称,直观又不容易出错: #define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */ 用户调用:(是不是很简单,不用去理会复杂的固件函数,对萌新极其友好)
说到这里,这篇文章也就结束了,可能有的同学会奇怪,为什么一定要用指针,直接赋值不是也能实现同样的效果?确实是这样的,但是直接赋值,一旦程序要进行改动或者部署到新的硬件上,就要把相关的参数全部修改一遍,因为直接赋值你的操作对象是房子中的东西(存储数据),而使用指针你的操作对象是房子(寄存器地址),什么样的东西是不确定的,而房子是有限且确定的,所以使用指针能极大提高程序的复用性和简洁程度,是让C语言变得华丽优美的关键所在。 |
|
来自: 山峰云绕 > 《电子工程与单片机io字符显示技术》