#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
const uint8_t aa = 0x66;
int main(void)
{
OLED_Init();
OLED_ShowHexNum(1, 1, aa, 2);
OLED_ShowHexNum(2, 1, (uint32_t)&aa, 8);
while(1)
{
}
}
小实验——来验证存储器的地址映像
- tips:C语言知识——const是关键字,在代码中使临时变量aa成为常量,这时的aa从原来的在运行内存SRAM中的地址映像——》现在的程序存储器Flash的地址映像。(存在尾部地址偏移)
- flash——存储C语言编译后的程序代码和常量数据
定义常量和变量
- 常量何时定义???
- 当程序中出现一大批数据,且不需要更改时。——advantage——节省SRAM的空间
- eg、查找表,字库数据等
- 对于变量与常量,其地址由编译器所决定,程序不同,地址不同,不固定。
- 对于外设寄存器,其地址固定,可由手册查得
OLED_ShowHexNum(2, 1, (uint32_t)&ADC1->DR, 8);
//以结构体方式访问寄存器
OLED显示:4001244C
寄存器实际地址:起始地址 + 偏移地址
如何以结构体方式访问寄存器——ADC1->DR???
40000000 + 10000 + 2400 = 40012400
有点俄罗斯套娃的意思
STM32使用结构体访问外设寄存器
现在得到基地址BASE,起始地址 + 偏移地址——STM32通过结构体来实现
- 结构体依次定义了各种寄存器,其结构体成员顺序与手册中寄存器实际存放地址一一对应。
- 即使结构体内存与外设寄存器内存完美重合。——所以——访问结构体的某个成员也就相当于访问这个外设的某个寄存器。
解析:ADC1->DR
- ADC1——结构体指针——指向ADC1外设的基地址BASE
- ->的妙用:结构体指针要使用 -> 来取成员
- DR——寄存器成员——对应于结构体成员
- 访问结构体成员,即加上其对应的地址偏移
- 所以:基地址BASE(起始地址) + 地址偏移 = 指定寄存器
- 以上是库函数访问寄存器的原理,较为麻烦和复杂
我们也可通过指针来访问某个物理地址——较为easy
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#define ADC1_DR (uint32_t *)0X4001244C //定义指针
int main(void)
{
OLED_Init();
// *ADC1_DR //利用*取指针内容
OLED_ShowHexNum(2, 1, (uint32_t)&ADC1->DR, 8);
OLED_ShowHexNum(3, 1, *ADC1_DR, 8); //error
while(1)
{
}
}
给DMA建立模块——有些不同
- 平时我们建立模块涉及外围电路,所以建立在
- DMA建立模块——不涉及外围电路,所以建立在
SRAM的地址由编译器分配,不固定,所以我们不会写绝对地址,而是通过数组名来获得地址。