使用寄存器点亮LED灯
一、LED原理
由电路图可以看到,LED0右端连接3.3高电平电压,左侧对应PB5端口。所以当PB5=0时,会形成从右向左的电流。LED灯点亮。
二、端口值的输出-(GPIOx_ODR) (x=A…E)
因为LED接的端口是PB5,所以对应了GPIOB_ODR
代码:
*( unsigned int * )0x40010C0C &= ~( 1 << 5 ) ;
- 0x40010C0C:在参考手册中查找存储器映射,可以查到GPIO寄存器的起始地址。GPIOB起始地址0x40010C00+ODR寄存器地址偏移0Ch(h表示16进制),所以为0x40010C0C
- ( unsigned int * )0x40010C0C &= ~( 1 << 5 ) :
等价于( unsigned int * )0x40010C0C &= ~(0010 0000)
等价于*( unsigned int * )0x40010C0C=(0000 0000)&(1101 1111)
等价于*( unsigned int * )0x40010C0C=1101 0000
操作中,常用 置位 |= ,清零 &=~
三、使端口模式为输出模式
每四位对应ODR中的1位,所以对于PB5来说,就是CRL中的20,21,22,23四位控制,要想使PB5端口为输出电压模式,要将CNF置为00,MODE置为01,偏移地址为0x00,实际地址即为0x40010C00
*( unsigned int * )0x40010C00 |=( (1) << (4*5) ) ;
四、打开端口时钟控制
时钟相当于单片机的心脏,任何程序的运行需要先将时钟使能
PB端口是第3位控制
*( unsigned int * )0x40021018 |=( 1 << 3 )
所以在RCC寄存器中,第3位的值要置1,才能开启B时钟,端口才能正常工作
完整代码
#include "stm32f10x.h"
int main(void)
{
*( unsigned int * )0x40021018 |=( 1 << 3 ) ; //打开GPIO端口的时钟
*( unsigned int * )0x40010C00 |=( (1) << (4*5) ) ; // 配置IO口为输出
*( unsigned int * )0x40010C0C &= ~( 1 << 5 ) ; // 控制 ODR 寄存器
}
//置位 |= ,清0 &=~
void SystemInit(void)
{
//函数体为空,为了不报错
}
改进
代码是直接通过地址来进行位操作的,可读性差。用寄存器外设来达到类似51单片机P0=1/0这样的效果。
操作时需要查阅STM32参考手册:
三条总线:APB1,APB2,AHB
参考存储器映像查看它们的起始地址
AHB起始地址:0x4001 8000,但是为了后面数据操作方便,将0x4002 0000设置为起始地址
APB2起始地址:0x4001 0000
AHB1起始地址:0x4000 0000
总代码如下:
//用来存放STM32寄存器映射的代码
//外设 peripheral
//首先定义外设的基地址
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB1PERIPH_BASE PERIPH_BASE
//APB2在基地址上+1000
#define APB2PERIPH_BASE (PERIPH_BASE+0x10000)
//AHP同理
#define AHBPERIPH_BASE (PERIPH_BASE+0x20000)
//时钟RCC
#define RCC_BASE (AHBPERIPH_BASE +0x1000)
//开始定义寄存器
#define GPIOB_BASE (APB2PERIPH_BASE +0x0C00)
#define RCC_APB2ENR *(unsigned int *)(RCC_BASE+0x18)
#define GPIOB_CRL *(unsigned int *)(GPIOB_BASE+0x00)
#define GPIOB_CRH *(unsigned int *)(GPIOB_BASE+0x04)
#define GPIOB_ODR *(unsigned int *)(GPIOB_BASE+0x0C)
寄存器代码映射完成后,原代码可以改为:
#include "stm32f10x.h"
int main(void)
{
while(1)
{
RCC_APB2ENR|=( 1 << 3 ) ; //打开GPIO端口的时钟
GPIOB_CRL |=( (1) << (4*5) ) ; // 配置IO口为输出
GPIOB_ODR &= ~( 1 << 5 ) ; // 控制 ODR 寄存器
}
}
void SystemInit(void)
{
//函数体为空,为了不报错
}
在原先基础上, 建立GPIO_TypeDef结构体,就可以通过结构体的方式来访为寄存器
完整代码:
#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 GPIOB_BASE (APB2PERIPH_BASE +0x0C00)
#define RCC_APB2ENR *(unsigned int *)(RCC_BASE+0x18)
typedef unsigned int unit32_t;
typedef unsigned short unit16_t;
typedef struct
{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
}GPIO_TypeDef;
#define GPIOB (( GPIO_Typedef* )GPIOB_BASE)
#include "stm32f10x.h"
int main(void)
{
while(1)
{
RCC_APB2ENR |=( 1 << 3 ) ; //打开GPIO端口的时钟
GPIOB->CRL |=( (1) << (4*5) ) ; // 配置IO口为输出
GPIOB->ODR &= ~( 1 << 5 ) ; // 控制 ODR 寄存器
}
}
void SystemInit(void)
{
//函数体为空,为了不报错
}