RTOS STM32F4 discovery - rtos

i'm actually working on my final project for graduation. I'm using FreeRTOS on STM32F4 discovery. It all works properly, but the tasks are not ordered as i like. they execute in this cycle : task3 twice, task2 once, then again task3 twice and tas2 once and then task1 once.
I want them to execute iin this order : task1 then task2 then task3. Thank you!
Here is a portion of my code:
/* The period of the example software timer, specified in milliseconds, and
converted to ticks using the portTICK_RATE_MS constant. */
#define mainSOFTWARE_TIMER_PERIOD_MS ( 1000 / portTICK_RATE_MS )
int main(void)
{
/* Configure the system ready to run the demo. The clock configuration
can be done here if it was not done before main() was called. */
prvSetupHardware();
/* Create the queue used by the queue send and queue receive tasks.
http://www.freertos.org/a00116.html */
xQueue = xQueueCreate( mainQUEUE_LENGTH, /* The number of items the queue can hold. */
sizeof( uint32_t ) ); /* The size of each item the queue holds. */
/* Add to the registry, for the benefit of kernel aware debugging. */
vQueueAddToRegistry( xQueue, ( signed char * ) "MainQueue" );
/* Create the semaphore used by the FreeRTOS tick hook function and the
event semaphore task. */
vSemaphoreCreateBinary( xEventSemaphore );
/* Add to the registry, for the benefit of kernel aware debugging. */
vQueueAddToRegistry( xEventSemaphore, ( signed char * ) "xEventSemaphore" );
/* Create the MPXV7002DP task */
xTaskCreate( vMPXV7002DPTask, /* The function that implements the task. */
( signed char * ) "MPXV7002DP", /* Text name for the task, just to help debugging. */
configMINIMAL_STACK_SIZE, /* The size (in words) of the stack that should be created for the task. */
NULL, /* A parameter that can be passed into the task. Not used in this simple demo. */
configMAX_PRIORITIES - 1, /* The priority to assign to the task. tskIDLE_PRIORITY (which is 0) is the lowest priority. configMAX_PRIORITIES - 1 is the highest priority. */
NULL ); /* Used to obtain a handle to the created task. Not used in this simple demo, so set to NULL. */
/* Create the MPU9250 task */
xTaskCreate( vMPU9250Task,
( signed char * ) "MPU9250",
configMINIMAL_STACK_SIZE,
NULL,
configMAX_PRIORITIES - 1,
NULL );
/* Create the MPL3115A2 task */
xTaskCreate( vMPL3115A2Task,
( signed char * ) "MPL3115A2",
configMINIMAL_STACK_SIZE,
NULL,
configMAX_PRIORITIES - 1,
NULL );
/* Create the TOPC task */
//xTaskCreate( vToPcTask,
// ( signed char * ) "ToPc",
// configMINIMAL_STACK_SIZE,
// NULL,
//configMAX_PRIORITIES - 4,
//NULL );
/* Start the tasks and timer running. */
vTaskStartScheduler();
}
static void vMPXV7002DPTask(void *pvParameters)
{
int convertedValue,pressure,v;
for(;;)
{
if(xSemaphoreTake( xEventSemaphore, mainSOFTWARE_TIMER_PERIOD_MS ))
{USART_puts(USART1, "mpxv begin\n\r");
ADC_SoftwareStartConv(ADC1);//Start the conversion
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));//Processing the conversion
convertedValue = ADC_GetConversionValue(ADC1); //Return the converted dat
convertedValue=(5*convertedValue)/255;
pressure=(convertedValue+0.0625*4)-0.5;
v=sqrt((2*pressure)/1.293);
USART_puts(USART1, "mpxv end\n\r");
xSemaphoreGive( xEventSemaphore );
}
vTaskDelay( mainSOFTWARE_TIMER_PERIOD_MS );
}
}
static void vMPU9250Task(void *pvParameters)
{
int16_t xa,ya,za,xg,yg,zg,xm,ym,zm;
uint8_t res[22];
for( ;; )
{
if(xSemaphoreTake( xEventSemaphore, mainSOFTWARE_TIMER_PERIOD_MS ))
{USART_puts(USART1, "mpu begin\n\r");
SPI_Tx(0x25,0x0C|0x80);//read from slv0
SPI_Tx(0x26,0x03);//reg from which start reading
SPI_Tx(0x27,0x87);//read 7 bytes
SPI_Rx_seq(0x3A,res,22);
xa=((int16_t)res[1]<<8)|res[2];
xa/=8192;
ya=((int16_t)res[3]<<8)|res[4];
ya/=8192;
za=((int16_t)res[5]<<8)|res[6];
za/=8192;
xg=((int16_t)res[9]<<8)|res[10];
xg/=131;
yg=((int16_t)res[11]<<8)|res[12];
yg/=131;
zg=((int16_t)res[13]<<8)|res[14];
zg/=131;
//AK8963_Rx_seq( 0x03, mag_asax, 7);
//SPI_Rx_seq(0x49,mag_asax,7);
xm=((int16_t)res[16]<<8)|res[15];
ym=((int16_t)res[18]<<8)|res[17];
zm=((int16_t)res[20]<<8)|res[19];
USART_puts(USART1, "mpu end\n\r");
xSemaphoreGive( xEventSemaphore );
}
vTaskDelay( mainSOFTWARE_TIMER_PERIOD_MS/2 );
}
}
static void vMPL3115A2Task( void *pvParameters )
{
uint8_t altitude[3];
uint32_t x;
char alt[1];
for( ;; )
{
if(xSemaphoreTake( xEventSemaphore, mainSOFTWARE_TIMER_PERIOD_MS ))
{USART_puts(USART1, "mpl begin\n\r");
Read_IIC1_seq(0x60<<1, 0x01, altitude, 3);
x=altitude[0];x<<=8;
x|=altitude[1];x<<=8;
x|=altitude[2];x>>=4;
x/=49920;
USART_puts(USART1, "mpl end\n\r");
xSemaphoreGive( xEventSemaphore );
}
vTaskDelay( mainSOFTWARE_TIMER_PERIOD_MS/4 );
}
}

