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,多线程点灯的主要内容,如果未能解决你的问题,请参考以下文章