STM32F103寄存器方式点亮LED流水灯
作者:互联网
目录
一、STM32F103系列芯片的地址映射和寄存器映射原理
1.地址映射
STM32固件库中,有个头文件叫stm32f10x.h,其中就定义了寄存器的映射,部分代码如下:
外设基地址PERIPH_BASE:
#define PERIPH_BASE ((uint32_t)0x40000000)
总线基地址,在外设基地址上加上偏移:
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
GPIO外设基地址,在APB2总线基地址上加上偏移:
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)
定义GPIO外设结构体,因为结构体成员在内存中是连续的,这种形式与寄存器组非常类似,所以用结构体能够很好的管理寄存器:
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
定义GPIOA结构体指针,因为单单定义GPIO外设结构体,并不能确定其内存地址,因此用指针将其绑定到GPIOA外设基地址:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
访问寄存器方式的对比,映射访问明显更为直观:
//直接的地址访问
*(unsigned int *)(0x4001_0800)|= 0x0001;
//映射访问
GPIOA->CRL |= 0x0001;
2.寄存器映射
寄存器映射主要针对于Block2。这种映射不同于存储器映射的分配地址操作,而是在程序中对具有特定功能的内存单元进行命名的过程,每个内存单元都是四个字节,称作寄存器。例如GPIOA外设有个寄存器地址范围为0x4001_0800~0x4001_0803,该寄存器是用来配置GPIOA部分端口工作模式的,因此被映射为GPIOA_CRL。由此可知,这种映射方便了对寄存器的访问操作,因为如果每次访问寄存器都要查地址范围的话是很痛苦的。
如何实现这种映射的呢?以GPIOA_CRL为例,其映射地址 = 外设总基地址(块基地址)+ 总线相对于外设总基地址的偏移 + 具体外设基地址相对于总线基地址的偏移 + 寄存器相对于具体外设基地址的偏移。
外设总基地址恰好就是Block2的起始地址0x4000_0000;
GPIO属于APB2总线外设,因此查阅芯片手册如下图所示,我们其实直接可以得到APB2起始地址和GPIOA的起始地址,但是程序中一般不这么做,而是以偏移量来表示层次关系。从图中可计算到总线相对于外设总基地址的偏移为0x1_0000,GPIOA相对于APB2基地址的偏移为0x800。
再查阅GPIO的寄存器组,如下图所示。可以得到CRL寄存器相对于GPIO基地址偏移为0x00。综上GPIOA_CRL的基地址为:0x4000_0000+0x1_0000+0x800+0x00。
二、了解GPIO端口的初始化设置三步骤
STM32F103ZE的开发板里总共有7组IO口,每组IO口有16个IO,即这块板子总共有112个IO口分别是GPIOA~GPIOG。每个I/O端口位可以自由编程,但I/O端口寄存器必须按32位字节访问,不允许半字或单字节访问。
GPIO的工作模式主要有八种:4种输入方式,4种输出方式,分别为输入浮空,输入上拉,输入下拉,模拟输入;输出方式为开漏输出,开漏复用输出,推挽输出,推挽复用输出。
(1)GPIO_Mode_AIN 模拟输入 (应用ADC模拟输入,或者低功耗下省电)
(2)GPIO_Mode_IN_FLOATING 浮空输入 (浮空就是浮在半空,可以被其他物体拉上或者拉下,可以用于按键输入)
(3)GPIO_Mode_IPD 下拉输入 (IO内部下拉电阻输入)
(4)GPIO_Mode_IPU 上拉输入 (IO内部上拉电阻输入)
(5)GPIO_Mode_Out_OD 开漏输出(开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行)
(6)GPIO_Mode_Out_PP 推挽输出 (推挽就是有推有拉电平都是确定的,不需要上拉和下拉,IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的 )
(7)GPIO_Mode_AF_OD 复用开漏输出(片内外设功能(I2C的SCL,SDA))
(8)GPIO_Mode_AF_PP 复用推挽输出 (片内外设功能(TX1,MOSI,MISO.SCK.SS))
GPIO初始化步骤
第一步:使能GPIOx口的时钟
第二步:指明GPIOx口的哪一位,这一位的速度大小以及模式。
第三步:调用GPIOx口初始化函数,进行初始化。
第四步:调用GPIO-SetBits函数,进行相应为的置位。
实例如下
☞对于单个GPIO口的初始化如下
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
第二步:设置GPIOA参数:输出OR输入,工作模式,端口翻转速率
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_6| GPIO_Pin_7| GPIO_Pin_8; //设定要操作的管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
第三步:调用GPIOA口初始化函数,进行初始化。
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA
第四步:调用GPIO-SetBits函数,进行相应为的置位。
GPIO_SetBits(GPIOA,GPIO_Pin_0); //输出高
☞对于多个GPIO口的初始化如下
GPIO_InitTypeDef GPIO_InitStructure;
第一步:使能GPIOA,GPIOE的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
第二步:设置GPIOA,GPIOE参数:输出OR输入,工作模式,端口翻转速率
第三步:调用GPIOA口初始化函数,进行初始化。
第四步:调用GPIO-SetBits函数,进行相应为的置位。
▶把第二、三、四步合并分别设置GPIOA和GPIOE
先设置GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 第四个口,PA4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOA,&GPIO-InitST); //根据设定参数初始化GPIOA
GPIO_SetBits(GPIOA,GPIO_Pin_4); //输出高
再设置GPIOE
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // 第三个口,PE3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
GPIO_Init(GPIOE,&GPIO-InitST); //根据设定参数初始化GPIOE
GPIO_SetBits(GPIOE,GPIO_Pin_3); //输出高
三、点亮LED流水灯
1.代码
1.c
#include "stm32f10x.h"
//----------------APB2??????? ---------------------
#define RCC_APB2ENR *((unsigned volatile int*)0x40021018)
//----------------GPIOA????? -----------------------
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
#define GPIOA_ODR *((unsigned volatile int*)0x4001080C)
//----------------GPIOB????? -----------------------
#define GPIOB_CRL *((unsigned volatile int*)0x40010C00)
#define GPIOB_ODR *((unsigned volatile int*)0x40010C0C)
//----------------GPIOC????? -----------------------
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
#define GPIOC_ODR *((unsigned volatile int*)0x4001100C)
extern void led(void);
//????
void Delay()
{
u32 i=0;
for(;i<5000000;i++);
}
//?1
void led1()
{
GPIOA_ODR|=1<<4; //PA4???
Delay();
GPIOA_ODR&=~(1<<4);//PA4???,???????
Delay();
}
//?2
void led2()
{
GPIOB_ODR|=1<<5; //PB4???
Delay();
GPIOB_ODR&=~(1<<5);//PB4???,???????
Delay();
}
//?3
void led3()
{
GPIOC_ODR|=1<<14; //PC14???
Delay();
GPIOC_ODR&=~(1<<14);//PC14???,???????
Delay();
}
int main(void)
{
led();
RCC_APB2ENR|=1<<2|1<<3|1<<4; //APB2-GPIOA?GPIOB?GPIOC??????
GPIOA_CRL&=0xFFF0FFFF; //??? ??
GPIOA_CRL|=0x00020000; //PA4????
GPIOA_ODR&=~(1<<4); //???????
GPIOB_CRL&=0xFF0FFFFF; //??? ??
GPIOB_CRL|=0x00200000; //PB5????
GPIOB_ODR&=~(1<<5); //???????
GPIOC_CRH&=0xF0FFFFFF; //??? ??
GPIOC_CRH|=0x02000000; //PC14????
GPIOC_ODR&=~(1<<14); //???????
while(1){
led1();
led2();
led3();
}
}
2.s
AREA MYDATA, DATA
AREA MYCODE, CODE
ENTRY
EXPORT led
led
;??A,B,C
ldr r0, =0x40021018
ldr r1, =0x0000001c
str r1, [r0]
;????A4
ldr r0, =0x40010800
ldr r1, [r0]
bic r1, r1, #0x000f0000
orr r1, r1, #0x00010000
str r1, [r0]
;????B5
ldr r0, =0x40010c00
ldr r1, [r0]
bic r1, r1, #0x00f00000
orr r1, r1, #0x00100000
str r1, [r0]
;????C14
ldr r0, =0x40011004
ldr r1, [r0]
bic r1, r1, #0x0f000000
orr r1, r1, #0x01000000
str r1, [r0]
;??A4??
ldr r0, =0x4001080c
ldr r1, =0x00000010
str r1, [r0]
ldr r0, =5000000;??
ldr r1, =0
;????
blink
add r1, r1, #1
cmp r1, r0
blt blink
;??A4?
ldr r1, =0x4001080c
ldr r2, [r1]
eor r2, r2, #0x00000010
str r2, [r1]
;??B5?
ldr r1, =0x40010c0c
ldr r2, [r1]
eor r2, r2, #0x00000020
str r2, [r1]
ldr r1, =0
blink1
add r1, r1, #1
cmp r1, r0
blt blink1
;??B5?
ldr r1, =0x40010c0c
ldr r2, [r1]
eor r2, r2, #0x00000020
str r2, [r1]
;??C14?
ldr r1, =0x4001100c
ldr r2, [r1]
eor r2, r2, #0x00004000
str r2, [r1]
ldr r1, =0
blink2
add r1, r1, #1
cmp r1, r0
blt blink2
;??C14?
ldr r1, =0x4001100c
ldr r2, [r1]
eor r2, r2, #0x00004000
str r2, [r1]
;??A4?
ldr r1, =0x4001080c
ldr r2, [r1]
eor r2, r2, #0x00000010
str r2, [r1]
ldr r1, =0
b blink
END
2. 执行波形
3.结果
四、参考文献
STM32存储器映射和寄存器映射 - KenSporger - 博客园 (cnblogs.com)
STM32入门-GPIO初始化步骤_爱学习的大喵喵的博客-CSDN博客
STM32F103寄存器方式点亮LED流水灯_平行叶子的博客-CSDN博客
标签:STM32F103,LED,r1,r2,ldr,BASE,GPIOA,寄存器,GPIO 来源: https://blog.csdn.net/m0_61816657/article/details/120923945