开放原子训练营(第二季)RT-Thread Nano学习营刘玉宽

目录

前言

RT-Thread简介

搭建开发环境

实验过程

1、LED 使用示例

2、按键使用示例

3、自动化执行的示例

4、自定义 msh 命令

5、多线程示例 

6、定时器示例 

7、消息队列示例 

8、摩斯电码使用

实验总结


前言

        4月22日周六,早早起床充满期待的来到了北京艾丽华酒店,参加“2023年RT-Thread线下培训”。这是疫情三年以来开放原子在北京第一次组织的线下活动。

        进入到会场,被震撼了,好火爆啊————满满一屋子人,有白发苍苍的老者、有职场专家、有在校大学生、还有年仅6岁的小小工程师。现场准备的很充分,每个桌子上都有插排,方便学员使用电脑。每个桌子上都配一块开发板,保证大家都能动手实操。

 

       

RT-Thread简介

        RT-Thread已经成为市面上装机量最大(超20亿台)、开发者数量最多(超15万)、软硬件生态最好的嵌入式操作系统之一。RT-Thread 支持多线程,比传统的裸机的串行方式以及前后台中断方式高效得多。在技术演化方面,针对不同的芯片产品,以RT-Thread底层核心平台构建了一系列差异化的平台,包括标准版本、Nano版本、Smart版本(混合微内核,应用与内核分离),可应用于消费物联网、行业物联网多种场景。

        RT-Thread Nano 实时操作系统遵循 Apache 许可证 2.0 版本,实时操作系统内核及所有开源组件可以免费在商业产品中使用,不需要公布应用程序源码,没有潜在商业风险。

        rtthread-nano atomGit 源码仓
https://atomgit.com/OpenAtomFoundation/rtthread-nano

搭建开发环境

         根据活动微信群里给的文档,大家都已经提前按照官方给的文档 train-note · AtomGit_开放原子开源基金会代码托管平台  完成了开发环境的下载准备工作。


        中午休息了一小时,旁边的咖啡厅喝了一杯咖啡,又元气满满的参加下午的动手实操课程。


实验过程

1、LED 使用示例

LED 灯以 500ms 为间隔闪烁,代码如下:

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

/* defined the LED0 pin: PA5 */
#define USER_LED_PIN               GET_PIN(A, 5)

int main(void)
{
    int count = 1;
    /* set LED0 pin mode to output */
    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);

    while (count++)
    {
        rt_pin_write(LED0_PIN, PIN_HIGH);
        rt_thread_mdelay(500);
        rt_pin_write(LED0_PIN, PIN_LOW);
        rt_thread_mdelay(500);
    }
    return RT_EOK;
}

2、按键使用示例

开发板上电,每次按下按键,串口终端都会输出 RT-Thread! 字样,代码如下:

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

#define USER_KEY GET_PIN(C, 13) 

void irq_callback()
{
    rt_kprintf("RT-Thread!\r\n");
}

int main(void)
{
    rt_pin_mode(USER_KEY, PIN_MODE_INPUT_PULLUP);
    rt_pin_attach_irq(USER_KEY, PIN_IRQ_MODE_RISING_FALLING, irq_callback, RT_NULL);
    rt_pin_irq_enable(USER_KEY, PIN_IRQ_ENABLE);
    return 0;
}

3、自动化执行的示例

开发板上电将会看到终端上印出export_app RT-Thread ,代码如下:

#include <rtthread.h>

int export_app(void)
{
    rt_kprintf("export_app RT-Thread!\r\n");
    return 0;
}
INIT_APP_EXPORT(export_app);

4、自定义 msh 命令

在终端中输入 hello 后回车,将会打印 hello RT-Thread! 字样,代码如下:

void hello(void)
{
    rt_kprintf("hello RT-Thread!\n");
}

MSH_CMD_EXPORT(hello , say hello to RT-Thread);

在终端中按Tab键,可以看到命令列表中多出来hello。输入hello可以打印出hello RT-Thread!

