基于寄存器与基于固件库的stm32 LED流水灯

本文章采用的开发板是野火stm32F103-MINI,下载器是ST_Link,开发环境是Keil5

一、创建一个keil工程(寄存器版)

  1. 打开keil,点击Project,选择New uVision Project
  2. 选择 CPU 型号,根据开发板进行选择
    在这里插入图片描述
  3. 在线添加库文件
    用寄存器控制 STM32 时,不需要在线添加库文件,可以直接关掉。
  4. 添加文件
    ①添加已经存在文件
    在新建的工程中添加启动文件(startup_stm32f10x_hd.s),该文件可以先到固件库中复制到此处startup_stm32f10x_hd.s。
    ②创建新文件
    stm32f10x.h
    手动新建,用于存放寄存器映射的代码,暂时为空。
    main.c
    手动新建,用于存放 main 函数,暂时为空。
  5. 配置魔术棒选项卡
    ①Target设置
    在这里插入图片描述
    ②Output设置
    在这里插入图片描述
    ③Listing设置
    在这里插入图片描述
    ④Debug设置
    在这里插入图片描述
    ⑤Utilities设置
    在这里插入图片描述
    ⑥Debug Settings设置
    在这里插入图片描述
    在这里插入图片描述

二、基于寄存器stm32 LED流水灯

  1. 寄存器的定义
    寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。

  2. GPIO (通用输入输出端口)
    STM32 可控制的引脚。

    此图引用零死角玩转STM32——F103指南版中对GPIO介绍,具体介绍可参考该书。
    GPIO的工作模式

     typedef enum
     {
          
          
    	GPIO_Mode_AIN = 0x0, // 模拟输入
    	GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
    	GPIO_Mode_IPD = 0x28, // 下拉输入
    	GPIO_Mode_IPU = 0x48, // 上拉输入
    	GPIO_Mode_Out_OD = 0x14, // 开漏输出
    	GPIO_Mode_Out_PP = 0x10, // 推挽输出
    	GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出
    	GPIO_Mode_AF_PP = 0x18 // 复用推挽输出
    } GPIOMode_TypeDef;
    

    输入模式:上拉和下拉输入的电平由上拉或者下拉,浮空输入的电平是不确定的,完全由外部的输入决定,一般接按键的时候用的是这个模式。模拟输入则用于 ADC 采集。
    输出模式:推挽模式时双 MOS 管以轮流方式工作,输出数据寄存器 GPIOx_ODR可控制 I/O 输出高低电平。开漏模式时,只有 N-MOS 管工作,输出数据寄存器可控制 I/O输出高阻态或低电平。

  3. 编写LED的代码(该过程需要参考STM32F10的手册)
    stm32f10x.h

    /*片上外设基地址 */
    #define PERIPH_BASE ((unsigned int)0x40000000)
    
    /*总线基地址,GPIO 都挂载到 APB2 上 */
    #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
    #define AHBPERIPH_BASE  (PERIPH_BASE +0x20000)
    /*GPIOC 外设基地址*/
    #define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)
    //GPIOC_BASE=0x40000000+0x10000+0x1000=0x40011000,该地址为GPIOC的基地址
    /* GPIOB 寄存器地址,强制转换成指针 */
    #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)
     
    /*RCC 外设基地址*/
    #define RCC_BASE (AHBPERIPH_BASE + 0x1000)
    /*RCC 的 AHB1 时钟使能寄存器地址,强制转换成指针*/
    #define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0x18)
    

    main.c

    int main(void)
    {
          
          
    	// 配置RCC寄存器,开启 GPIOC 端口时钟配置RCC寄存器
    	*(unsigned int *)0x40021018 |=(1<<4);
    	// 配置CRL寄存器,配置 PC2 为通用推挽输出,速度为 10M
    	//*(unsigned int *)GPIOC_CRL |=(1<<(4*2));
    	*(unsigned int *)0x40011000 |=(1<<(4*2));
    	//配置ODR寄存器,清空控制 PC2 的端口位
    	//*(unsigned int *)GPIOC_ODR &=~(1<<2);
    	*(unsigned int *)0x4001100C &=~(1<<2);
    	while (1)
    	{
          
          
    	}
    }
    

    编译会出现下面问题
    在这里插入图片描述
    解决方式
    在main.c添加下面函数

    // 函数为空,目的是为了骗过编译器不报错
    void SystemInit(void)
    {
          
          
    }
    

    编译烧录后,效果如下
    在这里插入图片描述

三、创建一个keil工程(固件库版)

方法类似于寄存器的创建。
不同点

  1. 需要添加组文件夹。
    在新建的工程中添加 5 个组文件夹,用来存放各种不同的文件,文件从本地建好的工程文件夹下获取,双击组文件夹就会出现添加文件的路径,然后选择文件即可。
  2. C/C++选项卡设置
    添加处理宏及编译器编译的时候查找的头文件路径。如果头文件路径添加有误,则编译的时候会报错找不到头文件。
    在这里插入图片描述

四、基于固件库stm32 LED流水灯

bsp_led.c

//初始化GPIO函数
void LED_GPIO_Config(void)
{
    
    		
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef GPIO_InitStructure;

		/*开启LED相关的GPIO外设时钟*/
		RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK , ENABLE);
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;	

		/*设置引脚模式为通用推挽输出*/
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

		/*设置引脚速率为50MHz */   
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

		/*调用库函数,初始化GPIO*/
		GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);	
		
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;

		/*调用库函数,初始化GPIO*/
		GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);		

		/* 关闭所有led灯	*/
		GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
		
		/* 关闭所有led灯	*/
		GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
}

main.c

#include "stm32f10x.h"
#include "bsp_led.h"
#define SOFT_DELAY Delay(0x0FFFFF);
void Delay(__IO u32 nCount); 
int main(void)
{
    
    	
	/* LED 端口初始化 */
	LED_GPIO_Config();	 
	while (1)
	{
    
    
		LED1_ON;			  // 亮
		SOFT_DELAY;
		LED1_OFF;		   // 灭

		LED2_ON;			 // 亮
		SOFT_DELAY;
		LED2_OFF;		   // 灭
	}
}
void Delay(__IO uint32_t nCount)	 //简单的延时函数
{
    
    
	for(; nCount != 0; nCount--);
}

编译烧录,效果如下
在这里插入图片描述

五、对比两种方式

两种方式的流程(该图引用于零死角玩转 STM32F103—指南者)
在这里插入图片描述
寄存器方法
缺点:
①开发速度慢
②程序可读性差
③维护复杂
优点:
①具体参数更直观
②程序运行占用资源少
开库开发方式则正好弥补了寄存器开发的缺点。
通过两个方式的实现,你会发现采用寄存器开发的方式,需要不断地查看对应的手册,了解对应寄存器的地址。

六、总结

通过两种方式实现LED流水灯,可以很清楚地了解到这两种方式各自的优缺点。对于初学者来说,尝试一些寄存器开发的方式,还是很有帮助理解整个过程。虽然,固件库的方式会更快,但是,去对于初学者来说,还是有些地方不是很容易搞清楚。实验过程,开始编译烧录后,灯一直不亮,我一直以为是程序的问题(寄存器地址),结果最后才发现是自己忘了接电源。如果,你也有类似的问题,先检查自己器件的连接是否有问题,不用急着去看代码。

七、参考资料

野火F103-MINI视频学习-【入门篇】

猜你喜欢

转载自blog.csdn.net/qq_43279579/article/details/110320013