RT-threadStudio利用线程特性去实现功能串讲

1.实验分析

想实现的效果:建立两个线程,线程2用于在按键按下时发送邮件,线程1用于等待接收邮件,在接收到邮件的时候去亮灯。那么想实现这个效果当然要先创建线程了。

创建线程前先了解一下线程三要素:(我之前有篇博客提到过)

①入口函数(函数入口)

②栈 (用于保存现场,如上所述)

③线程控制块(即一个线程结构体,在任务A中止时保护现场时需要将寄存器的值存放于A之间的栈中,这个栈的地址其实是存放在线程结构体里的,当执行完B任务返回时会从线程结构体中读取A栈的地址之后恢复寄存器的值)

我们在创建线程的时候要注意这三个的创建【下图是官网文档写的例程,

static char thread1_stack[1024]是创造线程栈,

static struct rt_thread thread1指的是建立一个线程控制块,包含这些成员

     void *sp; /* 线程栈指针 */
     void *entry; /* 线程入口地址 */
     void *parameter; /* 线程形参 */
     void *stack_addr; /* 线程起始地址 */
     rt_uint32_t stack_size; /* 线程栈大小,单位为字节 */
     rt_list_t tlist; /* 线程链表节点 */

最下面的是入口函数】

我们可以仿照官网例程去写。注意不能偷懒噢~有几个线程就建立几个三要素。其实我们看这个例程也能有个基本印象:线程入口函数包含线程要做的事情。还要额外去定义一些宏定义,如线程的优先级,时间片等(时间片用于两个优先级一样的任务,因为优先级一样所以系统根据时间片的设置去让两个任务交替执行实现同步),还有初始化线程。

我们还得了解一下邮箱,其实作用和我们日常会用到的扣扣邮箱差不多,就是接收信息的。邮箱大小为4字节。官网文档提到它的工作过程是这样的典型的邮箱也称作交换消息,如下图所示,线程或中断服务例程把一封 4 字节长度的邮件发送到邮箱中,而一个或多个线程可以从邮箱中接收这些邮件并进行处理。

如果想了解邮箱的内部工作机制可以看下面,当然不看也行 👇

【邮箱的数据核心是数组 
线程A写数据,线程B想去读数据 
假设线程b先运行,邮箱里没有数据,可以有两种情况 ①等②不等,return err
①的解决方法是线程B进入阻塞状态,也就是从就绪链表里移除B线程,由于A将数据写入后要让B知情,所以我们要事先把B自己记录到邮箱的某个链表里,方便A传输数据到邮箱时可以找到B
细化一点我们可以规定一个截止时间(截止时间一到 定时器会把B唤醒),阻塞时间超时后B被唤醒之后退出,如果没有超时且被发送者唤醒,B会读出数据val=buf[],return ok
之后到线程A写邮箱的部分了,和线程B的思路差不多,但是条件换成了邮箱满没满而导致A愿不愿意等邮箱
愿意进入阻塞状态的话 把自己从就绪链表移除,之后把自己记录在邮箱另一个链表里(和B记录的那个链表是不一样的),便于别人在读邮箱时把自己找出来
由此可知 mailbox必有两个链表
A阻塞后再次运行时,有两种情况,第一种是阻塞截止时间到了超时了被定时器唤醒后return err然后退出,另一种是被读取者唤醒后往邮箱里写信息之后return ok
我们把邮箱被读取写入的区域叫环形buff区 
我们还得在写邮箱时加入一个关中断的操作,这样做是为了避免两个线程同时写入邮箱的情况,因为这种情况下数据会被叠加(它们写入邮箱的同一个区域),关中断可以保证每次只有一个线程写入邮箱(因为线程是由中断触发的)
线程A假如愿意等待10 tick,我们要用线程结构里的定时器去把设为10 tick,定时器递减到0时,定时器的处理函数就会调用,然后它会把线程放入就绪链表(因为之前等待时把线程在就绪链表里删过了)且设置A的状态(statue=E_TIMEOUT)】

邮箱在写代码时要创建相关结构体(邮箱控制块)及初始化,还要把自己想传递的数组内容宏定,如图

发送/接收文件可以这样写,可以在if里写入自己想执行的操作

2.代码部分如下

#include <rtthread.h>
#include <stm32f4xx.h>
#include "stm32f4xx_hal.h"//必须加在最前面
#include<stm32f4xx_hal_gpio.h>
#include<stm32f4xx_hal_def.h>
#include <drivers/pin.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <drv_common.h>
#include <rtthread.h>
#define THREAD_PRIORITY      10
#define THREAD_TIMESLICE     5
/* 邮箱控制块 */
static struct rt_mailbox mb;
/* 用于放邮件的内存池 */
static char mb_pool[128];
static char mb_str1[] = "please open lights!";
static char mb_str3[] = "over!";

ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
/* 线程 1 入口 */
static void thread1_entry(void *parameter)
{
    char *str;

    while (1)
    {
        rt_kprintf("thread1: try to recv a mail\n");

        /* 从邮箱中收取邮件 */
        if (rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER) == RT_EOK)
        {
            rt_kprintf("thread1: get a mail from mailbox, the content:%s\n", str);
            HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9,GPIO_PIN_RESET);
                             rt_thread_mdelay(1000);
          HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9,GPIO_PIN_SET);
          rt_thread_mdelay(2000);

        }
    }
    /* 执行邮箱对象脱离 */
    rt_mb_detach(&mb);
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程 2 入口 */
static void thread2_entry(void *parameter)
{
  while (1)
    {
        if (rt_pin_read(GET_PIN(B,9))==PIN_LOW)
        {
            /* 发送 mb_str1 地址到邮箱中 */
            rt_mb_send(&mb, (rt_uint32_t)&mb_str1);
        }

        /* 延时 200ms */
        rt_thread_mdelay(200);
    }
    /* 发送邮件告诉线程 1,线程 2 已经运行结束 */
    rt_mb_send(&mb, (rt_uint32_t)&mb_str3);
}

int mailbox_sample(void)
{
    rt_err_t result;

    /* 初始化一个 mailbox */
    result = rt_mb_init(&mb,
                        "mbt",                      /* 名称是 mbt */
                        &mb_pool[0],                /* 邮箱用到的内存池是 mb_pool */
                        sizeof(mb_pool) / 4,        /* 邮箱中的邮件数目,因为一封邮件占 4 字节 */
                        RT_IPC_FLAG_FIFO);          /* 采用 FIFO 方式进行线程等待 */
    if (result != RT_EOK)
    {
        rt_kprintf("init mailbox failed.\n");
        return -1;
    }

    rt_thread_init(&thread1,
                   "thread1",
                   thread1_entry,
                   RT_NULL,
                   &thread1_stack[0],
                   sizeof(thread1_stack),
                   THREAD_PRIORITY, THREAD_TIMESLICE);
    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,
                   "thread2",
                   thread2_entry,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack),
                   THREAD_PRIORITY, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);
    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mailbox_sample, mailbox sample);

main()
{

    rt_pin_mode(GET_PIN(F, 9), PIN_MODE_OUTPUT);
    // HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9,GPIO_PIN_SET);
         rt_pin_mode(GET_PIN(F, 10), PIN_MODE_OUTPUT);
    //  HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10,GPIO_PIN_SET);
    mailbox_sample();
    while(1){};
}

终端效果:

                    实际效果:开发板的灯经按键按下后会亮

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值