STM 32のシリアルDMA受信不定長データ

10107 ワード

STM 32のシリアルDMA受信不定長データ
引用する
stm 32や他のチップマシンを使用する場合、シリアル通信がよく使われますが、データを効率的に受信するにはどうすればいいのでしょうか.もしこのデータが不定長であれば、どのように効率的に受信しますか?
クラスメートA:データが来たらシリアルに入って中断して、中断の中でデータを読めばいいんだよ.
中断はプログラムの正常な運行を中断することであり、どのように効率を保証することができますか?よくメインプログラムを中断しますが、メインプログラムはまだ実行しませんか?
学友B:シリアルポートはDMAの方式でデータを受信するように配置することができて、受信が終わってから読み取ることができます!
この学友は正しいので、私达はDMAを使ってデータを受信することができて、しかしDMAは定长してようやく受信の中断を生むことができて、どのように不定长のデータを受信しますか?
DMAの概要
余談:実は、上の問題は考えなければならないので、絶えず考えてこそ、進歩することができます.
DMAとは
DMA:フルネームDirectMemory Access、すなわちダイレクトメモリアクセス
DMA転送は、あるアドレス空間から別のアドレス空間にデータをコピーする.CPUはDMAを初期化するだけでよく、転送動作自体はDMAコントローラによって実現され、完了する.典型的な例は、外部メモリのブロックをチップ内部のより速いメモリ領域に移動することである.このような操作はプロセッサに処理に参加させないで、CPUはその他の事をすることができて、DMAの伝送が完成する时1つの中断を生んで、CPUに私がすでに完成したことを教えて、それからCPUは知ってデータを処理することができて、このようにCPUの利用率を高めて、CPUが大脳なため、主にデータの演算の仕事をして、データを運ぶのではありません.DMA伝送は効率的な組み込みシステムアルゴリズムとネットワークにとって重要である.
STM 32でのDMAリソース
STM 32 F 1シリーズのMCUには2つのDMAコントローラ(DMA 2は大容量製品にのみ存在する)があり、DMA 1には7つのチャネルがあり、DMA 2には5つのチャネルがあり、各チャネルは1つ以上の周辺機器からのメモリへのアクセス要求を管理するために専用されている.各DMA要求の優先権を調整する仲裁器もある.
一方、STM 32 F 4/F 7/H 7シリーズのMCUには、2つのDMAコントローラの合計16個のデータストリーム(DMAコントローラごとに8個)があり、各DMAコントローラは、1つ以上の周辺機器のメモリアクセス要求を管理するために使用される.各データ・ストリームには、合計8つのチャネル(またはリクエスト)があります.各チャネルには、DMA要求間の優先度を処理するための調停器がある.
DMA受信データ
DMAは、データを受信するとき、シリアルポートがDMAを受信するのは初期化時にオープン状態であり、データの到来を待ち続け、ソフトウェア上で何もする必要はなく、初期化構成時に構成を設定すればよい.データを受信したら、CPUに処理するように伝えてください.
判定データ受信完了
では、問題が来て、どのようにデータが受信されたかどうかを知っていますか?
実は、いろいろな方法があります.
  • 定長のデータについては、データの受信個数を判断するだけで、受信が完了したかどうかを知ることができ、これは簡単で、しばらく議論しない.
  • 不定長のデータに対して、実はいくつかの方法があって、面倒な私はきっと紹介しません.複雑な仕事に興味のある学生はネットで他の人がどうするかを見ることができます.次の方法は最も簡単で、stm 32のシリアルリソースを十分に利用して、効率も非常に高いです.

  • DMA+シリアルポートアイドル割込み
    この2つの资源の配合、まるで天衣无缝ですね、どんな不定长のデータを受け取っても、あなたのデータがいくらあっても、私は1つを受け取って、広东の人が“山竹”を食べて、1つを食べて~(最近风が大きくて、私は恐れています).
    多くの人がstm 32を勉強している間にidleが何なのか分からないかもしれませんが、まずstm 32シリアルポートのステータスレジスタを見てみましょう.
    シリアル・バスの空き割り込みがトリガーされたことを検出すると、このデータ転送が完了したことを知っています.その後、これらのデータを取得し、処理すればいいです.この方法は最も簡単で、私たちが多くの処理をする必要はありません.配置するだけで、シリアルポートはデータの到来を待っています.dmaも動作状態にあり、1つのデータが来ると自動的に1つのデータを運びます.
    データ受信時処理
    シリアルポートがデータを受信したら処理しますが、処理の手順はどうですか?
  • シリアルポート受信DMAチャネルを一時的に閉じるには、2つの理由がある.このときのデータはまだ処理されていないので、後でデータが受信され、干渉が発生することを防止します.2.DMAは再構成が必要です.
  • DMAフラグビット.
  • は、DMAレジスタから受信したデータバイト数を取得する(あってもなくてもよい).
  • DMAが次に受信するデータバイト数を再設定し、データ転送数は0〜65535の範囲であることに注意する.このレジスタは、チャネルが動作しない(DMA_CCRxのEN=0)場合にのみ書き込むことができる.チャネルがオンになると、レジスタは読み取り専用になり、残りの転送対象バイト数を示す.レジスタ内容はDMA転送毎に減少する.データ転送が完了すると、レジスタの内容が0になる.あるいは、このチャネルが自動再ロードモードに構成されている場合、レジスタの内容は、以前の構成時の数値として自動的に再ロードされる.レジスタの内容が0の場合、チャネルが開いているかどうかにかかわらず、データ転送は発生しません.
  • は信号量を与え、フロントプログラムが照会するために受信した新しいデータフラグを送信する.
  • DMAチャネルを開き、次のデータ受信を待つ.DMAの関連レジスタに書き込みを設定し、DMA受信データ長をリセットするなど、DMAを閉じる条件で行わなければならない.そうしないと、操作は無効である.

  • 注意事項
    STM 32のIDLEの割り込みは、シリアルポートにデータ受信がない場合、ずっと発生しないことが条件であり、IDLEフラグビットをクリアした後、最初のデータを受信した後にトリガを開始しなければならず、受信したデータのストリームを切断し、データを受信していない、すなわちIDLE割り込みを発生する.データフレームの送信を中断する速度が速い場合、MCUは今回受信したデータを処理するのに間に合わず、中断してまたデータを送信すると、ここでは開くことができず、データが上書きされます.解決方法は2つあります.
  • は、DMA受信チャネルを再開する前に、Rx_Bufバッファ内のデータを別の配列にコピーし、DMAをオンにして、コピーしたデータをすぐに処理します.
  • デュアルバッファを確立し、DMAを再構成するMemoryBaseAddrのバッファアドレスは、次回受信したデータが上書きされないように新しいバッファに保存されます.

  • プログラム実装
    実験効果:外部からチップマシンにデータを送信するとき、このフレームのデータ長が1000バイトであると仮定すると、チップマシンが1バイトを受信したときにシリアルポートが中断することはなく、DMAが背後で黙々とデータを指定したバッファに運ぶだけです.フレーム全体のデータ送信が完了してからシリアルポートが1回中断する場合、DMA_GetCurrDataCounter()関数を用いて今回のデータ受信長を算出し、データ処理を行うことができる.
    シリアルポートの配置は簡単で、基本的にシリアルポートを使用するときと一致していますが、一般的には受信バッファを開いて空中切断ではありませんが、今は開いている空き中断--USART_ITConfig(DEBUG_USARTx, USART_IT_IDLE, ENABLE); です.
    /**
      * @brief  USART GPIO   ,      
      * @param   
      * @retval  
      */
    void USART_Config(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
    
        //     GPIO   
        DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
        
        //          
        DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
    
        //  USART Tx GPIO         
        GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
    
      //  USART Rx GPIO         
        GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
        
        //          
        //      
        USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
        //         
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        //      
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
        //      
        USART_InitStructure.USART_Parity = USART_Parity_No ;
        //        
        USART_InitStructure.USART_HardwareFlowControl = 
        USART_HardwareFlowControl_None;
        //       ,    
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
        //           
        USART_Init(DEBUG_USARTx, &USART_InitStructure);
        //          
        NVIC_Configuration();
        
    #if USE_USART_DMA_RX 
        //        IDEL   
        USART_ITConfig(DEBUG_USARTx, USART_IT_IDLE, ENABLE);  
      //     DMA  
        USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Rx, ENABLE); 
        /*     DMA */
        USARTx_DMA_Rx_Config();
    #else
        //         
        USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);    
    #endif
    
    #if USE_USART_DMA_TX 
        //     DMA  
    //    USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE); 
        USARTx_DMA_Tx_Config();
    #endif
    
        //     
        USART_Cmd(DEBUG_USARTx, ENABLE);        
    }

    シリアルDMA構成
    DMAの配置が完了すると、DMAを直接開いて作業状態にすることができ、データがあるときに直接運ぶことができます.
    #if USE_USART_DMA_RX 
    
    static void USARTx_DMA_Rx_Config(void)
    {
        DMA_InitTypeDef DMA_InitStructure;
    
        //   DMA  
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
        //   DMA   :         */
        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDRESS;
        //     (         )
        DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Usart_Rx_Buf;
        //   :          
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
        //         
        DMA_InitStructure.DMA_BufferSize = USART_RX_BUFF_SIZE;
        //               
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
        //       
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
        //           
        DMA_InitStructure.DMA_PeripheralDataSize = 
        DMA_PeripheralDataSize_Byte;
        //       
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     
        // DMA  ,        
        //DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
        DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;    
        //    :     
        DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 
        //           
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
        //   DMA             
        DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure);        
        //   DMA    
        DMA_ClearFlag(DMA1_FLAG_TC5);
        DMA_ITConfig(USART_RX_DMA_CHANNEL, DMA_IT_TE, ENABLE);
        //   DMA
        DMA_Cmd (USART_RX_DMA_CHANNEL,ENABLE);
    }
    #endif

    受信済みデータ処理
    データを受信した後、idle割り込み、すなわち空き割り込みが発生するため、割り込みサービス関数で受信が完了したことを知ることができ、データを処理することができますが、割り込みサービス関数のコンテキスト環境は割り込みなので、できるだけ早く進み、早く出て、一般的に割り込みの中でいくつかのフラグをフロントクエリに配置します.割り込みでは、割り込みのタイプがidle割り込みであるかどうかを判断し、そうであれば次のステップに進みます.そうでなければ、気にする必要はありません.
    /**
      ******************************************************************
      * @brief           
      * @author  jiejie
      * @version V1.0
      * @date    2018-xx-xx
      ******************************************************************
      */ 
    void DEBUG_USART_IRQHandler(void)
    {
    #if USE_USART_DMA_RX
        /*     DMA */
        if(USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE)!=RESET)
        {        
            /*      */
            Receive_DataPack();
            //          
            USART_ReceiveData( DEBUG_USARTx );
        }    
    #else
      /*      */
        if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
        {        
        Receive_DataPack();
        }
    #endif
    }

    Receive_DataPack()
    これこそ本当の受信データ処理関数ですが、なぜ私はこの関数を単独でカプセル化しますか?この関数は実はとても重要なので、私のコードは普通のシリアルポートの受信と空き割り込みに互換性があるため、異なる受信タイプはその処理も異なるので、直接カプセル化するのがもっと良くて、ソースコードの中でマクロの定義を通じて受信の方式を選択することを実現します!さらに互換性のあるオペレーティングシステムを考慮すると、dma+アイドル割り込みをシステムで使用する可能性があるので、フロントクエリ用の信号量が異なる可能性があり、修正が必要な可能性があるので、カプセル化しました.でもどうでもいい、同じです.
    /************************************************************
      * @brief   Uart_DMA_Rx_Data
      * @param   NULL
      * @return  NULL
      * @author  jiejie
      * @github  https://github.com/jiejieTop
      * @date    2018-xx-xx
      * @version v1.0
      * @note         DMA         
      ***********************************************************/
    #if USE_USART_DMA_RX
    void Receive_DataPack(void)
    {
        /*         */
        uint32_t buff_length;
        
        /*   DMA ,     */
        DMA_Cmd(USART_RX_DMA_CHANNEL, DISABLE);  /*     dma,       */ 
        
        /*  DMA    */
        DMA_ClearFlag( DMA1_FLAG_TC5 );  
        
        /*                 */
        buff_length = USART_RX_BUFF_SIZE - DMA_GetCurrDataCounter(USART_RX_DMA_CHANNEL);
      
        /*        */
        Usart_Rx_Sta = buff_length;
    
        PRINT_DEBUG("buff_length = %d
    ",buff_length); /* , */ USART_RX_DMA_CHANNEL->CNDTR = USART_RX_BUFF_SIZE; /* , DataPack_Process() */ DMA_Cmd(USART_RX_DMA_CHANNEL, ENABLE); /* (OS) , , */ /* , DataPack_Handle */ Usart_Rx_Sta |= 0xC000; /* DMA , 。 , ,MCU , , , 。 2 : 1. DMA , Rx_Buf , DMA, 。 2. , DMA_MemoryBaseAddr , , 。 */ }

    f 1はdmaを使うのはとても簡単で、私はf 4がdmaを使う時もいくつかの問題に出会って、最後にマニュアルを見て解決して、次の文章はデバッグの過程を書くつもりで、debugが解決できないことは何もなくて、もしあれば、それは2回です.今日の台风の天気、友达のWiFiの更新の文章につながっています~中国の电信はやはり强くて、台风の天気の信号は少しも虚しくなくて、私のモバイルカードは动きません--.
    好きなら私に注目してください.
    関連コードは、公衆番号のバックグラウンドで返信して取得できます.
    「IoT開発」公式アカウントへようこそ