5、多线程示例 

线程1:每隔500ms打印一次计数

线程2:打印10次计数后,打印退出并退出

这个例子创建一个动态线程初始化一个静态线程,一个线程在运行完毕后自动被系统删除,另一个线程一直打印计数,代码如下:

#include <rtthread.h>

#define THREAD_PRIORITY         25
#define THREAD_STACK_SIZE       512
#define THREAD_TIMESLICE        5

static rt_thread_t tid1 = RT_NULL;

/* 线程 1 的入口函数 */
static void thread1_entry(void *parameter)
{
    rt_uint32_t count = 0;

    while (1)
    {
        /* 线程 1 采用低优先级运行,一直打印计数值 */
        rt_kprintf("thread1 count: %d\n", count ++);
        rt_thread_mdelay(500);
    }
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程 2 入口 */
static void thread2_entry(void *param)
{
    rt_uint32_t count = 0;

    /* 线程 2 拥有较高的优先级,以抢占线程 1 而获得执行 */
    for (count = 0; count < 10 ; count++)
    {
        /* 线程 2 打印计数值 */
        rt_kprintf("thread2 count: %d\n", count);
    }
    rt_kprintf("thread2 exit\n");
    /* 线程 2 运行结束后也将自动被系统脱离 */
}

/* 线程示例 */
int thread_sample(void)
{
    /* 创建线程 1,名称是 thread1,入口是 thread1_entry*/
    tid1 = rt_thread_create("thread1",
                            thread1_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);

    /* 如果获得线程控制块,启动这个线程 */
    if (tid1 != RT_NULL)
    rt_thread_startup(tid1);

    /* 初始化线程 2,名称是 thread2,入口是 thread2_entry */
    rt_thread_init(&thread2,
                    "thread2",
                    thread2_entry,
                    RT_NULL,
                    &thread2_stack[0],
                    sizeof(thread2_stack),
                    THREAD_PRIORITY - 1, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);

    return 0;
}

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

6、定时器示例 

定时器1:周期定时器,10次之后让其停止

定时器2:单次定时器

这个例程会创建两个动态定时器,一个是单次定时,一个是周期性定时并让周期定时器运行一段时间后停止运行。代码如下:

#include <rtthread.h>

/* 定时器的控制块 */
static rt_timer_t timer1;
static rt_timer_t timer2;
static int cnt = 0;

/* 定时器 1 超时函数 */
static void timeout1(void *parameter)
{
    rt_kprintf("periodic timer is timeout %d\n", cnt);

    /* 运行第 10 次,停止周期定时器 */
    if (cnt++ >= 9)
    {
        rt_timer_stop(timer1);
        rt_kprintf("periodic timer was stopped! \n");
    }
}

/* 定时器 2 超时函数 */
static void timeout2(void *parameter)
{
    rt_kprintf("one shot timer is timeout\n");
}

int timer_sample(void)
{
    /* 创建定时器 1  周期定时器 */
    timer1 = rt_timer_create("timer1", timeout1,
                             RT_NULL, 10,
                             RT_TIMER_FLAG_PERIODIC);

    /* 启动定时器 1 */
    if (timer1 != RT_NULL)
        rt_timer_start(timer1);

    /* 创建定时器 2 单次定时器 */
    timer2 = rt_timer_create("timer2", timeout2,
                             RT_NULL, 30,
                             RT_TIMER_FLAG_ONE_SHOT);

    /* 启动定时器 2 */
    if (timer2 != RT_NULL)
        rt_timer_start(timer2);
    return 0;
}

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

7、消息队列示例 

线程1:接收消息,20次后不再接收,最后删除消息队列。

线程2:发送消息,第8次发送一条紧急消息,20次后退出。

这是一个消息队列的应用例程,例程中初始化了 2 个静态线程,一个线程会从消息队列中收取消息;另一个线程会定时给消息队列发送普通消息和紧急消息,代码如下:

#include <rtthread.h>

/* 消息队列控制块 */
static struct rt_messagequeue mq;
/* 消息队列中用到的放置消息的内存池 */
static rt_uint8_t msg_pool[2048];

ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
/* 线程 1 入口函数 */
static void thread1_entry(void *parameter)
{
    char buf = 0;
    rt_uint8_t cnt = 0;

    while (1)
    {
        /* 从消息队列中接收消息 */
        if (rt_mq_recv(&mq, &buf, sizeof(buf), RT_WAITING_FOREVER) == RT_EOK)
        {
            rt_kprintf("thread1: recv msg from msg queue, the content:%c\n", buf);
            if (cnt == 19)
            {
                break;
            }
        }
        /* 延时 50ms */
        cnt++;
        rt_thread_mdelay(50);
    }
    rt_kprintf("thread1: detach mq \n");
    rt_mq_detach(&mq);
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程 2 入口 */
static void thread2_entry(void *parameter)
{
    int result;
    char buf = 'A';
    rt_uint8_t cnt = 0;

    while (1)
    {
        if (cnt == 8)
        {
            /* 发送紧急消息到消息队列中 */
            result = rt_mq_urgent(&mq, &buf, 1);
            if (result != RT_EOK)
            {
                rt_kprintf("rt_mq_urgent ERR\n");
            }
            else
            {
                rt_kprintf("thread2: send urgent message - %c\n", buf);
            }
        }
        else if (cnt >= 20) /* 发送 20 次消息之后退出 */
        {
            rt_kprintf("message queue stop send, thread2 quit\n");
            break;
        }
        else
        {
            /* 发送消息到消息队列中 */
            result = rt_mq_send(&mq, &buf, 1);
            if (result != RT_EOK)
            {
                rt_kprintf("rt_mq_send ERR\n");
            }

            rt_kprintf("thread2: send message - %c\n", buf);
        }
        buf++;
        cnt++;
        /* 延时 5ms */
        rt_thread_mdelay(5);
    }
}

/* 消息队列示例的初始化 */
int msgq_sample(void)
{
    rt_err_t result;

    /* 初始化消息队列 */
    result = rt_mq_init(
        &mq,
        "mqt",
        &msg_pool[0],     /* 内存池指向 msg_pool */
        1,                /* 每个消息的大小是 1 字节 */
        sizeof(msg_pool), /* 内存池的大小是 msg_pool 的大小 */
        RT_IPC_FLAG_PRIO  /* 如果有多个线程等待,优先级大小的方法分配消息 */
    );

    if (result != RT_EOK)
    {
        rt_kprintf("init message queue failed.\n");
        return -1;
    }

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

    rt_thread_init(&thread2,
                   "thread2",
                   thread2_entry,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack), 25, 5);
    rt_thread_startup(&thread2);

    return 0;
}

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

8、摩斯电码使用

摩斯电码目前已经被封装成软件包,可以直接选中 morse 软件包来体验摩斯电码

软件包的配置如下:

打开 samples\key\morse-key-example.c 文件,将 USER_KEY 宏所对应引脚对应到开发板的按键

即可编译烧录到开发板中运行,只需要按照摩斯电码的规律按下按键,串口终端上即可显示对应字符。

当使能了 PKG_USING_MORSE_SHELL 之后,输入回车所对应的摩斯电码即可执行输入的命令

实验总结

        第一次参加这种动手学习营活动,感觉非常开心,是一段非常好的体验,老师手把手的教授环境搭建、讲解实例、自己动手实操、课堂答疑、还布置了课后挑战作业。每个实验成功那一刻都非常有成就感,仿佛又回到了大学课堂。

        通过本次学习,我不但完成了对一个陌生领域的学习并且应用,而且我对物联网应用开发工具有了全新的认识,切身感受了RT-Thread Studio的便捷,可以方便的引入各种库函数,真的是一站式解决各种底层依赖。

感谢组织方,期待下一次进阶培训!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值