第六章 跑马灯实验
本章实验将介绍如何使用SDK编程让Kendryte K210控制板载的双色LED闪烁,以实现跑马灯的效果。通过本章的学习,读者将学习到用SDK编程技术控制Kendryte K210的GPIO输出高低电平。
本章分为如下几个小节:
6.1 FPIOA介绍
6.2 GPIO介绍
6.3 硬件设计
6.4 程序设计
6.5 运行验证
6.1 FPIOA介绍
FPIOA(Field Programmable Input and Output Array,现场可编程IO阵列)是Kendryte K210芯片内部的模块,FPIOA最主要的功能是允许用户将Kendryte K210芯片内部的255个功能映射到芯片外围的48个自由IO上,因为Kendryte K210芯片内部外设的功能与Kendryte K210引出的外部引脚是彼此独立的,这样的好处是IO引脚可以再不同时刻扮演不同的角色,极大地方便了软硬件的开发,同时它还具有以下功能:
1. 支持IO的可编程功能选择;
2. 支持IO输出的8种驱动能⼒选择;
3. 支持IO的内部上拉电阻选择;
4. 支持IO的内部下拉电阻选择;
5. 支持IO输入的内部施密特触发器设置;
6. 支持IO输出的斜率控制;
7. 支持内部输入逻辑的电平设置。
裸机SDK内置了FPIOA 驱动文件fpioa.c和对应的头文件,共有数十个API函数,能够帮助开发者将引脚与具体的硬件功能进行绑定或解绑,以及方便各个管脚的使用和功能配置。
Kendryte K210一共有48个自由IO,对应的引脚编号为0~47,本章实验只使用到几个普通GPIO口,更详细的硬件功能列表请见表6.1.1。





表6.1.1 Kendryte K210 FPIOA硬件功能表
我们简单介绍下即将使用的API函数fpioa_set_function(),该函数的功能是将硬件的管脚号与FPIOA的功能号绑定,从而设置引脚的功能,这个和STM32的引脚复用功能有点相似,使用需要传入两个参数,分别是硬件引脚和功能号,方法如下所示:
int fpioa_set_function(int number, fpioa_function_t function)
{
uint8_t index = 0;
/* Check parameters */
if(number < 0 || number >= FPIOA_NUM_IO || function < 0 || function >= FUNC_MAX)
return -1;
if(function == FUNC_RESV0)
{
fpioa_set_function_raw(number, FUNC_RESV0);
return 0;
}
/* Compare all IO */
for(index = 0; index < FPIOA_NUM_IO; index++)
{
if((fpioa->io[index].ch_sel == function) && (index != number))
fpioa_set_function_raw(index, FUNC_RESV0);
}
fpioa_set_function_raw(number, function);
return 0;
}
其他未用到的API函数这里暂时不再介绍,大家如果想对其他API函数进一步了解可以查阅《裸机SDK编程指南》这份文档,第一章节的时候我们也介绍过这份文档的作用,所以大家一定要先保存好这份文档方便我们查阅,在A盘存放路径是:A盘àKendryte K210参考资料àKendryte K210裸机SDK编程指南,接下来讲解GPIO功能。
6.2 GPIO介绍
Kendryte K210上有两种GPIO(General-purpose input/output,通用输入/输出),分别为GPIOHS(高速GPIO)和GPIO(通用GPIO)。
GPIOHS一共有32个,其特点如下所示:
1. 可配置输入输出信号
2. 每个IO具有独立的中断源
3. 中断支持边缘触发和电平触发
4. 每个IO可以分配到FPIOA上48个管脚之一
5. 可配置上下拉,或者高阻
GPIO一共有8个,其特点如下所示:
1. 可配置输入输出信号
2. 8个IO使用一个中断源
3. 可配置触发IO总中断,边沿触发和电平触发
4. 每个IO可以分配到FPIOA上48个管脚之一
5. 可配置上下拉,或者高阻态
6.2.1 Kendryte K210的GPIO简介
上面提到,Kendryte K210的自由IO有48个,可作为GPIO的管脚情况如下图红框部分所示。

图6.2.1.1 Kendryte K210可做GPIO功能管脚图
GPIO口输出来的信号是数字信号。数字信号是以0,1表示的不连续信号,也就是以二进制形式表示的信号。在SDK中数字信号用高低电平来表示,高电平为数字信号1,低电平为数字信号0。如下图所示:

图6.2.1.2数字信号
图中Kendryte K210输出的低电平为0V,输出的高电平为当前Kendryte K210的工作电压,我们K210开发板芯片的供电电压为3.3V,则其高电平为3.3V。
6.2.2 GPIO函数介绍
Kendryte K210提供非常多操作GPIO的函数,这里我们只讲述本实验用到的函数,这些函数介绍如下,高速GPIO口这里就不再介绍了,用法都差不多。
1, gpio_init函数
该函数是通用GPIO才有的,主要用于时钟使能,如下代码所示:
int gpio_init(void)
{
return sysctl_clock_enable(SYSCTL_CLOCK_GPIO);
}
2,gpio_set_drive_mode函数
该函数用来配置指定管脚的工作模式,包括输入输出模式、上下拉等,如下代码所示:
/* 函数原型 */
void gpio_set_drive_mode(uint8_t pin, gpio_drive_mode_t mode)
/**
* gpio_set_drive_mode功能的mode配置参数
*/
typedef enum _gpio_drive_mode
{
GPIO_DM_INPUT,
GPIO_DM_INPUT_PULL_DOWN,
GPIO_DM_INPUT_PULL_UP,
GPIO_DM_OUTPUT,
} gpio_drive_mode_t;
下面我们来讲解一下这几个变量的作用。
①:参数pin为引脚软件编号,最大值要小于8。
②:参数mode为指定的配置模式,如下表所示:

