kopia lustrzana https://github.com/mobilinkd/tnc3-firmware
Convert serial RX code to DMA to improve reliability, throughput and flow control.
rodzic
0aedbc6d0f
commit
d014d8055f
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Ładowanie…
Reference in New Issue