前言
在stm32的开发过程中,使用寄存器的方式自己去配置、去开发是开发效率十分低的方式,因为stm32的寄存器实在是太多了,现在都是使用hal库或者固件库开发stm32,在最大程度上提高了我们的开发效率和周期。当然,作为新手我们也应该尝试用寄存器开发,了解其开发基本流程和步骤,也有助于我们后面使用库开发的学习。
一、stm32的系统架构
stm32是已经封装好的成品,主要是由内核和片上的外设所组成。下面给一张stm32的系统结构图,通过这张图我们可以看到各个外设挂载在哪个总线上,以及stm32的运作流程。
1、五个驱动单元
- Cortex?-M3内核DCode总线(D-bus),和系统总线(S-bus)
- 通用DMA1和通用DMA2
- 以太网DMA
2、三个被动单元
- 内部SRAM
- 内部闪存存储器
- AHB到APB的桥(AHB2APBx),它所连接的所有的APB设备
二、什么是寄存器
1、寄存器的映射
在存储器Block2的这块区域,设计的是片上外设,它们以四个字节为一个单元,总共32bit,每个单元对应不同的功能, 当我们控制这些单元就可以驱动相应的外设进行工作。我们可以找到每个单元的起始地址,通过指针的方式来访问这些单元。我们可以根据每个单元功能的不同,给这个单元取一个别名,这个给已分配好的地址的有特定功能的内存单元取名的过程就是寄存器的映射 。
2、什么是寄存器
通过上诉的讲解我们已经了解了寄存器的映射。那么什么是寄存器呢?其实给特定单元取的别名就是寄存器,寄存器只是特定功能的单元的名字罢了,操作寄存器可以驱动相应外设进行工作。
3、stm32的外设地址映射
片上的外设区分为三条总线,根据外设速度的不同,挂载着不同的外设,APB1挂载低速外设,APB2和AHB挂载着高速外设。
下图列出了stm32F10系列中内置外设的起始地址,通过这张图我们也可以知道每条总线的基地址和外设的基地址
三、新建工程
- new prj
- 选择芯片型号
- CORE勾上,点击ok
- 右击Target,点击Manage…
- 添加一个Startup来放启动文件,Code来放用户代码
- 在Code文件下添加一个.c文件和.h文件
四、代码的编写
通过查看数据手册发现所有的GPIO外设都是挂载在APB2总线下的,通过查询数据手册可以知道相应寄存器的基地址和偏移地址,给不同寄存器取别名 ,提高程序的可读性和可移植性。
1、led.h
#ifndef __LED_H__
#define __LED_H__
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
#define GPIOA_CRL *(unsigned int*)(GPIOA_BASE + 0x00)
#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00)
#define GPIOC_CRL *(unsigned int*)(GPIOC_BASE + 0x00)
#define GPIOA_CRH *(unsigned int*)(GPIOA_BASE + 0x04)
#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE + 0x04)
#define GPIOC_CRH *(unsigned int*)(GPIOC_BASE + 0x04)
#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
将GPIO端口配置为通用推挽输出模式,2MHZ
#include "led.h"
void Delay()
{
int i = 0;
for(;i<6000000; i++);
}
int main()
{
RCC_APB2ENR |= (1<<2|1<<3|1<<4);
GPIOA_CRL |= (0X01<<20);
GPIOB_CRH |= (0X01<<4);
GPIOC_CRH |= (0X01<<20);
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();
}
}
五、软件仿真、逻辑分析仪分析
- 对keil软件的一些默认配置进行修改
- 进行Debug仿真并用逻辑分析仪观察分析3个gpio输出
- 观察结果,输出波形
观察上面的波形可知,已经实现了一个流水灯的功能!
总结
通过学习stm32寄存器实现流水灯的实验,大致了解了单片机开发的流程,开时钟,配置相应寄存器单元,后面库函数的学习也是差不多的,只是它的寄存器的配置它封装成了一个函数,使操作更加的简单,使开发效率得到大幅度提高。
|