文章目录
- 一、FSP配置
- 二、hal_entry入口函数
- 三、封装 LED 设备驱动程序
- 下载验证
一、FSP配置
对于 Keil 开发环境:
拷贝一份之前的 Keil 工程模板 “06_Template”, 然后将工程文件夹重命名为 “11_GPIO_LED”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。
工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “led” 文件夹, 再进入 “led” 文件夹里面新建 led 驱动的源文件和头文件:“bsp_led.c” 和 “bsp_led.h”。 工程文件结构如下。
11_GPIO_LED
├─ ......
└─ src
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
└─ hal_entry.c
首先打开以前 “11_GPIO_LED” 项目的 FSP 配置界面,接下来我们要在这个界面里配置芯片的引脚。 以下的配置过程是以启明6M5开发板为例,启明4M2/2L1开发板的配置方法也是一样的,只是需要配置的LED引脚不同。
在 FSP 配置界面里面点开 “Pins”-> “Ports”-> “P4”-> “P400”, 然后将连接到LED灯的 IO 引脚的 “Mode” 属性配置为 “Output mode (Initial High)”, 表示该引脚默认输出高电平,其他的属性默认即可。 其他两个LED引脚 “P403”、“P404” 也是按照这样进行配置。
Pin Configuration 页面的 IOPORT 属性介绍:
三个 LED 引脚都配置完成之后的配置界面如图所示。
配置完成之后按下快捷键 “Ctrl + S” 保存,最后点右上角的 “Generate Project Content” 图标, 让软件根据我们的设置自动生成配置代码即可。
对于 Keil 这边 RASC 的 FSP 配置也是一样的,需要先通过 RASC 软件打开 Keil 工程相关的 FSP 配置界面。
二、hal_entry入口函数
当使用 RTOS 时,程序从 main 函数开始进行线程调度; 当没有使用 RTOS 时,C语言程序的入口函数 main 函数调用了 hal_entry 函数。 由于我们新建的工程是没有选用 RTOS 的,因此,用户程序从 hal_entry 函数开始执行。 我们直接打开 “\src\hal_entry.c” 文件,在 hal_entry 函数里面编写我们的代码。
想要实现LED灯的闪烁效果,其思路非常地简单:首先初始化配置 LED 引脚, 然后在一个死循环里面重复此流程:LED 灯亮,延时1s,LED 灯灭,延时1s,然后 LED 灯又亮。 如此反复循环,就能实现 LED 灯的闪烁效果。
首先,我们需要通过 R_IOPORT_Open 函数来初始化 IOPORT 模块, 在调用 R_IOPORT_Open 函数时,需要传入控制块参数 g_ioport_ctrl 和配置参数 g_ioport.p_cfg。
//调用 R_IOPORT_Open 函数来初始化 IOPORT 模块
R_IOPORT_Open (&g_ioport_ctrl, g_ioport.p_cfg);
成功打开 IOPORT 模块后,说明 IO 引脚已经全部初始化完成。接着让程序继续往下执行,进入到 while(1) 死循环。 在 while(1) 循环里,我们使用 R_IOPORT_PinWrite 和 R_BSP_SoftwareDelay 这两个函数来实现前面所述的思路。
使用 R_IOPORT_PinWrite 函数可以控制引脚的输出高低电平,从而控制 LED 灯的亮灭。 它的第一个参数需要传入控制块 g_ioport_ctrl,第二个参数传入IO端口和引脚号,第三个参数传入IO引脚电平。
fsp_err_t R_IOPORT_PinWrite (ioport_ctrl_t * const p_ctrl, bsp_io_port_pin_t pin, bsp_io_level_t level);
使用 R_BSP_SoftwareDelay 函数可以进行延时,LED 灯维持亮和灭这两种状态的时间由此函数决定。 它的第一个参数表示延时的时间量,第二个参数表示时间单位。
void R_BSP_SoftwareDelay (uint32_t delay, bsp_delay_units_t units);
时间单位参数可选:
-
BSP_DELAY_UNITS_SECONDS,表示秒;
-
BSP_DELAY_UNITS_MILLISECONDS,表示毫秒;
-
BSP_DELAY_UNITS_MICROSECONDS,表示微秒。
以启明6M5开发板为例,完整代码如下:
void hal_entry(void)
{
/* TODO: add your own code here */
/* 初始化配置引脚(这里重复初始化了,可以注释掉) */
R_IOPORT_Open (&g_ioport_ctrl, g_ioport.p_cfg);
while(1)
{
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_00, BSP_IO_LEVEL_LOW); //LED1亮
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_03, BSP_IO_LEVEL_LOW); //LED2亮
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_LOW); //LED3亮
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_00, BSP_IO_LEVEL_HIGH); //LED1灭
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_03, BSP_IO_LEVEL_HIGH); //LED2灭
R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_HIGH); //LED3灭
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
到此,我们已经完全实现了让 LED 闪烁的效果。 按照编写驱动程序的一般要求,我们可以把 LED 的驱动单独拿出来,放到独立的源文件/头文件里面进行封装。 接下来将介绍封装 LED 设备驱动程序的一般方法。
三、封装 LED 设备驱动程序
重新规划一下我们的工程结构。 在 src 文件夹里面新建一个“led”文件夹,再在该文件夹里面新建两个文件:“bsp_led.c”和“bsp_led.h”, 如同前面“新建工程”小节所述,把它们加入到我们的工程中。 这两个文件的内容如下。
led/bsp_led.h
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "hal_data.h"
/* LED引脚置低电平 LED灯亮 */
#define LED1_ON R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_00, BSP_IO_LEVEL_LOW)
#define LED2_ON R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_03, BSP_IO_LEVEL_LOW)
#define LED3_ON R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_LOW)
/* LED引脚置高电平 LED灯灭 */
#define LED1_OFF R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_00, BSP_IO_LEVEL_HIGH)
#define LED2_OFF R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_03, BSP_IO_LEVEL_HIGH)
#define LED3_OFF R_IOPORT_PinWrite(&g_ioport_ctrl, BSP_IO_PORT_04_PIN_04, BSP_IO_LEVEL_HIGH)
/* 使用寄存器来实现 LED灯翻转 */
#define LED1_TOGGLE R_PORT4->PODR ^= 1<<(BSP_IO_PORT_04_PIN_00 & 0xFF)
#define LED2_TOGGLE R_PORT4->PODR ^= 1<<(BSP_IO_PORT_04_PIN_03 & 0xFF)
#define LED3_TOGGLE R_PORT4->PODR ^= 1<<(BSP_IO_PORT_04_PIN_04 & 0xFF)
/* LED初始化函数 */
void LED_Init(void);
#endif
`**加粗样式**``
**led/bsp_led.c**
```c
#include "bsp_led.h"
/* LED初始化函数 */
void LED_Init(void)
{
/* 初始化配置引脚(这里重复初始化了,可以注释掉) */
R_IOPORT_Open (&g_ioport_ctrl, g_ioport.p_cfg);
}
在 “hal_entry.c” 文件中添加对头文件 “bsp_led.h” 的包含, 然后将 hal_entry 入口函数的内容改为如下。
hal_entry入口函数
/* 用户头文件包含 */
#include "led/bsp_led.h"
void hal_entry(void)
{
/* TODO: add your own code here */
LED_Init(); // LED 初始化
while(1)
{
LED1_ON; // LED1亮
LED2_ON; // LED2亮
LED3_ON; // LED3亮
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
LED1_OFF; // LED1灭
LED2_OFF; // LED2灭
LED3_OFF; // LED3灭
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
通过这种方式来封装硬件的驱动程序可以使程序看起来更简单与清晰,并且会让我们以后的开发变得更加的顺畅。
下载验证
将程序编译并下载到开发板之后,按下复位按键来复位开发板, 可以观察到开发板上面除了电源指示灯之外的三个 LED 灯在同时缓慢闪烁, 三个 LED 灯每秒钟改变一次亮灭的状态。