Convert serial RX code to DMA to improve reliability, throughput and flow control.

kf7r_9600_experimental
Rob Riggs 2020-02-24 22:36:09 -06:00
rodzic 0aedbc6d0f
commit d014d8055f
3 zmienionych plików z 214 dodań i 66 usunięć

Wyświetl plik

@ -41,6 +41,8 @@
#include "main.h" #include "main.h"
extern osMessageQId ioEventQueueHandle; extern osMessageQId ioEventQueueHandle;
void idleInterruptCallback(UART_HandleTypeDef* huart);
/* USER CODE END 0 */ /* USER CODE END 0 */
/* External variables --------------------------------------------------------*/ /* External variables --------------------------------------------------------*/
@ -386,6 +388,12 @@ void USART3_IRQHandler(void)
{ {
/* USER CODE BEGIN USART3_IRQn 0 */ /* USER CODE BEGIN USART3_IRQn 0 */
if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE)) {
idleInterruptCallback(&huart3);
__HAL_UART_CLEAR_FLAG(&huart3, UART_FLAG_IDLE);
return;
}
/* USER CODE END USART3_IRQn 0 */ /* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3); HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */ /* USER CODE BEGIN USART3_IRQn 1 */

Wyświetl plik

@ -162,6 +162,8 @@ public:
uint16_t size() const {return free_list_.size();} uint16_t size() const {return free_list_.size();}
static constexpr uint16_t capacity() { return SIZE; }
frame_type* acquire() { frame_type* acquire() {
frame_type* result = nullptr; frame_type* result = nullptr;
auto x = taskENTER_CRITICAL_FROM_ISR(); auto x = taskENTER_CRITICAL_FROM_ISR();

Wyświetl plik

@ -13,20 +13,28 @@
#include "cmsis_os.h" #include "cmsis_os.h"
#include <cstdlib> #include <cstdlib>
#include <cstdint>
#include <cstring> #include <cstring>
#include <atomic> #include <atomic>
extern UART_HandleTypeDef huart3; extern UART_HandleTypeDef huart3;
extern osMessageQId ioEventQueueHandle; extern osMessageQId ioEventQueueHandle;
uint8_t rxBuffer; std::atomic<uint32_t> uart_error{HAL_UART_ERROR_NONE};
std::atomic<int> uart_error{HAL_UART_ERROR_NONE};
std::atomic<bool> txDoneFlag{true}; std::atomic<bool> txDoneFlag{true};
uint8_t tmpBuffer[mobilinkd::tnc::TX_BUFFER_SIZE]; uint8_t tmpBuffer[mobilinkd::tnc::TX_BUFFER_SIZE];
uint8_t tmpBuffer2[mobilinkd::tnc::TX_BUFFER_SIZE]; uint8_t tmpBuffer2[mobilinkd::tnc::TX_BUFFER_SIZE];
constexpr const int RX_BUFFER_SIZE = 127;
unsigned char rxBuffer[RX_BUFFER_SIZE * 2];
// 3 chunks of 128 bytes. The first byte in each chunk is the length.
typedef mobilinkd::tnc::memory::Pool<
3, RX_BUFFER_SIZE + 1> serial_pool_type;
serial_pool_type serialPool;
void log_frame(mobilinkd::tnc::hdlc::IoFrame* frame) void log_frame(mobilinkd::tnc::hdlc::IoFrame* frame)
{ {
int pos = 0; int pos = 0;
@ -41,7 +49,56 @@ void log_frame(mobilinkd::tnc::hdlc::IoFrame* frame)
DEBUG((char*)tmpBuffer2); DEBUG((char*)tmpBuffer2);
} }
extern "C" void startSerialTask(void const* arg) // HAL does not have
HAL_StatusTypeDef UART_DMAPauseReceive(UART_HandleTypeDef *huart)
{
/* Process Locked */
__HAL_LOCK(huart);
if ((huart->RxState == HAL_UART_STATE_BUSY_RX) &&
(HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)))
{
/* Disable PE and ERR (Frame error, noise error, overrun error) interrupts */
CLEAR_BIT(huart->Instance->CR1, USART_CR1_PEIE);
CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Disable the UART DMA Rx request */
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
}
/* Process Unlocked */
__HAL_UNLOCK(huart);
return HAL_OK;
}
HAL_StatusTypeDef UART_DMAResumeReceive(UART_HandleTypeDef *huart)
{
/* Process Locked */
__HAL_LOCK(huart);
if (huart->RxState == HAL_UART_STATE_BUSY_RX)
{
/* Clear the Overrun flag before resuming the Rx transfer */
__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF);
/* Reenable PE and ERR (Frame error, noise error, overrun error) interrupts */
SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Enable the UART DMA Rx request */
SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);
}
/* Process Unlocked */
__HAL_UNLOCK(huart);
return HAL_OK;
}
extern "C" void startSerialTask(void const* arg) __attribute__((optimize("-O1")));
void startSerialTask(void const* arg)
{ {
using namespace mobilinkd::tnc; using namespace mobilinkd::tnc;
@ -58,90 +115,116 @@ extern "C" void startSerialTask(void const* arg)
hdlc::IoFrame* frame = hdlc::acquire_wait(); hdlc::IoFrame* frame = hdlc::acquire_wait();
HAL_UART_Receive_IT(&huart3, &rxBuffer, 1); HAL_UART_Receive_DMA(&huart3, rxBuffer, RX_BUFFER_SIZE * 2);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
uint32_t last_sent_time = osKernelSysTick(); uint32_t last_sent_time = osKernelSysTick();
uint32_t current_sent_time = 0; uint32_t current_sent_time = 0;
bool paused = false;
while (true) { while (true) {
osEvent evt = osMessageGet(serialPort->queue(), osWaitForever); osEvent evt = osMessageGet(serialPort->queue(), osWaitForever);
if (evt.status != osEventMessage) { if (evt.status != osEventMessage) {
HAL_UART_Receive_IT(&huart3, &rxBuffer, 1);
continue; continue;
} }
if (evt.value.v & 0x100) { if (evt.value.v < FLASH_BASE) // Assumes FLASH_BASE < SRAM_BASE.
{
// Error received.
hdlc::release(frame); hdlc::release(frame);
ERROR("UART Error: %08lx", evt.value.v); ERROR("UART Error: %08lx", uart_error.load());
uart_error.store(HAL_UART_ERROR_NONE); uart_error.store(HAL_UART_ERROR_NONE);
frame = hdlc::acquire_wait(); frame = hdlc::acquire_wait();
HAL_UART_Receive_IT(&huart3, &rxBuffer, 1); HAL_UART_Receive_DMA(&huart3, rxBuffer, RX_BUFFER_SIZE * 2);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
continue; continue;
} }
uint8_t c = evt.value.v; auto block = (serial_pool_type::chunk_type*) evt.value.p;
switch (state) { auto data = static_cast<unsigned char*>(block->buffer);
case WAIT_FBEGIN:
if (c == FEND) state = WAIT_FRAME_TYPE; uint8_t end = data[0] + 1;
break; for (uint8_t i = 1; i != end; ++i) {
case WAIT_FRAME_TYPE: uint8_t c = data[i];
if (c == FEND) break; // Still waiting for FRAME_TYPE. switch (state) {
frame->type(c); case WAIT_FBEGIN:
state = WAIT_FEND; if (c == FEND) state = WAIT_FRAME_TYPE;
break;
case WAIT_FEND:
switch (c) {
case FESC:
state = WAIT_ESCAPED;
break; break;
case FEND: case WAIT_FRAME_TYPE:
frame->source(hdlc::IoFrame::SERIAL_DATA); if (c == FEND) break; // Still waiting for FRAME_TYPE.
if (osMessagePut(ioEventQueueHandle, reinterpret_cast<uint32_t>(frame), osWaitForever) != osOK) if (c < 8 or c == 0xFF) {
{ frame->type(c);
hdlc::release(frame); state = WAIT_FEND;
} else {
WARN("Bad frame type");
state = WAIT_FBEGIN;
} }
current_sent_time = osKernelSysTick();
if (last_sent_time + 50 > current_sent_time) {
uint32_t delay = (last_sent_time + 50) - current_sent_time;
osDelay(delay);
}
last_sent_time = current_sent_time;
frame = hdlc::acquire_wait();
state = WAIT_FBEGIN;
break; break;
default: case WAIT_FEND:
if (not frame->push_back(c)) { switch (c) {
case FESC:
state = WAIT_ESCAPED;
break;
case FEND:
frame->source(hdlc::IoFrame::SERIAL_DATA);
if (osMessagePut(
ioEventQueueHandle,
reinterpret_cast<uint32_t>(frame),
osWaitForever) != osOK)
{
WARN("Failed to send serial frame");
hdlc::release(frame);
}
if (hdlc::ioFramePool().size() < (hdlc::ioFramePool().capacity() / 4))
{
UART_DMAPauseReceive(&huart3);
WARN("Pausing UART RX");
while (hdlc::ioFramePool().size() < (hdlc::ioFramePool().capacity() / 2))
{
osThreadYield();
}
UART_DMAResumeReceive(&huart3);
}
frame = hdlc::acquire_wait();
state = WAIT_FBEGIN;
break;
default:
if (not frame->push_back(c)) {
hdlc::release(frame);
state = WAIT_FBEGIN; // Drop frame;
frame = hdlc::acquire_wait();
}
}
break;
case WAIT_ESCAPED:
state = WAIT_FEND;
switch (c) {
case TFESC:
if (not frame->push_back(FESC)) {
hdlc::release(frame);
state = WAIT_FBEGIN; // Drop frame;
frame = hdlc::acquire_wait();
}
break;
case TFEND:
if (not frame->push_back(FEND)) {
hdlc::release(frame);
state = WAIT_FBEGIN; // Drop frame;
frame = hdlc::acquire_wait();
}
break;
default:
hdlc::release(frame); hdlc::release(frame);
state = WAIT_FBEGIN; // Drop frame; state = WAIT_FBEGIN; // Drop frame;
frame = hdlc::acquire_wait(); frame = hdlc::acquire_wait();
} }
break;
} }
break;
case WAIT_ESCAPED:
state = WAIT_FEND;
switch (c) {
case TFESC:
if (not frame->push_back(FESC)) {
hdlc::release(frame);
state = WAIT_FBEGIN; // Drop frame;
frame = hdlc::acquire_wait();
}
break;
case TFEND:
if (not frame->push_back(FEND)) {
hdlc::release(frame);
state = WAIT_FBEGIN; // Drop frame;
frame = hdlc::acquire_wait();
}
break;
default:
hdlc::release(frame);
state = WAIT_FBEGIN; // Drop frame;
frame = hdlc::acquire_wait();
}
break;
} }
serialPool.deallocate(block);
} }
} }
@ -151,18 +234,73 @@ extern "C" void HAL_UART_TxCpltCallback(UART_HandleTypeDef*)
txDoneFlag = true; txDoneFlag = true;
} }
extern "C" void HAL_UART_RxCpltCallback(UART_HandleTypeDef *) extern "C" void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
{ {
osMessagePut(mobilinkd::tnc::getSerialPort()->queue(), (uint32_t) rxBuffer, 0); uint32_t len = (RX_BUFFER_SIZE * 2) - __HAL_DMA_GET_COUNTER(huart->hdmarx);
if (len == 0) return;
if (len > RX_BUFFER_SIZE) {
len = RX_BUFFER_SIZE; // wrapped.
}
HAL_UART_Receive_IT(&huart3, &rxBuffer, 1); auto block = serialPool.allocate();
if (!block) return;
memmove(block->buffer + 1, rxBuffer, len);
block->buffer[0] = len;
auto status = osMessagePut(mobilinkd::tnc::getSerialPort()->queue(), (uint32_t) block, 0);
if (status != osOK) serialPool.deallocate(block);
} }
extern "C" void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
uint32_t hdmarx = __HAL_DMA_GET_COUNTER(huart->hdmarx);
uint32_t len = 0;
if (hdmarx > RX_BUFFER_SIZE) {
len = RX_BUFFER_SIZE; // wrapped.
} else {
len = RX_BUFFER_SIZE - hdmarx;
}
if (len == 0) return;
auto block = serialPool.allocate();
if (!block) return;
memmove(block->buffer + 1, rxBuffer + RX_BUFFER_SIZE, len);
block->buffer[0] = len;
auto status = osMessagePut(mobilinkd::tnc::getSerialPort()->queue(), (uint32_t) block, 0);
if (status != osOK) serialPool.deallocate(block);
}
extern "C" void idleInterruptCallback(UART_HandleTypeDef* huart)
{
uint32_t len = (RX_BUFFER_SIZE * 2) - __HAL_DMA_GET_COUNTER(huart->hdmarx);
if (len == 0) return;
auto block = serialPool.allocate();
if (!block) return;
HAL_UART_AbortReceive(huart);
if (len > RX_BUFFER_SIZE) {
// Second half
len = len - RX_BUFFER_SIZE;
memmove(block->buffer + 1, rxBuffer + RX_BUFFER_SIZE, len);
} else {
// First half
memmove(block->buffer + 1, rxBuffer, len);
}
block->buffer[0] = len;
HAL_UART_Receive_DMA(huart, rxBuffer, RX_BUFFER_SIZE * 2);
auto status = osMessagePut(mobilinkd::tnc::getSerialPort()->queue(), (uint32_t) block, 0);
if (status != osOK) serialPool.deallocate(block);
}
extern "C" void HAL_UART_ErrorCallback(UART_HandleTypeDef* huart) extern "C" void HAL_UART_ErrorCallback(UART_HandleTypeDef* huart)
{ {
osMessagePut(mobilinkd::tnc::getSerialPort()->queue(), osMessagePut(mobilinkd::tnc::getSerialPort()->queue(), huart->ErrorCode, 0);
(uint32_t) (huart->gState<<16) | huart->ErrorCode | 0x100, 0);
uart_error.store((huart->gState<<16) | huart->ErrorCode); uart_error.store((huart->gState<<16) | huart->ErrorCode);
huart->ErrorCode = HAL_UART_ERROR_NONE; huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState = HAL_UART_STATE_READY; huart->gState = HAL_UART_STATE_READY;