9. 向工程中添加代码文件

9.1. 向工程中添加自己的文件

由于之前的工程实现的功能较为简单,我们直接将功能实现代码写在main函数的while循环中, 但当我们重新配置外设参数并生成新的工程代码时,mian.c文件以及各种外设初始化配置函数都会被覆盖, 这样就使得我们得重新写功能实现的代码了。为了防止自己的代码被覆盖最好的方法是在工程中添加额外的工程文件, 在里面添加自己需要的功能实现代码,这样也能够提高代码可移植性, 最后只需要在main.c主函数中调用我们的写好的函数即可。

在CM4工程目录下新建一个名为User的目录。

图 8‑1 新建User目录

在工程中添加工程目录,选中CM4工程点击鼠标右键,选择最后一个Properties

图 8‑2 Properties

依次选择C/C++ General -> Paths and Symbols -> Includes

图 8‑3 图片未找到

选择Add,输入我们的创建的文件名User,点击OK

图 8‑4 图片未找到

再点击Apply and Close

图 8‑5 图片未找到

可以看到工程目录上多了User文件夹,如果没有出现可以按F5或者右击工程选择Refresh刷新下工程目录。

图 8‑6 图片未找到 图 8‑7 图片未找到

再次选中CM4工程点击鼠标右键,选择最后一个Properties,进入Paths and Symbols,进行如下操作

图 8‑8 图片未找到

最后在工程目录User下创建led、key目录以及main_task.h main_task.c文件,

图 8‑9 图片未找到 图 8‑10 图片未找到

在led和key目录下分别再创建bsp_led.c、bsp_led.h和bsp_key.c、bsp_key.h文件

图 8‑11 图片未找到

9.2. 修改代码

接下来将我们上一章的用到的板载led以及板载按键的控制代码分别移到相对应bsp_led.c/h 以及bsp_key.c/h中。

9.2.1. LED

9.2.1.1. bsp_led.h

bsp_led.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifndef LED_BSP_LED_H_
#define LED_BSP_LED_H_

#include "main.h"

//引脚定义 当替换引脚时,只需要替换以下宏
/*******************************************************/
//LED1
#define LED1_PIN                  LED1_Pin
#define LED1_GPIO_PORT            LED1_GPIO_Port


//LED2
#define LED2_PIN                  LED2_Pin
#define LED2_GPIO_PORT            LED2_GPIO_Port


/** 控制LED灯亮灭的宏,
   * LED低电平亮,设置ON=0,OFF=1
   * 若LED高电平亮,把宏设置成ON=1 ,OFF=0 即可
   */
#define ON  GPIO_PIN_RESET
#define OFF GPIO_PIN_SET

/* 带参宏,可以像内联函数一样使用 */
#define LED1(a)       HAL_GPIO_WritePin(LED1_GPIO_PORT,LED1_PIN,a)
#define LED2(a)       HAL_GPIO_WritePin(LED2_GPIO_PORT,LED2_PIN,a)

/* 定义控制IO的宏 */
#define LED1_TOGGLE          HAL_GPIO_TogglePin(LED1_GPIO_PORT,LED1_PIN)
#define LED1_OFF             HAL_GPIO_WritePin(LED1_GPIO_PORT,LED1_PIN,OFF)
#define LED1_ON              HAL_GPIO_WritePin(LED1_GPIO_PORT,LED1_PIN,ON)

/* 定义控制IO的宏 */
#define LED2_TOGGLE          HAL_GPIO_TogglePin(LED2_GPIO_PORT,LED2_PIN)
#define LED2_OFF             HAL_GPIO_WritePin(LED2_GPIO_PORT,LED2_PIN,OFF)
#define LED2_ON              HAL_GPIO_WritePin(LED2_GPIO_PORT,LED2_PIN,ON)


void LED_GPIO_Config(void);

#endif /* LED_BSP_LED_H_ */
  • 第4行,一般只需要包含main.h文件即可。

  • 第9-15行,使用宏再次对板子上独立的蓝色LED的引脚宏进行封装, 这样做的好处在于如果想把这个文件移植到其他板子或者引脚上,只需要修改这些即可。

  • 第26-27行,当我们需要控制LED1时,也可使用LED1(ON)或者LED1(OFF),LED2同理。

  • 第30-37行,为每个LED提供了三个独立的宏对LED进行操作。

  • 第29行,定义了LED_Config函数,不同的用户可能对刚上电时对LED的初始状态有不同的需求, 可在这个函数中配置LED上电时的初始状态。

9.2.1.2. bsp_led.c

bsp_led.c
1
2
3
4
5
6
7
#include "./led/bsp_led.h"

