第六章 跑马灯实验 本章实验将介绍如何使用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。 | | | | | | | | | | | | | | | | | | | I2S1 Serial Data Output 0 | | | | I2S1 Serial Data Output 1 | | | | I2S1 Serial Data Output 2 | | | | I2S1 Serial Data Output 3 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I2S2 Serial Data Output 0 | | | | I2S2 Serial Data Output 1 | | | | I2S2 Serial Data Output 2 | | | | I2S2 Serial Data Output 3 | | | | | | UART High speed Transmitter | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | DVP Horizontal Reference output | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | UART1 Data Carrier Detect | | | | | | | | UART1 Serial Infrared Input | | | | UART1 Data Terminal Ready | | | | | | | | UART1 User-designated Output 2 | | | | UART1 User-designated Output 1 | | | | UART1 Serial Infrared Output | | | | UART1 Transmit Clock Output | | | | UART1 Receiver Output Enable | | | | UART1 Driver Output Enable | | | | | | | | | | | | | | | | UART2 Data Carrier Detect | | | | | | | | UART2 Serial Infrared Input | | | | UART2 Data Terminal Ready | | | | | | | | UART2 User-designated Output 2 | | | | UART2 User-designated Output 1 | | | | UART2 Serial Infrared Output | | | | UART2 Transmit Clock Output | | | | UART2 Receiver Output Enable | | | | UART2 Driver Output Enable | | | | | | | | | | | | | | | | UART3 Data Carrier Detect | | | | | | | | UART3 Serial Infrared Input | | | | UART3 Data Terminal Ready | | | | | | | | UART3 User-designated Output 2 | | | | UART3 User-designated Output 1 | | | | UART3 Serial Infrared Output | | | | UART3 Transmit Clock Output | | | | UART3 Receiver Output Enable | | | | UART3 Driver Output Enable | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I2S0 Serial Data Output 0 | | | | I2S0 Serial Data Output 1 | | | | I2S0 Serial Data Output 2 | | | | I2S0 Serial Data Output 3 | | | | | | | | | | | | | | | | | | |
表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 <stdio.h> #include <unistd.h> #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轮流亮起、熄灭,实现跑马灯的效果,这与理论推断的结果一致。
|