Los estudiantes escriben el controlador Uart para STM32F411


Buena salud a todos!


Hoy quiero contarles cómo gradualmente los estudiantes aprenden a desarrollar software para el microcontrolador utilizando el controlador UART en el STM32F411 como ejemplo. Intentaré dar el código y la arquitectura con mis pequeños cambios y mejoras aquí.


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


, , 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 ). , - , , — .


Revisé el código en PVS-Studio. Inicialmente se encontraron 4 advertencias.
Ya no recuerdo todas las advertencias, no guardé el informe: pero definitivamente hubo V2516 y V519, los errores no fueron críticos, pero no tuve que hacer eso :) Todo estaba arreglado, excepto V2516, señala el código utilizado para la depuración, configuró FIXME: .


Puede ver el código de ejemplo de trabajo en IAR8.40.2 aquí , no se necesitan bibliotecas adicionales, pero necesita una placa Nucleo-F411RE, el proyecto en sí está en la carpeta FlashUart \ DinamicStand .


El controlador principal y el código del módulo Uart están en el github .


PD: gracias Fougasse, apro, gleb_l y besitzeruf para comentarios prácticos


All Articles