RTCSD2017-2

解决方案

任务简述

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

Sender

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

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-Discoveryoop

仿真结果

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

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

运行结果

标准模式
正常运行中:

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

Monitor检测到错误:

Record模式
等待开始指令
this

Tracealyzer

注意事项

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

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

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

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

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

缘由未知
在trcConfig.h的151行上方的空格添加
#define TRC_CFG_ARM_CM_USE_SYSTICKcode

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

好比,现实生活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 )blog

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

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

有待改进

  • MCU里的时间和现实时间不同,多是config里CPU的主频设置错了。 ↑↑↑
  • 目前这种检查方式只能检查出错误,很大几率知道丢失了几个。可是不能知道丢失了那几个。 能够用一个O(1)读写的数据结构来存储,可是可能要占必定的空间
本站公众号
   欢迎关注本站公众号,获取更多信息