久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

嵌入式基礎(chǔ)--毫秒級定時模塊

 西北望msm66g9f 2021-07-11

大家好,,我是驚覺,。失蹤了三個月,,我回來了,。給大家?guī)硪粋€好消息和一個壞消息。壞消息是,,我尚未滿血復(fù)活,,Ardupilot第四篇將繼續(xù)延期。好消息是,,公眾號恢復(fù)更新,,先出一系列提升編碼能力的文章。

全國電賽在即,,昨天母校老師聯(lián)系我,,想讓我給學(xué)弟們做下賽前培訓(xùn)。我做過很多年的培訓(xùn),,很早就發(fā)現(xiàn)了一個問題:同學(xué)們在為比賽做準備時,,往往只注重去學(xué)習(xí)使用各種各樣的傳感器,,自動控制算法,,各種驅(qū)動。同學(xué)們只關(guān)注如何去實現(xiàn)功能,,而忽視了如何把代碼寫得更好,,更健壯,更易擴展和維護,。如果在比賽之前,,先準備好高質(zhì)量的代碼框架,基礎(chǔ)模塊,,熟練掌握調(diào)度技巧,,將極大提高賽時的開發(fā)和調(diào)試效率。

所謂高質(zhì)量,,涉及到很多方面,,比如:

  • 函數(shù)、變量的命名有統(tǒng)一的規(guī)則
  • 基礎(chǔ)接口要簡單易用
  • 設(shè)計模塊時需層次分明,,高內(nèi)聚低耦合
  • 盡量避免重復(fù)代碼

筆者不打算一一講解這些設(shè)計原則,,而是介紹一些實際的基礎(chǔ)模塊,講解它們的設(shè)計思路,,注意事項和編程技巧,,并在此過程中讓大家理解相關(guān)的設(shè)計原則。

毫秒級定時模塊

作為系列開篇,本文先介紹一個非?;A(chǔ)的模塊:毫秒級定時模塊,。

友情提醒,本模塊比較基礎(chǔ),,可能有的同學(xué)對此非常熟悉,,不過文末還有一個重要的小技巧噢。有基礎(chǔ)的同學(xué),,可直接往后翻,,跳到“再看comm_delay”一節(jié)

此模塊提供基礎(chǔ)的定時功能,,可細分為兩種:

  • 延時功能:毫秒極延時,。
  • 定時功能:過一段時間后執(zhí)行一項操作。

可能有的同學(xué)會想,,直接用單片機的定時器嘛,,一個任務(wù)用一個定時器,有啥好講的,。其實不然,。定時模塊肯定要依賴于硬件定時器,但是一個任務(wù)用一個定時器的話,,會有如下問題:

  • 浪費資源,。硬件定時器不僅僅只有定時功能,還有捕獲輸入信號,,輸出PWM,,編碼器等功能。如果濫用基礎(chǔ)定時功能的話,,等用到上述功能時,,將無資源可用。
  • 某個任務(wù)與某個定時器綁定在一起,,提高了系統(tǒng)耦合程度,,可移植性差。

因此,,我們需要一個統(tǒng)一的,,可移植性強的定時模塊。

我們再回頭看下兩個基礎(chǔ)的功能,,延時和定時,。

延時示例,1秒打印1次hello,。comm_delay實現(xiàn)毫秒極延時,。

void comm_delay(uint32_t ms);

while (1)
{
    printf('hello\r\n');
    comm_mdelay(1000);
}

定時示例,,1秒打印1次hello。comm_get_ms返回當(dāng)前系統(tǒng)時間,,即系統(tǒng)從啟動到現(xiàn)在經(jīng)過了多少毫秒,。

uint32_t comm_get_ms(void);

while (1)
{
    cur_time = comm_get_ms();
    if (cur_time >= timeout)
    {
        printf('hello\r\n');
        timeout = cur_time + 1000;
    }
}

可能有的同學(xué)覺得上述兩項功能差不多,而定時比延時的代碼要復(fù)雜,。定時的代碼確實多一些,,不過它具有并發(fā)能力,即支持多個定時任務(wù)同時進行,。

