一、STM32使用寄存器点亮LED灯
-
新建一个工程文件,并将启动文件 startup_stm32f10x_hd.s 引入到 Source Group 中。
引入的启动文件根据自己的 stm32 定,此处引入的启动文件支持的是 stm32F1系列;
文件所在位置:Libraries —> CMSIS —> CM3 —> DeviceSupport —> ST —> STM32F10x —> startup —> arm;
-
新建 main.c, 写入一下内容:
int main(void) { //打开GPIOB的时钟 *(unsigned int *)0x40021018 |= (1 << 3); //配置IO口为输出 *(unsigned int *)0x40010C00 |= (1 << (4*0)); // 控制 ODR 寄存器 *(unsigned int *)0x40010C0C &= ~(1 << 0); //(unsigned int *)将0x40010C0C强制转换为地址,前面再加指针进行指针的赋值操作 } void SystemInit(void) { //函数外定义SystemInt(), 骗过编译器不报错 }
-
程序的说明
-
定义一个 SystemInit 空函数的目的是为了骗过编译器;因为在启动文件中引入了 SystemInit 函数 (如图一),而我们此处在此处没有引入 stm32f10x.h 的固件库,所以应在 main 文件中定义一个空函数,不然将会报如下错误 (如图二)
图一 图二 -
配置端口输出数据寄存器(GPIOx_ODR)
由上图可知,要想 LED 灯PB0 亮,即将 PB0 对应的 GPIOB_ODR 置0,也就是将GPIOB_ODR0置0。
由上图可知,GPIOB_ODR地址 = 0x40010C00(GPIOB初始地址) + 0x0C(偏移地址) = 0x40010C0C
由上图知,将 GPIOB_ODR第0位置0,所以代码为:
*(unsigned int *)0x40010C0C &= ~(1 << 0);// 控制 ODR 寄存器
-
配置 PB0 的 IO 口为输出 (配置低寄存器GPIOB_CRL)
因为 stm32 的 IO 口可以输出或者输入,默认情况下为输入,如上图所示。
由上图可知,GPIOB_CRL地址 = 0x40010C00(GPIOB初始地址) + 0x00(偏移地址) = 0x40010C00
由上图,选择配置 GPIOB_CRL第四位为0001,所以代码如下:
*(unsigned int *)0x40010C00 |= (1 << (4*0));//配置IO口为输出
-
位情况下,时钟处于关闭状态,需要打开 GPIOB 的时钟,否则将不会工作;
由上图可知道,应打开 APB2 外设时钟使能寄存器(RCC_APB2ENR)
由上图可知,RCC_APB2ENR地址 = 0x40021000(RCC初始地址) + 0x18(偏移地址) = 0x40021018
由上图可知,要打开 GPIOB 的时钟,需将 RCC_APB2ENR 的第三位置1,所以代码应如下:
*(unsigned int *)0x40021018 |= (1 << 3); //打开GPIOB的时钟
二、STM32使用固件库点亮LED灯
-
新建一个工程文件,配置并引入固件库,具体操作这里将不会细诉 (此处引入的固件库版本为STM32F10x_StdPeriph_Lib_V3.5.0)
如何新建一个工程模板,可参考此篇博文:MDK5 Kil5中STM32工程的建立过程
-
由寄存器点亮 LED 灯可知,点亮 LED 灯大致应有如下步骤:
- 开启 GPIO 的端口时钟
- 选择要具体控制的 IO 口,即 pin
- 选择 IO 口输出的速率,即 speed
- 选择 IO 口输出的模式,即 mode
- 输出高/低电平
-
新建 main.c, 写入一下内容:
#include "stm32f10x.h" //定义宏,提高程序的可移植性 #define LED_GPIO_PIN GPIO_Pin_0 #define LED_GPIO_PORT GPIOB #define LED_GPIO_CLK RCC_APB2Periph_GPIOB void LED_GPIO_Config(void) { //定义GPIO_InitTypeDef结构体 GPIO_InitTypeDef GPIO_InitStruct; //开启时钟 RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE); //端口配置 GPIO_InitStruct.GPIO_Pin = LED_GPIO_PIN; //配置位(I/O口) GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //配置输出速度 //GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) 参数一:端口,参数二:GPIO_InitTypeDef结构体指针 GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct); //初始化GPIO //点亮LED GPIO_ResetBits(LED_GPIO_PORT, LED_GPIO_PIN); //熄灭LED //GPIO_SetBits(LED_G_GPIO_PORT, LED_G_GPIO_PIN); } int main(void) { // 程序来到 main 函数之前,启动文件:statup_stm32f10x_hd.s 已经调用 // SystemInit()函数把系统时钟初始化成 72MHZ // SystemInit()在 system_stm32f10x.c 中定义 // 如果用户想修改系统时钟,可自行编写程序修改 LED_GPIO_Config(); }
C语言的标准中变量必须在函数或者块的开始部分进行 声明/定义,而不像C++中是可以在任意位置定义变量,否则将会出现如下错误。
-
程序的说明
-
RCC_APB2PeriphClockCmd(RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState) 控制 APB2 时钟函数 :
-
GPIO_InitTypeDef 将端口配置的 pin, speed,mode封装成结构体,同样 speed,mode被分别封装成枚举(enum) GPIOSpeed_TypeDef,GPIOMode_TypeDef
typedef 的作用是给已知的数据类型命名别名
-
GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) 把配置好的端口参数写到 CRL 或者 CRH 这两个寄存器中。
-
GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) 设置指定的数据端口(置0)。
-
GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) 清除指定的数据端口(置1)。
三、Proteus进行仿真
-
在 keil5 工程目录下进行配置,点击魔法棒—>Output —> Create HEX File;
-
打开 Proteus,新建一个工程文件,并引入如下器件:
-
仿真点亮 PB0 端口上的 led 灯,电路图连接如下:
注意:此处将 R1 的电阻值修改为了330,否则将会因为原电阻值太导致 led 不亮。
-
配置电网
设计 —> 配置供电网 —> 将未连接电网增加到网络连接到GND
-
双击 STM32F103R6 芯片,链接 MDK 生成的 .hex 文件:
-
点击运行仿真,结果如下:
四、C51程序的设计和仿真
-
新建一个C51工程,可参考🔗:如何在KEIL C51 软件上创建一个工程
注意:MDK和 KEIL C51 是不同开发工具,MDK是专为微控制器开发的工具;KEIL C51是支持绝大部分8051内核的微控制器开发工具
-
点击魔法棒,勾选 Create HEX File:
-
新建一个 main.c 文件,并写入如下内容:
#include "reg51.h" void main(void) { P0 = 0xFE; //点亮P0端口上的LED灯 }
-
切换到 Proteus,新建一个工程并引入如下器件:
-
仿真点亮 P0 端口上的 LED 灯,电路图设计如下:
-
双击 AT89C51芯片,链接 Keil C51 生成的 .hex 文件:
-
点击运行仿真,效果如下: