久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 2162|回復(fù): 0
收起左側(cè)

單片機(jī)軟件擴(kuò)展的多串口數(shù)據(jù)轉(zhuǎn)發(fā)模型

[復(fù)制鏈接]
ID:505284 發(fā)表于 2021-10-10 17:47 | 顯示全部樓層 |閱讀模式
實(shí)用型軟件架構(gòu)
==============
多串口數(shù)據(jù)交互模型
-------------------

實(shí)際需求
~~~~~~~~
最近在做一個(gè)西門(mén)子 ``Step 200`` 系列的PLC通訊口擴(kuò)展項(xiàng)目時(shí),遇到了這樣的問(wèn)題:
``224XP`` ,這個(gè)CPU的外部通訊端口只用兩個(gè),在物聯(lián)網(wǎng)大火的當(dāng)下,這樣的擴(kuò)展口數(shù)量,在加入聯(lián)網(wǎng)模塊后,顯然無(wú)法滿足更多的
聯(lián)網(wǎng)需求。當(dāng)前實(shí)際需求如下:

.. csv-table:: **通訊口對(duì)應(yīng)功能**
   :header: "編號(hào)", "功能"
   :widths: 5, 10
   :align: center

   01, "PLC串口屏通訊"
   02, "EBM風(fēng)扇通訊"
   03, "4G/WIFI模塊通訊"
   04, "以太網(wǎng)通訊"
51hei.png
在考慮到成本與技術(shù)可行性前提下,盡可能保留產(chǎn)品研發(fā)核心技術(shù)手段,選用STC8系列單片機(jī)對(duì)PLC原有的兩個(gè)通訊口
利用串口進(jìn)行擴(kuò)展。設(shè)計(jì)思路如下:

.. figure:: mode1.png
    :align: center
    :alt: NULL
    :scale: 70%

    圖 4.1 理論中多串口數(shù)據(jù)交互模型

從圖中可以看出,數(shù)據(jù)信息的主要請(qǐng)求目標(biāo)主要是通過(guò) ``PLC_PORT0`` 獲得PLC內(nèi)部存儲(chǔ)區(qū)數(shù)據(jù)( ``PLC_PORT1`` 默認(rèn)用于連接屏幕)。
因此,進(jìn)行軟件拓展的目標(biāo)物理鏈路就是 ``PLC_PORT0`` 。

矛盾的產(chǎn)生
~~~~~~~~~~

從上面的模型可以看出,當(dāng)前工作模式應(yīng)該是一個(gè)多主單從結(jié)構(gòu)。那么按照常理應(yīng)該是由STC8的4個(gè)串口通過(guò)輪詢的方式對(duì)共享設(shè)備PLC
目標(biāo)地址發(fā)出數(shù)據(jù)請(qǐng)求的命令,隨后由PLC把響應(yīng)數(shù)據(jù)返回給當(dāng)前請(qǐng)求對(duì)象。如果嚴(yán)格遵循這樣的工作模式,不會(huì)存在任何問(wèn)題。
但是,實(shí)際的架構(gòu)設(shè)計(jì)需求如下:
51hei.png
.. figure:: mode2.png
    :align: center
    :alt: NULL
    :scale: 70%

    圖 4.2 實(shí)際的多串口數(shù)據(jù)交互模型

.. attention::
    其中每個(gè)通訊端口上端的標(biāo)號(hào)都代表在實(shí)際的通訊過(guò)程中,STC8單片機(jī)作為擴(kuò)展主機(jī)時(shí)輪詢框架下的調(diào)度關(guān)系(數(shù)字越小,優(yōu)先級(jí)越高;數(shù)字相等,代表處于同一優(yōu)先級(jí))。

這里實(shí)際使用的時(shí)候是通過(guò) ``PLC_PORT0`` 與 ``STC8_UART4`` 進(jìn)行物理上的連接,在通過(guò)STC8內(nèi)部軟件協(xié)議通過(guò)其他串口與拓展設(shè)備
進(jìn)行數(shù)據(jù)交互。很顯然當(dāng)前的架構(gòu)無(wú)法滿足這樣的實(shí)際需求,矛盾就應(yīng)運(yùn)而生了。

.. note::
    既然多主機(jī),單從機(jī)的通訊模型無(wú)法在PLC作為主機(jī)時(shí)滿足需求,那么就可以重新考慮另外一種工作模式。為了適應(yīng)更多可能的情況,
    建立一種不分主從結(jié)構(gòu)的工作模式,在多對(duì)象數(shù)據(jù)交互的基礎(chǔ)上建立一種相對(duì)是一對(duì)一的通訊機(jī)制。

.. figure:: mode3.png
    :align: center
    :alt: NULL
    :scale: 70%
51hei.png
    圖 4.2 改進(jìn)后的多串口數(shù)據(jù)交互模型

軟件設(shè)計(jì)思想
~~~~~~~~~~~~~~
51hei.png
.. figure:: F0.png
    :align: center
    :alt: NULL
    :scale: 70%

    圖 4.3 基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

.. note::
    從圖中可以看出,最上層采用的是循環(huán)隊(duì)列,每個(gè)隊(duì)列的元素由一條鏈表進(jìn)行連接,每條鏈表的一個(gè)節(jié)點(diǎn)代表一幀數(shù)據(jù)。

.. csv-table:: **單節(jié)點(diǎn)上成員描述**
   :header: "標(biāo)識(shí)符", "意義"
   :widths: 8, 20
   :align: center

   "Frame_Flag", "幀標(biāo)志:由定時(shí)器幀中斷機(jī)制置為true;輪詢轉(zhuǎn)發(fā)程序轉(zhuǎn)發(fā)當(dāng)前幀后置為false"
   "Timer_Flag", "幀中斷定時(shí)器開(kāi)啟標(biāo)志:當(dāng)任意串口接收中斷收到一個(gè)字節(jié)數(shù)據(jù)時(shí)設(shè)置為true;超時(shí)后設(shè)置false"
   "Rx_Buffer", "數(shù)據(jù)幀接收緩沖區(qū)"
   "Rx_Length", "當(dāng)前數(shù)據(jù)幀長(zhǎng)度"
   "OverTime", "幀判定時(shí)間:該變量在串口中斷有字節(jié)數(shù)據(jù)接收時(shí)會(huì)不斷刷新;在幀仲裁定時(shí)器中其值不斷減小至0"

**詳細(xì)工作原理:**
以PLC通過(guò)485總線發(fā)送數(shù)據(jù)為例,假設(shè)PLC當(dāng)前要像EBM請(qǐng)求某一個(gè)狀態(tài)值,發(fā)出一幀數(shù)據(jù) ``15 21 01 CA``,此時(shí)EBM響應(yīng)數(shù)據(jù)為 ``35 01 01 00 CA`` ,則:

1、串口四接收中斷收到PLC發(fā)出的第一個(gè)字節(jié),打開(kāi)幀中斷定時(shí)器,判斷當(dāng)前寫(xiě)指針?biāo)鶎?duì)應(yīng)的鏈表節(jié)點(diǎn)幀標(biāo)志是否為false,條件成立后判斷當(dāng)前節(jié)點(diǎn)幀長(zhǎng)度是否溢出,
如果沒(méi)有就刷新當(dāng)前幀鏈表塊中 ``OverTime`` , 最后把當(dāng)前字節(jié) ``15`` 存到當(dāng)前幀緩沖區(qū) ``Rx_Buffer`` 的位置上。

2、后續(xù)字符 ``21 01 CA`` 的接收操作與第一個(gè)字符一致,其中每個(gè)字節(jié)間間隔由通訊的波特率決定,*<<Timer(OverTime)* ,當(dāng)接收完這一幀數(shù)據(jù)后,``OverTime``
值將不會(huì)在串口接收中斷中被刷新,而是由幀中斷定時(shí)器中不斷減小為0,最終標(biāo)志該節(jié)點(diǎn)上這幀數(shù)據(jù)接收完成,并把對(duì)應(yīng)的 ``Frame_Flag``
置為true。

3、在主程序輪詢機(jī)制中,一旦檢測(cè)到有 ``Frame_Flag`` 產(chǎn)生,則利用讀指針訪問(wèn)當(dāng)前節(jié)點(diǎn)幀緩沖區(qū),對(duì)目標(biāo)設(shè)備發(fā)出請(qǐng)求命令。

4、響應(yīng)數(shù)據(jù)返回給目標(biāo)對(duì)象的工作過(guò)程與前三個(gè)步驟完全一致。值得注意的是,入果存在對(duì)個(gè)數(shù)據(jù)交換序列(:menuselection:`PLC_PORT0-->UART4-->UART3` 和 :menuselection:`UART2-->UART4-->PLC_PORT0` ,
存在相反的公共序列 :menuselection:`PLC_PORT0-->UART4` , :menuselection:`UART4-->PLC_PORT0`),此時(shí)如果公用的是同一個(gè)緩沖區(qū),且不對(duì)不同類型的數(shù)據(jù)進(jìn)行分流,將會(huì)造成不同請(qǐng)求對(duì)象數(shù)據(jù)響應(yīng)錯(cuò)誤,
所以必須加以條件限制。

建立數(shù)據(jù)結(jié)構(gòu)
~~~~~~~~~~~~~~

.. code-block:: c
   :caption: 1.0.0 基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
   :linenos:
   :emphasize-lines: 3,5

    /*鏈隊(duì)數(shù)據(jù)結(jié)構(gòu)*/
    typedef struct
    {
    uint8_t Frame_Flag;             /*幀標(biāo)志*/
        uint8_t Timer_Flag;             /*打開(kāi)定時(shí)器標(biāo)志*/
        uint8_t Rx_Buffer[MAX_SIZE];    /*數(shù)據(jù)接收緩沖區(qū)*/
        uint16_t Rx_Length;             /*數(shù)據(jù)接收長(zhǎng)度*/
        uint16_t OverTime;              /*目標(biāo)設(shè)備響應(yīng)超時(shí)時(shí)間*/
    }Uart_Queu;

    typedef struct
    {
    Uart_Queu LNode[MAX_NODE];
        /*存儲(chǔ)R ,W指針,表示一個(gè)隊(duì)列*/
        uint8_t Wptr;
        uint8_t Rptr;
    }Uart_List;

    /*聲明鏈隊(duì)*/
    extern Uart_List Uart_LinkList[MAX_LQUEUE];

.. note::
    頂層數(shù)據(jù)結(jié)構(gòu)采用環(huán)形隊(duì)列,只不過(guò)隊(duì)列中的單個(gè)元素并不是一個(gè)單一的值,而是一個(gè)帶有記錄信息的數(shù)據(jù)塊 ``Uart_Queu`` 。
    這樣做的目的在于,使用的單片機(jī)是C51,其本身的串口是不帶有空閑中斷或者DMA這些高級(jí)硬件的,那這就需要我們通過(guò)軟件算法模擬這一些硬件功能
    來(lái)完成功能設(shè)計(jì)。

.. code-block:: c
   :caption: 1.0.1 改進(jìn)后基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
   :linenos:
   :emphasize-lines: 3,5
        
    /*鏈隊(duì)數(shù)據(jù)結(jié)構(gòu)*/
    typedef struct
    {
    uint8_t Frame_Flag;             /*幀標(biāo)志*/
        uint8_t Timer_Flag;             /*打開(kāi)定時(shí)器標(biāo)志*/
        uint8_t Rx_Buffer[MAX_SIZE];    /*數(shù)據(jù)接收緩沖區(qū)*/
        uint16_t Rx_Length;             /*數(shù)據(jù)接收長(zhǎng)度*/
        uint16_t OverTime;              /*目標(biāo)設(shè)備響應(yīng)超時(shí)時(shí)間*/
        Uart_Queu *Next;                /*指向下一個(gè)節(jié)點(diǎn)*/
    }Uart_Queu;

