يكتب الطلاب سائق Uart لـ STM32F411


صحة جيدة للجميع!


اليوم أود أن أخبركم كيف يتعلم الطلاب تدريجيًا تطوير برنامج للمتحكم الدقيق باستخدام برنامج تشغيل UART على STM32F411 كمثال. سأحاول أن أعطي الكود والهندسة المعمارية مع التغييرات والتحسينات الصغيرة هنا.


, , :) ( , , - , ), . , , .


, , Uart ++.



:




, UART .



— UART, , .



, :



:


  • WriteData(const uint8_t *pData, uint8_t size)
  • ReadData(uint8_t size)

:


  • OnTransmit() — UART
  • OnTransmitComplete() — UART
  • OnReceive() — UART

. 2 :


  • UartDriverTransmitCompleteObserversOnTransmitComplete() OnTransmitComplete()

UartDriverTransmitCompleteObservers
template<typename ...Observers>
struct UartDriverTransmitCompleteObservers
{
  __forceinline static void OnWriteComplete() 
  {
    (Observers::OnTransmitComplete(), ...) ;
  }
};

  • UartDriverReceiveObserversOnReceiveComplete() OnReceiveComplete()

UartDriverReceiveObservers
template<typename ...Observers>
struct UartDriverReceiveCompleteObservers
{
  __forceinline static void OnReadComplete(tBuffer& buffer, std::size_t bytesReceived)
  {
    (Observers::OnReceiveComplete(buffer, bytesReceived), ...) ;
  }
};

, OnReceiveComplete()
OnTransmitComplete(). SomeProtocol OtherObserver.



WriteData()


, . :


:
  • ( , ( ), )
  • ,
  • UART
  • 1,

:


static void WriteData(const std::uint8_t *pData, std::uint8_t bytesTosend)
  {
    assert(bytesTosend < txRxBuffer.size()) ;
    const CriticalSection cs;
   // ,        . 
   // ..        
    if ((status != Status::Write) && (status != Status::Read))    
    {

      bufferIndex = 0U;
      bufferSize = bytesTosend;
      std::memcpy(txRxBuffer.data(), pData, static_cast<std::size_t>(bytesTosend));

      Uart::WriteByte(txRxBuffer[bufferIndex]);
      bufferIndex++;
      //  ,   
      status = Status::Write;
      Uart::StartTransmit();
    }
  }

OnTransmit()


, , ( ) UART OnTransmit() UartDriver . OnTransmit() — .


__forceinline static void OnTransmit()
  {
    //      ( )
    if(bufferIndex < bufferSize)    
    {
      Uart::WriteByte(txRxBuffer[bufferIndex]) ;
      bufferIndex ++ ;
    }  else    
    {
     //   ,      
     // ,        
      Uart::EnableTcInterrupt() ;
    }
  };

OnTransmitComplete()


UART : , (.. ), , , .


OnTransmitComplete() :
  • ,
  • OnTransmitComplete(), UartDriverTransmitCompleteObservers


static void OnTransmitComplete()   {
    bufferIndex = 0U;
    bufferSize = 0U;

    Uart::DisableTcInterrupt();
    Uart::DisableTxInterrupt() ;
    Uart::DisableTransmit();

    status = Status::WriteComplete;
   //    ,   
    UartDriverTransmitCompleteObservers::OnWriteComplete() ;
  }

ReadData()


.


:
  • ,

:


static auto ReadData(std::uint8_t size)  {
    assert(size < txRxBuffer.size()) ;
    const CriticalSection cs;
   //  ,         . 
   // ..        
    if ((status != Status::Write) && (status != Status::Read))
    {
      Uart::DisableTcInterrupt();
      Uart::DisableTxInterrupt();
      Uart::DisableTransmit();

      bufferIndex = 0U;      
      bufferSize = size;
        //  
      status = Status::Read;

      Uart::EnableReceive();
      Uart::EnableRxInterrupt();
    }

OnReceive()


UART , . , , .


 static void OnReceive()  
 {
    txRxBuffer[bufferIndex] = Uart::ReadByte() ;
    bufferIndex ++ ;
    if (bufferIndex == bufferSize)    
    {   
      status = Status::ReadComplete ;
      const auto length = bufferIndex ;
      bufferIndex  = 0U;   

      UartDriverReceiveObservers::OnReadComplete(txRxBuffer, bufferIndex) ;
    }
  }



uartdriver.hpp
#ifndef REGISTERS_UARTDRIVER_HPP
#define REGISTERS_UARTDRIVER_HPP

#include "susudefs.hpp" //for __forceinline
#include "hardwareuarttx.hpp" // for HardwareUartTx
#include "hardwareuarttc.hpp" //for HardwareUartTc
#include "hardwareuartrx.hpp" // for HardwareUartRx
#include <cstring> // for memcpy
#include "criticalsectionconfig.hpp" // for CriticalSection
#include "uartdriverconfig.hpp" // for tBuffer

template<typename UartModule, typename  UartDriverTransmitCompleteObservers, typename UartDriverReceiveObservers>
struct UartDriver
{
  using Uart = UartModule ;

  enum class Status: std::uint8_t
  {
    None  = 0,
    Write = 1,
    WriteComplete = 2,
    Read = 3,
    ReadComplete = 4
  } ;

  static void WriteData(const std::uint8_t *pData, std::uint8_t bytesTosend)
  {
    assert(bytesTosend < txRxBuffer.size()) ;
    const CriticalSection cs;
    if ((status != Status::Write) && (status != Status::Read))
    {

      bufferIndex = 0U;
      bufferSize = bytesTosend;
      std::memcpy(txRxBuffer.data(), pData, static_cast<std::size_t>(bytesTosend));

      Uart::WriteByte(txRxBuffer[bufferIndex]);
      bufferIndex++;

      status = Status::Write;
      Uart::StartTransmit();
      //   ,    
      if constexpr (!std::is_base_of<UartTxInterruptable, typename Uart::Base>::value)
      {
        for(; bufferIndex < bytesTosend; ++bufferIndex)
        {
          while (!Uart::IsDataRegisterEmpty())
          {

          }
          Uart::WriteByte(txRxBuffer[bufferIndex]);          
        }
        while (!Uart::IsTransmitComplete())
        {

        }

        status = Status::WriteComplete ;
        UartDriverTransmitCompleteObservers::OnWriteComplete() ;
      } else
      {

      }
    }
  }

  __forceinline static void OnTransmit()
  {
    if(bufferIndex < bufferSize)
    {
      Uart::WriteByte(txRxBuffer[bufferIndex]) ;
      bufferIndex ++ ;
    }
    else
    {
      Uart::EnableTcInterrupt() ;
    }
  };

  static void OnTransmitComplete()
  {
    bufferIndex = 0U;
    bufferSize = 0U; 

    status = Status::WriteComplete;
    Uart::DisableTcInterrupt();
    Uart::DisableTxInterrupt() ;
    Uart::DisableTransmit();

    UartDriverTransmitCompleteObservers::OnWriteComplete() ; 

  }

  static auto ReadData(std::uint8_t size)
  {
    assert(size < txRxBuffer.size()) ;
    const CriticalSection cs;
    if ((status != Status::Write) && (status != Status::Read))
    {
      Uart::DisableTcInterrupt();
      Uart::DisableTxInterrupt();
      Uart::DisableTransmit();

      bufferIndex = 0U;      
      bufferSize = size;
      status = Status::Read;

      Uart::EnableRxInterrupt();
      Uart::EnableReceive();

    }

  }

  static void OnReceive()
  {
    txRxBuffer[bufferIndex] = Uart::ReadByte() ;
    bufferIndex ++ ;
    if (bufferIndex == bufferSize)
    {   
      status = Status::ReadComplete ;
      const auto length = bufferIndex ;
      bufferIndex = 0 ;
      UartDriverReceiveObservers::OnReadComplete(txRxBuffer, static_cast<std::size_t>(length)) ;      
    }
  }

  static Status GetStatus()
  {
    return status ;
  }

  static void ResetAll()
  {
    Uart::DisableTcInterrupt();
    Uart::DisableTxInterrupt();
    Uart::DisableTransmit();

    Uart::DisableReceive();
    Uart::DisableRxInterrupt() ;

    bufferIndex = 0U;
    bufferSize = 0U;
    status = Status::None;    
  }

  friend UartDriver& operator<<(UartDriver &rOs, const char* pString)
  {
    WriteData(reinterpret_cast<const std::uint8_t*>(pString), strlen(pString)) ;
    return rOs;
  }

  friend UartDriver& operator<<(UartDriver &rOs, float value)
  {
    WriteData(reinterpret_cast<const std::uint8_t*>(&value), sizeof(float)) ;
    return rOs;
  }

private:

  inline static  tBuffer txRxBuffer = {} ;
  inline static std::uint8_t bufferSize = 0U ;
  inline static std::uint8_t bufferIndex = 0U ;
  inline static Status status = Status::None ;

};
#endif //REGISTERS_UARTDRIVER_HPP

?


, SomeProtocol, 10 10 . — , — . 8 , .. , 10, . ( , , , )


OnTransmitComplete() OnReceiveComplete().


:
template <typename UartDriver>
struct SomeProtocol 
{
  __forceinline static void OnTransmitComplete()
  {
    //   10 ; 
    Proceed() ;
  }

  __forceinline static void OnReceiveComplete(tBuffer& buffer, std::size_t length)  
  {
     //  ,  
      assert(length <= buffer.size()) ;
    //   ,     
    if (CheckData(buffer))     
    {
      //   0  .  
      //       . ..  ,  
      //  ,  ...        
      //  .      
      cmds::ProceedCmd(buffer[0], buffer); //    .
      // 
      UartDriver::WriteData(buffer.data(), length) ;
    } else    
    {
      UartDriver::ResetAll() ;
    }
  }  
  __forceinline static void Proceed()  
  {
    //   10 .
    UartDriver::ReadData(10) ;
  }
  //  
  using cmds = CmdContainer<
      Singleton<CmdWriteSomeData>::GetInstance(),
      Singleton<CmdReadSomeData>::GetInstance()
  > ;
};

//      ,    
struct TestObserver 
{
  __forceinline static void OnTransmitComplete()  
  {
    Led1::Toggle() ;
  }
};

- UartDriver


struct MyUartDriver: UartDriver<
    //   UART
    HardwareUart,
    //  SomeProtocol   TestObserver    OnTransmitComplete()
    UartDriverTransmitCompleteObservers<SomeProtocol<MyUartDriver>, TestObserver>,
   //   SomeProtocol    OnReceiveComplete()
    UartDriverReceiveCompleteObservers<SomeProtocol<MyUartDriver>>
    > { };

using MyProtocol = SomeProtocol<MyUartDriver> ;

, . , TestObserver SomeProtocol, — SomeProtocol. UART .


:


int main()
{
  //  
   MyProtocol::Proceed() ;

  while(true)   {  } 
  return 1 ;
}

UART


, , HardwareUart UART . :



— UART , 3 :


  • HardwareUartTx — , ,
  • HardwareUartTc — , ,
  • HardwareUartRx — , ,

HandleInterrupt() HardwareUartBase,


template<typename... Modules>
struct InterruptsList
{
  __forceinline static void OnInterrupt()
  {
    //    
    (Modules::HandleInterrupt(), ...) ; 
  }
} ;

template<typename UartModule, typename InterruptsList>
struct HardwareUartBase 
{    
  static void HandleInterrupt()  
  {
    //   HardwareUartTx, HardwareUartTc, HardwareUartRx  
    //    
    InterruptsList::OnInterrupt() ;
  }
 ...
} ;

HardwareUartTx , HardwareUartRx, HardwareUartTx
template<typename UartModule, typename UartTransmitObservers>
struct HardwareUartTx 
{
  using Uart = typename UartModule::Uart ;
  static void HandleInterrupt()  
  {
   //       
    if(Uart::SR::TXE::DataRegisterEmpty::IsSet()  && 
      Uart::CR1::TXEIE::InterruptWhenTXE::IsSet())    
    {
      UartTransmitObservers::OnTxDataRegEmpty();
    }
  }
};

template<typename UartModule, typename UartReceiveObservers>
struct HardwareUartRx 
{
   using Uart = typename UartModule::Uart ;
   static void HandleInterrupt()  
   {
     //      
     if(Uart::CR1::RXNEIE::InterruptWhenRXNE::IsSet()  && 
        Uart::SR::RXNE::DataRecieved::IsSet() )   
     {
       UartReceiveObservers::OnRxData();
     }
  }
};

template<typename UartModule, typename UartTransmitCompleteObservers>
struct HardwareUartTc 
{
  using Uart = typename UartModule::Uart ;  
  static void HandleInterrupt()  
  {
    //      
    if(Uart::SR::TC::TransmitionComplete::IsSet()  && 
      Uart::CR1::TCIE::InterruptWhenTC::IsSet())    
    {
      UartTransmitCompleteObservers::OnComplete();
      Uart::SR::TC::TransmitionNotComplete::Set() ;
    }
  }
};

HardwareUartBase
#ifndef REGISTERS_UART_HPP
#define REGISTERS_UART_HPP

#include "susudefs.hpp" //for __forceinline
#include <array> // for std::array
#include <cassert> // for assert
#include <cstring> // for memcpy
#include "criticalsectionguard.hpp" //for criticalsectionguard

template<typename UartModule,  typename InterruptsList>
struct HardwareUartBase
{
  using Uart = UartModule ;
  using Base = Interface ;

    __forceinline static void EnableTransmit()
  {
    UartModule::CR1::TE::Enable::Set();
  };

  static void DisableTransmit()
  {
    UartModule::CR1::TE::Disable::Set();
  };

  static void EnableReceive()
  {
    UartModule::CR1::RE::Enable::Set();
  };

  static void DisableReceive()
  {
    UartModule::CR1::RE::Disable::Set();
  };

  static void EnableTxInterrupt()
  {
    UartModule::CR1::TXEIE::InterruptWhenTXE::Set();
  };

  static void EnableRxInterrupt()
  {
    UartModule::CR1::RXNEIE::InterruptWhenRXNE::Set();
  };

  static void DisableRxInterrupt()
  {
    UartModule::CR1::RXNEIE::InterruptInhibited::Set();
  };

  static void DisableTxInterrupt()
  {
    UartModule::CR1::TXEIE::InterruptInhibited::Set();
  };

  static void EnableTcInterrupt()
  {
    UartModule::CR1::TCIE::InterruptWhenTC::Set();
  };

  static void DisableTcInterrupt()
  {
    UartModule::CR1::TCIE::InterruptInhibited::Set();
  };

  static void HandleInterrupt()
  {
    InterruptsList::OnInterrupt() ;
  }

  __forceinline static void ClearStatus()
  {
    UartModule::SR::Write(0);
  }

  static void WriteByte(std::uint8_t chByte)
  {
    UartModule::DR::Write(static_cast<std::uint32_t>(chByte)) ;
  }

  static std::uint8_t ReadByte()
  {
    return static_cast<std::uint8_t>(UartModule::DR::Get()) ;
  }

   static void StartTransmit()
  {
    EnableTransmit() ;
    if constexpr (std::is_base_of<UartTxInterruptable, Interface>::value)
    {
      EnableTxInterrupt() ;
    }
  }

  static bool IsDataRegisterEmpty()
  {
    return UartModule::SR::TXE::DataRegisterEmpty::IsSet() ;
  }

  static bool IsTransmitComplete()
  {
    return UartModule::SR::TC::TransmitionComplete::IsSet() ;
  }

};
#endif //REGISTERS_UART_HPP

UART :


struct HardwareUart : HardwareUartBase<
    USART2,    
    InterruptsList<
     //      
      HardwareUartTx<HardwareUart,
      //        Uart
        UartTransmitObservers<MyUartDriver>>,
      //     
      HardwareUartTc<HardwareUart,
      //      
        UartTransmitCompleteObservers<MyUartDriver>>,
      //      
      HardwareUartRx<HardwareUart,
      //       
        UartReceiveObservers<MyUartDriver>>
    >   
>
{
};

. , UartDriver, - . UART, USART2.


Uart , . Uart .


struct MyUartDriver: UartDriver<
    //   UART
    HardwareUart,
    //  SomeProtocol   TestObserver    OnTransmitComplete()
    UartDriverTransmitCompleteObservers<SomeProtocol<MyUartDriver>, TestObserver>,
   //   SomeProtocol    OnReceiveComplete()
    UartDriverReceiveCompleteObservers<SomeProtocol<MyUartDriver>>
    > { };

using MyProtocol = SomeProtocol<MyUartDriver> ;


.


inline, 1600 . : 12 Flash . , , , . , UART. :)


, 2 ( 20 ). , - , , — .


تم التحقق من الرمز من قبلي في PVS-Studio. في البداية تم العثور على 4 تحذيرات.
لم أعد أتذكر جميع التحذيرات بعد الآن ، لم أحفظ التقرير: ولكن كانت هناك بالتأكيد V2516 و V519 ، ولم تكن الأخطاء حرجة ، ولكن لم يكن علي فعل ذلك :) تم إصلاح كل شيء ، باستثناء V2516 ، فهو يشير إلى الرمز المستخدم لتصحيح الأخطاء ، وقد حدد FIXME: .


يمكنك رؤية رمز المثال العامل على IAR8.40.2 هنا ، لا حاجة إلى مكتبات إضافية ، ولكنك تحتاج إلى لوحة Nucleo-F411RE ، المشروع نفسه موجود في مجلد FlashUart \ DinamicStand .


برنامج التشغيل الرئيسي ورمز وحدة Uart موجود على جيثب .


ملاحظة: شكرا فوجاس، أبرو، gleb_l و besitzeruf للتعليقات العملية


All Articles