1 硬件环境
stm32f103c8t6板及stm32f401ccu6板,都是目前性价比最高的stm32开发板,基本上不到20元。由于rt-thread操作系统占用还是比较大的,所以stm32f103c8t6这样的板子,64K的内存已经十分局促了,因此stm32 rt-thread的开发还是基于stm32f401ccu6,比较适合。
ttl转usb板,我用的是ch340芯片。
st-link.。
esp01s,也是目前性价比很高的wifi模块了。
几个数字传感器:单总线的ds18b20温度传感器、IIC的dht20温湿度传感器和bmp280气压传感器等。
将stm32f103c8t6与st-link用swd连接,并接入PC usb。
将ttl转usb接stm32f103c8t6 uart2(pa2和pa3)。
将esp01s接stm32f103c8t6 uart2(pa9和pa10)。
传感器通过杜邦线插接面包板,接入stm32f103c8t6.。
2 开发环境
下载安装rt-thread studio。

创建一个rt-thread项目,选择开发板、串口和st-link连接。
根据我们的硬件连接,串口连接选择UART2,修改连接针PA2和PA3,选择下载器ST-LINK,端口SWD。打开,基本是这样子。

编译、下载测试应该没有问题。
修改系统时钟。rt-thread缺省配置的时钟是stm32内部高速时钟,我们修改为外部时钟。打开driver/board.h文件,修改。
/*-------------------------- CLOCK CONFIG BEGIN --------------------------*/
//#define BSP_CLOCK_SOURCE ("HSI")
//#define BSP_CLOCK_SOURCE_FREQ_MHZ ((int32_t)0)
#define BSP_CLOCK_SOURCE ("HSE")
#define BSP_CLOCK_SOURCE_FREQ_MHZ ((int32_t)8)
#define BSP_CLOCK_SYSTEM_FREQ_MHZ ((int32_t)72)
对于stm32f103c8t6,修改scripts/STM32F103C8/link.lds,将ROM大小修改为128.。原来64k对rt-thread太小了。由于stm32f103c8和stm32f103cB是同样的晶圆相同生产线上生产的,基本上stm32f103c8可以当是stm32f103cB对待。
再修改项目配置,选择编译目标大小优化,交叉编译连接点选使用newlib-nano。

3 添加Finsh shell
添加rt-thread内置的命令调试终端。打开Rt-thread Settings,勾选组件与服务下的Finsh Shell。
注释掉main.c文件的调试打印,编译下载。开启终端,选择PC连接的Com端口,我的是Com10.。


4 Wifi连接并接入阿里云
通过RT-Thread Settings添加AT_DEVICE->Espressif ESP8266和IoT Cloud->Ali-iotkit组件。填写你的Wifi接入的SSID、password,指定串口为uart1。
填写阿里云接入product key、product securet、device name、device securet等信息。
打开driver/board.h文件,添加修改uart1宏定义。
#define BSP_USING_UART1
#define BSP_UART1_TX_PIN "PA9"
#define BSP_UART1_RX_PIN "PA10"保存修改,编译下载,打开调试终端。
5 时钟日历支持
打开driver/board.h文件,按照指示开启stm32内部时钟日历。

打开RT-Thread Settings,开启时钟日历支持。
在board.h取消#define BSP_USING_ONCHIP_RTC注释(我们已经取消了)。
打开drivers/stm32f1xx_hal_config.h,取下#define HAL_RTC_MODULE_ENABLED注释。
保存、编译、下载。

