前言

软件开发环境:RT-Thread Studio、STM32CubeMX
硬件:STM32F407ZGT6

一、RT-Thread Studio 与 STM32CubeMX 下载安装

RT-Thread Studio 主要包括工程创建和管理,代码编辑,SDK管理,RT-Thread配置,构建配置,调试配置,程序下载和调试等功能,结合图形化配置系统以及软件包和组件资源,减少重复工作,提高开发效率。一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效。

STM32CubeMX是一种图形工具,通过分步过程可以非常轻松地配置STM32微控制器和微处理器,以及为Arm® Cortex®-M内核或面向Arm® Cortex®-A内核的特定Linux®设备树生成相应的初始化C代码

RT-Thread官网:https://www.rt-thread.org/studio.html
STM32CubeMX官网:https://www.st.com/zh/development-tools/stm32cubemx.html
在这里插入图片描述
在这里插入图片描述

默认安装即可。

二、新建工程

①进入RT-Thread Studio,左上角 文件–>新建–>RT-Thread项目
在这里插入图片描述

②填入工程信息并选择相应型号
在这里插入图片描述

③打开新建工程,双击RT-Thread Settings,在“组件和服务层”中单击ulog日志,随后再进行保存设置、编辑
在这里插入图片描述

④编译后可发现工程报错,双击错误可定位到具体位置,知为switch语句出现问题,屏蔽即可,不影响使用,然后重新编译
在这里插入图片描述

该段yiswitch代码是一个在串口初始化过程中处理流控制选项的代码片段。代码中的 switch 语句根据配置结构体 cfg 中的流控制选项 flowcontrol 的值进行判断,并根据不同的选项设置对应的串口硬件流控制模式。

流控制是一种在数据传输过程中进行流量控制的技术,用于协调发送方和接收方之间的数据传输速率,以避免数据丢失或溢出。在串口通信中,常见的流控制选项包括无流控制、硬件流控制(如 CTS/RTS)和软件流控制(如 XON/XOFF)。

⑤重新编译后,无警告报错,则 开始下载程序到单片机
在这里插入图片描述

⑥下载成功,观察main.c函数,可知为串口打印功能

在这里插入图片描述

⑦打开串口,测试代码
在这里插入图片描述

至此新建工程完成!

三、点亮LED灯

首先可以直接复制上节的工程,在其基础上进行修改,具体操作为:选中工程,Crtl+C → Ctrl+V, 然后修改项目名即可
在这里插入图片描述

①观察电路图
在这里插入图片描述

②获取led引脚编号

GET_PIN(port, pin);     

这里为 GET_PIN(F, 2)

③设置引脚模式

void rt_pin_mode(rt_base_t pin, rt_base_t mode);   

这里为 rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);

④设置引脚电平

void rt_pin_write(rt_base_t pin, rt_base_t value);     

由上图知为上拉,即低电平点亮,所以这里为 rt_pin_write(LED0_PIN, PIN_LOW);

综上,代码如下:

#include <rtthread.h>
#include <rtdbg.h>
#include <board.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG

#define LED0_PIN    GET_PIN(F, 2)

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

    while (1)
    {
        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;
}

这里分析一下较陌生的头文件和宏定义 :

#include <rtthread.h>:包含了 RT-Thread 实时操作系统的头文件。RT-Thread 是一个开源的实时操作系统,适用于嵌入式系统和物联网设备。

#include <rtdbg.h>:包含了 RT-Thread 调试宏的头文件。该头文件提供了一些用于调试和日志输出的宏定义和函数。

#include <board.h>:包含了与硬件板级支持相关的头文件。该头文件通常包含了与硬件平台相关的宏定义、函数声明和硬件初始化代码等

#define DBG_TAG "main":定义了一个名为 DBG_TAG 的宏,将其值设置为字符串 “main”。在后续的调试输出中,可以使用该宏来标记输出的日志标签。

#define DBG_LVL DBG_LOG:定义了一个名为 DBG_LVL 的宏,将其值设置为 DBG_LOG。该宏用于设置调试输出的级别,这里设置为输出所有级别的日志信息。

return RT_EOK:RT_EOK通常被定义为值为0的宏,表示操作成功完成。

四、按键中断

①观察电路图知都为上拉,默认高电平。
在这里插入图片描述

②获取led引脚编号

#define KEY1_PIN    GET_PIN(F, 5)

③设置引脚模式

rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT_PULLUP)

④设置引脚中断回调函数

rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode, void (*hdr)(void *args), void *args);

其中 pin为引脚编号,mode为中断触发模式,hdr为中断回调函数,args为中断回调函数参数,不需要时设置为RT_NULL

这里为设置为下降沿模式,回调函数名为led_on rt_pin_attach_irq(KEY1_PIN, PIN_IRQ_MODE_FALLING , led_on, RT_NULL);

⑤使能中断

rt_pin_irq_enable(KEY1_PIN, PIN_IRQ_ENABLE);

⑥编写中断回调函数

void led_on(void *args)
{
    rt_kprintf("turn on led!\n");

    rt_pin_write(LED0_PIN, PIN_LOW);
}

综上,代码如下:

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include <board.h>
#define LED0_PIN    GET_PIN(F, 2)
#define KEY1_PIN    GET_PIN(F, 5)
#define KEY2_PIN    GET_PIN(F, 6)

void led_on(void *args);
void led_off(void *args);

int main(void)
{
    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT_PULLUP);
    rt_pin_mode(KEY2_PIN, PIN_MODE_INPUT_PULLUP);
    rt_pin_write(LED0_PIN, PIN_HIGH);

    rt_pin_attach_irq(KEY1_PIN, PIN_IRQ_MODE_FALLING , led_on, RT_NULL);/* 绑定中断,下降沿模式,回调函数名为led_on */
    rt_pin_attach_irq(KEY2_PIN, PIN_IRQ_MODE_FALLING , led_off, RT_NULL);
    rt_pin_irq_enable(KEY1_PIN, PIN_IRQ_ENABLE);/* 使能中断 */
    rt_pin_irq_enable(KEY2_PIN, PIN_IRQ_ENABLE);

    return RT_EOK;
}


/* 中断回调函数 */
void led_on(void *args)
{
    rt_kprintf("turn on led!\n");

    rt_pin_write(LED0_PIN, PIN_LOW);
}

void led_off(void *args)
{
    rt_kprintf("turn off led!\n");

    rt_pin_write(LED0_PIN, PIN_HIGH);
}

五、串口通信

①在工程中,双击打开cubemx 进行相关配置
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

②关闭cubemx,回到RT-Thread Studio进行更新

在这里插入图片描述

③查找串口设备(一般情况下,注册到系统的串口设备名称为 uart0,uart1等)

static rt_device_t serial;                /* 串口设备句柄 */

serial=rt_device_find(const char* name);

④定义串口配置参数

struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;   // 初始化配置参数 

config.baud_rate = BAUD_RATE_115200;        //修改波特率为 115200
config.data_bits = DATA_BITS_8;           //数据位 8
config.stop_bits = STOP_BITS_1;           //停止位 1
config.bufsz     = 128;                   //修改缓冲区 buff size 为 128
config.parity    = PARITY_NONE;           //无奇偶校验位

rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);    //控制串口设备。通过控制接口传入命令控制字,与控制参数

⑤打开串口设备

rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);     //以中断接收及轮询发送模式打开串口设备

⑥设置串口接收回调函数

rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));   dev表示串口设备句柄,rx_ind表示回调函数指针

⑦串口发送/接收 函数

rt_device_write(serial, 0, str, (sizeof(str) - 1));   //serial表示串口设备句柄、0表示偏移量、str表示要发送的缓存区、(sizeof(str) - 1)表示发送的大小

rt_device_read(serial, 0, &ch, 1)   //serial表示串口设备句柄、0表示偏移量、ch表示要接收的缓存区、1表示接收的大小

⑧编写串口接收回调函数

static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{

    return RT_EOK;
}

⑨定义信号量用于接收处理

static struct rt_semaphore rx_sem;       /* 用于接收消息的信号量 */

rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);   /* 初始化信号量 */

rt_sem_take(&rx_sem, RT_WAITING_FOREVER);    /* 阻塞等待接收信号量,等到信号量后再次读取数据 */

rt_sem_release(&rx_sem);  /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */

综上,代码如下:

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include <string.h>
#include <board.h>
#define LED0_PIN    GET_PIN(F, 2)

#define SAMPLE_UART_NAME       "uart1"    /* 串口设备名称 */

