艳遇的技术小站

导航

RTCSD2017-2

解决方案

任务简述

本次作业主体由三个任务组成,分别是Sender,Receiver,Monitor。分别完成发送,接收和监控的任务。

Sender

Sender作为数据生产者,要求每隔2ms生成一个逐次递增的数。当增加到10000时,返回1继续累加。

Receiver

Receiver作为数据消费者,要求每隔1s统计一次生成的数的总和。

Monitor

Monitor作为监控者,要求每隔10s检查一次数据是否成功接收。

思路简述

  1. 由作业一可知,可以用FreeRTOS提供的延时函数vTaskDelay()来完成周期性的任务。
  2. 由上述要求,Receiver执行两次之间,Sender会执行500次,即产生500个数。所以需要缓冲区来承载这些数。
  3. 不妨选择队列来作为缓冲区,即FreeRTOS提供的xQueueCreate()等一系列函数。而且这种队列也支持多任务同时读写。
  4. 之后需要确定监控的方式。可以发现,Receiver产生的数都是依次递增的,前后两个之间相隔1。
  5. 所以Sender可以将当前从队列里取到的数与上一个数比较,将两个的差减一后累加到某个值上去。如果这个值不为0,则出现问题。
  6. 如果后一个数比前一个数小,则将后一个数加上10000再做以上操作。比如当前收到1,上一个收到的是10000:(1+10000)-10000=1,1-1=0
  7. 为了更好地表述,记第5步中所说的值为Diff。当前Diff由Sender累加,但Monitor也会去访问。由于不确定累加和访问这两个操作的原子性,所以需要对这两个加锁。
  8. 不妨选择xSemaphoreCreateMutex()作为锁。

代码展示

/**
 * Sender_Task: Product the number
 */
void SenderTask(void* arg)
{
    traceString stLogger = xTraceRegisterString("Sender Task");
    uint32_t uiNum = 1;
    TickType_t xLastWakeTime = xTaskGetTickCount();
    while (1)
    {
        if (pdPASS != xQueueSend(g_hNumberBuff, &uiNum, 0))
        {
            vTracePrintF(stLogger, "Send to queue failed.");
            if (pdPASS == xSemaphoreTake(g_uiMutexSL, 1))
            {
                g_uiFailed++;
                vTracePrintF(stLogger, "Fail: %d", g_uiFailed);
                xSemaphoreGive(g_uiMutexSL);
            }
        }
        uiNum += 1;
        if (uiNum > 10000)
        {
            uiNum = 1;
        }
        vTaskDelayUntil(&xLastWakeTime, 2 / portTICK_RATE_MS);
    }
}

/**
 * Receiver_Task: Produce the number
 */
void ReceiverTask(void* arg)
{
    traceString stLogger = xTraceRegisterString("Receiver Task");
    uint64_t ulTmpSum = 0;
    uint32_t uiNumber = 0;
    uint32_t uiLast = 0;
    uint32_t uiTmpDiff;
    uint32_t uiTmpLoopCnt;
    TickType_t xLastWakeTime = xTaskGetTickCount();
    while (1)
    {
        uiTmpDiff = 0;
        uiTmpLoopCnt = 0;
        ulTmpSum = 0;
        while (pdPASS == xQueueReceive(g_hNumberBuff, &uiNumber, 0))
        {
            ulTmpSum += uiNumber;
            if (uiNumber > uiLast)
            {
                uiTmpDiff += uiNumber - uiLast - 1;
            }
            else
            {
                uiTmpDiff += uiNumber + 10000 - uiLast - 1;
                uiTmpLoopCnt += 1;
            }
            uiLast = uiNumber;
        }
        if (pdPASS == xSemaphoreTake(g_uiMutexRML, 10 / portTICK_RATE_MS))
        {
            g_uiDiff += uiTmpDiff;
            g_uiLoopCnt += uiTmpLoopCnt;
            g_ulSum += ulTmpSum;
            xSemaphoreGive(g_uiMutexRML);
        }
        vTracePrintF(stLogger, "The sum of this round is %u\n", ulTmpSum);
        vTaskDelayUntil(&xLastWakeTime, 1000 / portTICK_RATE_MS);
    }
}