注意,激活Using RTC alarm会和我们使用uart1有冲突。
6 通过AT命令获取NTP时间,同步rtc时钟。
修改GitHub RT-Thread_at_device/at_sample_client.c。
#include <stdlib.h>
#include <string.h>
#include <rtthread.h>
#include <at.h>
#include <rtc.h>
#include "main.h"
//#define LOG_TAG "at.ntp"
//#include <at_log.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
//#define AT_CLIENT_RECV_BUFF_LEN 512
date_time_t dt;
int main(void)
{
//at_client_init("uart1", AT_CLIENT_RECV_BUFF_LEN);
if (!at_client_ntp())
{
set_date(dt.year,dt.month,dt.day);
set_time(dt.hour,dt.minus,dt.seconds);
}
time_t now; /* 保存获取的当前时间值 */
/* 获取时间 */
now = time(RT_NULL);
/* 打印输出时间信息 */
rt_kprintf("%s\n", ctime(&now));
return RT_EOK;
}
int at_client_ntp(void)
{
at_response_t resp = RT_NULL;
int result = 0;
resp = at_create_resp(256, 0, rt_tick_from_millisecond(5000));
if (resp == RT_NULL)
{
LOG_E("No memory for response structure!");
return -2;
}
/* send AT */
at_exec_cmd(resp, "AT");
if (result != RT_EOK)
{
LOG_E("AT client send commands AT failed or return response error!");
goto __exit;
}
/* close echo */
at_exec_cmd(resp, "ATE0");
if (result != RT_EOK)
{
LOG_E("AT client send commands ATE0 failed or return response error!");
goto __exit;
}
result = at_exec_cmd(resp, "AT+CIPSNTPCFG=1,8,\"ntp1.aliyun.com\",\"cn.pool.ntp.org\"");
if (result != RT_EOK)
{
LOG_E("AT client send commands AT+CIPSNTPCFG failed or return response error!");
goto __exit;
}
rt_thread_mdelay(500);
at_exec_cmd(resp, "AT+CIPSNTPTIME?");
if (result != RT_EOK)
{
LOG_E("AT client send commands AT+CIPSNTPTIME? failed or return response error!");
goto __exit;
}
/* Print response line buffer */
{
const char *line_buffer = RT_NULL;
LOG_D("Response buffer");
for(rt_size_t line_num = 1; line_num <= resp->line_counts; line_num++)
{
if((line_buffer = at_resp_get_line(resp, line_num)) != RT_NULL)
{
LOG_D("line %d buffer : %s", line_num, line_buffer);
}
else
{
LOG_E("Parse line buffer error!");
}
}
}
{
char resp_arg[AT_CMD_MAX_LEN] = { 0 };
const char * resp_expr = "+CIPSNTPTIME:%24[^\r\n]";
LOG_D(" Parse arguments");
if (at_resp_parse_line_args(resp, 1, resp_expr, resp_arg) == 1)
{
LOG_D("NTP Time: %s", resp_arg);
char dt_s[4] = {0};
memcpy(dt_s,resp_arg+4,3);
for (int i=0;i<12;i++)
{
if (memcmp(month_s[i],dt_s,3) == 0)
{
dt.month = i+1;
}
}
memset(dt_s,0,4);
memcpy(dt_s,resp_arg+8,2);
dt.day = atoi(dt_s);
memset(dt_s,0,4);
memcpy(dt_s,resp_arg+11,2);
dt.hour = atoi(dt_s);
memset(dt_s,0,4);
memcpy(dt_s,resp_arg+14,2);
dt.minus = atoi(dt_s);
memset(dt_s,0,4);
memcpy(dt_s,resp_arg+17,2);
dt.seconds = atoi(dt_s);
memset(dt_s,0,4);
memcpy(dt_s,resp_arg+20,4);
dt.year = atoi(dt_s);
rt_kprintf("ntp time is: %d-%d-%d %d:%d:%d\r\n",dt.year,dt.month,dt.day,dt.hour,dt.minus,dt.seconds);
rt_memset(resp_arg, 0x00, AT_CMD_MAX_LEN);
if (dt.year == 1970)
{
LOG_E("Error! Do not catch real time.");
goto __exit;
}
}
else
{
LOG_E("Parse error, current line buff : %s", at_resp_get_line(resp, 3));
goto __exit;
}
}
__exit:
if(resp)
{
at_delete_resp(resp);
}
return result;
}
7 接入DS18B20温度传感器
通过RT-Thread Settings,以此选择Packages->peripheral libraries and drivers->sensors drivers->DS18B20 sensor driver,保存。
修改ds18b20/ds18b20_sample.c at master · willianchanlovegithub/ds18b20 · GitHub
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-05-08 RT-Thread first version
*/
#include <rtthread.h>
#include <stdlib.h>
#include <rtthread.h>
#include "board.h"
#include "sensor.h"
#include "sensor_dallas_ds18b20.h"
#include "drv_common.h"
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
/* Modify this pin according to the actual wiring situation */
#define DS18B20_DATA_PIN GET_PIN(B, 10)
static int rt_hw_ds18b20_port(void);
static int ds18b20_read_temp_sample(void);
static void read_temp_entry(void *parameter);
int main(void)
{
rt_hw_ds18b20_port();
ds18b20_read_temp_sample();
return RT_EOK;
}
static void read_temp_entry(void *parameter)
{
rt_device_t dev = RT_NULL;
struct rt_sensor_data sensor_data;
rt_size_t res;
dev = rt_device_find(parameter);
if (dev == RT_NULL)
{
rt_kprintf("Can't find device:%s\n", parameter);
return;
}
if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
{
rt_kprintf("open device failed!\n");
return;
}
rt_device_control(dev, RT_SENSOR_CTRL_SET_ODR, (void *)100);
while (1)
{
res = rt_device_read(dev, 0, &sensor_data, 1);
if (res != 1)
{
rt_kprintf("read data failed!size is %d\n", res);
rt_device_close(dev);
return;
}
else
{
if (sensor_data.data.temp >= 0)
{
rt_kprintf("temp:%3d.%dC, timestamp:%5d\n",
sensor_data.data.temp / 10,
sensor_data.data.temp % 10,
sensor_data.timestamp);
}
else
{
rt_kprintf("temp:-%2d.%dC, timestamp:%5d\n",
abs(sensor_data.data.temp / 10),
abs(sensor_data.data.temp % 10),
sensor_data.timestamp);
}
}
rt_thread_mdelay(5000);
}
}
static int ds18b20_read_temp_sample(void)
{
rt_thread_t ds18b20_thread;
ds18b20_thread = rt_thread_create("18b20tem",
read_temp_entry,
"temp_ds18b20",
1024,
RT_THREAD_PRIORITY_MAX / 2,
20);
if (ds18b20_thread != RT_NULL)
{
rt_thread_startup(ds18b20_thread);
}
return RT_EOK;
}
//INIT_APP_EXPORT(ds18b20_read_temp_sample);
static int rt_hw_ds18b20_port(void)
{
struct rt_sensor_config cfg;
cfg.intf.user_data = (void *)DS18B20_DATA_PIN;
rt_hw_ds18b20_init("ds18b20", &cfg);
return RT_EOK;
}
//INIT_COMPONENT_EXPORT(rt_hw_ds18b20_port);

