文章目录
- 前言
- 一、RT-Thread Studio 与 STM32CubeMX 下载安装
- 二、新建工程
- 三、点亮LED灯
- 四、按键中断
- 五、串口通信
- 六、OLED显示
前言
软件开发环境: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;
}