我们需要在外部定义一个SystemInit 函数设置 STM32 的时钟;STM32 上电后,会执行 SystemInit 函数,最后执行我们 C 语言中的 main 函数。
下面就开始使用寄存器来操作 STM32 使 PC0 输出一个低电平。要操作 STM32寄存器,我们就需要使用 C 语言对其封装,这部分程序我们都放在 stm32f10x.h中。具体代码如下:
#define PERIPH_BASE ((unsigned int)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOC_CRL *(unsigned int*)(GPIOC_BASE+0x00)
#define GPIOC_CRH *(unsigned int*)(GPIOC_BASE+0x04)
#define GPIOC_IDR *(unsigned int*)(GPIOC_BASE+0x08)
#define GPIOC_ODR *(unsigned int*)(GPIOC_BASE+0x0C)
#define GPIOC_BSRR *(unsigned int*)(GPIOC_BASE+0x10)
#define GPIOC_BRR *(unsigned int*)(GPIOC_BASE+0x14)
#define GPIOC_LCKR *(unsigned int*)(GPIOC_BASE+0x18)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18)
要控制 PF9 输出低电平,需知道 GPIO 这个外设它是挂接在哪个总线上的,
通过Block2外设基地址及APB2总线的偏移地址就可以得到APB2外设的基地址。
GPIO 就是挂接在 APB2 总线上的,根据 GPIOC 的偏移地址就可以得到 GPIOC 外设的基地址,GPIOC 外设内部含有很多个寄存器,比如GPIOC_CRL、GPIOC_CRH 端口配置寄存器、GPIOC_BSRR 置位复位寄存器等,通过他们各自的偏移地址就可以获取对应的寄存器地址,然后要操作地址里面的内容就需要使用到指针,将其强制转换为 unsigned int*指针类型,然后在通过一个*指针来操作该地址里面的内容。在 STM32 中凡是使用到外设功能,都要使能对应的外设时钟,否则即使配置好端口初始化也无法正常使用。因此还需要知道时钟 RCC 外设的基地址,通过数据手册“存储器映射”章节可以知道 RCC 时钟外设是挂接在 AHB 总线上, 根据其偏移值可以得到 RCC 时钟外设的基地址,然后可通过《STM32F1xx 中文参考手册》的“6 小容量、中容量和大容量产品的复位和时钟控制(RCC)”的“6.3.7 APB2 外设时钟使能寄存器(RCC_APB2ENR)”可找到对应的端口 RCC 使能寄存器,只要将 GPIOC 端口时钟使能即可。
使用 C 语言封装好寄存器后,就开始编写 main 函数。
main.c代码:
#include "stm32f10x.h"
#define GPIOC_BSRR *(unsigned int*)(GPIOC_BASE+0x10)
//在引用的头文件中声明过GPIOC_BASE
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18)
#define GPIOC_CRL *(unsigned int*)(GPIOC_BASE+0x00)
void SystemInit(){}
u32 i;
void Delay(i)
{
while(i--);
}
int main()
{
RCC_APB2ENR =(0X01<<4); //使能时钟
while(1)
{
//D1点亮
GPIOC_CRL &= ~( 0x0F<< (4*0)); //设置推挽输出模式
GPIOC_CRL |= (3<<4*0);
GPIOC_BSRR = (0x01<<(16+0)); //PC0管脚输出低电平
Delay(0x3FFFF); //延时
GPIOC_BSRR = (0x0000ffff);//D1输出高电平
//D2亮
GPIOC_CRL &= ~( 0x0F<< (4*1));
GPIOC_CRL |= (3<<4*1);
GPIOC_BSRR = (0x01<<(16+1));
Delay(0x3FFFF);
GPIOC_BSRR = (0x0000ffff);
//D3
GPIOC_CRL &= ~( 0x0F<< (4*2));
GPIOC_CRL |= (3<<4*2);
GPIOC_BSRR = (0x01<<(16+2));
Delay(0x3FFFF);
GPIOC_BSRR = (0x0000ffff);
//D4
GPIOC_CRL &= ~( 0x0F<< (4*3));
GPIOC_CRL |= (3<<4*3);
GPIOC_BSRR = (0x01<<(16+3));
Delay(0x3FFFF);
GPIOC_BSRR = (0x0000ffff);
//D5
GPIOC_CRL &= ~( 0x0F<< (4*4));
GPIOC_CRL |= (3<<4*4);
GPIOC_BSRR = (0x01<<(16+4));
Delay(0x3FFFF);
GPIOC_BSRR = (0x0000ffff);
//D6
GPIOC_CRL &= ~( 0x0F<< (4*5));
GPIOC_CRL |= (3<<4*5);
GPIOC_BSRR = (0x01<<(16+5));
Delay(0x3FFFF);
GPIOC_BSRR = (0x0000ffff);
//D7
GPIOC_CRL &= ~( 0x0F<< (4*6));
GPIOC_CRL |= (3<<4*6);
GPIOC_BSRR = (0x01<<(16+6));
Delay(0x3FFFF);
GPIOC_BSRR = (0x0000ffff);
//D8
GPIOC_CRL &= ~( 0x0F<< (4*7));
GPIOC_CRL |= (3<<4*7);
GPIOC_BSRR = (0x01<<(16+7));
Delay(0x3FFFF);
GPIOC_BSRR = (0x0000ffff);
}
}
注意:
①包含 stm32f10x.h 头文件,在这个头文件中我们定义的都是寄存器,因此如果要在其他文件中使用这些寄存器就需要把这个头文件包含进来, 否则编译就会报错。
②SystemInit 函数,在前面讲解启动文件时已经说明,程序运行的时候先进入这个函数进行 STM32 的初始化,如果不写这个函数编译器就会报错。这里我们编写这个函数,里面并不对其操作。
③开启 GPIOC 时钟。要使 PC0 正常工作输出一个低电平,必须要打开它的时钟。RCC_APB2ENR 寄存器是在 stm32f10x.h 头文件中定义好的,只要查下《STM32F1xx 中文参考手册》RCC 时钟使能寄存器内容就可以知道此寄存器的第4 位是控制 GPIOC 外设的时钟使能位,只有该位为 1 时才使能,如果为 0 即关闭GPIOC 时钟。所以要让 1 左移 4 位。
④配置 GPIOC 为通用推完输出模式。STM32 的 GPIO 模式有很多,可根据CRx 寄存器设置,CRL 对应 GPIO 的低 8 位,CRH 对应 GPIO 的高 8 位。如果不是特殊需求,一般输出采用推挽输出模式。我们要让 PC0 管脚输出一个低电平,故使用推挽输出模式。只要查下《STM32F1xx 中文参考手册》GPIO 配置寄存器内容就可以知道此寄存器内每 4 位控制一个管脚。
⑤使 PC0 输出低电平。GPIOC_BSRR 为置位、复位寄存器,只要查下
《STM32F1xx 中文参考手册》GPIO 置位复位寄存器内容就可以知道,其高 16 位用于复位,如果当高 16 位某位为 1,表示那一位管脚输出低电平,为 0 不影响其输出电平。如果当低 16 位的某位为 1,表示那一位管脚输出高电平,为 0 不影响其输出电平。所以要让 1 左移 16+0 位。