手撕STM32库函数工程(1)--自建stm32f10x.h

手撕STM32库函数工程(1)–自建stm32f10x.h

文章目录

0. 准备工作及工程文件概览:

0.1 新建一个keil_LED工程文件夹

0.2 在keil_LED文件夹中建立一个keil点灯工程:xxxx

事先放入启动文件:startup_stm32f10x_hd.S ???

startup_stm32f10x_ld.S 小容量的Flash芯片:16-32K STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_md.S 中容量的Flash芯片:64-128K STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_hd.S 大容量的Flash芯片:256-512K STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_xl.S 超大容量的Flash芯片:512-1024K STM32F101xx,STM32F102xx,STM32F103xx

0.3 keil工程中最终包含以下四个文件:

  • main.c 主程序
  • startup_stm32f10x_hd.S 启动文件
  • stm32f10x_gpio.c GPIO外设驱动文件
  • system_stm32f10x.c 系统时钟配置函数等
main.c 主程序
startup_stm32f10x_hd.S 启动文件
stm32f10x_gpio.c GPIO外设驱动文件
system_stm32f10x.c 系统时钟配置文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tafLh5j6-1635738492274)(建立自己的STM32库函数工程(1)–自建stm32f10x.h.assets/L(7LTP]OC$SXO2)]6C3EN}TW.png)

1、建立Keil工程

​ 新建xxx文件夹,新建keil工程(包括选择器件,配置keil工程,生产hex文件等),并将工程保存到xxx文件夹下。

1.1 往keil工程中添加 startup_stm32f10x_hd.S头文件

  1. 将startup_stm32f10x_hd.S文件copy到xxx工程目录下****
  2. 将 startup_stm32f10x_hd.S添加到kei工程中去****

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i5E4bby4-1635738492276)(建立自己的STM32库函数工程(1)–自建stm32f10x.h.assets/66-16356454926281-16356792208641.png)]

1.2 自建 stm32f10x.h

1.2.1 准备工作:建立core_cm3.h、stdint.h、system_stm32f10x.h/c文件

说明:库函数的stm32f10x.h文中中包含了以下三个文件,所以我们要提前建立它们:

  • core_cm3.h : 内核寄存器映射及volatile关键字
  • stdint.h : 定义各种数据类型
  • system_stm32f10x.h : 系统时钟配置,SystemInit(void)
1.2.1.1 建立core_cm3.h (该文件无需添加到工程,会在stm32f10x.h中include进去)

​ 在keil工程中,新建core_cm3.h头文件。代码如下:

#ifndef __CORE_CM3_H__
#define __CORE_CM3_H__
// stm32f10x.h 外设寄存器映射  <---------> core_cm3.h  内核的寄存器映射头 
/*   stm32f10x.h/core_cm3.h: 
                            (1) 内核/外设寄存器: 结构体定义
							(2) 内核/外设寄存器: 基地址映射
							(3) 内核/外设寄存器: 指向寄存器结构体的指针的声明
*/ 
// 其他辅助定义,如_IO, IO_ ,__IO														
#define     __IO    volatile    

// 其他代码详见标准库中的标准core_cm3.h 和core_cm3.c
	
#endif

说明:core_cm3.h主要实现了内核寄存器映射,其中定义了我们经常要用到的volatile关键字相关的宏。

​ core_cm3.c主要实现了操作内核外部寄存器的函数,用的比较少,这里不考虑

​ 这里只是为了学习标准库的结构,不涉及内核寄存器映射代码,详细代码参见标准库中的标准core_cm3.h

1.2.1.2 建立stdint.h (该文件无需添加到工程,会在stm32f10x.h中include进去)

​ 在keil工程中,新建stdint.h 头文件。代码如下:

#ifndef __STDINT_H__
#define __STDINT_H__

//  stdint.h 中定义来各种数据类型,类似于C语言中的stdio.h

