使用timer定时器控制LED亮灭
本节实验目的为实现定时器的功能应用。这一节计划采取使用定时器中断来实现LED状态反转,实现LED的亮灭循环。计划实现功能为:使用定时器0的两个通道,通道0设置定时1s,通道2设置2s,在通道0中实现红色LED的状态反转,通道2中实现绿色LED的状态反转。
1)、带发光二极管(LED)的K210开发板一块,用于实践并查看实验现象;
2)、官方裸机编程指导手册:kendryte_standalone_programming_guide,用于查阅SDK中接口说明。
定时器实质上是一个加1计数器,每过一个机器周期,计数器自动加1,当达到我们设定值时,计数器溢出,产生中断,执行中断回调函数。由于每个机器周期的时长是固定的,所以,根据每个机器周期的时长,我们很方便的便能计算出我们想要设定的时间的计数设定值,从实现定时的目的。
硬件电路图如下:
软件流程图如下:
根据硬件设计和软件设计可知,本节应用实现步骤如下:
1)、设置引脚复用功能:由硬件原理图可知:我们需要将IO12、IO13设置为GPIOHS功能,如下图:
2)、LED初始化,如下图:
3)、timer0初始化,如下图:
4)、实现timer0通道0和通道1的中断回调函数,在中断回调函数中,实现LED状态信息更新,如下图:
5)、检测是否更新LED状态,如果更新,进入更新,将LED更新状态设置为不更新,并控制LED状态,如下图:
根据上述实现步骤,最终代码如下:
#include <fpioa.h> #include <gpiohs.h> #include <timer.h> #include <plic.h> #include <sysctl.h> #include <sleep.h> #define TIMER_1s (1e9) #define LED_R_PIN (12) #define LED_G_PIN (13) #define LED_R_GPIOHSNUM (0) #define LED_G_GPIOHSNUM (1) #define LED_R_FUNC (FUNC_GPIOHS0+LED_R_GPIOHSNUM) #define LED_G_FUNC (FUNC_GPIOHS0+LED_G_GPIOHSNUM) enum LEDNUM { LED_R = 0, LED_G, }; struct UNITLED { enum LEDNUM led_num; gpio_pin_value_t led_value; char ledst_updata; }; struct LEDINFO { struct UNITLED led_r; struct UNITLED led_g; }; /********************************* * 管脚功能初始化 ********************************/ void init_hardware(void) { fpioa_set_function(LED_R_PIN, LED_R_FUNC); fpioa_set_function(LED_G_PIN, LED_G_FUNC); } /********************************* * LED初始化 ********************************/ void init_led() { gpiohs_set_drive_mode(LED_R_GPIOHSNUM, GPIO_DM_OUTPUT); gpiohs_set_drive_mode(LED_G_GPIOHSNUM, GPIO_DM_OUTPUT); gpiohs_set_pin(LED_R_GPIOHSNUM, GPIO_PV_HIGH); gpiohs_set_pin(LED_G_GPIOHSNUM, GPIO_PV_HIGH); } /********************************* * 控制LED亮灭 ********************************/ void ctl_led(enum LEDNUM led, gpio_pin_value_t value) { gpiohs_set_pin(led, value); } /********************************* * 定时器0通道0 中断回调函数 ********************************/ int irq_timer00_callback_t(void *ctx) { struct UNITLED *led = (struct UNITLED *)ctx; led->led_num = LED_R; led->led_value = !led->led_value; led->ledst_updata = 1; return 0; } /********************************* * 定时器0通道1 中断回调函数 ********************************/ int irq_timer01_callback_t(void *ctx) { struct UNITLED *led = (struct UNITLED *)ctx; led->led_num = LED_G; led->led_value = !led->led_value; led->ledst_updata = 1; return 0; } /********************************* * 定时器初始化 ********************************/ void init_timer(struct LEDINFO *ledinfo) { plic_init(); timer_init(TIMER_DEVICE_0); timer_set_interval(TIMER_DEVICE_0, TIMER_CHANNEL_0, TIMER_1s); timer_irq_register(TIMER_DEVICE_0, TIMER_CHANNEL_0, 0, 1, irq_timer00_callback_t, &ledinfo->led_r); timer_set_enable(TIMER_DEVICE_0, TIMER_CHANNEL_0, 1); timer_set_interval(TIMER_DEVICE_0, TIMER_CHANNEL_1, 2*TIMER_1s); timer_irq_register(TIMER_DEVICE_0, TIMER_CHANNEL_1, 0, 1, irq_timer01_callback_t, &ledinfo->led_g); timer_set_enable(TIMER_DEVICE_0, TIMER_CHANNEL_1, 1); sysctl_enable_irq(); } int main(void) { init_hardware(); init_led(); struct LEDINFO led_info = { .led_r = { .led_num = LED_R, .led_value = GPIO_PV_HIGH, .ledst_updata = 0, }, .led_g = { .led_num = LED_G, .led_value = GPIO_PV_HIGH, .ledst_updata = 0, }, }; init_timer(&led_info); while(1) { if(led_info.led_r.ledst_updata == 1) { led_info.led_r.ledst_updata = 0; ctl_led(led_info.led_r.led_num, led_info.led_r.led_value); } if(led_info.led_g.ledst_updata == 1) { led_info.led_g.ledst_updata = 0; ctl_led(led_info.led_g.led_num, led_info.led_g.led_value); } usleep(2000); } }
1)、同上一节类似,在SDK中创建timer文件夹,在新建的文件夹中创建一个main.c文件,然后将本节代码输入到main.c文件中,如下图:
2)、同上一节的编译方式类似,打开vscode终端,在终端中进入上一节创建的build文件夹,然后输入:cmake ../ -DPROJ=timer -G "MinGW Makefiles" ,生成makefile文件,如下图:
3)、生成makefile文件后,输入:make ,开始编译,如下图:
4)、编译完成后,会在build目录下生成烧录文件:timer.bin,如下图:
同上一节的烧录方式类似,注意:Firmware那一项选择我们刚编译出的timer.bin文件。
红色LED亮1s,灭1s;同时绿色LED亮2s,灭2s;以此循环。
1)、K210总有三个定时器,每个定时器有四个通道,每个通道都拥有独立中断,用户可根据需求自由选择;
2)、K210定时器可使用timer_set_enable函数来开启和关闭定时器,初始化完成之后,开启和关闭定时器不需要重新配置。