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);
}
Related
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;
}
}
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.
I'm working on a university project that involves a lot of programming in C, especially with Portaudio & ALSA. At the moment i'm trying to make a callback function to pass audio through, standard input/output job. I was wondering if anybody could tell me how to print the floats from my inputBuffer to display in real time in the terminal? Here is the internal structure of my callback function so far.
Thanks very much for your help in advance!
#define SAMPLE_RATE (44100)
#define PA_SAMPLE_TYPE paFloat32
#define FRAMES_PER_BUFFER (64)
typedef float SAMPLE;
static int audio_callback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
SAMPLE *out = (SAMPLE*)outputBuffer;
const SAMPLE *in = (const SAMPLE*)inputBuffer;
unsigned int i;
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) userData;
if( inputBuffer == NULL )
{
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = *in++; /* left - clean */
*out++ = *in++; /* right - clean */
}
}
return paContinue;
}
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.
I'm learning how to use Libevent.While I can't understand the difference between pending and active.In my opinion,when a event is added to a event_base and the event hasn't happened, then it's in pending state,while the event which is waited by the caller is happened,then in the active state,is that right?Hoever,when I read the description of event_pending,see the code blew,it says when the event is pending,it's bad to change the inner state of it,i think the word "pending" here is misunderstand,it should be "event_active"....Am i wrong ?
#include <event2/event.h>
#include <stdio.h>
/* Change the callback and callback_arg of 'ev', which must not be
* pending. */
int replace_callback(struct event *ev, event_callback_fn new_callback,
void *new_callback_arg)
{
struct event_base *base;
evutil_socket_t fd;
short events;
int pending;
pending = event_pending(ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT,
NULL);
if (pending) {
/* We want to catch this here so that we do not re-assign a
* pending event. That would be very very bad. */
fprintf(stderr,
"Error! replace_callback called on a pending event!\n");
return -1;
}
event_get_assignment(ev, &base, &fd, &events,
NULL /* ignore old callback */ ,
NULL /* ignore old callback argument */);
event_assign(ev, base, fd, events, new_callback, new_callback_arg);
return 0;
}
pending event means that some action triggered and execution of event callback is queued in event_base.
active event means that event callback is currently executed current thread or some other(do not forget that you can manipulate events from other thread, that is not running event loop)
Reassigning a pending event is not thread safe operation, you can see it in event.c.
I think if you writing single-threaded application it's safe to reassigning event at any time.
libevent book quote (http://www.wangafu.net/~nickm/libevent-book/Ref4_event.html):
Once you call a Libevent function to set up an event and associate it with an event base, it becomes initialized. At this point, you can add, which makes it pending in the base. When the event is pending, if the conditions that would trigger an event occur (e.g., its file descriptor changes state or its timeout expires), the event becomes active, and its (user-provided) callback function is run.
So "pending" in libevent terms means "just added in reactor". No action triggering need to get pending event. I've checked this behavior with simple program:
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <event.h>
void read_cb( int sock, short which, void *arg )
{
printf( "read_cb() called\n" );
fflush( stdout );
// while our callback is running event is active
}
int main()
{
struct event_base *base = event_base_new();
int fd[ 2 ];
assert( pipe( fd ) == 0 );
struct event ev;
event_set( & ev, fd[ 0 ], EV_READ | EV_PERSIST, read_cb, NULL );
event_base_set( base, & ev );
assert( event_add( & ev, NULL ) != -1 );
// it's pending now, just after adding
printf( "event_pending( ... ) == %d\n", event_pending( & ev, EV_READ, NULL ) );
fflush( stdout );
event_base_loop( base, EVLOOP_ONCE );
return 0;
}
Output:
event_pending( ... ) == 2