AB32VG1开发板学习GPIO,多线程点灯

Posted 小辉_Super

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AB32VG1开发板学习GPIO,多线程点灯相关的知识,希望对你有一定的参考价值。

上一篇文章我学习了如何在 RT-Thread Studio 上搭建一个AB32VG1的工程,可以说非常简单,今天继续学习一个简单的操作——点灯。由于上个实验已经点过灯,这次我就用上RT-Thread,实现多线程点灯。

硬件引脚

我们先看看AB32VG1开发板上LED的IO引脚,该开发板上有一个3色灯,分别是LED-R(红)、LED-G(绿)和LED-B(蓝),对应的GPIO为PE1,PE4和PA1。


GPIO配置

下面是GPIO的配置代码,GPIO编号是uint8_t类型变量,使用rt_pin_get()函数可以获取对应IO端口的编号,然后可以通过rt_pin_mode()函数设置GPIO的模式。

/* gpio pin脚  */
uint8_t led_r, led_g, led_b;

/* LED IO初始化 */
void gpio_init(void)

    /* 获取IO号 */
    led_r = rt_pin_get("PE.1");
    led_g = rt_pin_get("PE.4");
    led_b = rt_pin_get("PA.1");

    /* 设置输出模式  */
    rt_pin_mode(led_r, PIN_MODE_OUTPUT);
    rt_pin_mode(led_g, PIN_MODE_OUTPUT);
    rt_pin_mode(led_b, PIN_MODE_OUTPUT);

//    rt_kprintf("led_r = %d\\n", led_r);
//    rt_kprintf("led_g = %d\\n", led_g);
//    rt_kprintf("led_b = %d\\n", led_b);

编写功能代码

完整工程代码见文末(main.c)

先创建一个空白的工程,然后修改main.c(小实验,不另创工程文件了)

我打算创建3个线程,线程1用来控制LED-R,线程2用来控制LED-G,线程3用来控制LED-B。RT-Thread 的线程创建方法有两种,一种是动态创建,另一种是静态创建。由于我也是刚刚接触RT-Thread,所以不知道它们的优劣之处。

创建动态线程

全局属性
下面是一些动态线程需要用到的宏定义全局变量。线程参数包括线程的优先级、线程的栈空间大小和线程时间片大小。

/* 动态线程参数  */
#define THREAD_PRIORITY   25  //线程优先级
#define THREAD_STACK_SIZE 512 //线程栈大小
#define THREAD_TIMESLICE  5   //时钟片

/* 指向线程控制块的指针(动态)  */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;

线程控制块指针是一种用来存放线程属性的变量类型,这是一个结构体指针变量,成员众多,但本实验几乎用不上它的属性,所以这里不作深入讲解。

线程创建函数

rt_thread_create()函数用来创建动态线程,其原型如下所示。这些参数基本都在上面定义好了,线程名name可以随意填写,void (*entry)(void* parameter)是一个函数指针,指向线程入口函数(线程真正的功能函数)。

rt_thread_t rt_thread_create(const char* name,   //线程名(自定义)
void (*entry)(void* parameter), void* parameter, //线程入口函数,及其参数
rt_uint32_t stack_size,                          //线程堆栈大小
rt_uint8_t priority,                             //线程优先级
rt_uint32_t tick);                               //线程时钟片

线程创建和启动

下面是线程的创建启动代码,user_init()函数的主要功能就是创建两个线程,同时启动它们(启动后,线程就开始运行了)。唯一要注意的是rt_thread_create()的第三个参数,我将GPIO编号作为参数传给线程入口函数,因为我3个线程共用了同一个线程函数,通过形参不同而控制不同的LED(如果没有参数,可以填RT_NULL)。