typedef   signed          char int8_t;
typedef   signed short     int int16_t;
typedef   signed           int int32_t;
// typedef   signed       __INT64 int64_t;

typedef unsigned          char uint8_t;
typedef unsigned short     int uint16_t;
typedef unsigned           int uint32_t;
// typedef unsigned       __INT64 uint64_t;

// 其他代码详见标准库中的标准stdint.h

#endif
1.2.1.3 建立system_stm32f10x.h和system_stm32f10x.c(system_stm32f10x.c要添加到工程中,system_stm32f10x.h不用)

​ 在keil工程中,新建system_stm32f10x.h和system_stm32f10x.c, 进行系统时钟配置。代码如下:

#include "system_stm32f10x.h"
// 实现系统SMT32的时钟配置,操作的是片上的RCC这个外设。
// 系统上电后,首先会执行由汇编编写的启动文件,启动文件中的复位函数会调用本文件中的SystemInit()函数来配置时钟。
//  调用完毕后,系统的时钟就被初始化为72M.
//  如果需要重新配置系统时钟,则修改此函数 
void SystemInit(void)
{
   ;   // 暂时为空,系统时钟默认被初始化为72M
}

// 其他代码详见:systme_stm32f10x.h
#ifndef __STM32F10X_H__
#define __STM32F10X_H__
// 实现系统SMT32的时钟配置,操作的是片上的RCC这个外设。
// 系统上电后,首先会执行由汇编编写的启动文件,启动文件中的复位函数会调用本文件中的SystemInit()函数来配置时钟。
//  调用完毕后,系统的时钟就被初始化为72M.
//  如果需要重新配置系统时钟,则修改此函数 

void SystemInit(void); 
// 其他代码详见:systme_stm32f10x.h
 
#endif
1.2.1.4 xxxx.h和xxxx.c的说明

xxxx.h和xxxx.c成对出现,xxxx.h中定义变量和函数声明,而xxxx.c中则为xxxx.h中声明函数的实现体

