K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

使用timer定时器控制LED亮灭

  • 实验目的

        本节实验目的为实现定时器的功能应用。这一节计划采取使用定时器中断来实现LED状态反转,实现LED的亮灭循环。计划实现功能为:使用定时器0的两个通道,通道0设置定时1s,通道2设置2s,在通道0中实现红色LED的状态反转,通道2中实现绿色LED的状态反转。

  • 实验准备

        1)、带发光二极管(LED)的K210开发板一块,用于实践并查看实验现象;

        2)、官方裸机编程指导手册:kendryte_standalone_programming_guide,用于查阅SDK中接口说明。

  • 实验原理

        定时器实质上是一个加1计数器,每过一个机器周期,计数器自动加1,当达到我们设定值时,计数器溢出,产生中断,执行中断回调函数。由于每个机器周期的时长是固定的,所以,根据每个机器周期的时长,我们很方便的便能计算出我们想要设定的时间的计数设定值,从实现定时的目的。

  • 硬件设计

        硬件电路图如下:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

  • 软件设计

        软件流程图如下:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

  • 软件实现

        根据硬件设计和软件设计可知,本节应用实现步骤如下:

        1)、设置引脚复用功能:由硬件原理图可知:我们需要将IO12、IO13设置为GPIOHS功能,如下图:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

        2)、LED初始化,如下图:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

        3)、timer0初始化,如下图:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

        4)、实现timer0通道0和通道1的中断回调函数,在中断回调函数中,实现LED状态信息更新,如下图:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

        5)、检测是否更新LED状态,如果更新,进入更新,将LED更新状态设置为不更新,并控制LED状态,如下图:

K210应用6-使用定时器timer的不同通道分别控制不同的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文件中,如下图:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

        2)、同上一节的编译方式类似,打开vscode终端,在终端中进入上一节创建的build文件夹,然后输入:cmake ../ -DPROJ=timer -G "MinGW Makefiles" ,生成makefile文件,如下图:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

        3)、生成makefile文件后,输入:make ,开始编译,如下图:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

        4)、编译完成后,会在build目录下生成烧录文件:timer.bin,如下图:

K210应用6-使用定时器timer的不同通道分别控制不同的LED状态

  • 烧录

        同上一节的烧录方式类似,注意:Firmware那一项选择我们刚编译出的timer.bin文件。

  • 实验现象

        红色LED亮1s,灭1s;同时绿色LED亮2s,灭2s;以此循环。

  • 实验总结

        1)、K210总有三个定时器,每个定时器有四个通道,每个通道都拥有独立中断,用户可根据需求*选择;

        2)、K210定时器可使用timer_set_enable函数来开启和关闭定时器,初始化完成之后,开启和关闭定时器不需要重新配置。

上一篇:主要设备


下一篇:<Linux开发>--驱动开发-- 字符设备驱动(2) 过程详细记录