Look at the call to vTaskDelay() in each task function. One task delays for PERIOD, the next for PERIOD/2, and the third for PERIOD/4. The task that delays for PERIOD/4 will be ready to run four times for every time that the task that delays for PERIOD will be ready to run. This is why you are seeing one task run four times, the next two times, and the third once. Why did you use different delay periods if you want the tasks to run at the same rate?
As for which task runs first at the beginning, that is going to depend on how the FreeRTOS scheduler is implemented. You assigned the same priority (configMAX_PRIORITIES - 1) to each task in the calls to xTaskCreate(). The FreeRTOS scheduler is probably using its round-robin scheduling algorithm for tasks with the same priority. And I'm guessing that the scheduler readies the tasks in the order they were created (or maybe reverse order). So you might be able to affect the ready order by changing the creation order. But I'm just guessing and you should look at the source code for the FreeRTOS scheduler to learn what it does. Or maybe you should give the tasks different priorities. Then the FreeRTOS scheduler should make the task with the highest priority ready to run first.

Related

Callback parameter in stm32 freeRTOS software timer

I'm trying to use a software timer with cubeMx integration of freeRTOS (basically, it's freeRTOS with a nearly transparent layer above).
I thought I would be able to pass a pointer to a structure as timer parameter and getting it as a parameter in the callback function. Something like this:
typedef struct{
uint8_t a;
uint8_t b;
uint8_t c;
}T;
T t = {1, 2, 3};
osTimerDef(myTimer01, Callback01);
myTimer01Handle = osTimerCreate(osTimer(myTimer01), osTimerPeriodic, (void*) &t);
osTimerStart(myTimer01Handle, 5000);
callback:
void Callback01(void const * argument)
{
T* a = argument;
}
Unfortunately argument does not point to the same address as &t. When I look to freeRTOS code, it appears that the lib passes a structure "Timer_t" casted as void* to the callback function (see end of the code below):
static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow )
{
BaseType_t xResult;
Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );
/* Remove the timer from the list of active timers. A check has already
been performed to ensure the list is not empty. */
( void ) uxListRemove( &( pxTimer->xTimerListItem ) );
traceTIMER_EXPIRED( pxTimer );
/* If the timer is an auto reload timer then calculate the next
expiry time and re-insert the timer in the list of active timers. */
if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE )
{
/* The timer is inserted into a list using a time relative to anything
other than the current time. It will therefore be inserted into the
correct list relative to the time this task thinks it is now. */
if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) != pdFALSE )
{
/* The timer expired before it was added to the active timer
list. Reload it now. */
xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY );
configASSERT( xResult );
( void ) xResult;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Call the timer callback. */
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
}
The structure is:
typedef struct tmrTimerControl
{
const char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */
TickType_t xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */
UBaseType_t uxAutoReload; /*<< Set to pdTRUE if the timer should be automatically restarted once expired. Set to pdFALSE if the timer is, in effect, a one-shot timer. */
void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */
TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */
} xTIMER;
typedef xTIMER Timer_t;
This structure contains the data pointer I passed when I created the timer. It is stored in pvTimerId.
This means that I should cast the callback parameter as Timer_t in order to have access to pvTimerId. Something like this:
void Callback01(void const * argument)
{
T* a =((Timer_t*)argument)->pvTimerID;
}
BUT this Timer_t structure is not public. I don't really understand why the callback is called with this structure as parameter and moreover casted as const void*...
How should I do?
Considering the call to the osTimerCreate function in your version of cmsis stores realy the argument parameter to the pvTimerID of the Timer_t structure then you could use pvTimerGetTimerID to get your passed data back:
void Callback01(void const * argument)
{
T* data = (T*)pvTimerGetTimerID((TimerHandle_t)argument);
}

