玩转RT-Thread系列教程(11)--综合485通信+文件系统综合实战


玩转RT-Thread系列教程(11)–综合485通信+文件系统综合实战

根据前两篇文章我们学习了485总线读取温湿度数据+虚拟文件系统的使用,今天让我们来结合二者进行一次综合实战

一、案例分析

  • 1.挂载SD卡
  • 2.获取温湿度数据
  • 3.在创建SD卡中创建文件,保存数据
  • 4.将异常温湿度数据保存到SD文件中

二、系统优化

在进行我们今天的综合案例前,我们先对我们之前的代码进行优化

1.SD热插处理

void FlieSystem_entry(void *parameter)
{
    static rt_err_t result;

    rt_device_t dev;

    while(1)
    {
        dev = rt_device_find("sd0");

        if (dev != RT_NULL)
        {
            if (dfs_mount("sd0", "/", "elm", 0, 0) == RT_EOK)
            {
                rt_kprintf("SD mount to / success\n");
                break;
            }
            else
            {
                rt_kprintf("SD mount to / failed\n");
            }
        }
        rt_thread_mdelay(500);
    }
}

static int FileSystemInit(void)
{
    //创建sd线程
    rt_thread_t thread_filesystem = rt_thread_create("file_sys", FlieSystem_entry, RT_NULL, 1024, 18, 20);

    if (thread_filesystem != RT_NULL)
    {
        rt_thread_startup(thread_filesystem);
    }
}

INIT_ENV_EXPORT(FileSystemInit);

2.温湿度数据发送

之前我们读取到的温湿度数据都是直接打印出来或者通过全局变量进行处理,这对于简单的工程我们是可以接受的,但对于复杂的功能来说,我们需要进行线程之间的通信来获取其他线程的数据。这里就用到了我们之前学习到的消息邮箱机制。

发送线程:

接收线程:

通过永久等待方式等待邮箱消息的来到。

3.创建ADC处理线程

三、案例实战

1.文件的读写操作

这里我操作文件使用的是POSIX方式

//保存数据到SD
void Sensor_DataTo_SD(char* buff)
{
    /* 以创建和读写模式打开 /text.txt 文件,如果该文件不存在则创建该文件 */
    FILE *recvdata_p0;
    recvdata_p0 = fopen("/Sensor_Data.csv", "a+");
    if (recvdata_p0 != RT_NULL)
    {
        fputs(buff, recvdata_p0);
        fputs("\n", recvdata_p0);
        fclose(recvdata_p0);
    }
}

//从SD读取信息
void Data_ReadFSD(void)
{
    FILE *fp;
    char buffer[120];
    fp = fopen("/Sensor_Data.csv", "r");
    if (fp != RT_NULL)
    {
        fread(buffer, sizeof(char), sizeof(buffer), fp);
        rt_kprintf("%s", buffer);
        fclose(fp);
    }
}

2.温度阈值判断逻辑

判断阈值逻辑我是这样写的,当超过最高/低设定阈值时,通过累加器去进行累加,当超过一定次数便真正进行警报处理

void Save_Data_TOSD(float data1, float data2)
{
    if(data1 >= HIGHT_TEMPVALUE)
    {
        detect_logic.T_Count_Alarm++;
    }
    else
        {
            detect_logic.T_Count_Alarm--;
            if(detect_logic.T_Count_Alarm <= 0)
                detect_logic.T_Count_Alarm = 0;
        }
        if(detect_logic.T_Count_Alarm >= MAX_COUNTER)
        {
                detect_logic.T_Count_Alarm = MAX_COUNTER;
                rt_kprintf("温度超过标准!%d\n",detect_logic.T_Count_Alarm);
                sprintf((char*)detect_logic.Alarm_buff, "Temp:%.2f,humi:%.2f", data1, data2); //拼接到温度数组里
                //保存异常数据到SD卡
                Sensor_DataTo_SD((char*)detect_logic.Alarm_buff);
        }
        //
    if(data2 >= HIGHT_HUMIVALUE)
    {
        detect_logic.H_Count_Alarm++;
    }
    else
        {
            detect_logic.H_Count_Alarm--;
            if(detect_logic.H_Count_Alarm <= 0)
                detect_logic.H_Count_Alarm = 0;
        }

        if(detect_logic.H_Count_Alarm >= MAX_COUNTER)
        {
                detect_logic.H_Count_Alarm = MAX_COUNTER;
                rt_kprintf("湿度超过标准!%d\n",detect_logic.H_Count_Alarm);
                sprintf((char*)detect_logic.Alarm_buff, "Temp:%.2f,humi:%.2f", data1, data2); //拼接到温度数组里
                //保存异常数据到SD卡
                Sensor_DataTo_SD((char*)detect_logic.Alarm_buff);
        }
}

