File SmartServoController.cpp
File List > fw > rbcx-coprocessor > src > SmartServoController.cpp
Go to the documentation of this file.
#include "SmartServoController.hpp"
#include "Bsp.hpp"
#include "ControlLink.hpp"
#include "stm32f1xx_ll_gpio.h"
#include "stm32f1xx_ll_usart.h"
#include "utils/Debug.hpp"
#include "utils/TickTimer.hpp"
enum SmartServoState { IDLE, TX, RX };
// assert that response fits into gBuff
static_assert(sizeof(CoprocStat_SmartServoStat_data_t::bytes)
<= sizeof(CoprocReq_SmartServoReq_data_t::bytes));
static std::array<uint8_t, sizeof(CoprocReq_SmartServoReq_data_t::bytes)> gBuff;
static size_t gBuffSize = 0;
static size_t gBuffIndex = 0;
static SmartServoState gState = IDLE;
static bool gExpectResponse = false;
static bool gResponseReady = false;
static TickTimer gResponseTimeout;
static TickTimer gStateTimeout;
void smartServoInit() {
LL_USART_InitTypeDef init;
LL_USART_StructInit(&init);
init.BaudRate = 115200;
init.DataWidth = LL_USART_DATAWIDTH_8B;
init.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
init.Parity = LL_USART_PARITY_NONE;
init.StopBits = LL_USART_STOPBITS_1;
init.TransferDirection = LL_USART_DIRECTION_TX;
if (LL_USART_Init(servoUart, &init) != SUCCESS)
abort();
LL_USART_ConfigHalfDuplexMode(servoUart);
LL_USART_Enable(servoUart);
HAL_NVIC_SetPriority(servoUartIRQn, servoUartIrqPrio, 0);
HAL_NVIC_EnableIRQ(servoUartIRQn);
pinInit(
servoUartTxRxPin, GPIO_MODE_AF_OD, GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH);
}
static void smartServoSetResponseReady() {
if (gResponseReady) {
DEBUGLN(
"Invalid smartServoSetResponseReady call, it was already ready");
}
gResponseReady = true;
}
static void smartServoStopRx() {
LL_USART_DisableIT_TC(servoUart);
LL_USART_DisableIT_RXNE(servoUart);
smartServoSetResponseReady();
gState = IDLE;
gResponseTimeout.stop();
}
void smartServoPoll() {
if (gResponseReady) {
CoprocStat stat = {
.which_payload = CoprocStat_smartServoStat_tag,
.payload = {
.smartServoStat = {
.data = {
.size = gBuffSize,
},
},
},
};
memcpy(stat.payload.smartServoStat.data.bytes, gBuff.data(), gBuffSize);
gResponseReady = false;
controlLinkTx(stat);
}
if (gResponseTimeout.poll()) {
smartServoStopRx();
}
if (gStateTimeout.poll() && gState != IDLE) {
LL_USART_DisableIT_TXE(servoUart);
smartServoStopRx();
}
}
extern "C" void SERVOUART_HANDLER(void) {
// TX empty
if (gState == TX && LL_USART_IsActiveFlag_TXE(servoUart)) {
if (gBuffIndex < gBuffSize) {
LL_USART_TransmitData8(servoUart, gBuff[gBuffIndex++]);
} else {
LL_USART_DisableIT_TXE(servoUart);
gBuffSize = 0;
if (!gExpectResponse) {
smartServoSetResponseReady();
gState = IDLE;
} else {
gState = RX;
LL_USART_EnableIT_TC(servoUart);
gResponseTimeout.restart(5);
}
}
}
if (gState == RX) {
// Transmission complete
if (LL_USART_IsActiveFlag_TC(servoUart)) {
LL_USART_DisableIT_TC(servoUart);
LL_USART_ClearFlag_TC(servoUart);
LL_USART_ClearFlag_IDLE(servoUart);
LL_USART_SetTransferDirection(servoUart, LL_USART_DIRECTION_RX);
LL_USART_ReceiveData8(servoUart);
LL_USART_EnableIT_RXNE(servoUart);
gResponseTimeout.restart(2);
}
// RX not empty
if (LL_USART_IsActiveFlag_RXNE(servoUart)) {
const uint8_t ch = LL_USART_ReceiveData8(servoUart);
if (gBuffSize < gBuff.size()) {
gBuff[gBuffSize++] = ch;
} else {
DEBUGLN("SmartServo RX buffer overrun!");
}
gResponseTimeout.restart(2);
}
// RX overrun, is enabled by the same bit as RXNE so we have to clear it
if (LL_USART_IsActiveFlag_ORE(servoUart)) {
LL_USART_ClearFlag_ORE(servoUart);
DEBUGLN("SmartServo ORE bit set!");
}
}
}
void smartServoSendRequest(const CoprocReq_SmartServoReq& req) {
if (gState != IDLE) {
DEBUGLN("Invalid CoprocReq_SmartServoReq, state is %d", gState);
return;
}
if (req.data.size == 0) {
gBuffSize = 0;
smartServoSetResponseReady();
return;
}
gStateTimeout.restart(10);
gState = TX;
memcpy(gBuff.data(), req.data.bytes, req.data.size);
gBuffSize = req.data.size;
gBuffIndex = 1;
gExpectResponse = req.expect_response;
LL_USART_SetTransferDirection(servoUart, LL_USART_DIRECTION_TX);
LL_USART_ClearFlag_TC(servoUart);
LL_USART_TransmitData8(servoUart, gBuff[0]);
LL_USART_EnableIT_TXE(servoUart);
}