2019-04-15 04:13 AM
Dear community,
I am working on a project, and I am running into problems with the use of (multiple) UART prots at the same time. I hope, that you can help me.
Project description
-------------------
The project I am working on is a modular system of connected devices. The MCU of every device is STM32F071C8U6 and it is placed on square PCB. On each side of the square (PCB) I've placed pads which are connected to the 4 UART interfaces of the MCU in such way, that you can connect 2 PCBs on any side. The idea is to create a matrix of devices which are able to "talk" to each other via the UART interfaces.
A control interface (PC serial port) is connected to one of the units, and sends commands to the device. Every device must listen for commands on every port. When a command is recieved it is processed, stored (every command changes global variable) and if the command is valid and new for the device it should be sent to the rest localy connected devices - for example - if the command is recieved on UART2 it should be processed and sent to UART1, UART3 and UART4. Using this simple rule I am trying to achieve simple broadcast functionality over all connected devices on the matrix.
Implementation
-------------------
I am using CubeMX to configure the MCU and Keil uVision environment. For the UART comms I am using HAL drivers. I am unable to use DMA, because there are not enough DMA streams for all 4 uarts, polling is also not an option, because I am using the main loop for communication with sensors, PWM control and other calculations. My current implementation is interrupt based.
The communication protocol I am using is very simple - just two bytes :
-------------------
| 8 bits | 8 bits |
|Command | data |
-------------------
The problem
-------------------
1. Data corruption - When I have only 1 or two devices connected everithing works. The command from the control interface is recieved by the first device and sent to the next, and both devices are executing the commands propperly. When I connect more devices some of the devices are executig random commands, which are not sent by the control interface. I susspect that it is possible for the recieving function to miss one byte and the data to be out of allignment (data interpreted like a command and/or vice versa). I need a mechanism to avoid such scenario.
2. Commands not recieved - happens to the devices in the middle of the matrix - maybe the device does not have enough time to process UART data comming almost simultaneously from 3 different ports.
Baisically I need a way to be able to send and recieve UART data from and to all 4 ports (almost) simultaneosuly and reliably.
Any help would be greatly appreciated!
2019-04-15 04:13 AM
// Buffers
uint8_t rxbuffer1[2];
uint8_t rxbuffer2[2];
uint8_t rxbuffer3[2];
uint8_t rxbuffer4[2];
uint8_t txbuffer1[2];
uint8_t txbuffer2[2];
uint8_t txbuffer3[2];
uint8_t txbuffer4[2];
// Init
/* USART1 init function */
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* USART2 init function */
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* USART3 init function */
static void MX_USART3_UART_Init(void)
{
huart3.Instance = USART3;
huart3.Init.BaudRate = 9600;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_SWAP_INIT;
huart3.AdvancedInit.Swap = UART_ADVFEATURE_SWAP_ENABLE;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/* USART4 init function */
static void MX_USART4_UART_Init(void)
{
huart4.Instance = USART4;
huart4.Init.BaudRate = 9600;
huart4.Init.WordLength = UART_WORDLENGTH_8B;
huart4.Init.StopBits = UART_STOPBITS_1;
huart4.Init.Parity = UART_PARITY_NONE;
huart4.Init.Mode = UART_MODE_TX_RX;
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_SWAP_INIT;
huart4.AdvancedInit.Swap = UART_ADVFEATURE_SWAP_ENABLE;
if (HAL_UART_Init(&huart4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
// main
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_TIM1_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_TIM15_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
MX_USART4_UART_Init();
MX_CRC_Init();
/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
HAL_UART_Receive_IT(&huart1,rxbuffer1,sizeof(rxbuffer1));
HAL_UART_Transmit_IT(&huart1,(uint8_t*)txbuffer1,2);
__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
HAL_UART_Receive_IT(&huart2,rxbuffer2,sizeof(rxbuffer2));
HAL_UART_Transmit_IT(&huart2,(uint8_t*)txbuffer2, 2);
__HAL_UART_ENABLE_IT(&huart3,UART_IT_RXNE);
HAL_UART_Receive_IT(&huart3,rxbuffer3,sizeof(rxbuffer3));
HAL_UART_Transmit_IT(&huart3,(uint8_t*)txbuffer3, 2);
__HAL_UART_ENABLE_IT(&huart4,UART_IT_RXNE);
HAL_UART_Receive_IT(&huart4,rxbuffer4,sizeof(rxbuffer4));
HAL_UART_Transmit_IT(&huart4,(uint8_t*)txbuffer4, 2);
...
// stm320fxx_it.c
...
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
HAL_UART_Receive_IT(&huart1,rxbuffer1,2);
/* USER CODE END USART1_IRQn 1 */
}
/**
* @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
HAL_UART_Receive_IT(&huart2,rxbuffer2,2);
/* USER CODE END USART2_IRQn 1 */
}
/**
* @brief This function handles USART3 and USART4 global interrupts.
*/
void USART3_4_IRQHandler(void)
{
/* USER CODE BEGIN USART3_4_IRQn 0 */
/* USER CODE END USART3_4_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
HAL_UART_IRQHandler(&huart4);
/* USER CODE BEGIN USART3_4_IRQn 1 */
HAL_UART_Receive_IT(&huart3,rxbuffer3,2);
HAL_UART_Receive_IT(&huart4,rxbuffer4,2);
/* USER CODE END USART3_4_IRQn 1 */
}
...
// RxCpltCallback
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart == &huart1) {
uint8_t command = rxbuffer1[0];
uint8_t value = rxbuffer1[1];
bool_t retranslate = 0;
retranslate = processCommand(command,value,&huart1);
if (retranslate) {
txbuffer2[0]=rxbuffer1[0];
txbuffer2[1]=rxbuffer1[1];
txbuffer3[0]=rxbuffer1[0];
txbuffer3[1]=rxbuffer1[1];
txbuffer4[0]=rxbuffer1[0];
txbuffer4[1]=rxbuffer1[1];
HAL_UART_Transmit_IT(&huart2,txbuffer2,2);
HAL_UART_Transmit_IT(&huart3,txbuffer3,2);
HAL_UART_Transmit_IT(&huart4,txbuffer4,2);
}
}
if (huart == &huart2) {
uint8_t command = rxbuffer2[0];
uint8_t value = rxbuffer2[1];
bool_t retranslate = 0;
retranslate = processCommand(command,value,&huart2);
if (retranslate) {
txbuffer1[0]=rxbuffer2[0];
txbuffer1[1]=rxbuffer2[1];
txbuffer3[0]=rxbuffer2[0];
txbuffer3[1]=rxbuffer2[1];
txbuffer4[0]=rxbuffer2[0];
txbuffer4[1]=rxbuffer2[1];
HAL_UART_Transmit_IT(&huart1,txbuffer1,2);
HAL_UART_Transmit_IT(&huart3,txbuffer3,2);
HAL_UART_Transmit_IT(&huart4,txbuffer4,2);
}
uart2Data = 0;
}
if (huart == &huart3) {
uint8_t command = rxbuffer3[0];
uint8_t value = rxbuffer3[1];
bool_t retranslate = 0;
retranslate = processCommand(command,value,&huart3);
if (retranslate) {
txbuffer1[0]=rxbuffer3[0];
txbuffer1[1]=rxbuffer3[1];
txbuffer2[0]=rxbuffer3[0];
txbuffer2[1]=rxbuffer3[1];
txbuffer4[0]=rxbuffer3[0];
txbuffer4[1]=rxbuffer3[1];
HAL_UART_Transmit_IT(&huart1,txbuffer1,2);
HAL_UART_Transmit_IT(&huart2,txbuffer2,2);
HAL_UART_Transmit_IT(&huart4,txbuffer4,2);
}
}
if (huart == &huart4) {
uint8_t command = rxbuffer4[0];
uint8_t value = rxbuffer4[1];
bool_t retranslate = 0;
retranslate = processCommand(command,value,&huart4);
if (retranslate) {
txbuffer1[0]=rxbuffer4[0];
txbuffer1[1]=rxbuffer4[1];
txbuffer2[0]=rxbuffer4[0];
txbuffer2[1]=rxbuffer4[1];
txbuffer3[0]=rxbuffer4[0];
txbuffer3[1]=rxbuffer4[1];
HAL_UART_Transmit_IT(&huart1,txbuffer1,2);
HAL_UART_Transmit_IT(&huart2,txbuffer2,2);
HAL_UART_Transmit_IT(&huart3,txbuffer3,2);
}
}
}
// processCommand
bool_t processCommand(uint8_t command,uint8_t value, UART_HandleTypeDef *huart){
switch (command) {
case 101:
if (value != startColor[0]) {
startColor[0] = value;
return 1;
}
break;
case 102:
if (value != startColor[1]) {
startColor[1] = value;
return 1;
}
break;
case 103:
if (value != startColor[2]) {
startColor[2] = value;
return 1;
}
break;
case 104:
if (value != endColor[0]) {
endColor[0] = value;
return 1;
}
break;
case 105:
if (value != endColor[1]) {
endColor[1] = value;
return 1;
}
break;
case 106:
if (value != endColor[2]) {
endColor[2] = value;
return 1;
}
break;
case 107:
if (value != sensMin) {
sensMin = value;
return 1;
}
break;
case 108:
if (value != sensMax) {
sensMax = value;
return 1;
}
break;
case 109:
if (value != usingBlock && (value == 0 || value == 1)) {
usingBlock = value;
return 1;
}
break;
case 110:
if (value != delogEnabled && (value == 0 || value == 1)) {
delogEnabled = value;
return 1;
}
break;
case 111:
if (value != lpFilter) {
lpFilter = value;
return 1;
}
break;
...
2019-04-15 09:09 AM
I'd use deeper buffing, and use a single IRQ Handler between all USART, and at the same priority/prempt level.
I would loop in there servicing all four input/output streams, until all TXE/RXNE were in an idle/serviced state.
I would perhap use 9-bit mode so as to clearly identify command vs data, and to be able to recover synchronization.
Transmit buffer depth doesn't look deep enough to echo input from 3 ports, and the system could obviously get saturated with traffic from short term bursts of activity.
2019-04-15 09:48 AM
Hmmm..
HAL_UART_Receive_IT(&huart1,rxbuffer1,2);
location seems weird
First, you can use the BREAK big pause between messages to resync the communication.
Now are you sure that when you cascade messages it is only as TREE type cascading? If the structure is a square grid, there will be criss cross.
How do you know where the message ends? (or its destination device)
RX and TX are asynchroneous and could occur at any time or even in reverse order than expected.
Implement RX and TX SW FIFOs between USARTS (and which priority from bottom to top host?)
Instead of a 2D structure, try first a 1D chained structure which is more commonly present and called DaisyChain.
2019-04-16 03:14 AM
Hi Clive One.
Thank you for your help. I have a few questions regarding your recommendations - how to use single IRQ handler for all 4 UARTS? In my stm32f0xx__int.c there are 3 IRQ handler functions generated by CubeMX. I cant fully understand you, what do you mean by "servicing all four input/output streams, until all TXE/RXNE were in an idle/serviced state"? Can you give me an implementation hint? The 9 bit word is great idea, I will it to see if synchronization issue will be resolved. Regarding buffers - I am using 4 receive and 4 transmit buffers of size 2. How deep they should be?
2019-04-16 03:35 AM
Hi KIC8462852 EPIC204278916,
Thank you for your help.
Where should I put HAL_UART_Receive_IT(&huart1,rxbuffer1,2) call?
What is BEAK big pause and how to use it ?
Regarding criss-cross - with this set of rules for message propagation criss cross (i.e. receiving the same message from different ports) will occur. Since every message changes a global variable every node checks if the received message value is different from the stored one. If not the message is discarded and not transmitted to other nodes. The criss-cross is also desired here, because eliminates single point of failure. If the messages are propagated only horizontally or vertically and if one node fails to receive/transmit the message there is a chance not all nodes to receive the message.
The message ends when 2 bytes are received. Every message is 2 bytes, I am using HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) , which fires on every 2 received bytes. What do you mean by "Implement RX and TX SW FIFOs between USARTS (and which priority from bottom to top host?)"? Can you provide an example? I tried 1d structure (e.g. row of nodes) and it works. But I need 2d matrix.
2019-04-16 08:20 AM
You are calling HAL_UART_Transmit_IT() in main() before you have any data to send! In main(), only start the receive process. You can't start the transmit until you actually receive something, no?
Your interrupt handlers always call HAL_UART_Receive_IT() (for BOTH UARTs in the USART 3/4 handler), regardless of what generated the interrupt. Move that code out of the USARTx_IRQHandler() functions and into the appropriate callback (HAL_UART_RxCpltCallback).
And check the return codes from the HAL functions, specially the Receive_IT and Transmit_IT. I bet some of those calls are returning an error, specially if you are receiving commands on multiple ports (say uart 1 & 2) which then both try to transmit to uart3 and 4. Something is going to bounce off. As @Community member mentioned, you need a bigger transmit buffer (at least, though I would add a bigger rx buffer as well). Using HAL_UART_Transmit_IT() can get cumbersome when you try to buffer data to send while the HAL layer is currently busy sending other data.
2019-04-16 08:25 AM
One unified IRQ Handler, directed via the vector table in startup.s, check all inputs, services any pending/serviceable outputs.
If I can get input from three different UART, which could be different, or at least need some elasticity, 6 bytes Tx buffer depth would seem to be a minimum. Having 2 for everything seems way too rigid, and the HAL is too fragile.
2019-04-16 09:00 AM
Hi Bob S, thanks for the suggestions!
Yes, you're right - I can't transmit until something is received. I will remove these calls from main.
I will move all HAL_UART_Receive_IT() calls in HAL_UART_RxCpltCallback for code consistency.
How to check error codes? I've tried to add void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart), but the debugger didn't stop there, so I assumed no errors were generated. How big the buffers should be? 8, 32, 128 bytes? If I make them so big how to process the buffer 2 bytes at a time? HAL_UART_RxCpltCallback gets called only when buffer is full.
2019-04-16 10:01 AM
The error callback is called when the UART gets something like a framing error, parity error, overrun, etc.
HAL_UART_Transmit_IT() and HAL_UART_Receive_IT(), and most of the HAL functions, return an integer value. What I was suggesting was checking that return value. It will be something like HAL_OK, HAL_BUSY, etc. Most of the code you see posted on these forums ignores these return values, which means they miss configuration or setup errors.
To expand your RX buffer, you need to keep input and output pointers (aka head/tail) into the buffer. Your interrupt functions writes to the input/head pointer, and your main code look reads from the output/tail pointer (after checking to make sure there is data in the buffer).