Simulate a Hardware Timer Interrupt in C

I want to understand RTOSs better and therefore started implementing a scheduler. I want to test my code, but unfortunately I have no HW lying around right now. What is an easy way to pretend executing an ISR corresponding to timer in C?
EDIT: Thanks to the answer of Sneftel I was able to simulate a timer interrupt. The code below is inspired by http://www.makelinux.net/alp/069. The only thing I am missing is to do it in a nested way. So if the ISR is running another timer interrupt would cause a new instance of the ISR preempting the first one.
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>
#include<signal.h>
#include<sys/time.h>
#include<string.h>
#ifdef X86_TEST_ENVIRONMENT
void simulatedTimer(int signum)
{
static int i=0;
printf("System time is %d.\n", i);
}
#endif
int main(void)
{
#ifdef X86_TEST_ENVIRONMENT
struct sigaction sa;
struct itimerval timer;
/* Install timer_handler as the signal handler for SIGVTALRM. */
memset (&sa, 0, sizeof (sa));
sa.sa_handler = &simulatedTimer;
sigaction (SIGVTALRM, &sa, NULL);
/* Configure the timer to expire after 250 msec... */
timer.it_value.tv_sec = 0;
timer.it_value.tv_usec = CLOCK_TICK_RATE_MS * 1000;
/* ... and every 250 msec after that. */
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = CLOCK_TICK_RATE_MS * 1000;
/* Start a virtual timer. It counts down whenever this process is executing. */
setitimer (ITIMER_VIRTUAL, &timer, NULL);
#endif
#ifdef X86_TEST_ENVIRONMENT
/* Do busy work. */
while (1);
#endif
return 0;
}
The closest thing in POSIX terms is probably signal handlers; SIGALRM is fired asynchronously within the process in much the same way that an ISR is. There's significant differences in what's safe to do, though, so I wouldn't go too far with the analogy.

STM32 HAL USART receive by interrupt