注意:

  • 在xxxx.c中 “#include xxxx.h”
  • 要将xxxx.c文件添加到工程中,(xxxx.h不用添加,因为xxxx.c中已经#include进去了,编译时系统会自动查找xxxx.h并将其导入工程中)
  • 当yyyy.c要调用xxxx.c中所定义的函数时,只需要在yyyy.c中#include "xxxx.h"即可(不是xxxx.c !!!

所以:

  • 要先将 system_stm32f10x.c 文件添加到工程中
  • system_stm32f10x.h不用添加,只需要在stm32f10x.h中#include "system_stm32f10x.h"即可

1.2 建立stm32f10x.h 文件 (核心文件之一!!!!但是不用添加到工程中)

#ifndef __STM32F10X_H
#define __STM32F10X_H

#include "core_cm3.h"    // #define     __IO    volatile 
#include "stdint.h"
#include "system_stm32f10x.h"   // 这里包含来SystemInit()函数,默认将时钟配置为72M,即此时AHB时钟为72M,APB2为72MHz,APB1为36MHz。
                                // 当STM32上电后,执行xxxx.S启动文件时,会调用SystemInit()函数,将系统时钟配置为以上状态

///  1、 每个外设的寄存器结构体定义 
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;

typedef struct
{
	uint32_t CR;
	uint32_t CFGR;
	uint32_t CIR;
	uint32_t APB2RSTR;
	uint32_t APB1RSTR;
	uint32_t AHBENR;
	uint32_t APB2ENR;
	uint32_t APB1ENR;
	uint32_t BDCR;
	uint32_t CSR;
}RCC_TypeDef;

/// 2、 总线及外设基地址映射
/*!< Peripheral memory map */
#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)   // RCC寄存器的基地址

#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)
 
/// 3、 声明指向该外设基地址的结构体指针

#define RCC                 ((RCC_TypeDef*)RCC_BASE) 

#define GPIOA               ((GPIO_TypeDef*) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef*) GPIOB_BASE) 
#define GPIOC               ((GPIO_TypeDef*) GPIOC_BASE)
#define GPIOD               ((GPIO_TypeDef*) GPIOD_BASE)
#define GPIOE               ((GPIO_TypeDef*) GPIOE_BASE)
#define GPIOF               ((GPIO_TypeDef*) GPIOF_BASE)
#define GPIOG               ((GPIO_TypeDef*) GPIOG_BASE)

#endif /* __STM32F10X_H */

1.3. 建立外设驱动文件: GPIO外设

1.3.1 建立stm32f10x_gpio.h 头文件(不用添加到工程中)

#ifndef __STM32F10X_GPIO_H__
#define __STM32F10X_GPIO_H__

// 本头文件要用到stm32f10x.h中的如下定义:  uint16_t等 数据类型, GPIO_TypeDef等外设结构体定义, GPIOx等外设结构体指针声明  
#include "stm32f10x.h"   // 

// 1. GPIO 的引脚定义,即宏定义 1<<n
#define GPIO_Pin_0    ((uint16_t)0x0001)  /*!< 选择Pin0 */    //(00000000 00000001)b
#define GPIO_Pin_1    ((uint16_t)0x0002)  /*!< 选择Pin1 */    //(00000000 00000010)b
#define GPIO_Pin_2    ((uint16_t)0x0004)  /*!< 选择Pin2 */    //(00000000 00000100)b
#define GPIO_Pin_3    ((uint16_t)0x0008)  /*!< 选择Pin3 */    //(00000000 00001000)b
#define GPIO_Pin_4    ((uint16_t)0x0010)  /*!< 选择Pin4 */    //(00000000 00010000)b
#define GPIO_Pin_5    ((uint16_t)0x0020)  /*!< 选择Pin5 */    //(00000000 00100000)b
#define GPIO_Pin_6    ((uint16_t)0x0040)  /*!< 选择Pin6 */    //(00000000 01000000)b
#define GPIO_Pin_7    ((uint16_t)0x0080)  /*!< 选择Pin7 */    //(00000000 10000000)b

#define GPIO_Pin_8    ((uint16_t)0x0100)  /*!< 选择Pin8 */    //(00000001 00000000)b
#define GPIO_Pin_9    ((uint16_t)0x0200)  /*!< 选择Pin9 */    //(00000010 00000000)b
#define GPIO_Pin_10   ((uint16_t)0x0400)  /*!< 选择Pin10 */   //(00000100 00000000)b
#define GPIO_Pin_11   ((uint16_t)0x0800)  /*!< 选择Pin11 */   //(00001000 00000000)b
#define GPIO_Pin_12   ((uint16_t)0x1000)  /*!< 选择Pin12 */   //(00010000 00000000)b
#define GPIO_Pin_13   ((uint16_t)0x2000)  /*!< 选择Pin13 */   //(00100000 00000000)b
#define GPIO_Pin_14   ((uint16_t)0x4000)  /*!< 选择Pin14 */   //(01000000 00000000)b
#define GPIO_Pin_15   ((uint16_t)0x8000)  /*!< 选择Pin15 */   //(10000000 00000000)b
#define GPIO_Pin_All  ((uint16_t)0xFFFF)  /*!< 选择全部引脚*/ //(11111111 11111111)b

// 2. 定义引脚模式和速度的枚举类型
typedef enum
{ 
  GPIO_Speed_10MHz = 1,         // 10MHZ        (01)b
  GPIO_Speed_2MHz,              // 2MHZ         (10)b
  GPIO_Speed_50MHz              // 50MHZ        (11)b
}GPIOSpeed_TypeDef;

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

//3. 定义初始化结构体
typedef struct
{
  uint16_t GPIO_Pin;             /*要配置的引脚号*/
  GPIOSpeed_TypeDef GPIO_Speed;  /* 引脚的输出速度 */
  GPIOMode_TypeDef GPIO_Mode;    /* 引脚的输入/输出模式*/
}GPIO_InitTypeDef;

// 4. GPIO各操作函数的声明
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); // GPIOx端口引脚初始化
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);         // GPIOx端口的GPIO_Pin引脚置高, = 1  
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);       // GPIOx端口的GPIO_Pin引脚置低, = 0 
#endif 