.. note::
    主要改進(jìn)了隊(duì)列下數(shù)據(jù)塊元素的內(nèi)存分配方式,由原來(lái)的靜態(tài)的分配,改為程序運(yùn)行過(guò)程根據(jù)實(shí)際需求來(lái)分配。考慮 ``Malloc`` 函數(shù)在
    51編譯器中安全性和適用性,實(shí)際使用過(guò)程建議非必要情況采用靜態(tài)內(nèi)存分配方式。當(dāng)然,采用動(dòng)態(tài)內(nèi)存分配方式,使用循環(huán)鏈表將會(huì)帶來(lái)更多的
    可操作性、靈活性和內(nèi)存節(jié)約。

.. code-block:: c
   :caption: 1.0.2 串口幀中斷機(jī)制設(shè)計(jì)
   :linenos:
   :emphasize-lines: 3,5

   /**
    * @brief    定時(shí)器0的中斷服務(wù)函數(shù)
    * @details  
    * @param    None
    * @retval   None
    */
    void Timer0_ISR() interrupt 1
    {

        if(COM_UART1.LNode[COM_UART1.Wptr].Timer_Flag)
            /*以太網(wǎng)串口接收字符間隔超時(shí)處理*/
            SET_FRAME(COM_UART1);
        if(COM_UART2.LNode[COM_UART2.Wptr].Timer_Flag)
            /*4G/WiFi串口接收字符間隔超時(shí)處理*/
            SET_FRAME(COM_UART2);
        if(COM_UART3.LNode[COM_UART3.Wptr].Timer_Flag)
            /*RS485串口接收字符間隔超時(shí)處理*/
            SET_FRAME(COM_UART3);
        if(COM_UART4.LNode[COM_UART4.Wptr].Timer_Flag)
            /*PLC串口接收字符間隔超時(shí)處理*/
            SET_FRAME(COM_UART4);
    }

    /**
    * @brief    串口4中斷函數(shù)
    * @details  使用的是定時(shí)器4作為波特率發(fā)生器,PLC口用
    * @param    None
    * @retval   None
    */
    void Uart4_Isr() interrupt 18
    {   /*發(fā)送中斷*/
        if (S4CON & S4TI)
        {
            S4CON &= ~S4TI;
            /*發(fā)送完成,清除占用*/
            Uart4.Uartx_busy = false;
        }
        /*接收中斷*/
        if (S4CON & S4RI)
        {
            S4CON &= ~S4RI;

            /*當(dāng)收到數(shù)據(jù)時(shí)打開(kāi)幀中斷定時(shí)器*/
            COM_UART4.LNode[COM_UART4.Wptr].Timer_Flag = true;
            /*當(dāng)前節(jié)點(diǎn)還沒(méi)有收到一幀數(shù)據(jù)*/
            if (!COM_UART4.LNode[COM_UART4.Wptr].Frame_Flag)
            {
                /*刷新幀超時(shí)時(shí)間*/
                COM_UART4.LNode[COM_UART4.Wptr].OverTime = MAX_SILENCE;
                if (COM_UART4.LNode[COM_UART4.Wptr].Rx_Length < MAX_SIZE)
                { /*把數(shù)據(jù)存到當(dāng)前節(jié)點(diǎn)的緩沖區(qū)*/
                    COM_UART4.LNode[COM_UART4.Wptr].Rx_Buffer[COM_UART4.LNode[COM_UART4.Wptr].Rx_Length++] = S4BUF;
                }
            }
        }
    }

.. note::
    因?yàn)橛布〞r(shí)器數(shù)量有限,所以幾個(gè)串口的幀中斷機(jī)定時(shí)器均采用了 ``Timer0`` 進(jìn)行仲裁,可能會(huì)存在中斷延時(shí)的問(wèn)題,在硬件定時(shí)器資源充足情況下,盡可能選用硬件定時(shí)器較佳。