I have some trouble to receive data over the USART. What I actually want to achieve ist, that I can receive a command over USART with no specific length (only a maximum possible length). So I use the interrupt routine to check each character received, but I somehow still cannot achieve what I want. The routine is called each time I receive a new character, but somehow HAL_UART_Receive_IT(&huart1,rx_data,buff_size_rx) does not upgrade in realtime, then I don't see the received character when I check rx_data[pointer], but a few time later it is in the rx_data buffer.
What I have so far:
int pointer =0;
...
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
if ( USART1->ISR & UART_IT_TXE) {
}
if ( USART1->ISR & UART_IT_RXNE) {
HAL_UART_Receive_IT(&huart1,rx_data,buff_size_rx);
if(rx_data[pointer]=='\0') {
pointer=0;
readCommand(rx_data);
clearBuffer(rx_data,buff_size_rx);
} else {
pointer++;
if(pointer>=buff_size_rx) {
pointer=0;
}
}
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
HAL_UART_Receive_IT() is not meant to be called from an interrupt handler that way, but to initiate receiving a fixed number of bytes via interrupt.
A possible workaround is to check your input buffer after HAL_UART_IRQHandler() completes, i.e. in the /* USER CODE BEGIN USART1_IRQn 1 */ section. When a command is processed, you can reset pRxBuffPtr and RxXferCount in the handle structure to their original values to start from the start of the buffer again.
Another horrible possible workaround would be to call HAL_UART_Receive_IT() with a buffer size of 1, and set up a HAL_UART_RxCpltCallback() handler that checks the received byte each time, and calls HAL_UART_Receive_IT() again when necessary.
Of course you could do it without HAL, as PeterJ and others (always) suggest.
You've already implemented pin and interrupt setup, leave them unchanged at first.
Calculate the UART->BRR value according to the reference manual, or copy the relevant code from hal.
set UART->CR1=USART_CR1_RE|USART_CR1_TE|USART_CR1_UE|USART_CR1_RXNEIE; Now, you are getting interrupts.
In the interrupt function, read UART->SR into a temporary variable, and examine it.
Read UART->DR when there is a received byte waiting, do the error handling otherwise (later).
Get rid of the rest of the HAL calls when the above is working.
Interrupt response and processing time is often critical in embedded applications, and the HAL just wastes a lot of that.
The normal HAL library is not useful for continuous reception or commands with different length.
If you have the complete HAL package installed, you could look at the examples for the LowLevel interface.
Projects\STM32F411RE-Nucleo\Examples_LL\USART\USART_Communication_Rx_IT_Continuous
The main thing is to set you usart to continuous reception:
void Configure_USART(void) {
/* (1) Enable GPIO clock and configures the USART pins *********************/
/* Enable the peripheral clock of GPIO Port */
USARTx_GPIO_CLK_ENABLE();
/* Configure Tx Pin as : Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinMode(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_MODE_ALTERNATE);
USARTx_SET_TX_GPIO_AF();
LL_GPIO_SetPinSpeed(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_PULL_UP);
/* Configure Rx Pin as : Alternate function, High Speed, Push pull, Pull up */
LL_GPIO_SetPinMode(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_MODE_ALTERNATE);
USARTx_SET_RX_GPIO_AF();
LL_GPIO_SetPinSpeed(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
LL_GPIO_SetPinOutputType(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_PULL_UP);
/* (2) NVIC Configuration for USART interrupts */
/* - Set priority for USARTx_IRQn */
/* - Enable USARTx_IRQn */
NVIC_SetPriority(USARTx_IRQn, 0);
NVIC_EnableIRQ(USARTx_IRQn);
/* (3) Enable USART peripheral clock and clock source ***********************/
USARTx_CLK_ENABLE();
/* (4) Configure USART functional parameters ********************************/
/* TX/RX direction */
LL_USART_SetTransferDirection(USARTx_INSTANCE, LL_USART_DIRECTION_TX_RX);
/* 8 data bit, 1 start bit, 1 stop bit, no parity */
LL_USART_ConfigCharacter(USARTx_INSTANCE, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);
/* No Hardware Flow control */
/* Reset value is LL_USART_HWCONTROL_NONE */
// LL_USART_SetHWFlowCtrl(USARTx_INSTANCE, LL_USART_HWCONTROL_NONE);
/* Oversampling by 16 */
/* Reset value is LL_USART_OVERSAMPLING_16 */
// LL_USART_SetOverSampling(USARTx_INSTANCE, LL_USART_OVERSAMPLING_16);
/* Set Baudrate to 115200 using APB frequency set to 100000000/APB_Div Hz */
/* Frequency available for USART peripheral can also be calculated through LL RCC macro */
/* Ex :
Periphclk = LL_RCC_GetUSARTClockFreq(Instance); or
LL_RCC_GetUARTClockFreq(Instance); depending on USART/UART instance
In this example, Peripheral Clock is expected to be equal to
100000000/APB_Div Hz => equal to SystemCoreClock/APB_Div
*/
LL_USART_SetBaudRate(USARTx_INSTANCE, SystemCoreClock/APB_Div, LL_USART_OVERSAMPLING_16, 115200);
/* (5) Enable USART *********************************************************/
LL_USART_Enable(USARTx_INSTANCE);
}
The USART IT Handler should look like
void USARTx_IRQHandler(void)
{
/* Check RXNE flag value in SR register */
if(LL_USART_IsActiveFlag_RXNE(USARTx_INSTANCE) && LL_USART_IsEnabledIT_RXNE(USARTx_INSTANCE))
{
/* RXNE flag will be cleared by reading of DR register (done in call) */
/* Call function in charge of handling Character reception */
USART_CharReception_Callback();
}
else
{
/* Call Error function */
Error_Callback();
}
}
The last thing to set up is the Callback
void USART_CharReception_Callback(void);
Where you could put the bytes into an buffer and handle it in the main loop or where you want.
Since I stumbled over the problem today and could not find a good solution to it, I like to present a very simple one, using most of the HAL but avoiding the problems described...
Short verison of my approach is:
In the last user code section (for the appropriate USART instance, if using more than one) of void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle), enable the IRQ with:
__HAL_UART_ENABLE_IT(&huartx, UART_IT_RXNE);
Then put the desired code in your interrupt function:
void USART3_IRQHandler(void) {
/* USER CODE BEGIN USART3_IRQn 0 */
CallMyCodeHere();
return; // To avoid calling the default HAL handler at all
// (in case you want to save the time)
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3); // This is the CubeMX generated HAL handler
/* USER CODE BEGIN USART3_IRQn 1 */
/* USER CODE END USART3_IRQn 1 */
}
Don't use HAL_UART_Receive_IT anywhere, it will disable the IRQ and you need to re-enable it, if you want to get called with every reception.
The long version could be found in my post here...
Here is the full example of receiving data and idle line detection by interrupts:
Enable the receive interrupts and idle line detection in main.c:
/* USER CODE BEGIN USART2_Init 2 */
__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE); // enable receive intterupts
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // enable idle line detection
/* USER CODE END USART2_Init 2 */
Sort out the idle line event from within USARTx_IRQHandler in stm32f4xx_it.c:
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) {
__HAL_UART_CLEAR_IDLEFLAG(&huart2); // taken from https://electronics.stackexchange.com/questions/471272/setting-up-stm32-timer-for-uart-idle-detection#comment1353999_480556
uart2_idleHandler();
} else {
uart2_handler();
}
return;
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
Testing:
Create the following handlers:
char my_uart_buffer[256];
int my_uart_buffer_index = 0;
void uart2_handler(void){
char buff;
HAL_UART_Receive (&huart2, (uint8_t *)&buff, 1, 400);
my_uart_buffer[my_uart_buffer_index++] = buff;
}
void uart2_idleHandler(){
my_uart_buffer_index = 0;
}
Open a serial port client in your PC, setup as 115200 baud, 8N1.
Set a breakpoint to uart2_idleHandler().
Send "Hello world".
You can examine your buffer by p/c *my_uart_buffer#20 when breakpoint hits.
Example Project
Here is the full example that runs on STM32F407 Discovery board.
You can make it work using HAL! It may not be as elegant as other implementations but it is doable.
You have to create an error handler function and then the function that calls the HAL_UART_RCV_IT must flush the UART RX whenever there is an overrun error.
In addition, I work with two buffers. While the Interrupt is filling one buffer the main loop is emptying the other.
Here it how it is working well for me:
typedef enum
{
UARTREADY = 0,
UARTBUSY = 1,
UARTDATA = 2,
UARTERROR = 3
} enumUartStatus;
while(1){
if(UARTREADY == isUsart3RxReady()){
Usart3RxBuffer((char *)&SOMRxBytesBuffer[use_buffer_index], RCV_BUFFER_BANK_SIZE); // receive bytes in the raw buffer
if(use_buffer_index == RCV_BUFFER_BANK1_INDEX){
use_buffer_index = RCV_BUFFER_BANK2_INDEX;
rxb1_stats++;
}else{
use_buffer_index = RCV_BUFFER_BANK1_INDEX;
rxb2_stats++;
}
}
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if(huart == NULL){
return;
}
if(huart->Instance == USART3){
if(HAL_UART_GetError(huart) == HAL_UART_ERROR_FE){
Usart3Ready = UARTREADY;
}else{
Usart3Ready = UARTERROR;
}
}
}
void Usart3RxBuffer(char *buffer, unsigned short rxbuffersize){
/* Reset transmission flag */
if(Usart3Ready != UARTREADY)
{
return;
}
if(HAL_UART_GetState(&huart3) == HAL_UART_STATE_READY){
/*##-3- Put UART peripheral in reception process ###########################*/
if (HAL_UART_Receive_IT(&huart3, (uint8_t *)buffer, rxbuffersize) != HAL_OK)
{
// TODO: Error_Handler();
DEBUG_TRACE(DEBUG_MSK_MAIN, "UART3 error starting receiver!\r\n");
}else{
// An interrupt HAL_UART_ErrorCallback hit right here !!!!
// There is an overrun error happening here so we have to retry
// this is because we are using the Receive_IT in a continous communication and there is no handshake or flow control
if(Usart3Ready != UARTERROR){
/* Busy waiting to receive bytes */
Usart3Ready = UARTBUSY;
}
}
}
if(Usart3Ready == UARTERROR){
HAL_UART_AbortReceive_IT(&huart3);
Usart3Ready = UARTREADY;
}
}