3.利用事件进行线程同步

既然利用到了事件,那么我们先来介绍一下RTT的事件集的作用以及用法:

3.1、事件的介绍

事件集是线程间同步的机制之一,一个事件集可以包含多个事件,利用事件集可以完成一对多,多对多的线程间同步。

事件集主要用于线程间的同步,与信号量不同,它的特点是可以实现一对多,多对多的同步。即一个线程与多个事件的关系可设置为:其中任意一个事件唤醒线程,或几个事件都到达后才唤醒线程进行后续的处理;同样,事件也可以是多个线程同步多个事件。

RT-Thread 定义的事件集有以下特点:

  • 事件只与线程相关,事件间相互独立:每个线程可拥有 32 个事件标志,采用一个 32 bit 无符号整型数进行记录,每一个 bit 代表一个事件;

  • 事件仅用于同步,不提供数据传输功能;

  • 事件无排队性,即多次向线程发送同一事件 (如果线程还未来得及读走),其效果等同于只发送一次。

在 RT-Thread 中,每个线程都拥有一个事件信息标记,它有三个属性,分别是 RT_EVENT_FLAG_AND(逻辑与)**,RT_EVENT_FLAG_OR(逻辑或)以及 **RT_EVENT_FLAG_CLEAR(清除标记)。当线程等待事件同步时,可以通过 32 个事件标志和这个事件信息标记来判断当前接收的事件是否满足同步条件。

3.2、事件在系统中的使用

因为我们分别有ADC线程,温湿度获取线程等多个线程,假设一个线程采集数据很快,另一个线程采集速度很慢,那么假设我们不使用线程间同步方式进行处理的话,很容易会导致数据的不一致性。所以我们使用事件方式来处理线程之间的同步。

4.接收线程处理

在接收线程中,这里我参考了WillianChan在分布式温度监控系统中对接收数据线程的方法,使用一个永久等待接收的事件以及超时接收事件来优化文件存储耗时的问题——只有当超过阈值时才进行数据的转存。

static void Publish_th(void *pram)
{
    //传感器数据结构体
    Sensor_msg sensor_msg;
    static rt_uint32_t e;
    static char r_buff[64];

    while(1)
    {
        if (rt_event_recv(Sensor_event, EVENT_ADC_FLAG, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &e) != RT_EOK)
            continue;

        //5s等待接收邮件
        if(rt_mb_recv(Sensor_msg_mb, (rt_ubase_t*)&sensor_msg, rt_tick_from_millisecond(5000)) == RT_EOK)
        {
          sprintf((char*)r_buff, "Temp:%.2f,humi:%.2f", (float)sensor_msg->temp, (float)sensor_msg->hum); //拼接到温度数组里
          rt_kputs((char*)r_buff);
          rt_kputs("\n");

          //阈值判断逻辑
          Save_Data_TOSD((float)sensor_msg->temp, (float)sensor_msg->hum);

          //释放内存块
          rt_mp_free(sensor_msg);
          sensor_msg = RT_NULL;
          continue;            
        }
                //5s超时直接存数据
                //阈值判断逻辑
                rt_kputs("@1s接收事件超时--存储数据\n");
                Sensor_DataTo_SD((char*)r_buff);
          continue;
    }
}

5.编译、下载、验证

可以看到,当湿度超过阈值时,异常数据才会被保存到excell文档中。


文章作者: Rb菌
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Rb菌 !
  目录