1 硬件环境
stm32f103c8t6板及stm32f401ccu6板是目前性价比最高的stm32开发板,基本不到20元。由于rt-thread操作系统占用还是比较大的,所以stm32f103c8t64板,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连接。
根据我们的硬件连接,选择串口连接UART二、修改连接针PA2和PA三、选择下载器ST-LINK,端口SWD。打开,基本就是这样。
编译和下载测试应该没有问题。
修改系统时钟。rt-thread缺乏配置的时钟是stm内部高速时钟,我们将其修改为外部时钟。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?"); 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, lin_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