Why xv6 scheduler calls sti() in the begining of every loop?

The companion book says
The reason to enable interrupts periodically on an idling CPU is that
there might be no RUNNABLE process because processes (e.g., the shell)
are waiting for I/O; if the scheduler left interrupts disabled all the
time, the I/O would never arrive.
But I think we just need to call sti() once before the outter for-loop, since everytime we release ptable.lock, the interrupts are enabled again.
It's possible that schedule() is called with interrupts disabled, in which case releasing the ptable spinlock will not reenable them.
If you look at the code for releasing a lock, you will see that it does not explicitly enable interrupts. Instead it uses the function popcli.
void release ( struct spinlock* lk )
{
...
popcli(); // enable interrupts
}
The function popcli does not always enable interrupts. It is used in tandem with pushcli to track nesting level. "Pushcli/popcli are like cli/sti except that they are matched: it takes two popcli to undo two pushcli". 1
void popcli ( void )
{
// If interrupts are enabled, panic...
if ( readeflags() & FL_IF )
{
panic( "popcli: interruptible" );
}
// Track depth of cli nesting
mycpu()->ncli -= 1;
// Popped more than were pushed...
if ( mycpu()->ncli < 0 )
{
panic( "popcli" );
}
// Reached outermost, so restore interrupt state
if ( mycpu()->ncli == 0 && mycpu()->intena )
{
sti(); // enable interrupts
}
}
Whereas popcli sometimes enables interrupts, pushcli always disables interrupts.
void pushcli ( void )
{
int eflags;
eflags = readeflags();
// Disable interrupts
cli();
// Save interrupt state at start of outermost
if ( mycpu()->ncli == 0 )
{
mycpu()->intena = eflags & FL_IF;
}
// Track depth of cli nesting
mycpu()->ncli += 1;
}
By explicitly calling sti, the scheduler overrides whatever the current push/popcli state is. I think this provides the brief window desired to allow an IO interrupt to occur. I.e. the period of time between the call to sti and the call to cli (via acquire -> pushcli -> cli).
void scheduler ( void )
{
...
for ( ;; )
{
// Enable interrupts on this processor.
sti();
// Acquire process table lock
acquire( &ptable.lock );
// Loop over process table looking for process to run.
for ( p = ptable.proc; p < &ptable.proc[ NPROC ]; p += 1 )
{
...
}
// Release process table lock
release( &ptable.lock );
}
}

