认识STM32

天大地大妈咪最大 提交于 2020-08-18 09:09:45

认识STM32 (内部)

一、STM32内部

芯片里面有什么:内核(ARM的Cortex-M3)+外设。内核相当于电脑主板上的CPU;外设相当于显卡、内存。内核和外设之间通过总线连接。

在这里插入图片描述

1、ICode总线

程序存在Flash中,通过ICode(Instruction Code)总线与Cortex连接。

2、驱动单元

DCode总线

数据被存放在外设内部Flash(SRAM)中,通过DCode(Data Code)访问。

System总线

访问外设的寄存器,通常说的寄存器编程就是用这条总线的。

DMA总线

数据变量拷贝时可以不占用CPU,通过DMA(Direct Memory Access)总线和DMA1、DMA2完成。

3、被动单元

闪存存储器

即FLASH存放程序指令,内核通过ICode来读取指令。

SRAM

存放变量,内核通过DCode来访问。

FSMC

可以用来扩展内存。

AHB到APB的桥

挂着很多stm32特色外设,如:GPIO、串口、IIC、SPI等。学STM32的重点就是学编程这些外设去驱动外部的各种设备。

二、存储器映射

1、寄存器操作

操作单片机的本质都是操作存储中的寄存器,实际的操作过程就是改变内存中一定的地址对应的值。最原始的办法是直接访问地址取值进行修改。

例如*(unsigned int *)(0x40010c0c) = 0xffff

2、存储器地址划分

存储器本身不具有地址信息,地址由芯片厂家分配,给存储器分配地址。STM32具有32位寻址能力,寻址内存可达32Gbit,这分为了8个Block。其中Block0——2非常重要。

序号 用途
Block0 Code
Block1 SRAM
Block2 片上外设

3、存储器映射

而如果每一次的操作都通过绝对地址来访问的话过于麻烦,因此可以给不同的地址块重新命名。给对应的地址取别名重新定义的过程称为存储器映射。

例如51单片机的reg52.h就是进行存储器映射的一个头文件。(使用sfr关键字)

4、Block2片上外设

Block2连接着3条总线APB1、APB2、AHB。这些总线上挂着许多不同的外设。

三、C语言对寄存器的封装

以GPIO口的寄存器为例

1、封装总线和外设基地址

用宏定义对外设和总线的基地址进行取别名,如下

1 /* 外设基地址 */ 
2 #define PERIPH_BASE			  ((unsigned int)0x40000000) 
    
3 /* 总线基地址 */ 
4 #define APB1PERIPH_BASE        PERIPH_BASE 
5 #define APB2PERIPH_BASE       (PERIPH_BASE + 0x00010000) 
6 #define AHBPERIPH_BASE        (PERIPH_BASE + 0x00020000) 
    
7 /* GPIO 外设基地址 */ 
8 #define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800) 
9 #define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00) 

2、封装寄存器列表

如果用上述办法定义每一个GPIO的寄存器的话略显繁琐,因为每一个GPIO口都是有着一组相同功能的寄存器的。为了更方便的访问不同GPIO的寄存器,可以用C语言中结构体的语法对寄存器进行封装。如下:

 1 typedef unsigned           int uint32_t; /*无符号 32 位变量*/ 
 2 typedef unsigned short     int uint16_t; /*无符号 16 位变量*/ 
 3  
 4 /* GPIO 寄存器列表 */ 
 5 typedef struct { 
 6     uint32_t CRL;     /*GPIO 端口配置低寄存器    地址偏移: 0x00 */ 
 7     uint32_t CRH;     /*GPIO 端口配置高寄存器    地址偏移: 0x04 */ 
 8     uint32_t IDR;     /*GPIO 数据输入寄存器      地址偏移: 0x08 */ 
 9     uint32_t ODR;     /*GPIO 数据输出寄存器      地址偏移: 0x0C */ 
10     uint32_t BSRR;    /*GPIO 位设置/清除寄存器   地址偏移: 0x10 */ 
11     uint32_t BRR;     /*GPIO 端口位清除寄存器     地址偏移: 0x14 */ 
12     uint16_t LCKR;    /*GPIO 端口配置锁定寄存器   地址偏移: 0x18 */ 
13 } GPIO_TypeDef;  

之后便可以通过结构体访问寄存器了,如下:

GPIO_TypeDef * GPIOx;      //定义一个 GPIO_TypeDef 型结构体指针 GPIOx 
GPIOx = GPIOB_BASE;        //把指针地址设置为宏 GPIOH_BASE 地址 
GPIOx->IDR = 0xFFFF;        
GPIOx->ODR = 0xFFFF;        
   
uint32_t temp; 
temp = GPIOx->IDR;          //读取 GPIOB_IDR 寄存器的值到变量 temp 中 

3、修改寄存器的位操作方法

如果只希望修改某长串地址中的某一位的话可以通过如下的方式:

置1
unsigned char a = 0x02;
//a = 0000 0010
//现在希望将a的最高位置1
a |= (1<<7);//将1左移7位,然后进行或赋值
置0
unsigned char a = 0x02;
//a = 0000 0010
//现在希望将a的第2位置0
a &= ~(1<<1);//将1左移1位,取反,然后进行与赋值
取反
unsigned char a = 0x02;
//a = 0000 0010
//现在希望将a的第2位取反
a ^= (1<<1);//将1左移1位,然后进行取反赋值

如果想操作很多位的话可以如下:

unsigned char a = 0x02;
//a = 0000 0010
//现在希望将a改为0x32
a |= 0x30;
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!