.. code-block:: c
   :caption: 1.0.3 幀中斷宏
   :linenos:
   :emphasize-lines: 3,5

    /*置位目標(biāo)串口接收幀標(biāo)志*/
    #define SET_FRAME(COM_UARTx) (COM_UARTx.LNode[COM_UARTx.Wptr].OverTime ? \
    (COM_UARTx.LNode[COM_UARTx.Wptr].OverTime--): \
    ((COM_UARTx.LNode[COM_UARTx.Wptr].Frame_Flag = true), \
    (COM_UARTx.Wptr = ((COM_UARTx.Wptr + 1U) % MAX_NODE)), \
    (COM_UARTx.LNode[COM_UARTx.Wptr].Timer_Flag = false)))

最后,有了這些軟件機(jī)制,僅僅只需要編寫(xiě)對(duì)應(yīng)的邏輯就可以了。

.. code-block:: c
   :caption: 1.0.4 多串口數(shù)據(jù)輪詢處理機(jī)制
   :linenos:
   :emphasize-lines: 3,5

    /*設(shè)置隊(duì)列讀指針*/
    #define SET_RPTR(x) ((COM_UART##x).Rptr = (((COM_UART##x).Rptr + 1U) % MAX_NODE))                  
    /*設(shè)置隊(duì)列寫(xiě)指針*/
    #define SET_WPTR(x) ((COM_UART##x).Wptr = (((COM_UART##x).Wptr + 1U) % MAX_NODE))

    /*串口一對(duì)一數(shù)據(jù)轉(zhuǎn)發(fā)數(shù)據(jù)結(jié)構(gòu)*/
    typedef struct
    {
        SEL_CHANNEL Source_Channel; /*數(shù)據(jù)起源通道*/
        SEL_CHANNEL Target_Channel; /*數(shù)據(jù)交付通道*/
        void (*pHandle)(void);
    } ComData_Handle;

    /*定義當(dāng)前串口交換序列*/
    const ComData_Handle ComData_Array[] =
    {
        {CHANNEL_PLC, CHANNEL_RS485, Plc_To_Rs485},
        {CHANNEL_WIFI, CHANNEL_PLC, Wifi_To_Plc},
    };

    /*增加映射關(guān)系時(shí),計(jì)算出當(dāng)前關(guān)系數(shù)*/
    #define COMDATA_SIZE (sizeof(ComData_Array) / sizeof(ComData_Handle))

    /**
    * @brief    串口1對(duì)1數(shù)據(jù)轉(zhuǎn)發(fā)
    * @details  
    * @param    None
    * @retval   None
    */
    void Uart_DataForward(SEL_CHANNEL Src, SEL_CHANNEL Dest)
    {
        uint8_t i = 0;

        for (i = 0; i < COMDATA_SIZE; i++)
        {
            if ((Src == ComData_Array[ i].Source_Channel) && (Dest == ComData_Array[ i].Target_Channel))[ i][ i]
            {
            ComData_Array[ i].pHandle();[ i]
            }
        }
    }

    /**
    * @brief    串口事件處理
    * @details  
    * @param    None
    * @retval   None
    */
    void Uart_Handle(void)
    {
        /*數(shù)據(jù)交換序列1:PLC與RS485進(jìn)行數(shù)據(jù)交換*/
        Uart_DataForward(CHANNEL_PLC, CHANNEL_RS485);
        /*數(shù)據(jù)交換序列2:WIFI與PLC進(jìn)行數(shù)據(jù)交換*/
        Uart_DataForward(CHANNEL_WIFI, CHANNEL_PLC);
    }

    /**
    * @brief    PLC數(shù)據(jù)交付到RS485
    * @details  
    * @param    None
    * @retval   None
    */
    void Plc_To_Rs485(void)
    {
        /*STC串口4收到PLC發(fā)出的數(shù)據(jù)*/
        if ((COM_UART4.LNode[COM_UART4.Rptr].Frame_Flag)) //&& (COM_UART4.LNode[COM_UART4.Rptr].Rx_Length)
        {                                                
            /*如果串口4接收到的數(shù)據(jù)幀不是EBM所需的,過(guò)濾掉*/
            if (COM_UART4.LNode[COM_UART4.Rptr].Rx_Buffer[0] != MODBUS_SLAVEADDR)
            {   /*標(biāo)記該接收幀以進(jìn)行處理*/
                COM_UART4.LNode[COM_UART4.Rptr].Frame_Flag = false;
                /*允許485發(fā)送*/
                USART3_EN = 1;
                /*數(shù)據(jù)轉(zhuǎn)發(fā)給RS485時(shí),數(shù)據(jù)長(zhǎng)度+1,可以保證MAX3485芯片能夠最后一位數(shù)據(jù)剛好不停止在串口的停止位上*/
                Uartx_SendStr(&Uart3, COM_UART4.LNode[COM_UART4.Rptr].Rx_Buffer, COM_UART4.LNode[COM_UART4.Rptr].Rx_Length + 1U);
                /*接收到數(shù)據(jù)長(zhǎng)度置為0*/
                COM_UART4.LNode[COM_UART4.Rptr].Rx_Length = 0;
                /*發(fā)送中斷結(jié)束后,清空對(duì)應(yīng)接收緩沖區(qū)*/
                memset(&COM_UART4.LNode[COM_UART4.Rptr].Rx_Buffer[0], 0, MAX_SIZE);
                /*發(fā)送完一幀數(shù)據(jù)后拉低*/
                USART3_EN = 0;
                /*讀指針指到下一個(gè)節(jié)點(diǎn)*/
                SET_RPTR(4);
            }

            /*目標(biāo)設(shè)備發(fā)出應(yīng)答*/
            if ((COM_UART3.LNode[COM_UART3.Rptr].Frame_Flag)) //&& (COM_UART3.LNode[COM_UART3.Rptr].Rx_Length)
            {
                /*標(biāo)記該接收幀已經(jīng)進(jìn)行處理*/
                COM_UART3.LNode[COM_UART3.Rptr].Frame_Flag = false;
                /*數(shù)據(jù)返回給請(qǐng)求對(duì)象*/
                Uartx_SendStr(&Uart4, COM_UART3.LNode[COM_UART3.Rptr].Rx_Buffer, COM_UART3.LNode[COM_UART3.Rptr].Rx_Length);
                /*接收到數(shù)據(jù)長(zhǎng)度置為0*/
                COM_UART3.LNode[COM_UART3.Rptr].Rx_Length = 0;
                /*發(fā)送中斷結(jié)束后,清空對(duì)應(yīng)接收緩沖區(qū)*/
                memset(&COM_UART3.LNode[COM_UART3.Rptr].Rx_Buffer[0], 0, MAX_SIZE);
                /*讀指針指到下一個(gè)節(jié)點(diǎn)*/
                SET_RPTR(3);
            }
        }
    }