TMS320F2812 FatFs f_write returns FR_DISK_ERR

I have problem with an SD card. I'm using the FatFs library ver R0.10b to access the SD card.
My code:
// .... //
FATFS fatfs;
FIL plik;
FRESULT fresult,res1,res2,res3,res4,res5;
UINT zapisanych_bajtow = 0 , br;
UINT zapianie_bajtow = 0;
char * buffor = "123456789abcdef\r\n";
unsigned short int i;
void main(void) {
// ... //
res1 = f_mount(0,&fatfs); // returns FA_OK
res2 = f_open( &plik, "f721.txt", FA_OPEN_ALWAYS | FA_WRITE ); // returns FA_OK
if( res2 == FR_OK )
{
res3 = f_write( &plik, ( const void * ) buffor, 17, &zapisanych_bajtow ); // returns FR_DISK_ERR
}
res4 = f_close( &plik );// returns FR_DISK_ERR
for(;;)
{
}
}
Any idea what might be wrong?
I had similar error with just one difference. I tried to write 4096bytes with f_write function at once. And it always returned FR_DISK_ERR.
And this was caused because I tried to write more then is size of IO buffer in FIL structure in FatFS (defined in ff.h).
typedef struct {
FATFS* fs; /* Pointer to the related file system object (**do not change order**) */
WORD id; /* Owner file system mount ID (**do not change order**) */
BYTE flag; /* Status flags */
BYTE err; /* Abort flag (error code) */
DWORD fptr; /* File read/write pointer (Zeroed on file open) */
DWORD fsize; /* File size */
DWORD sclust; /* File start cluster (0:no cluster chain, always 0 when fsize is 0) */
DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */
DWORD dsect; /* Sector number appearing in buf[] (0:invalid) */
DWORD dir_sect; /* Sector number containing the directory entry */
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */
DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
BYTE buf[_MAX_SS]; /* File private data read/write window */
} FIL;
The last array buf[_MAX_SS] is the file IO buffer. But _MAX_SS is user defined parameter (defined in ff.h) so you can decrease the amount of bytes written at once or eventually change the _MAX_SS value.
I know this is not your case because you only write 17 bytes at once, but this can be helpful for others.
It's few years when I finished with TMS but maybe it will help you:
FA_OPEN_ALWAYS Opens the file if it is existing. If not, a new file is created.
To append data to the file, use f_lseek() function after file open in this method.
If file does not exists use:
FA_CREATE_NEW Creates a new file. The function fails
with FR_EXIST if the file is existing.
I had the same issue with implementation of Chan FatFs on MSP430- always received FR_DISK_ERR result on calling disk_write().
My reason of the issue was the following:
operation failed on xmit_datablock() call, it returned 0.
xmit_datablock() failed because of xmit_spi_multi() failed.
xmit_spi_multi() failed because it was not enough to just transmit bytes from buffer.
It was necessary to read from RXBUF after every write.
Here it is how it looks after the issue was fixed:
/* Block SPI transfers */
static void xmit_spi_multi (
const BYTE* buff, /* Data to be sent */
UINT cnt /* Number of bytes to send */
)
{
do {
volatile char x;
UCA1TXBUF= *buff++; while(! (UCA1IFG & UCRXIFG)) ; x = UCA1RXBUF;
UCA1TXBUF= *buff++; while(! (UCA1IFG & UCRXIFG)) ; x = UCA1RXBUF;
} while (cnt -= 2);
}
Before fixing the issue there was no read from UCA1RXBUF following every write to UCA1TXBUF.
After fixing xmit_spi_multi() my issue with FR_DISK_ERR in disk_write() was solved.