1.3.2. 建立stm32f10x_gpio.c文件(需要添加到工程中)

#include "stm32f10x.h"            //  
#include "stm32f10x_gpio.h"
 
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  GPIOx->BSRR |= GPIO_Pin;
}

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  GPIOx->BRR  |= GPIO_Pin;
}

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  
/*---------------------- GPIO 模式配置 --------------------------*/
  // 把输入参数GPIO_Mode的低四位暂存在currentmode
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
	
  // bit4是1表示输出,bit4是0则是输入 
  // 判断bit4是1还是0,即首选判断是输入还是输出模式
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  { 
	// 输出模式则要设置输出速度
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
/*-------------GPIO CRL 寄存器配置 CRL寄存器控制着低8位IO- -------*/
  // 配置端口低8位,即Pin0~Pin7
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
	// 先备份CRL寄存器的值
    tmpreg = GPIOx->CRL;
		
	// 循环,从Pin0开始配对,找出具体的Pin
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
	 // pos的值为1左移pinpos位
      pos = ((uint32_t)0x01) << pinpos;
      
	  // 令pos与输入参数GPIO_PIN作位与运算,为下面的判断作准备
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
			
	  //若currentpin=pos,则找到使用的引脚
      if (currentpin == pos)
      {
		// pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
        pos = pinpos << 2;
       //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
				
        // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);  
				
		// 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
		  // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }				
        else
        {
          // 判断是否为上拉输入模式
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
		    // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
		// 把前面处理后的暂存值写入到CRL寄存器之中
    GPIOx->CRL = tmpreg;
  }
/*-------------GPIO CRH 寄存器配置 CRH寄存器控制着高8位IO- -----------*/
  // 配置端口高8位,即Pin8~Pin15
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
		// // 先备份CRH寄存器的值
    tmpreg = GPIOx->CRH;
		
	// 循环,从Pin8开始配对,找出具体的Pin
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
			
      // pos与输入参数GPIO_PIN作位与运算
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
			
	 //若currentpin=pos,则找到使用的引脚
      if (currentpin == pos)
      {
		//pinpos的值左移两位(乘以4),因为寄存器中4个寄存器位配置一个引脚
        pos = pinpos << 2;
        
	    //把控制这个引脚的4个寄存器位清零,其它寄存器位不变
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
				
        // 向寄存器写入将要配置的引脚的模式
        tmpreg |= (currentmode << pos);
        
		// 判断是否为下拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
		  // 下拉输入模式,引脚默认置0,对BRR寄存器写1可对引脚置0
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
         // 判断是否为上拉输入模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
		  // 上拉输入模式,引脚默认值为1,对BSRR寄存器写1可对引脚置1
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
	// 把前面处理后的暂存值写入到CRH寄存器之中
    GPIOx->CRH = tmpreg;
  }
}

1.4 . 建立main.c主函数 (需要添加到工程中)

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"

void Delay_ms(uint32_t timeMs)
{	
	uint32_t i = 0;
	while(timeMs--)
	{
	   i = 1200;
		 while(i--);
		 
	}
}

int main (void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	// 打开 GPIOB 端口的时钟
	 RCC->APB2ENR  |=  ( (1) << 3 );
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;           // GPIO_Pin_12
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     // 推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //输出速度为50M
	GPIO_Init(GPIOB, &GPIO_InitStructure);	             // PB12 引脚初始化 

  while(1)
  {
		GPIO_SetBits(GPIOB,GPIO_Pin_12);        // PB12置高, = 1, 灭灯
		Delay_ms(1000);
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);      // PB12置低, = 0, 亮灯
		Delay_ms(1000);
	}
}