定時示例,,1秒打印1次you,2秒打印1次me,。

static void show_you(void)
{
    static uint32_t timeout = 0;
    
    uint32_t cur_time = 0;
    
    cur_time = comm_get_ms();
    if(cur_time < timeout)
    {
        return;
    }
    
    printf('you\r\n');
    timeout = cur_time + 1000;
}

static void show_me(void)
{
    static uint32_t timeout = 0;
    
    uint32_t cur_time = 0;
    
    cur_time = comm_get_ms();
    if(cur_time < timeout)
    {
        return;
    }
    
    printf('me\r\n');
    timeout = cur_time + 2000;
}

int main(int argc, char **argv)
{
    while (1)
    {
        show_you();
        show_me();
    }
    
    return 0;
}

小結(jié):

  • 延時功能需要提供一個延時函數(shù),。
  • 定時功能需要提供獲取系統(tǒng)時間的函數(shù)。

即:

void comm_delay(uint32_t ms);
uint32_t comm_get_ms(void);

其實延時函數(shù)很簡單,,因為它也可以看成是一個定時任務(wù):

void comm_delay(uint32_t ms)
{
    uint32_t timeout = comm_get_ms() + ms;
    while(comm_get_ms() < timeout);
}

系統(tǒng)時間

實現(xiàn)comm_get_ms,,即記錄系統(tǒng)時間,自然要靠硬件定時器啦,。大家用的單片機,,無論是TI,STM32,,NXP等,,大部分都是cortex-m的內(nèi)核,該內(nèi)核有一個專門干這事情的定時器:SysTick timer,。其名稱為系統(tǒng)滴答定時器,,只有簡單的定時功能,。配置好reload計數(shù)并使能后,,其由reload值遞減至0,觸發(fā)中斷,,再從reload遞減,。如果根據(jù)其時鐘配置相應(yīng)的reload值,實現(xiàn)每1ms觸發(fā)1次中斷,,那就可以記錄毫秒 級的系統(tǒng)時間,。

其配置函數(shù)位于單片機驅(qū)動庫的CMSIS組件中,一般的工程都包含了這個組件,,比如筆者使用TRUEStudio創(chuàng)建的工程:圖片

下面是stm32的示例,。SystemCoreClock為單片機的主頻,這也是SysTick的輸入時鐘,。SystemCoreClock / 1000即為1ms的定時計數(shù),,將reload配置為此值即可實現(xiàn)每1ms觸發(fā)1次定時中斷,。其他單片機方法類似。

#include 'stm32l1xx.h'
#include <stdint.h>

static uint32_t  sys_tick = 0;

void sys_tick_init(void)
{
    if(SysTick_Config(SystemCoreClock / 1000));
    NVIC_SetPriority(SysTick_IRQn, 0);
}

uint32_t sys_tick_get(void)
{
    return sys_tick;
}

void SysTick_Handler(void)
{
    sys_tick++;
}

用法非常簡單,,單片機啟動時調(diào)用sys_tick_init配置并使能SysTick,,每1ms觸發(fā)1次SysTick_Handler,其內(nèi)對當(dāng)前時間sys_tick進行加1操作,。應(yīng)用層通過sys_tick_get獲取當(dāng)前時間,。

SysTick_Handler在中斷向量表中指定,大家根據(jù)具體的MCU對號入座,。圖片

comm_get_ms只需對sys_tick_get進行簡單的封裝:

uint32_t comm_get_ms(void)
{
    return sys_tick_get();
}

再看comm_delay

我們再看一下毫秒極延時的實現(xiàn),,大家覺得它有問題嗎?

void comm_delay(uint32_t ms)
{
    uint32_t timeout = comm_get_ms() + ms;
    while(comm_get_ms() < timeout);
}

對于只需要進行幾分鐘演示的電賽來說,,它沒有問題,。不過,電賽只是同學(xué)們實踐所學(xué)的一條途徑,。正兒八經(jīng)的產(chǎn)品,,需要具有足夠的健壯性,可長期穩(wěn)定地運行,。上述代碼能長期運行嗎,?

static uint32_t  sys_tick = 0;