static rt_err_t uart_input(rt_device_t dev, rt_size_t size);    /* 接收数据回调函数 */
static rt_device_t serial;                /* 串口设备句柄 */
static struct rt_semaphore rx_sem;       /* 用于接收消息的信号量 */
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;  /* 初始化配置参数 */
char str[] = "hello RT-Thread!\r\n";
char ch;
int main(void)
{
    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
    rt_pin_write(LED0_PIN, PIN_HIGH);

    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    /* step1:查找串口设备 */
    serial = rt_device_find(SAMPLE_UART_NAME);

    /* step2:修改串口配置参数 */
    config.baud_rate = BAUD_RATE_115200;        //修改波特率为 115200
    config.data_bits = DATA_BITS_8;           //数据位 8
    config.stop_bits = STOP_BITS_1;           //停止位 1
    config.bufsz     = 128;                   //修改缓冲区 buff size 为 128
    config.parity    = PARITY_NONE;           //无奇偶校验位

    /* step3:控制串口设备。通过控制接口传入命令控制字,与控制参数 */
    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);

    /* step4:打开串口设备。以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);

    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(serial, uart_input);

    /* 发送字符串 */
    rt_device_write(serial, 0, str, (sizeof(str) - 1));


    while (1)
    {
        /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
        if(rt_device_read(serial, 0, &ch, 1) != 0)
        {
            //rt_thread_mdelay(10);
            /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
            rt_kprintf("%c\n",ch);

            if(ch=='l')
                rt_pin_write(LED0_PIN, PIN_LOW);
            else if(ch=='e')
                rt_pin_write(LED0_PIN, PIN_HIGH);
        }
    }

    return RT_EOK;
}


/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

六、OLED显示

①观察电路图
在这里插入图片描述

②进入RT-Thread Settings选中软件模拟I2C,单击开启并进入配置项
在这里插入图片描述
在这里插入图片描述

在软件包中,添加u8g2图形库,并进入配置项
在这里插入图片描述

③退出RT-Thread Settings,进入头文件board.h进行设置
在这里插入图片描述

④保存设置,回到main.c 直接调用u8g2的图形库API函数 即可驱动oled显示屏显示数据

  • 创建图形设备对象
u8g2_t u8g2; 
  • 初始化oled显示屏
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay_rtthread);      参数分别为: 对象、旋转角度、消息回调函数的指针、显示回调函数的指针
  • 设置控制 OLED 显示屏的引脚
u8x8_SetPin(u8g2_GetU8x8(&u8g2), U8X8_PIN_I2C_CLOCK, GET_PIN(F,1));//选择CLK引脚
u8x8_SetPin(u8g2_GetU8x8(&u8g2), U8X8_PIN_I2C_DATA, GET_PIN(F,0));//选择SDA引脚
  • 初始化设备对象
u8g2_InitDisplay(&u8g2);
  • 退出省电模式(默认开启)
u8g2_SetPowerSave(&u8g2, 0);
  • 清屏
u8g2_ClearBuffer(&u8g2); 
  • 设置字体
u8g2_SetFont(&u8g2, u8g2_font_ncenB14_te);
  • 设置要显示的字符
u8g2_DrawStr(&u8g2, 1, 50, "Nie Dong");
  • 将绘制好的图像数据发送到显示设备进行显示
u8g2_SendBuffer(&u8g2);

综上,代码为:

#include <rtthread.h>

#include <u8g2_port.h>
#include <rtdevice.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include <board.h>


int main(void)
{


    u8g2_t u8g2;  //图形设备对象                                        对象         旋转角度         消息回调函数的指针             显示回调函数的指针
    u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay_rtthread);
    u8x8_SetPin(u8g2_GetU8x8(&u8g2), U8X8_PIN_I2C_CLOCK, GET_PIN(F,1));//选择CLK引脚
    u8x8_SetPin(u8g2_GetU8x8(&u8g2), U8X8_PIN_I2C_DATA, GET_PIN(F,0));//选择SDA引脚
    u8g2_InitDisplay(&u8g2);   // 初始化设备对象
    u8g2_SetPowerSave(&u8g2, 0);   // 退出省电模式

    u8g2_ClearBuffer(&u8g2);         //清屏
    u8g2_SetFont(&u8g2, u8g2_font_ncenB14_te);   //字体
    u8g2_DrawStr(&u8g2, 1, 50, "Nie Dong");//输入要显示的字符
    u8g2_SendBuffer(&u8g2);   //将绘制好的图像数据发送到显示设备进行显示

    return RT_EOK;
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