如何实现对单片机寄存器的访问

2021-05-31 20:42发布

大家好,今天给初学者介绍一下寄存器的访问。我们知道单片机的控制是通过相应寄存器的配置来实现的,对吧?那么我们如何实现对寄存器的访问呢?包括我们的串口驱动都是通过对寄存器的相应的读写来实现的。

每个模块的寄存器定义都定义在某一个地址,这个地址都是固定的。然后它有这么多功能的寄存器,这些功能的寄存器也是排序也是固定的。因此我们可以定义一个一个数据结构。

333.png

我们以串口为例,这个数据结构它的状态寄存器,数据寄存器和这个表里面的排序是不是完全一致的,因为这是32位单片机,因此它全部定义成32位的变量。这是一个我们平时定义数据结构,不也是这么定义的嘛。

444.png

555.png

666.png

但是有一点大家可能注意到了,这里有一个比较特殊的__IO的标志。这个标志他就是告诉编译器每一次访问这个结构体的成员的时候啊,必须访问他的原始位置,不要进行优化。因为我们在编译代码的时候,为了提高它的效率有可能会对某些变量进行优化的,它直接访问它的中间变量,这个中间变量你是不知道的,是编译器运算过程中产生的中间变量。它可能就直接使用那个中间变量了,但是对于我们这个外设的控制呢。比如说像串口,他什么时候接收到数据,你不知道的。所以必须访问他的原始的信息。那么我们如何来访问相应的寄存器呢就是这里涉及到一个指针的概念。

你看这个地址我们看一下USART2看是他是怎么定义的。他就把这个USART2BASE的这是一个常数地址,对吧?都是一个常数,第一个状态寄存器UART2_SR定义的一个起始地址,这个是固定的,在单片机里面生产的时候就固定了。然后我们把这个地址转化为相应的指针。刚才我们看了这个数据结构,把它转换为这个数据结构的指针。

777.png

那么指针的成员呢,相当于是不是就和这个实际的寄存器匹配起来了,这个指针变量的起始地址是不是就是这个UART2_BASE地址?因此,我们对相应指针成员的访问就可以直接实现对相应的寄存器的访问了。所以我们在实际的使用过程中,我们并不需要知道它的实际地址,所有的寄存器地址头文件都给我们定义好了。同样的,对相应寄存器的访问呢,我们要也要我们也要使用这个库里面的这个库里面给我们定义的一些宏或者函数。

999.png

现在就像类似这个来判断某一位是不是置一对吧,要采用这种方法,而不是我们直接比如说这个与一下,把这两位与一下也可以判断,但我不介意大家这么做。因为你采用这种方法的话,它是通用的。你换一个芯片这个代码的从可重用性、可维护性都非常高,如果你采用这种方法的话,那就会比较差。因为在有些单片机里面,比如说这个功能定义,在这个控制寄存器1里面,在另外一个单片机里面,它可能定义在控制寄存器2里面。对吧,那你的代码就不能通用了。