void LED_GPIO_Config(void)
{
   LED1_OFF;         //配置LED的初始状态为灭
   LED2_OFF;         //配置LED的初始状态为灭
}

在bsp_led.c文件中的内容很简单,只有LED_GPIO_Config函数,将LED的状态设置为OFF。

在上面的两个文件中,实现了对板载LED的各种操作函数/宏,在编写功能实现代码时能够 一目了然,增加了文件的可读性;对于不同的板子上的引脚,只需要修改相对应的宏即可, 这对于使用HAL库开发的STM32各个系列的单片机都是通用的,具有很高的移植性。

9.2.2. 按键

9.2.2.1. bsp_key.h

bsp_key.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef KEY_BSP_KEY_H_
#define KEY_BSP_KEY_H_

#include "main.h"
//引脚定义
/*******************************************************/
#define KEY1_PIN                  KEY1_Pin
#define KEY1_GPIO_PORT            KEY1_GPIO_Port

#define KEY2_PIN                  KEY2_Pin
#define KEY2_GPIO_PORT            KEY2_GPIO_Port

/*******************************************************/

/** 按键按下标置宏
   * 按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
   * 若按键按下为低电平,把宏设置成KEY_ON=0 ,KEY_OFF=1 即可
   */
#define KEY_ON       1
#define KEY_OFF      0

void Key_GPIO_Config(void);
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

#endif /* KEY_BSP_KEY_H_ */
  • 第4-5行,只需包含main.h文件即可,即可使用HAL提供的各种宏及函数、结构体等。

  • 第7-11行,和LED引脚操作方式类型,定义了按键引脚所在的GPIO编号以及GPIO端口的宏,当配置按键 的引脚名字不是KEY1或KEY2时,只需要修改这两个宏即可,依葫芦画瓢可在这个文件添加更多的按键引脚定义。

  • 第19-20行,定义了两个宏来表示引脚的状态,当按键被按下是用KEY_ON表示,按键松开是用按键KEY_OFF表示。

  • 第20-21行,声明了两个按键操作函数,分别用于按键引脚的配置以及按键扫描。

9.2.2.2. bsp_key.c

bsp_key.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <./key/bsp_key.h>

void Key_GPIO_Config(void)
{

}

uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{

   /*检测是否有按键按下 */

   if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON )

   {

      /*等待按键释放 */

      while(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == KEY_ON);

      return         KEY_ON;

   }

   else
      return KEY_OFF;

}

在bsp_key.c文件中主要实现Key_Scan函数,Key_GPIO_Config函数内容为空,由于按键所在的GPIO 引脚的初始化工作STM32CubeIDE已经帮我们做好了,这里留空即可,当然也可以选择删掉。 按键扫描函数的内容和在上个章节中读取按键的逻辑是一样,如果按键被按下Key_Scan函数将返回 KEY_ON,如果按键没被按键则返回KEY_OFF;

同样在bsp_key.c/h文件中,根据GPIO的功能将按键将按键封装了一遍, 使得程序更加具有可读性以及移植性。

9.2.3. main_task

9.2.3.1. main_task.h

main_task.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#ifndef MAIN_TASK_H_
#define MAIN_TASK_H_

#include "./key/bsp_key.h"
#include "./led/bsp_led.h"

void Main_Config(void);      //配置函数
void Main_Task(void);        //主要的任务函数

#endif /* MAIN_TASK_H_ */

在main_task.h中的内容很简单,包含了bsp_key.h和bsp_led.h两个文件,并声明了 Main_Task以及Main_Config函数,Main_Config主要用于配置一些初始化状态, 而Main_Task函数为裸机开发中的功能实现部分的主体。

9.2.3.2. main_task.c

main_task.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include "main_task.h"

void Main_Config(void)
{
   LED_GPIO_Config();                //初始化LED状态为灭
}

void Main_Task(void)
{

   if(Key_Scan(KEY1_GPIO_Port,KEY1_Pin) == KEY_ON)
   {
      LED1_TOGGLE;
   }

   if(Key_Scan(KEY2_GPIO_Port,KEY2_Pin) == KEY_ON)
   {
      LED2_TOGGLE;
   }

}

main_task函数中,判断按键是否被按下当按键被按下时翻转LED。

最终在main.c文件中包含 main_task.h 头文件,并在while(1)前调用Main_Config函数,而在while(1) 中调用Main_Task函数。

图 8‑12 图片未找到 图 8‑13 图片未找到

9.3. 进入调试验证

将板子设置为工程模式并运行程序,按键KEY1可控制LED1亮灭,按键KEY2可控制LED2亮灭。