表6.2.2.1 引脚配置的模式
该函数无返回值。
3,gpio_set_pin函数
该函数用来设置GPIO的电平状态,该函数原型及参数描述如下所示:
void gpio_set_pin(uint8_t pin, gpio_pin_value_t value)
typedef enum _gpio_pin_value
{
GPIO_PV_LOW,
GPIO_PV_HIGH
} gpio_pin_value_t;
下面我们讲解下这个函数的两个参数,第一个是引脚的软件编号,同上,第二个参数用于设置IO管脚的高低电平状态,GPIO_PV_LOW为指向IO输出低电平, GPIO_PV_HIGH为输出的高电平。该函数无返回值。
4,gpio_get_pin函数
该函数用来获取IO的电平状态,该函数原型如下所示:
gpio_pin_value_t gpio_get_pin(uint8_t pin)
pin用来指向获取那个IO的电平,该函数的返回值就是指定IO的电平状态0或1。
6.3 硬件设计
6.3.1 例程功能
1. 控制板载双色LED轮流闪烁,实现跑马灯的效果
6.3.2 硬件资源
1. 双色LED
LEDR - IO24
LEDB - IO25
6.3.3 原理图
本章实验内容,需要控制板载双色LED轮流闪烁,以实现跑马灯的效果,正点原子DNK210开发板上双色LED的连接原理图,如下图所示:

图6.3.3.1 双色LED连接原理图
通过以上原理图可以看出,双色LED中红色和蓝色LED对应的IO编号分别为IO24和IO25,且都是当IO输出低电平时LED亮起,当IO输出高电平时LED熄灭。
6.3 程序设计
6.3.1 led驱动代码
这里我们只讲核心部分,LED驱动源码包括两个文件:led.c和led.h(正点原子团队编写的外设驱动基本都是包含一个.c 文件和一个.h 文件,下同),下面我们先介绍led.h文件。
/*****************************HARDWARE-PIN*********************************/ /* 硬件IO口,与原理图对应 */ #define PIN_LED_G (25) #define PIN_LED_B (24) /*****************************SOFTWARE-GPIO********************************/ /* 软件GPIO口,与程序对应 */ #define LEDR_GPIONUM (0) #define LEDB_GPIONUM (1) /*****************************FUNC-GPIO************************************/ /* GPIO口的功能,绑定到硬件IO口 */ #define FUNC_LEDR (FUNC_GPIO0 + LEDR_GPIONUM) #define FUNC_LEDB (FUNC_GPIO0 + LEDB_GPIONUM)
这部分宏定义了两个led引脚的硬件引脚号、软件编号和功能号,通过这里,我们便可通过函数将引脚功能绑定起来,同时也方便我们修改功能和硬件管脚。
/* IO操作 */
#define LEDR(x) do { (x) ? \
gpio_set_pin(LEDR_GPIONUM, GPIO_PV_HIGH): \
gpio_set_pin(LEDR_GPIONUM, GPIO_PV_LOW); \
} while (0)
#define LEDB(x) do { (x) ? \
gpio_set_pin(LEDB_GPIONUM, GPIO_PV_HIGH): \
gpio_set_pin(LEDB_GPIONUM, GPIO_PV_LOW); \
} while (0)
LEDR和LEDB的这两个宏定义,分别控制红灯和蓝灯连接的IO引脚的高低电平,进而控制灯的亮灭,通过硬件连接图我们可以,当连接LED灯的引脚为低电平时,LED亮,为高电平时,LED灭。
当x=1时,对应引脚输出高电平,当x=0时,对应引脚输出低电平,这样我们就非常方便的控制led的亮灭。
接下来我们看led.c文件。
void led_init(void)
{
gpio_init(); /* 使能GPIO时钟 */
fpioa_set_function(PIN_LED_R, FUNC_LEDR);
fpioa_set_function(PIN_LED_B, FUNC_LEDB);
/* 设置LEDR和LEDB的GPIO模式为输出 */
gpio_set_drive_mode(LEDR_GPIONUM, GPIO_DM_OUTPUT);
gpio_set_drive_mode(LEDB_GPIONUM, GPIO_DM_OUTPUT);
/* 先关闭LEDR和LEDB */
gpio_set_pin(LEDR_GPIONUM, GPIO_PV_HIGH);
gpio_set_pin(LEDB_GPIONUM, GPIO_PV_HIGH);
}
这个文件只有led_init()这个函数,我们首先是进行GPIO使能,然后通过fpioa的函数将硬件管脚和功能绑定,然后设置GPIO的功能,最后将管脚电平拉高,使两个LED灯处于关闭状态,这部分内容还是比较容易理解。
6.3.2 main.c代码
main.c中的代码如下所示:
#include#include #include "./BSP/LED/led.h" #include "gpio.h" #include "fpioa.h" int main(void) { led_init(); /* LED初始化 */ while (1) { LEDR(1); /* 红灯灭 */ LEDB(0); /* 蓝灯亮 */ sleep(1); /* 延时1秒 */ LEDR(0); /* 红灯亮 */ LEDB(1); /* 蓝灯灭 */ sleep(1); /* 延时1秒 */ } }
可以看到,首先通过led_init()为控制双色LED的两个IO分别分配了GPIO0和GPIO1的功能,并配置为输出模式,最后在一个循环中轮流设置LEDR和LEDB输出不同的高低电平并延时一秒时间,从而应该能看到板载的双色LED轮流亮起、熄灭,实现跑马灯的效果。
6.4 运行验证
将DNK210开发板连接到电脑主机,通过VSCode将固件烧录到开发板中(方法参考上一章内容,下同),可以看到板载的双色LED轮流亮起、熄灭,实现跑马灯的效果,这与理论推断的结果一致。