STM32 FreeRTOS - UART Deferred Interrupt Problem - stm32

I am trying to read data with unkown size using UART Receive Interrupt. In the call back function, I enabled Rx interrupt in order to read characters until \n is gotten. If \n is get, then higher priority task which is deferred interrupt handler is woken. The problem is that I tried to read one by one byte via call back function and I tried to put each character into a buffer, but unfortunately buffer could not get any character. Moreover, deferred interrupt handler could not be woken.
My STM32 board is STM32F767ZI, and my IDE is KEIL.
Some Important notes before sharing the code:
1. rxIndex and gpsBuffer are declared as global.
2. Periodic function works without any problem.
Here is my code:
Periodic Function, Priority = 1
void vPeriodicTask(void *pvParameters)
{
const TickType_t xDelay500ms = pdMS_TO_TICKS(500UL);
while (1) {
vTaskDelay(xDelay500ms);
HAL_UART_Transmit(&huart3,(uint8_t*)"Imu\r\n",sizeof("Imu\r\n"),1000);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_7);
}
}
Deferred Interrupt, Priority = 3
void vHandlerTask(void *pvParameters)
{
const TickType_t xMaxExpectedBlockTime = pdMS_TO_TICKS(1000);
while(1) {
if (xSemaphoreTake(xBinarySemaphore,xMaxExpectedBlockTime) == pdPASS) {
HAL_UART_Transmit(&huart3,(uint8_t*)"Semaphore Acquired\r\n",sizeof("Semaphore
Acquired\r\n"),1000);
// Some important processes will be added here
rxIndex = 0;
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_14);
}
}
}
Call back function:
void HAL_UART_RxCptlCallBack(UART_HandleTypeDef *huart)
{
gpsBuffer[rxIndex++] = rData;
if (rData == 0x0A) {
BaseType_t xHigherPriorityTaskWoken;
xSemaphoreGiveFromISR(xBinarySemaphore,&xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
HAL_UART_Receive_IT(huart,(uint8_t*)&rData,1);
}
Main function
HAL_UART_Receive_IT(&huart3,&rData,1);
xBinarySemaphore = xSemaphoreCreateBinary();
if (xBinarySemaphore != NULL) {
//success
xTaskCreate(vHandlerTask,"Handler",128,NULL,1,&vHandlerTaskHandler);
xTaskCreate(vPeriodicTask,"Periodic",128,NULL,3,&vPeriodicTaskHandler);
vTaskStartScheduler();
}

Using HAL for it is a best way to get into the troubles. It uses HAL_Delay which is systick dependant and you should rewrite this function to read RTOS tick instead.
I use queues to pass the data (the references to data) but it should work. There is always a big question mark when using the HAL functions.
void HAL_UART_RxCptlCallBack(UART_HandleTypeDef *huart)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
gpsBuffer[rxIndex++] = rData;
if (rData == 0x0A) {
if(xSemaphoreGiveFromISR(xBinarySemaphore,&xHigherPriorityTaskWoken) == pdFALSE)
{
/* some error handling */
}
}
HAL_UART_Receive_IT(huart,(uint8_t*)&rData,1);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
Concluding if I use HAL & RTOS I always modify the way HAL handles timeouts.

Related

STM32 HAL UART receive by interrupt cleaning buffer

I'm working on an application where I process commands of fixed length received via UART.
I'm also using FreeRTOS and the task that handles the incoming commands is suspended until the uart interrupt handler is called, so my code is like this
void USART1_IRQHandler()
{
HAL_UART_IRQHandler(&huart1);
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart){
HAL_UART_Receive_IT(&huart1, uart_rx_buf, CMD_LEN);
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
BaseType_t higherTaskReady = pdFALSE;
HAL_UART_Receive_IT(&huart1, uart_rx_buf, CMD_LEN); //restart interrupt handler
xSemaphoreGiveFromISR(uart_mutex, &higherTaskReady);
portYIELD_FROM_ISR( higherTaskReady); //Relase the semaphore
}
I am using the ErrorCallBack in case if an overflow occurs. Now I successfully catch every correct command, even if they are issued char by char.
However, I'm trying to make the system more error-proof by considering the case where more characters are received than expected.
The command length is 4 but if I receive, for example, 5 chars, then the first 4 is processed normally but when another command is received it starts from the last unprocessed char, so another 3 chars are needed until I can correctly process the commands again.
Luckily, the ErrorCallback is called whenever I receive more than 4 chars, so I know when it happens, but I need a robust way of cleaning the UART buffer so the previous chars are gone.
One solution I can think of is using UART receive 1 char at a time until it can't receive anymore, but is there a better way to simply flush the buffer?
Yes, the problem is the lack of delimiter, because every byte can can carry a value to be processed from 0 to 255. So, how can you detect the inconsistency?
My solution is a checksum byte in the protocol. If the checksum fails, a blocking-mode UART_Receive function is called in order to put the rest of the data from the "system-buffer" to a "disposable-buffer". In my example the fix size of the protocol is 6, I use the UART6 and I have a global variable RxBuffer. Here is the code:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
if(UartHandle->Instance==USART6) {
if(your_checksum_is_ok) {
// You can process the incoming data
} else {
char TempBuffer;
HAL_StatusTypeDef hal_status;
do {
hal_status = HAL_UART_Receive(&huart6, (uint8_t*)&TempBuffer, 1, 10);
} while(hal_status != HAL_TIMEOUT);
}
HAL_UART_Receive_IT(&huart6, (uint8_t*)RxBuffer, 6);
}
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle) {
if(UartHandle->Instance==USART6) {
HAL_UART_Receive_IT(&huart6, (uint8_t*)RxBuffer, 6);
}
}

WSAEventSelect() makes a socket descriptor no longer a socket

I am writing a cross-platform socket handling library (which also handles serial and a whole bunch of other protocols in a protocol agnostic way. - I am not re-inventing the wheel).
I need to emulate the Linux poll function. The code I started with used select and worked fine, but there was no way to interrupt it from another thread, and so I was forced to start using event objects. My initial attempt called:
WSACreateEvent()
WSAEventSelect() to associate the socket with the event object.
WaitForMultipleObjectsEx() to wait on all sockets plus my interrupt event object.
select() to work out what events actually occurred on the socket.
accept()/send()/recv() to process the sockets (later and elsewhere).
This failed. accept() was claiming that the file descriptor was not a socket. If I commented out the call to WSAEventSelect(), essentially reverting to my earlier code, it all works fine (except that I cannot interrupt).
I then realised that I did something wrong (according to the Microsoft dictatorship). Instead of using select() to work out what events have happened on each socket, I should be using WSAEnumNetworkEvents(). So I rewrote my code to do it the proper way, remembering to call WSAEventSelect() afterwards to disassociate the event object from the file descriptor so that (fingers crossed) accept() would now work.
Now WSAEnumNetworkEvents() is returning an error and WSAGetLastError() tells me that the error is WSAENOTSOCK.
This IS a socket. I am doing things the way MSDN tells me I should (allowing for the general poor quality of the documentation). It appears however that WSAEventSelect() is causing the file descriptor to be marked as a file rather than a socket.
I hate Microsoft so much right now.
Here is a cut down version of my code:
bool do_poll(std::vector<struct pollfd> &poll_data, int timeout)
{
...
for (const auto &fd_data : poll_data) {
event_mask = 0;
if (0 != (fd_data.events & POLLIN)) {
// select() will mark a socket as readable when it closes (read size = 0) or (for
// a listen socket) when there is an incoming connection. This is the *nix paradigm.
// WSAEventSelect() hasseparate events.
event_mask |= FD_READ;
event_mask |= FD_ACCEPT;
event_mask |= FD_CLOSE;
}
if (0 != (fd_data.events & POLLOUT)) {
event_mask |= FD_WRITE;
}
event_obj = WSACreateEvent();
events.push_back(event_obj);
if (WSA_INVALID_EVENT != event_obj) {
(void)WSAEventSelect((SOCKET)fd_data.fd, event_obj, event_mask);
}
}
lock.lock();
if (WSA_INVALID_EVENT == interrupt_obj) {
interrupt_obj = WSACreateEvent();
}
if (WSA_INVALID_EVENT != interrupt_obj) {
events.push_back(interrupt_obj);
}
lock.unlock();
...
(void)WaitForMultipleObjectsEx(events.size(), &(events[0]), FALSE, dw_timeout, TRUE);
for (i = 0u; i < poll_data.size(); i++) {
if (WSA_INVALID_EVENT == events[i]) {
poll_data[i].revents |= POLLERR;
} else {
if (0 != WSAEnumNetworkEvents((SOCKET)(poll_data[i].fd), events[i], &revents)) {
poll_data[i].revents |= POLLERR;
} else {
if (0u != (revents.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))) {
poll_data[i].revents |= POLLIN;
}
if (0u != (revents.lNetworkEvents & FD_WRITE)) {
poll_data[i].revents |= POLLOUT;
}
}
(void)WSAEventSelect((SOCKET)(poll_data[i].fd), NULL, 0);
(void)WSACloseEvent(event_obj);
}
}
...
}

Boost ASIO asynchronous socket with timeout

I am trying to find the proper / canonical way to implement the code below that provides a synchronous wrapper around async asio methods in order to have a timeout. The code appears to work, but none of the examples I have looked at use the boolean in the lambda to terminate the do/while loop running i/o service, so I'm not sure if this is the proper form or if it will have unintended consequences down the road. Some do things like
while(IOService.run_one);
but that never terminates.
Edit:
I'm trying to follow this example:
http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/example/timeouts/blocking_tcp_client.cpp
But in this code they avoid needing the number of bytes read by using a \n terminator. I need the number of bytes read, hence the callback.
I have seen many other solutions that use boost async futures as well as other methods, but they do not seem to compile with the versions of gcc / boost standard for Ubuntu 16.04 and I would like to stay with those versions.
ByteArray SessionInfo::Read(const boost::posix_time::time_duration &timeout)
{
Deadline.expires_from_now(timeout);
auto bytes_received = 0lu;
auto got_callback = false;
SessionSocket->async_receive(boost::asio::buffer(receive_buffer_,
1024),
[&bytes_received, &got_callback](const boost::system::error_code &error, std::size_t bytes_transferred) {
bytes_received = bytes_transferred;
got_callback = true;
});
do
{
IOService.run_one();
}while (!got_callback);
auto bytes = ByteArray(receive_buffer_, receive_buffer_ + bytes_received);
return bytes;
}
This is how I'd do it. The first event that fires causes io_service::run() to return.
ByteArray SessionInfo::Read(const boost::posix_time::time_duration &timeout)
{
Deadline.expires_from_now(timeout); // I assume this is a member of SessionInfo
auto got_callback{false};
auto result = ByteArray();
SessionSocket->async_receive( // idem for SessionSocket
boost::asio::buffer(receive_buffer_, 1024),
[&](const boost::system::error_code error,
std::size_t bytes_received)
{
if (!ec)
{
result = ByteArray(receive_buffer_, bytes_received);
got_callback = true;
}
Deadline.cancel();
});
Deadline.async_wait([&](const boost::system::error_code ec)
{
if (!ec)
{
SessionSocket->cancel();
}
});
IOService.run();
return result;
}
Reading the conversation below M. Roy's answer, your goal is to make sure that
IOService.run(); returns. All points are valid, the instance of boost::asio::io_service should only be run once (meaning not simultaneously but it could be run multiple times in series) per thread of execution so it is imperative to know how it is used. That said, to make the IOService stop I would amend M. Roy's solution like so:
ByteArray SessionInfo::Read(const boost::posix_time::time_duration &timeout) {
Deadline.expires_from_now(timeout);
auto got_callback{false};
auto result = ByteArray();
SessionSocket->async_receive(
boost::asio::buffer(receive_buffer_, 1024),
[&](const boost::system::error_code error,
std::size_t bytes_received) {
if (!ec) {
result = ByteArray(receive_buffer_, bytes_received);
got_callback = true;
}
Deadline.cancel();
});
Deadline.async_wait(
[&](const boost::system::error_code ec) {
if (!ec) {
SessionSocket->cancel();
IOService.stop();
}
});
IOService.run();
return result;
}

Why event flag related functions does not work correctly outside of tasks in keil rtx?

As you know event flags are very useful (e.g. let task running),but unfortunately their control functions (os_evt_clr/set/wait) does not work outside of tasks bodies correctly(e.g. in interrupt handling functions). For alternative i used a variable ,I initialized it in Interrupt handler when needed ,then used it on another task to running a os_evt_set() function for let MCU entering a task.
bool Instance_Variable;
Interrupt_Handler()
{
if(xxxx)
Instance_Variable=1
}
//--------------------------
Secondary_Task()
{
//This is frequently run task
if(Instance_Variable==1)
{
os_evt_set (0x0001, Primary_Task_ID);
Instance_Variable=0;
}
}
//--------------------------
Primary_Task()
{
Result = os_evt_wait_or (0x0001, 0xFFFF);
//Task's body
os_evt_clr (0x0001, Primary_Task_ID);
}
Any better approach?WBR.
You can't use function prefixed with os_ inside ISR. Usage hints from RTX documentation:
Functions that begin with os_ can be called from a task, but not from an interrupt service routine.
Functions that begin with isr_ can be called from an IRQ interrupt service routine but not from a task.
This code will work:
Interrupt_Handler() {
if(xxxx) {
isr_evt_set (0x0001, Primary_Task_ID);
}
}
//--------------------------
Primary_Task() {
Result = os_evt_wait_or (0x0001, 0xFFFF);
//Task's body
os_evt_clr (0x0001, Primary_Task_ID);
}

RTOS μC/OS-II not running as expected

I'm using ST STM32F101xB and μC/OS-II, I was having external clock (HSE) on old board and it's running fine. We wanted to use internal clock (HSI) on new board, however, the RTOS (Appmaintask()) doesn't run using internal clock, i have changed my code as below, any idea what's wrong with the change:
void BSP_Init (void)
{
RCC_DeInit();
//RCC_HSEConfig(RCC_HSE_ON);
//RCC_WaitForHSEStartUp();
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1); // APB2 clock divide by 1 => 64MHz
RCC_PCLK1Config(RCC_HCLK_Div2); // APB1 clock divide by 2 => 32MHz
FLASH_SetLatency(FLASH_Latency_2);
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
//RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_8); // 64MHz
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_8);
RCC_PLLCmd(ENABLE);
RCC_LSEConfig(RCC_LSE_OFF);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
;
}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while (RCC_GetSYSCLKSource() != 0x08) {
;
}
//Set the Vector Table base location at 0x08000000
//NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
// Need to finalize and arange priority for each interrupts in future,
// So that 1 interrupt wont blocks another interrupt.
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
}
void main()
{
INT8U err;
cpuObj = new Cstm32f10x();
BSP_Init();
BSP_IntDisAll(); /* Disable all ints until we are ready to accept them. */
OSInit();
err = OSTaskCreateExt (AppMainTask,
(void *)0,
(OS_STK *)&AppMainTaskStk[APP_MAIN_TASK_STK_SIZE-1],
APP_MAIN_TASK_PRIO,
APP_MAIN_TASK_ID,
(OS_STK *)&AppMainTaskStk[0],
APP_MAIN_TASK_STK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);
OSStart(); // Start multitasking (i.e. give control to uC/OS-II)
}
void AppMainTask (void *p_arg)
{
OS_CPU_SysTickInit();
while(TRUE)
{
OSTimeDly(1);
}
}
Thanks.
Setting up of the PLL is normally performed in the CMSIS start-up code provided by ST/ARM. This code executes as part of the runtime environment start-up before main() is called. I recommend that you use this code for chip initialisation since static data initialisation and static object constructors run before main() and will be running before possibly critical initialisation.
The CMSIS with Cortex-M3 core and STM32F1xx specific device support is included in the STM32 Standard Peripheral Library. The file that actually does the work is system_stm32f10x.c. Other functions you are performing in BSP_Init() such as flash latency are also dealt with by the CMSIS start-up code. Even if you customise this code, I strongly recommend that you use this method of early environment initialisation.
Another possibility is to use the STM32CubeMX utility to generate configuration code. This appears to be a replacement for the apparently now unavailable STM32 MicroXplorer utility.