8 IIC传感器接入
通过RT-Thread Settings使能i2c driver支持。
在board.h取消#define BSP_USING_I2C1注释,针对硬件连接修改针脚,比如SCL->PB6,SDA->PB7。
BMP280气压传感器
通过RT-Thread Settings打开BMP280气压传感器的支持。

主程序添加传感器初始化。
#include "sensor_bs_bmp280.h"
#define BMP280_I2CBUS_NAME "i2c1"
int rt_hw_bmp280_port(void)
{
struct rt_sensor_config cfg;
// cfg.intf.dev_name = BMP280_I2CBUS_NAME;
cfg.intf.dev_name = "i2c1";
cfg.intf.user_data = (void *)BMP280_ADDR_DEFAULT;
rt_hw_bmp280_init("bmp280", &cfg);
return RT_EOK;
}
INIT_APP_EXPORT(rt_hw_bmp280_port);修改C:\RT-ThreadStudio\workspace\stm32f103-sensor\packages\bmp280-latest\sensor_bs_bmp280.h
#ifndef SENSOR_BS_BMP280_H__
#define SENSOR_BS_BMP280_H__
#include "sensor.h"
#include "bmp280.h"
//#define BMP280_ADDR_DEFAULT BMP280_I2C_ADDR_SEC
//#define BMP280_I2CBUS_NAME "i2c2"
#define BMP280_ADDR_DEFAULT BMP280_I2C_ADDR_PRIM
int rt_hw_bmp280_init(const char *name, struct rt_sensor_config *cfg);
#endif
注意宏定义#define BMP280_ADDR_DEFAULT BMP280_I2C_ADDR_PRIM,即BMP2800 I2C地址为0x76,这时需要将BMP280 SDO管脚接GND。
调试终端输入sensor_polling temp_bmp和sensor_polling baro_bmp,可以看到数据。
DHT20温湿度传感器
直接用现有AHT10传感器模块。

主程序添加传感器初始化。
#include "sensor_asair_aht10.h"
#define AHT10_I2C_BUS "i2c1"
int rt_hw_aht10_port(void)
{
struct rt_sensor_config cfg;
cfg.intf.dev_name = AHT10_I2C_BUS;
cfg.intf.user_data = (void *)AHT10_I2C_ADDR;
rt_hw_aht10_init("aht10", &cfg);
return RT_EOK;
}
INIT_ENV_EXPORT(rt_hw_aht10_port);调试终端输入sensor_polling temp_aht和sensor_polling humi_aht,可以看到数据。
9 使用STM32F401CCU6开发板
修改编译器Linker,选择do not use syscalls (--specs=nosys-specs)。
修改linkscritps/STM32F401CC/link.lds,SECTIONS添加。
.heap :
{
__end__ = .;
PROVIDE(end = .);
*(.heap*)
__HeapLimit = .;
} > RAM