以上圖文的pdf格式文檔下載(內(nèi)容和本網(wǎng)頁(yè)上的一模一樣,方便保存): sphinx.pdf (427.3 KB, 下載次數(shù): 6)

評(píng)分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 国产做a爱免费视频 | 久久青草av | 精品不卡 | 日本天天操 | 亚洲一区二区三区在线视频 | 在线观看中文字幕一区二区 | 91传媒在线播放 | 国产精品日韩 | 精品久久99| а√中文在线8 | 特黄特黄a级毛片免费专区 av网站免费在线观看 | 亚洲最大福利网 | 亚洲欧美激情四射 | 四虎影视免费在线 | 亚洲国产一区二区三区, | 日本午夜视频 | 毛片免费观看视频 | 中文字幕免费中文 | 在线国产一区 | 国产成人99久久亚洲综合精品 | 欧美一区2区三区4区公司 | 亚洲 欧美 另类 综合 偷拍 | 欧美激情综合 | 午夜天堂 | 国产一区二区三区久久久久久久久 | 国产一区二区视频免费在线观看 | 偷拍亚洲色图 | 九九久久精品视频 | 狠狠干影院 | 国产一二三区在线 | 中文字幕精品一区二区三区精品 | 欧美在线一区二区三区 | 久久欧美高清二区三区 | 理论片87福利理论电影 | www.天天操.com | 久久精品一 | 亚洲欧美日韩中文在线 | wwww.xxxx免费 | 在线观看的av| 欧美中文字幕在线观看 | 91亚洲国产|