/**
 * Monitor_Task: Check the task
 */
void MonitorTask(void* arg)
{
    traceString stLogger = xTraceRegisterString("Monitor Task");
    uint32_t uiTmpDiff;
    uint32_t uiTmpLoopCnt;
    uint32_t uiTmpState;
    TickType_t xLastWakeTime = xTaskGetTickCount();
    while (1)
    {
        uiTmpState = 0;
        if (pdPASS == xSemaphoreTake(g_uiMutexRML, 10))
        {
            uiTmpDiff = g_uiDiff;
            uiTmpLoopCnt = g_uiLoopCnt;
            g_uiState = (uiTmpDiff != 0);
            xSemaphoreGive(g_uiMutexRML);
        }

        if (uiTmpDiff == 0)
        {
            vTracePrintF(stLogger, "OK, %d loops are done.", uiTmpLoopCnt);
        }
        else
        {
            STM_EVAL_LEDOn(LED4);
            STM_EVAL_LEDOff(LED3);
            vTracePrintF(stLogger, "Wrong, difference is %d.", uiTmpDiff);
        }
        vTaskDelayUntil(&xLastWakeTime, 10000 / portTICK_RATE_MS);
    }
}

成果展示

GitHub

https://github.com/89yanyu/STM32F429I-Discovery

仿真结果

开始是绿灯,一段时间后变为红灯。
因为只有在第二次运行Monitor的时候才会检查到有错误,将灯的状态改变。

  1. 初始化(红,绿灯亮)
  2. 正常运行(绿灯亮)
  3. 初始化失败或检查到错误(红灯亮)

运行结果

标准模式
正常运行中:

出现错误但未被Monitor检测到:

Monitor检测到错误:

Record模式
等待开始指令

Tracealyzer

注意事项

仿真的时候出现LED不亮,LED不变化或者延迟变化。

因为仿真的时候用的是BSRR寄存器。
这个寄存器的功能是把要改变的Bit写入,由MCU去改变相应的数据。
而Qemu在仿真的时候似乎同步没有做好,就出现了以上情况
将访问BSRR的操作改为ODR,这个寄存器就是直接操作io。
具体操作比如:
Set: GPIOx->ODR |= GPIO_Pin_xx;
ReSet:GPIOx->ODR &= ~GPIO_Pin_xx;

用Tracealyzer通过SEGGER传输数据时,出现速度不稳定,甚至跳的超过100%

查看结果时,出现任务执行时长异常,长时间没有任务在工作

仿真时,将snapshot保存出来,查看时出现未找到时间标签

原因未知
在trcConfig.h的151行上方的空格添加
#define TRC_CFG_ARM_CM_USE_SYSTICK

仿真或者在板子上运行时,时间长度不正确

比如,现实生活7s相当于板子里的20s
因为FreeRTOS设置了MCU的主频,默认是62500000
而FreeRTOS的定时都是基于MCU主频,即内部是根据MCU主频来确定Tick(可以认为是一个CPU周期),而定时都是将ms转化为Tick。
比如,20s * 62500000Hz / 180000000Hz = 6.944s
修改FreeRTOSConfig.h的第98行,改为:
#define configCPU_CLOCK_HZ ( ( unsigned long ) 180000000 )

开着Traceanlyzer的流模式运行速度会变慢

原因未知
所以提供正常模式和Record模式。
开机时按住USER_BUTTON(蓝色按钮),或者按住USER_BUTTON,再按RESET(黑色按钮)可以进入Record模式
此时,会等待Traceanlyzer启动。
可能需要多点几次Start

有待改进

  • MCU里的时间和现实时间不一样,可能是config里CPU的主频设置错了。 ↑↑↑
  • 目前这种检查方式只能检查出错误,很大概率知道丢失了几个。但是不能知道丢失了那几个。
    可以用一个O(1)读写的数据结构来存储,但是可能要占一定的空间

posted on 2017-09-28 21:45  89艳遇  阅读(332)  评论(5编辑  收藏  举报