Les élèves écrivent le pilote Uart pour STM32F411


Bonne santé à tous!


Aujourd'hui, je veux vous dire comment les étudiants apprennent progressivement à développer un logiciel pour le microcontrôleur en utilisant le pilote UART sur le STM32F411 comme exemple. Je vais essayer de donner le code et l'architecture avec mes petits changements et améliorations ici.


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


, , Uart ++.



:




, UART .



— UART, , .



, :



:


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

:


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

. 2 :


  • UartDriverTransmitCompleteObservers — OnTransmitComplete() OnTransmitComplete()

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

  • UartDriverReceiveObservers — OnReceiveComplete() 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 ). , - , , — .


Le code a été vérifié par moi dans PVS-Studio. Initialement, 4 avertissements ont été trouvés.
Je ne me souviens pas de tous les avertissements, je n'ai pas enregistré le rapport: mais il y avait certainement V2516 et V519, les erreurs n'étaient pas critiques, mais je n'avais pas à le faire :) Tout était corrigé, sauf pour V2516, il pointe vers le code utilisé pour le débogage, il a défini FIXME: .


Vous pouvez voir l' exemple de code de travail sur IAR8.40.2 ici , aucune bibliothèque supplémentaire n'est nécessaire, mais la carte Nucleo-F411RE est nécessaire, le projet lui-même est dans le dossier FlashUart \ DinamicStand .


Le pilote principal et le code du module Uart se trouvent sur le github .


PS: Merci fougasse, apro, gleb_l et besitzeruf pour des commentaires pratiques


All Articles