/* 线程创建和启动  */
int user_init()

    /* 创建线程1 */
    tid1 = rt_thread_create("1", led_thread_entry, led_r,\\
            THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
    if(tid1 != RT_NULL)
    
        /* 启动线程1 */
        rt_thread_startup(tid1);
    
    else
    
        rt_kprintf("线程1初始化失败!\\n");
        return -1;
    
    rt_thread_mdelay(500);

    /* 创建线程2 */
    tid2 = rt_thread_create("2", led_thread_entry, led_g,\\
            THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
    if(tid2 != RT_NULL)
    
        /* 启动线程2 */
        rt_thread_startup(tid2);
    
    else
    
        rt_kprintf("线程2初始化失败!\\n");
        return -1;
    
    rt_thread_mdelay(500);
    return 0;


线程入口函数
线程启动函数rt_thread_startup()被调用后,运行的就是线程入口函数,我的代码3个线程共用一个入口函数,功能是实现LED的闪烁。功能比较简单,不作具体介绍。【注意】线程函数需要是一个死循环,不能退出。

/* 线程入口  */
static void led_thread_entry(void *parameter)

    //获取pin编号
    uint8_t pin = (uint8_t*)parameter;

    while(1)
    
        rt_pin_write(pin, PIN_LOW);
        rt_thread_mdelay(500);
        rt_pin_write(pin, PIN_HIGH);
        rt_thread_mdelay(2000);
    

创建动态线程

全局属性

静态线程与动态线程有一个很多的区别:静态线程需要自己定义栈空间,而动态线程又系统自动动态分配,但栈空间大小都是需要指定的。
线程控制块struct rt_thread和上面提到的动态线程的控制块指针rt_thread_t其实是同一种变量,只不过后者是结构体指针,前者是结构体。

/* 静态线程堆栈 */
static rt_uint8_t rt_thread_led3_stack[THREAD_STACK_SIZE];

/* 线程控制块(静态)  */
static struct rt_thread tid3;

静态线程创建函数(初始化函数)

静态线程通过rt_thread_init()进行创建,参数和rt_thread_create()的参数相似,唯一的区别是rt_thread_init()函数多了一个线程栈数组的参数,需要填写数组首地址。

rt_err_t rt_thread_init(struct rt_thread* thread,
const char* name,
void (*entry)(void* parameter), void* parameter,
void* stack_start, rt_uint32_t stack_size,
rt_uint8_t priority, rt_uint32_t tick);

线程创建和启动

线程3是静态线程,我将它的创建与启动代码与线程1,线程2的放在同一个函数。与动态线程创建启动差别不大,只有创建函数不同这一个区别。

/* 线程创建和启动  */
int user_init()

    /* 创建线程1 */
    /* ... */
    rt_thread_mdelay(500);

    /* 创建线程2 */
   	/* ... */
    rt_thread_mdelay(500);

    rt_err_t ret;
    /* 创建线程3(静态) */
    ret = rt_thread_init(&tid3, "3", led_thread_entry, led_b,\\
            (rt_uint8_t*)&rt_thread_led3_stack[0], THREAD_STACK_SIZE,\\
            THREAD_PRIORITY, THREAD_TIMESLICE);
    if(ret == RT_EOK)
    
        /* 启动线程3 */
        rt_thread_startup(&tid3);
    
    else
    
        rt_kprintf("线程3初始化失败!\\n");
        return -1;
    
    return 0;

线程3的入口函数和线程1,线程2相同,在此不进行赘述。

实验效果

三个线程依次运行,分别控制3个灯的亮灭(3个灯集成在一起)。

完整工程代码

由于本实验的代码都写在main.c中(其他代码均使用官方Demo代码)


#include <rtthread.h>
#include "board.h"

/* 动态线程参数  */
#define THREAD_PRIORITY   25  //线程优先级
#define THREAD_STACK_SIZE 512 //线程栈大小
#define THREAD_TIMESLICE  5   //时钟片

/* 静态线程堆栈 */
static rt_uint8_t rt_thread_led3_stack[THREAD_STACK_SIZE];

/* gpio pin脚  */
uint8_t led_r, led_g, led_b;

/* 指向线程控制块的指针(动态)  */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
/* 线程控制块(静态)  */
static struct rt_thread tid3;


/* LED IO初始化 */
void gpio_init(void)

    /* 获取IO号 */
    led_r = rt_pin_get("PE.1");
    led_g = rt_pin_get("PE.4");
    led_b = rt_pin_get("PA.1");

    /* 设置输出模式  */
    rt_pin_mode(led_r, PIN_MODE_OUTPUT);
    rt_pin_mode(led_g, PIN_MODE_OUTPUT);
    rt_pin_mode(led_b, PIN_MODE_OUTPUT);

//    rt_kprintf("led_r = %d\\n", led_r);
//    rt_kprintf("led_g = %d\\n", led_g);
//    rt_kprintf("led_b = %d\\n", led_b);


/* 线程入口  */
static void led_thread_entry(void *parameter)

    //获取pin编号
    uint8_t pin = (uint8_t*)parameter;

    while(1)
    
        rt_pin_write(pin, PIN_LOW);
        rt_thread_mdelay(500);
        rt_pin_write(pin, PIN_HIGH);
        rt_thread_mdelay(2000);
    


/* 线程创建和启动  */
int user_init()

    /* 创建线程1 */
    tid1 = rt_thread_create("1", led_thread_entry, led_r,\\
            THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
    if(tid1 != RT_NULL)
    
        /* 启动线程1 */
        rt_thread_startup(tid1);
    
    else
    
        rt_kprintf("线程1初始化失败!\\n");
        return -1;
    
    rt_thread_mdelay(500);

    /* 创建线程2 */
    tid2 = rt_thread_create("2", led_thread_entry, led_g,\\
            THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
    if(tid2 != RT_NULL)
    
        /* 启动线程2 */
        rt_thread_startup(tid2);
    
    else
    
        rt_kprintf("线程2初始化失败!\\n");
        return -1;
    
    rt_thread_mdelay(500);

    rt_err_t ret;
    /* 创建线程3(静态) */
    ret = rt_thread_init(&tid3, "3", led_thread_entry, led_b,\\
            (rt_uint8_t*)&rt_thread_led3_stack[0], THREAD_STACK_SIZE,\\
            THREAD_PRIORITY, THREAD_TIMESLICE);
    if(ret == RT_EOK)
    
        /* 启动线程3 */
        rt_thread_startup(&tid3);
    
    else
    
        rt_kprintf("线程3初始化失败!\\n");
        return -1;
    
    return 0;



int main(void)

    /* gpio 初始化 */
    gpio_init();

    /* 线程创建和启动  */
    user_init();

 //   INIT_APP_EXPORT(led_thread_entry);

    rt_kprintf("Hello, world\\n");


以上是关于AB32VG1开发板学习GPIO,多线程点灯的主要内容,如果未能解决你的问题,请参考以下文章

RT-Thread Studio快速配置GPIO进行点灯

RT-Thread Studio快速配置GPIO进行点灯

STM8S903K3T6C基于IAR开发GPIO点灯示例

STM32寄存器点灯

STM32寄存器点灯

STM32寄存器点灯