基于RT-Thread系统的机智云数字仪表教程(一)
目录:
1.基于RT-Thread系统的机智云数字仪表教程(一)——RT-Thread的BSP模板移植
2.基于RT-Thread系统的机智云数字仪表教程(二)——机智云云端开发
3.基于RT-Thread系统的机智云数字仪表教程(三)——STM32G0代码移植
4.基于RT-Thread系统的机智云数字仪表教程(四)——机智云APP代码移植
5.基于RT-Thread系统的机智云数字仪表教程(五)——机智云web代码移植
6.基于RT-Thread系统的机智云数字仪表教程(六)——STM32F7下位机toughGFX界面制作
实验内容:
- 功能需求:
- 在MCU设备端,美观漂亮的温度和湿度数据的UI展示
- 在机智云云端,实现MCU设备端连接机智云,及数据采集并展示
- 在Android⼿手机端,实现通过机智云读取MCU设备端温度和湿度数据并展示
- 系统结构:
- STM32F767+STM32G071双MCU设计,易易于扩展
- STM32G071读取传感器器数据和机智云的连接
- STM32F767负责UI界⾯面的展示
- 可⽀支持二次开发(推荐):
- 数字示波器器 激光测距仪 设备⼿手持操作器器等
- 信号发⽣生器器 空⽓气质量量分析仪 ⽔水质检测仪等
实验平台:
硬件: ST的STM32G071RBT6公版
软件: 最新版本的STM32CubeF4固件库, STM32CubeMX v5.2.1,开发环境MDK v5.26,
RT-Thread系统源码
实验前准备工作:
1.如果手头没有STM32G071RBT6公版,可以准备一个其他的STM32开发板,准备一个ESP8266
2.下载 RT-Thread系统源码
3.下载 STM32CubeMX
4.下载 MDK v5.26
代码:
代码持续更新中
github代码下载地址:https://github.com/Aladdin-Wang/stm32g071-st-gizwits
联系作者:
有问题欢迎添加微信交流,加项目备注:
移植RT-Thread的BSP模板:
1.学习RT-Thread系统(学过的可以跳过此步骤)
- 通读RT-Thread系统官方学习文档,https://www.rt-thread.org/document/site/
- 快速了解RT-Thread系统的内核,下载安装Env开发工工具
- 了解设备和驱动的使用方法:
2.移植BSP
- 参考官方使用 Env 创建 RT-Thread 项目工程的教程
- 选择 BSP
获取 RT-Thread 源代码后需要根据自己手上的开发板型号找到对应的 BSP,我手里有一个ST的STM32G071RBT6开发板,所以选择对应于STM32G071的BSP包,首先找到如下目录:…\rt-thread-v4.0.1\rt-thread\bsp\stm32\stm32g071-st-nucleo。 - 搭建项目框架
打开 Env 工具进入 stm32g071-st-nucleo 目录,运行scons --dist 命令。使用此命令会在 stm32g071-st-nucleo 目录下生成 dist 目录,这便是开发项目的目录结构,RT-Thread 源码位于项目文件夹内,仅包含 stm32g071-st-nucleo 的 BSP,可以随意拷贝此 BSP 到任何目录下使用。
进入dist目录下,把工程里面的 stm32g071-st-nucleo压缩包拷贝到你的项目目录下待使用。 - 修改BSP
参考这个官方教程
- 选择 BSP
STM32G0管脚功能定义
GPIO | MODE | 说明 |
---|---|---|
PC-15 | WIFI_EN | ESP8266使能 低电平关机,保持高电平 STM32采用pull_up 输出模式 |
PC-12 | WIFI_RST | ESP8266复位 低电平复位,保持高电平 STM32采用pull_up 输出模式,初始化输出高电平 |
PB-9 | WIFI_CONF | ESP8266模式 低电平使能下载,保持高电平模块正常工作 STM32采用pull_up 输出模式 |
PB-15 | SHT30_RST | SHT复位引脚 此引脚需要上拉输出 STM32采用pull_up 输出模式,初始化高电平 |
PB-14 | SHT30_ALT | SHT报警引脚 此引脚不能上拉或者下拉 STM32采用no_pull_up and no_pull_down 输入模式 |
PC-13 | SOFTAP_KEY | 按键,上拉输入模式,SOFTAP |
PC-14 | AIRLINK_KEY | 按键,上拉输入模式,AIRLINK |
PD-2 | LED0 | 低电平有效,开机初始化保持高电平 |
PB-4 | LED1 | 高电平有效,开机初始化为低电平,高速推挽输出 |
PA-9 | I2C1_SCL | SHT30数据通讯I2C口 |
PA-10 | I2C1_SDA | SHT30数据通讯I2C口 |
PC-10 | USART3-TX ESP8266-RX | WIFI协议通讯串口波特率9600 |
PC-11 | USART3-RX ESP8266-TX | WIFI协议通讯串口波特率9600 |
PA-0 | USART4-TX | 备用波特率9600 |
PA-1 | USART4-RX | 备用波特率9600 |
PA-2 | USART2-TX | 备用波特率9600 |
PA-3 | USART2-RX | 备用波特率9600 |
PC-4 | USART1-TX | 备用波特率9600 |
PC-5 | USART1-RX | 备用波特率9600 |
PC-1 | LPUART1-TX | 日志打印串口波特率115200 |
PC-0 | LPUART1-RX | 日志打印串口波特率115200 |
在路径…\stm32g071-st-nucleo\board\CubeMX_Config下,打开 CubeMX 工程,CubeMX_Config只需要配置串口的管脚即可生成工程,其他GPIO在系统里配置
生成工程后只需要保留如下文件即可
- 配置和裁剪 RT-Thread
打开 Env 工具使用 menuconfig 命令打开配置界面
打开LPUART1、UART1、UART2、UART3、UART4、软件IIC和硬件定时器2,保存,退出。
原本BSP没有UART3、UART4。参考这个教程添加UART3、UART4
- 生成工程
使用scons --target=mdk5 命令或者 scons --target=iar 命令生成 MDK 或者 IAR 工程
- 修改工程USART驱动
由于STM32G071RBT6的 LPUART1、UART3、UART4共用一个中断号,所以当这三个中断发生中断时会进入同一个中断函数,为了加以区分是哪个串口,需要修改USART的驱动代码,具体修改如下:
void LPUART1_IRQHandler(void)
{
uint32_t isrflags = READ_REG(uart_obj[LPUART1_INDEX].handle.Instance->ISR);
uint32_t cr1its = READ_REG(uart_obj[LPUART1_INDEX].handle.Instance->CR1);
uint32_t cr3its = READ_REG(uart_obj[LPUART1_INDEX].handle.Instance->CR3);
uint32_t errorflags;
/* If no error occurs */
errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE));
if (errorflags == 0U)
{
if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
&& (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
|| ((cr3its & USART_CR3_RXFTIE) != 0U)))
{
/* enter interrupt */
rt_interrupt_enter();
uart_isr(&(uart_obj[LPUART1_INDEX].serial));
/* leave interrupt */
rt_interrupt_leave();
}
else
{
UART4_IRQHandler();
}
}
}
void UART4_IRQHandler(void)
{
uint32_t isrflags = READ_REG(uart_obj[UART4_INDEX].handle.Instance->ISR);
uint32_t cr1its = READ_REG(uart_obj[UART4_INDEX].handle.Instance->CR1);
uint32_t cr3its = READ_REG(uart_obj[UART4_INDEX].handle.Instance->CR3);
uint32_t errorflags;
if (errorflags == 0U)
{
if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
&& (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
|| ((cr3its & USART_CR3_RXFTIE) != 0U)))
{
/* enter interrupt */
rt_interrupt_enter();
uart_isr(&(uart_obj[UART4_INDEX].serial));
/* leave interrupt */
rt_interrupt_leave();
}
else
{
USART3_IRQHandler();
}
}
}
void USART3_IRQHandler(void)
{
uint32_t isrflags = READ_REG(uart_obj[UART3_INDEX].handle.Instance->ISR);
uint32_t cr1its = READ_REG(uart_obj[UART3_INDEX].handle.Instance->CR1);
uint32_t cr3its = READ_REG(uart_obj[UART3_INDEX].handle.Instance->CR3);
uint32_t errorflags;
if (errorflags == 0U)
{
if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
&& (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
|| ((cr3its & USART_CR3_RXFTIE) != 0U)))
{
/* enter interrupt */
rt_interrupt_enter();
uart_isr(&(uart_obj[UART3_INDEX].serial));
/* leave interrupt */
rt_interrupt_leave();
}
else
{
}
}
}
在main函数添加线程,验证功能,具体内容请看注释
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-11-06 SummerGift change to new framework
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
/*
*************************************************************************
* 变量
*************************************************************************
*/
static rt_thread_t led1_thread = RT_NULL;/* 定义线程控制块 */
static struct rt_semaphore rx_sem;/* 用于接收消息的信号量 */
static rt_thread_t receivedata_thread;/* 用于接收消息的线程 */
static rt_device_t hwtime2_dev; /* 定时器设备句柄*/
static rt_hwtimer_mode_t mode; /* 定时器模式*/
static rt_hwtimerval_t timeout_s; /* 定时器超时值*/
static rt_device_t uart1_serial;/* 串口设备句柄*/
static rt_device_t uart2_serial;/* 串口设备句柄*/
static rt_device_t uart3_serial;/* 串口设备句柄*/
static rt_device_t uart4_serial;/* 串口设备句柄*/
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* 配置参数 */
/************************* 全局变量声明 ****************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些全局变量。
*/
#define LED0_PIN GET_PIN(D, 2)
#define HWTIMER_DEV_NAME "timer2" /* 定时器名称*/
#define SAMPLE_UART1_NAME "uart1" /* 串口设备名称*/
#define SAMPLE_UART2_NAME "uart2" /* 串口设备名称*/
#define SAMPLE_UART3_NAME "uart3" /* 串口设备名称*/
#define SAMPLE_UART4_NAME "uart4" /* 串口设备名称*/
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void led1_thread_entry(void* parameter);
static void serial_thread_entry(void *parameter);
/*
*************************************************************************
* 函数回调
*************************************************************************
*/
/* 定时器超时回调函数*/
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
if(hwtime2_dev == dev)
{
}
return 0;
}
/* 接收数据回调函数*/
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
if(dev == uart1_serial)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
rt_sem_release(&rx_sem);
}
if(dev == uart2_serial)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
}
if(dev == uart3_serial)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
rt_sem_release(&rx_sem);
}
if(dev == uart4_serial)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
}
return RT_EOK;
}
/*
*************************************************************************
* main 函数
*************************************************************************
*/
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
/*************************** 局部变量 ****************************/
/************************* 初始化信号量 *************************/
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
/******************定时器********************/
hwtime2_dev = rt_device_find(HWTIMER_DEV_NAME);
/* 以读写方式打开设备*/
rt_device_open(hwtime2_dev, RT_DEVICE_OFLAG_RDWR);
/* 设置超时回调函数*/
rt_device_set_rx_indicate(hwtime2_dev, timeout_cb);
/* 设置模式为周期性定时器*/
mode = HWTIMER_MODE_PERIOD;
rt_device_control(hwtime2_dev, HWTIMER_CTRL_MODE_SET , &mode);
/* 设置定时器超时值为5s 并启动定时器*/
timeout_s.sec = 0; /* 秒*/
timeout_s.usec = 1000; /* 微秒*/
rt_device_write(hwtime2_dev, 0, &timeout_s, sizeof(timeout_s));
/****************************串口******************************/
/* 查找串口设备*/
uart1_serial = rt_device_find(SAMPLE_UART1_NAME);
/* 以中断接收及轮询发送方式打开串口设备*/
rt_device_open(uart1_serial, RT_DEVICE_FLAG_INT_RX);
config.baud_rate = BAUD_RATE_9600;
config.data_bits = DATA_BITS_8;
config.stop_bits = STOP_BITS_2;
config.parity = PARITY_NONE;
/* 打开设备后才可修改串口配置参数 */
rt_device_control(uart1_serial, RT_DEVICE_CTRL_CONFIG, &config);
/* 设置接收回调函数*/
rt_device_set_rx_indicate(uart1_serial, uart_input);
/* 查找串口设备*/
uart2_serial = rt_device_find(SAMPLE_UART2_NAME);
/* 以中断接收及轮询发送方式打开串口设备*/
rt_device_open(uart2_serial, RT_DEVICE_FLAG_INT_RX);
config.baud_rate = BAUD_RATE_9600;
config.data_bits = DATA_BITS_8;
config.stop_bits = STOP_BITS_2;
config.parity = PARITY_NONE;
/* 打开设备后才可修改串口配置参数 */
rt_device_control(uart2_serial, RT_DEVICE_CTRL_CONFIG, &config);
/* 设置接收回调函数*/
rt_device_set_rx_indicate(uart2_serial, uart_input);
/* 查找串口设备*/
uart4_serial = rt_device_find(SAMPLE_UART4_NAME);
/* 以中断接收及轮询发送方式打开串口设备*/
rt_device_open(uart4_serial, RT_DEVICE_FLAG_INT_RX);
config.baud_rate = BAUD_RATE_9600;
config.data_bits = DATA_BITS_8;
config.stop_bits = STOP_BITS_2;
config.parity = PARITY_NONE;
/* 打开设备后才可修改串口配置参数 */
rt_device_control(uart4_serial, RT_DEVICE_CTRL_CONFIG, &config);
/* 设置接收回调函数*/
rt_device_set_rx_indicate(uart4_serial, uart_input);
/* 查找串口设备*/
uart3_serial = rt_device_find(SAMPLE_UART3_NAME);
/* 以中断接收及轮询发送方式打开串口设备*/
rt_device_open(uart3_serial, RT_DEVICE_FLAG_INT_RX);
config.baud_rate = BAUD_RATE_9600;
config.data_bits = DATA_BITS_8;
config.stop_bits = STOP_BITS_2;
config.parity = PARITY_NONE;
/* 打开设备后才可修改串口配置参数 */
rt_device_control(uart3_serial, RT_DEVICE_CTRL_CONFIG, &config);
/* 设置接收回调函数*/
rt_device_set_rx_indicate(uart3_serial, uart_input);
/****************************线程******************************/
/* 创建 serial 线程 */
receivedata_thread = /* 线程控制块指针 */
rt_thread_create( "recdata", /* 线程名字 */
serial_thread_entry, /* 线程入口函数 */
RT_NULL, /* 线程入口函数参数 */
512, /* 线程栈大小 */
5, /* 线程的优先级 */
10); /* 线程时间片 */
/* 创建成功则启动线程 */
if (receivedata_thread != RT_NULL)
rt_thread_startup(receivedata_thread);
else
return -1;
led1_thread = /* 线程控制块指针 */
rt_thread_create( "led1", /* 线程名字 */
led1_thread_entry, /* 线程入口函数 */
RT_NULL, /* 线程入口函数参数 */
512, /* 线程栈大小 */
10, /* 线程的优先级 */
20); /* 线程时间片 */
/* 启动线程,开启调度 */
if (led1_thread != RT_NULL)
rt_thread_startup(led1_thread);
else
return -1;
return RT_EOK;
}
/*
*************************************************************************
* 线程定义
*************************************************************************
*/
static void led1_thread_entry(void* parameter)
{
/* 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);
}
}
static void serial_thread_entry(void *parameter)
{
char ch;
while (1)
{
/* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
while (rt_device_read(uart3_serial, -1, &ch, 1) != 1)
{
/* 阻塞等待接收信号量,等到信号量后再次读取数据 */
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
}
/* 读取到的数据通过串口错位输出 */
//rt_device_write(serial, 0, &ch, 1);
//rt_kprintf("%c", ch);
}
}
- 验证工程
LED0 500ms闪烁一次
串口FinSH运行良好