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)讀寫的數據結構來存儲,可是可能要佔必定的空間
本站公眾號
   歡迎關注本站公眾號,獲取更多信息