1.5 将工程所需要的文件添加到工程中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AZ9KDSzm-1635738492280)(%E5%BB%BA%E7%AB%8B%E8%87%AA%E5%B7%B1%E7%9A%84STM32%E5%BA%93%E5%87%BD%E6%95%B0%E5%B7%A5%E7%A8%8B%EF%BC%881%EF%BC%89–%E8%87%AA%E5%BB%BAstm32f10x.h.assets/1111-16356986004072-16356986269583.png)]

2. 改进:定义自定义的文件,保存自定义的变量和函数,如延时函数等

2.1 定义myfun.h(不用添加到工程中)

#ifndef __MYFUN_H
#define __MYFUN_H

#include "stm32f10x.h" // 要用到 uint32_t

void Delay_ms(uint32_t timeMs);
void Delay_us(uint32_t count);

#endif

2.2 定义myfun.c(需要添加到工程中)

#include "myfun.h"

void Delay_us(uint32_t count)
{
	for( ; count !=0; count-- );
}

void Delay_ms(uint32_t timeMs)
{	
	uint32_t i = 0;
	while(timeMs--)
	{
	   i = 1200;
		while(i--);		 
	}
}

2.3 修改main.c函数

  • 删除main.c中的Delay()函数,并在main.c中添加:#include “myfun.h”
  • 将GPIOB、GPIO_Pin_12、开端口时钟 进一步用宏“封装”
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "myfun.h"

int main (void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	// 打开 GPIOB 端口的时钟
	RCC->APB2ENR  |=  ( (1) << 3 );
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;           // GPIO_Pin_12
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     // 推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //输出速度为50M
	GPIO_Init(GPIOB, &GPIO_InitStructure);	             // PB12 引脚初始化 

  while(1)
  {
		GPIO_SetBits(GPIOB,GPIO_Pin_12);        // PB12置高, = 1, 灭灯
		Delay_ms(1000);
		GPIO_ResetBits(GPIOB,GPIO_Pin_12);      // PB12置低, = 0, 亮灯
		Delay_ms(1000);
	}

}

2.4 进一步封装LED初始化函数

2.4.1 新建bsp_led.h

#ifndef __BSP_LED_H__
#define __BSP_LED_H__

#define   LED_D3_GPIO_PORT                   GPIOB
#define   LED_D3_GPIO_CLK_ENABLE            (RCC->APB2ENR  |=  ( (1) << 3 ))
#define   LED_D3_GPIO_PIN                    GPIO_Pin_12

void LED_Init(void);	

#endif

2.4.2 新建bsp_led.c

#include "bsp_led.h"
#include "stm32f10x_gpio.h" //注意添加,注意该头文件已经添加了#include "stm32f10x.h",意味着我们可以直接用数据类型、外设寄存器 
 

//uint32_t time;
//GPIOC->RHL = 0xff;

void LED_Init(void)	
{
  GPIO_InitTypeDef  GPIO_InitStructure;
	
	// 打开 GPIOB 端口的时钟
	LED_D3_GPIO_CLK_ENABLE;
	
	GPIO_InitStructure.GPIO_Pin = LED_D3_GPIO_PIN;           // GPIO_Pin_12
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     // 推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //输出速度为50M
	GPIO_Init(LED_D3_GPIO_PORT, &GPIO_InitStructure);	             // PB12 引脚初始化
}

2.4.3 封装后后的main.c

  • 在main.c中添加:#include “bsp_led.h”
  • 将bsp_led.c添加到工程中去
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "myfun.h"
#include "bsp_led.h"

