
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()
— UARTOnTransmitComplete()
— UARTOnReceive()
— UART
. 2 :
UartDriverTransmitCompleteObservers
— OnTransmitComplete()
OnTransmitComplete()
UartDriverTransmitCompleteObserverstemplate<typename ...Observers>
struct UartDriverTransmitCompleteObservers
{
__forceinline static void OnWriteComplete()
{
(Observers::OnTransmitComplete(), ...) ;
}
};
UartDriverReceiveObservers
— OnReceiveComplete()
OnReceiveComplete()
UartDriverReceiveObserverstemplate<typename ...Observers>
struct UartDriverReceiveCompleteObservers
{
__forceinline static void OnReadComplete(tBuffer& buffer, std::size_t bytesReceived)
{
(Observers::OnReceiveComplete(buffer, bytesReceived), ...) ;
}
};
, OnReceiveComplete()
OnTransmitComplete()
. SomeProtocol
OtherObserver
.
WriteData()
, . :
:
:
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"
#include "hardwareuarttx.hpp"
#include "hardwareuarttc.hpp"
#include "hardwareuartrx.hpp"
#include <cstring> // for memcpy
#include "criticalsectionconfig.hpp"
#include "uartdriverconfig.hpp"
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
?
, SomeProtocol
, 10 10 . — , — . 8 , .. , 10, . ( , , , )
OnTransmitComplete()
OnReceiveComplete()
.
:template <typename UartDriver>
struct SomeProtocol
{
__forceinline static void OnTransmitComplete()
{
Proceed() ;
}
__forceinline static void OnReceiveComplete(tBuffer& buffer, std::size_t length)
{
assert(length <= buffer.size()) ;
if (CheckData(buffer))
{
cmds::ProceedCmd(buffer[0], buffer);
UartDriver::WriteData(buffer.data(), length) ;
} else
{
UartDriver::ResetAll() ;
}
}
__forceinline static void Proceed()
{
UartDriver::ReadData(10) ;
}
using cmds = CmdContainer<
Singleton<CmdWriteSomeData>::GetInstance(),
Singleton<CmdReadSomeData>::GetInstance()
> ;
};
struct TestObserver
{
__forceinline static void OnTransmitComplete()
{
Led1::Toggle() ;
}
};
- UartDriver
struct MyUartDriver: UartDriver<
HardwareUart,
UartDriverTransmitCompleteObservers<SomeProtocol<MyUartDriver>, TestObserver>,
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()
{
InterruptsList::OnInterrupt() ;
}
...
} ;
HardwareUartTx , HardwareUartRx, HardwareUartTxtemplate<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"
#include <array> // for std::array
#include <cassert> // for assert
#include <cstring> // for memcpy
#include "criticalsectionguard.hpp"
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
UART :
struct HardwareUart : HardwareUartBase<
USART2,
InterruptsList<
HardwareUartTx<HardwareUart,
UartTransmitObservers<MyUartDriver>>,
HardwareUartTc<HardwareUart,
UartTransmitCompleteObservers<MyUartDriver>>,
HardwareUartRx<HardwareUart,
UartReceiveObservers<MyUartDriver>>
>
>
{
};
. , UartDriver, - . UART, USART2.
Uart , . Uart .
struct MyUartDriver: UartDriver<
HardwareUart,
UartDriverTransmitCompleteObservers<SomeProtocol<MyUartDriver>, TestObserver>,
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