提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一,stm的系统架构
`
上面的架构图主要包含:
ARM公司提供的内核:cortex-M3
ARM公司提供的总线:Icode,Dcode,System总线
ST公司提供的总线矩阵,DMA控制器,AHB总线,APB总线,以及各个外设控制器
以上整体称之为片上系统(SOC),每个模块各司其职,存在竞争与合作。
二,了解寄存器
1,寄存器的映射以及什么是寄存器
寄存器映射是在存储器映射的基础上进行的。 以STM32为例,操作硬件本质上就是操作寄存器。 在存储器片上外设区域,四字节为一个单元,每个单元对应不同的功能。 当我们控制这些单元时就可以驱动外设工作,我们可以找到每个单元的起始地址,然后通过C 语言指针的操作方式来访问这些单元。 但若每次都是通过这种方式访问地址,不好记忆且易出错。 这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名实质上就是寄存器名字。 给已分配好地址 (通过存储器映射实现)的有特定功能的内存单元取别名的过程就叫寄存器映射 。
其实给特定单元取的别名就是寄存器,寄存器只是特定功能的单元的名字罢了,操作寄存器可以驱动相应外设进行工作。
2、stm的地址映射
片上的外设区分为三条总线,根据外设速度的不同,挂载着不同的外设,APB1挂载低速外设,APB2和AHB挂载着高速外设。
- 总线基地址
- 外设基地址
总线上挂载着各种外设,这些外设也有自己的地址范围,特定外设的首个地址称为“XX 外设基地址”,也叫XX 外设的边界地址。具体有关STM32F10xx 外设的边界地址请参考《STM32F10xx 参考手册》的2.3 小节的存储器映射的表1:STM32F10xx 寄存器边界地址。
这里面我们以GPIO 这个外设来讲解外设的基地址,GPIO 属于高速的外设 ,挂载到APB2 总线
3. 外设寄存器
在XX 外设的地址范围内,分布着的就是该外设的寄存器。以GPIO 外设为例,GPIO是通用输入输出端口的简称,简单来说就是STM32 可控制的引脚,基本功能是控制引脚输出高电平或者低电平。最简单的应用就是把GPIO 的引脚连接到LED 灯的阴极,LED 灯的阳极接电源,然后通过STM32 控制该引脚的电平,从而实现控制LED 灯的亮灭。
GPIO 有很多个寄存器,每一个都有特定的功能。每个寄存器为32bit,占四个字节,在该外设的基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。这里我们以GPIOB 端口为例,来说明GPIO都有哪些寄存器。
三,新建工程
在上方点击这里新建一个工程文件
然后选择芯片后点击ok
然后勾上CORE
如图点击manage project item
添加一个startup来启动文件,code来放用户代码:
在code文件创建一个.c和.h文件
四,编写代码
1,led.h
#ifndef __LED_H__
#define __LED_H__
//片上外设基地址
#define PERIPH_BASE ((unsigned int)0x40000000)
//APB1 总线基地址
#define APB1PERIPH_BASE PERIPH_BASE
//APB2 总线基地址
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
//AHB总线基地址
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
//RCC外设基地址
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
//GPIOA外设基地址
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
//GPIOB外设基地址
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
//GPIOC外设基地址
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
//APB2使能时钟寄存器
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
//GPIOx的端口配置低寄存器
#define GPIOA_CRL *(unsigned int*)(GPIOA_BASE + 0x00)
#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00)
#define GPIOC_CRL *(unsigned int*)(GPIOC_BASE + 0x00)
//GPIOx的端口配置高寄存器
#define GPIOA_CRH *(unsigned int*)(GPIOA_BASE + 0x04)
#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE + 0x04)
#define GPIOC_CRH *(unsigned int*)(GPIOC_BASE + 0x04)
//GPIOx的端口输出寄存器
#define GPIOA_ODR *(unsigned int*)(GPIOA_BASE + 0x0C)
#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE + 0x0C)
#define GPIOC_ODR *(unsigned int*)(GPIOC_BASE + 0x0C)
#endif
2 led.c
#include "led.h"
//定义软件延时函数
void Delay()
{
int i = 0;
for(;i<6000000; i++);
}
int main()
{
RCC_APB2ENR |= (1<<2|1<<3|1<<4);//使能GPIO端口时钟
GPIOA_CRL |= (0X01<<20);//配置GPIOA_5引脚的模式
GPIOB_CRH |= (0X01<<4);//配置GPIOB_9引脚的模式
GPIOC_CRH |= (0X01<<20);//配置GPIOC_13引脚的模式
GPIOA_ODR &= ~(0X01<<5);//设置几个引脚的初始状态
GPIOB_ODR &= ~(0X01<<9);
GPIOC_ODR &= ~(0X01<<13);
while(1)
{
Delay();
GPIOA_ODR |= 1<<5;
GPIOB_ODR &= ~(0X01<<9);
GPIOC_ODR &= ~(0X01<<13);
Delay();
GPIOA_ODR &= ~(0X01<<5);
GPIOB_ODR |= (1<<9);
GPIOC_ODR &= ~(0X01<<13);
Delay();
GPIOA_ODR &= ~(0X01<<5);
GPIOB_ODR &= ~(0X01<<9);
GPIOC_ODR |= (1<<13);
Delay();
}
}
五,仿真分析
修改 一些配置,如下
进行Debug仿真并用逻辑分析仪观察分析3个gpio输出
输出波形