comm_get_ms是對sys_tick_get的簡單封裝,而sys_tick_get返回的是一個32位無符號整型變量,,它記錄的是系統(tǒng)從啟動到現(xiàn)在所經(jīng)過的毫秒數(shù),。32位無符號整型變量最大能表示多長的時間呢?

2^32 / 1000 / 3600 / 24 = 49.71

其可記錄49天,。在49天后,,sys_tick將會溢出,從零重新開始累加,。為了方便描述,,要使用到一個宏UINT32_MAX。UINT32_MAX表示32位無符號整型變量的最大值,,即0xffffffff,。

假設(shè)我們要延時1分鐘,即60000ms,。當(dāng)前時間為UINT32_MAX - 59999,,下面計算timeout。

uint32_t timeout = comm_get_ms() + ms;

timeout發(fā)生溢出,,計算結(jié)果為0,。

UINT32_MAX - 59999 + 60000 = UINT32_MAX + 1 = 0

那么下面的等待循環(huán)將立刻退出,而不需要等待1分鐘,。

while(comm_get_ms() < timeout);

在接下來的1分鐘內(nèi),,comm_get_ms(60000)都是失效的,,每1分鐘執(zhí)行1次的任務(wù)將不停地執(zhí)行。還有其他溢出的場景,,這里不再一一描述,。我們只要明確一點就好:comm_delay不能長期運行。

健壯的comm_delay

怎么修改comm_delay以解決溢出問題呢,?其實很簡單,,直接給出答案:

void comm_delay(uint32_t ms)
{
    uint32_t timeout = comm_get_ms() + ms;
    while(comm_get_ms() - timeout > UINT32_MAX / 2);
}

我們簡單地驗證幾個場景:

當(dāng)前時間為10ms,延時2ms,。

先計算timeout:timeout = 10 + 2 = 12

下面看看從現(xiàn)在開始,,什么時候while(comm_get_ms() - timeout > UINT32_MAX / 2);會退出。

  • 第0ms秒時,,當(dāng)前時間為10,,10 - 12 = -2,請注意,,comm_get_ms() - timeout中的操作數(shù)都是uint32_t類型,,即32位無符號整型,它們相減的結(jié)果還是無符號整型,。所以-2 --> UINT32_MAX - 1 > UINT32_MAX / 2,,循環(huán)繼續(xù)。
  • 第2秒時,,當(dāng)前時間為12,,12 - 12 = 0 < UINT32_MAX / 2,循環(huán)等待結(jié)束,。

此種場景,,成功實現(xiàn)延時2ms。

當(dāng)前時間為(UINT32_MAX - 1)ms,,延時2ms,。

之所以定成UINT32_MAX - 1,是想測試時間溢出的場景,,2ms后時間溢出,。

先計算timeout,,timeout在加2時溢出,,最終結(jié)果為0。timeout = (UINT32_MAX - 1) + 2 = UINT32_MAX + 1 = 0

下面看看從現(xiàn)在開始,,什么時候while(comm_get_ms() - timeout > UINT32_MAX / 2);會退出,。

  • 第0秒時,當(dāng)前時間為(UINT32_MAX - 1),,(UINT32_MAX - 1) - 0 = (UINT32_MAX - 1) > UINT32_MAX / 2,,循環(huán)繼續(xù),。
  • 第2秒時,當(dāng)前時間如timeout一樣加2溢出,,最終為0,。0 - 0 = 0 < UINT32_MAX / 2,循環(huán)等待結(jié)束,。

此種場景,,成功實現(xiàn)延時2ms。

總結(jié)

經(jīng)過兩種情況的測試,,我們發(fā)現(xiàn),,無論計算過程中時間有無溢出,改進后的comm_delay都圓滿完成延時,。

這種實現(xiàn)的原理是什么呢,?原理很重要噢,否則大家在使用時,,可能把大于和小于關(guān)系搞反,,或者是把被減數(shù)與減數(shù)的關(guān)系搞反。至于原理是什么呢,,今天來不及講了,,請待下回分解。劇透一下,,下篇的名字叫:張三與李四誰跑的快,。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多