int main (void)
{
  LED_Init();
  
  while(1)
  {
		GPIO_SetBits(LED_D3_GPIO_PORT,LED_D3_GPIO_PIN);        // PB12置高, = 1, 灭灯
		Delay_ms(1000);
		GPIO_ResetBits(LED_D3_GPIO_PORT,LED_D3_GPIO_PIN);      // PB12置低, = 0, 亮灯
		Delay_ms(1000);
	}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qk5FDUa3-1635738492282)(%E5%BB%BA%E7%AB%8B%E8%87%AA%E5%B7%B1%E7%9A%84STM32%E5%BA%93%E5%87%BD%E6%95%B0%E5%B7%A5%E7%A8%8B%EF%BC%881%EF%BC%89–%E8%87%AA%E5%BB%BAstm32f10x.h.assets/%5BD1ME@P%7D1$8(3EF)]O4B%7D8H-16357006028724.png)

3. 尝试用库中的stm32f10x.h

在需要用到数据类型、外设寄存器结构体定义、外设寄存器基地址映射、指向外设寄存器基地址的指针声明的时候,在该文件中#include “stm32f10x.h”即可

注意,如果是xxx.h 和xxx.c 成对出现时,只需要在xxx.h文件中 #include “stm32f10x.h”即可,而xxx.c文件中不需要,否则出现重定义错误。

4. 继续封装stm32f10x_xxx.h

例如:要用到gpio、rcc、spi、tim、usart外设,则需要分别将各外设的配置头文件#include stm32f10x_xxx.h进来。当工程中要用到的外设比较多时,则需要包含很多文件。所以,

为了简单,直接用stm32f10x_conf.h 对所有外设的头文件进行来封装,所以直接# include "stm32f10x_conf.h"后,就自动把所有外设的头文件包括进来了

。当然为了简化代码,可以手动将不用的外设头文件从stm32f10x_conf.h中暂时注释掉。

例如:LED点灯时,只需要用到stm32f10x_gpio.h,则在 **#include “stm32f10x.h” **中将 其他的都注释掉。

#ifndef __STM32F10X_CONF_H
#define __STM32F10X_CONF_H

#include "stm32f10x_gpio.h"

//#include "stm32f10x_adc.h" 
//#include "stm32f10x_bkp.h" 
//#include "stm32f10x_can.h" 
//#include "stm32f10x_cec.h" 
//#include "stm32f10x_crc.h" 
//#include "stm32f10x_dac.h" 
//#include "stm32f10x_dbgmcu.h"
//#include "stm32f10x_dma.h"
//#include "stm32f10x_exti.h" 
//#include "stm32f10x_flash.h"
//#include "stm32f10x_fsmc.h" 

//#include "stm32f10x_i2c.h" 
//#include "stm32f10x_iwdg.h" 
//#include "stm32f10x_pwr.h" 
//#include "stm32f10x_rcc.h" 
//#include "stm32f10x_rtc.h" 
//#include "stm32f10x_sdio.h" 
//#include "stm32f10x_spi.h" 
//#include "stm32f10x_tim.h" 
//#include "stm32f10x_usart.h" 
// #include "stm32f10x_wwdg.h" 
// #include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */

#endif

stm32f10x_conf.h中包含了所有外设的头文件,因此任意源文件只要包含了stm32f10x.h,就可以在源文件调用任意外设的函数。

若有外设未使用到,在stm32f10x_conf.h注释相应部分,项目编译时就不会在编译去掉的外设。

4.1 如何使用

方法1:直接在main.c中 #include “stm32f10x_conf.h”

方法2:使用固件库时,配置keil编译器---->魔法棒—> 在Define处添加:USE_STDPERIPH_DRIVER

​ 解释,编译器中如上配置好后,stm32f10x.h中的如下代码起作用,会把stm32f10x_conf.h包含进来

#ifdef USE_STDPERIPH_DRIVER
  #include "stm32f10x_conf.h"
#endif

5. 模仿继stm32f10x_conf.h的思路,续封装用户自定义文件

6 作业:用一个按键点亮LED灯

参考文献:野火–STM32库开发实战指南

上一篇:2021/10/11 智能家具 嵌入式实训 第一天 建立工程文件+烧录


下一篇:[转]ARM Cortex-M底层技术(3)—编译内核的原理及其应用 - 中蓝闻道 - 博客园