Estoy luchando con un problema que ya me está costando 2 días de trabajo productivo.
El código de main.c al final de la pregunta es una versión simplificada de todo el proyecto, y contiene el código relevante relacionado con el tema tratado.
Un breve resumen de toda la configuración: Microcontrolador: PIC32MX250F128B PICkit3 MPLAB X v4.20 Compilador XC32 v1.34 Windows 10 de 64 bits
Tengo un proyecto que implica recibir datos a través de UART y realizar ciertas funciones basadas en los datos recibidos. Se utiliza un búfer FIFO de software para almacenar temporalmente los datos recibidos para su procesamiento (ya que el FIFO de hardware es demasiado pequeño para la velocidad a la que se reciben los datos). El FIFO suave se rellena con los datos recibidos utilizando una rutina de interrupción. Estoy experimentando un problema muy peculiar al intentar compilar el proyecto (tanto en los modos de depuración como de versión).
Al principio pensé que el problema que se describe a continuación se debe a un problema de hardware (PICkit3 defectuoso), pero el problema persiste incluso si uso el simulador en lugar del PICkit3. Luego investigué sobre posibles problemas de MPLAB X, pero el mismo problema también persiste en varias versiones de MPLAB X (lo he probado con v4.05, v4.15 y v4.20). También he probado esto tanto en Windows 7 como en Windows 10, con exactamente los mismos resultados. Tras una investigación adicional, lo he reducido a la FIFO flexible y la función main () en main.c.
El FIFO flexible se define de la siguiente manera (fragmentos relevantes de main.c):
/* Software FIFO buffer size */
#define FIFO_BUFFER_SIZE (2100u)
/* Software FIFO buffer type */
typedef struct
{
char dataBuffer[FIFO_BUFFER_SIZE];
int firstByteIndex;
int lastByteIndex;
int numBytes;
} SOFT_FIFO;
/* Variables used with FIFO */
static SOFT_FIFO rxFifo = {{0}, 0, 0, 0};
static volatile BOOL fifoFlagBufEmpty = TRUE;
static volatile BOOL fifoFlagFull = FALSE;
static volatile BOOL fifoFlagOverflow = FALSE;
Aquí está la parte en main.c que he reducido aún más para estar relacionada con el problema:
void main(void)
{
char c;
/* ... initialisation code is here (see full main.c file)... */
/* ... higher-level infinite loop is here (see full main.c file)... */
/* While there is data in the software FIFO buffer */
while (fifoFlagBufEmpty == FALSE)
{
/* Process data in the FIFO buffer */
c = getByte();
processGeneralMessage(&c);
} /* while */
} /* main() */
Siempre que compilo el proyecto para la depuración (tanto para PICKit3 como para el simulador), todo el proceso de compilación simplemente se bloquea y nunca se completa. Debe eliminarse manualmente en el Administrador de tareas de Windows finalizando el proceso "cc1.exe". Después de dejarlo para intentar construir durante más de 2 horas, creo que es bastante seguro llegar a la conclusión de que no completará la construcción en absoluto.
La causa aparente del problema parece estar relacionada con la forma en que se define y declara la variable fifoFlagBufEmpty. Si elimino el especificador "volátil" de su definición:
//static volatile BOOL fifoFlagBufEmpty = TRUE; /* Old definition */
static BOOL fifoFlagBufEmpty = TRUE;
entonces el proyecto se compila perfectamente y el depurador comienza a ejecutar el código. Si devuelvo el especificador "volátil", la compilación se bloquea de nuevo y nunca se completa.
Lo que es aún más interesante es que cuando comento cualquiera de las siguientes dos líneas en el bloque while:
c = getByte();
processGeneralMessage(&c);
el proyecto se compila completamente y comienza a ejecutarse normalmente (como cuando quito el especificador "volátil")
No tengo ninguna explicación de por qué sucede esto. El problema más frustrante es que tengo exactamente la misma definición y uso del FIFO flexible en otro proyecto que usa otro PIC (PIC32MX664F128H) y funciona perfectamente allí. ¿Alguien puede ayudarme con esto e identificar por qué sucede esto?
Aquí está el archivo main.c completo:
/*------------------------------------------------------------------------------
Module : main.c
Microcontroller : PIC32MX250F128B
------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
CONFIGURATION BITS
------------------------------------------------------------------------------*/
// DEVCFG3
// USERID = No Setting
#pragma config PMDL1WAY = ON // Peripheral Module Disable Configuration (Allow only one reconfiguration)
#pragma config IOL1WAY = ON // Peripheral Pin Select Configuration (Allow only one reconfiguration)
#pragma config FUSBIDIO = ON // USB USID Selection (Controlled by the USB Module)
#pragma config FVBUSONIO = ON // USB VBUS ON Selection (Controlled by USB Module)
// DEVCFG2
#pragma config FPLLIDIV = DIV_2 // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20 // PLL Multiplier (20x Multiplier)
#pragma config UPLLIDIV = DIV_12 // USB PLL Input Divider (12x Divider)
#pragma config UPLLEN = OFF // USB PLL Enable (Disabled and Bypassed)
#pragma config FPLLODIV = DIV_2 // System PLL Output Clock Divider (PLL Divide by 2)
// DEVCFG1
#pragma config FNOSC = FRCPLL // Oscillator Selection Bits (Fast RC Osc with PLL)
#pragma config FSOSCEN = OFF // Secondary Oscillator Enable (Disabled)
#pragma config IESO = ON // Internal/External Switch Over (Enabled)
#pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled)
#pragma config FPBDIV = DIV_1 // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)
#pragma config FCKSM = CSDCMD // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))
#pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window Size is 25%)
// DEVCFG0
#pragma config JTAGEN = OFF // JTAG Enable (JTAG Disabled)
#pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (Communicate on PGEC1/PGED1)
#pragma config PWP = OFF // Program Flash Write Protect (Disable)
#pragma config BWP = OFF // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF // Code Protect (Protection Disabled)
/*------------------------------------------------------------------------------
INCLUDES
------------------------------------------------------------------------------*/
#define _SUPPRESS_PLIB_WARNING /* Suppress PLIB warnings */
#define _DISABLE_OPENADC10_CONFIGPORT_WARNING /* Suppress ADC warning */
#include <p32xxxx.h>
#include <plib.h>
/*------------------------------------------------------------------------------
DEFINES
------------------------------------------------------------------------------*/
#define SYS_CLOCK (40000000u) /* 40 MHz */
#define SYS_CLOCK_MHZ (SYS_CLOCK / 1000000)
#define PB_CLOCK (SYS_CLOCK) /* Use system clock for peripherals */
#define BAUD_RATE_COMMS (19200u)
#define UART_MODULE (1u)
#define BRGH_BIT (FALSE)
/* Missing interrupt defines in int_1xx_2xx_legacy.h */
#define mU1RXClearIntFlag() (IFS1CLR = _IFS1_U1RXIF_MASK)
#define mU1RXGetIntFlag() (IFS1bits.U1RXIF)
#define mU1RXGetIntEnable() (IEC1bits.U1RXIE)
#define mU1RXIntEnable(enable) (IEC1CLR = _IEC1_U1RXIE_MASK, IEC1SET = ((enable) << _IEC1_U1RXIE_POSITION))
#define mU1RXSetIntPriority(priority) (IPC8CLR = _IPC8_U1IP_MASK, IPC8SET = ((priority) << _IPC8_U1IP_POSITION))
/* Logic state definitions */
#define HIGH (1)
#define LOW (0)
/* Software FIFO buffer size */
#define FIFO_BUFFER_SIZE (2100u)
/* LED pin */
#define LED_PIN (LATBbits.LATB7)
/*------------------------------------------------------------------------------
LOCAL DATA TYPES
------------------------------------------------------------------------------*/
/* Software FIFO buffer type */
typedef struct
{
char dataBuffer[FIFO_BUFFER_SIZE];
int firstByteIndex;
int lastByteIndex;
int numBytes;
} SOFT_FIFO;
/*------------------------------------------------------------------------------
LOCAL FUNCTION PROTOTYPES
------------------------------------------------------------------------------*/
static unsigned int calcBRG(unsigned int baudRate,
BOOL BRGH_bit,
unsigned int clk);
static void initUART(unsigned int channel,
unsigned int baudRate,
BOOL BRGH_bit,
unsigned int clk);
static void initMain(void);
static void configureInterrupts(void);
static char getByte(void);
static void processGeneralMessage(unsigned char *c);
/*------------------------------------------------------------------------------
GLOBAL VARIABLES
------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
LOCAL VARIABLES
------------------------------------------------------------------------------*/
static char rxData;
static BOOL byteReceived = FALSE;
static int rxCount = 0;
static SOFT_FIFO rxFifo = {{0}, 0, 0, 0};
static volatile BOOL fifoFlagBufEmpty = TRUE;
static volatile BOOL fifoFlagFull = FALSE;
static volatile BOOL fifoFlagOverflow = FALSE;
/*------------------------------------------------------------------------------
GLOBAL FUNCTION DEFINITIONS
------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
Function : main
Purpose : Entry point of the system.
Parameters : None.
Returns : None.
Notes: : None.
------------------------------------------------------------------------------*/
void main(void)
{
char c;
/* Initialise the system */
initMain();
/* Wait for commands from the user interface */
while (1)
{
/* While there is data in the software FIFO buffer */
while (fifoFlagBufEmpty == FALSE)
{
/* Process data in the FIFO buffer */
c = getByte();
processGeneralMessage(&c);
} /* while */
} /* while */
} /* main() */
/*------------------------------------------------------------------------------
LOCAL FUNCTION DEFINITIONS
------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------
Function : initUART
Purpose : Initialises the UART peripheral.
Parameters : channel - which UART channel to initialise.
baudRate - baud rate used for UART comms
BRGH_bit - bit to indicate high/low speed baud rate generator
clk - clock speed of the PIC
Returns : None.
Notes : None.
------------------------------------------------------------------------------*/
static void initUART(unsigned int channel,
unsigned int baudRate,
BOOL BRGH_bit,
unsigned int clk)
{
/* Enable UART, BRGH = 0, simplex, RX idle LOW, 1 stop, no parity */
unsigned int uxMode = 0x8800;
/* Enable RX & TX */
unsigned int uxSta = 0x1400;
unsigned int brg;
brg = calcBRG(baudRate, BRGH_bit, clk);
switch (channel)
{
case 1:
U1BRG = brg;
U1MODE = uxMode;
U1STA = uxSta;
break;
case 2:
U2BRG = brg;
U2MODE = uxMode;
U2STA = uxSta;
break;
#if (UART_COUNT_MAX >= 3)
case 3:
U3BRG = brg;
U3MODE = uxMode;
U3STA = uxSta;
break;
#endif
} /* switch */
} /* initUART() */
/*------------------------------------------------------------------------------
Function : calcBRG
Purpose : Calculates the BRG value required for the UART baud rate
generator initialisation.
Parameters : baudRate - baud rate used for UART comms
BRGH_bit - bit to indicate high/low speed baud rate generator
clk - clock speed of the PIC
Returns : BRG value
Notes : The formula used to calculate the BRG value is obtained from the
PIC32 Family Reference Manual, sect. 21 (UART) - in section 21.3.
------------------------------------------------------------------------------*/
static unsigned int calcBRG(unsigned int baudRate,
BOOL BRGH_bit,
unsigned int clk)
{
unsigned int BRGval;
if (BRGH_bit == FALSE) /* low speed baud rate generator used */
{
BRGval = (clk / (16 * baudRate)) - 1;
}
else /* high speed baud rate generator used */
{
BRGval = (clk / (4 * baudRate)) - 1;
}
return BRGval;
} /* calcBRG() */
/*------------------------------------------------------------------------------
Function : initMain
Purpose : Main initialisation routine.
Parameters : None.
Returns : None.
Notes : None.
------------------------------------------------------------------------------*/
void initMain(void)
{
/* Initialise the system clock */
SYSTEMConfigPerformance(SYS_CLOCK);
/* Initialise the UART peripheral */
initUART(1, BAUD_RATE_COMMS, FALSE, PB_CLOCK);
/* Configure interrupts */
configureInterrupts();
/* Disable JTAG */
DDPCONbits.JTAGEN = 0;
TRISBbits.TRISB7 = 0; /* LED pin - output */
U1RXR = 0x00; /* U1RX on RA2 */
RPB3R = 0x01; /* U1TX on RB3 */
} /* initMain() */
/*------------------------------------------------------------------------------
Function : configureInterrupts
Purpose : Configure the interrupts used in this module.
Parameters : None.
Returns : None.
Notes : None.
------------------------------------------------------------------------------*/
static void configureInterrupts(void)
{
/* Configure UART interrupt */
mU1RXSetIntPriority(1);
INTEnableSystemMultiVectoredInt();
mU1RXIntEnable(HIGH);
mU1RXClearIntFlag();
} /* configureInterrupts() */
/*------------------------------------------------------------------------------
Function : getByte
Purpose : Retrieve the next available data byte from the FIFO buffer.
Parameters : None.
Returns : Data byte.
Notes : None.
------------------------------------------------------------------------------*/
static char getByte(void)
{
char byteValue = -1;
/* If data exists in the buffer */
if (rxFifo.numBytes > 0)
{
byteValue = rxFifo.dataBuffer[rxFifo.firstByteIndex];
rxFifo.firstByteIndex++;
rxFifo.numBytes--;
//rxFifo.lastByteIndex--;
}
/* NOTE: this condition should actually never be reached */
else /* If the software FIFO buffer is empty */
{
asm("nop"); /* TODO: Doen dalk iets hier? */
//fifoFlagBufEmpty = TRUE; /* (Now it IS empty) */
//return byteValue; /* with value = -1, indicating no data */
}
/* If the software FIFO buffer is empty, after the above read */
/* This is the same as (numBytes == 0) */
if (rxFifo.firstByteIndex == rxFifo.lastByteIndex)
{
fifoFlagBufEmpty = TRUE;
/* Note that numBytes should now also be 0 */
}
/* If the index has reached the end of the buffer */
if (rxFifo.lastByteIndex == FIFO_BUFFER_SIZE)
{
rxFifo.lastByteIndex = 0; /* Roll-over the index counter */
}
/* If at the end of the buffer */
if (rxFifo.firstByteIndex == FIFO_BUFFER_SIZE)
{
rxFifo.firstByteIndex = 0; /* Roll-over index counter */
}
return byteValue;
} /* getByte() */
/*------------------------------------------------------------------------------
Function : processGeneralMessage
Purpose : Process general incoming messages from console system.
Parameters : c - pointer to one byte of the incoming message.
Returns : None.
Notes : None.
------------------------------------------------------------------------------*/
static void processGeneralMessage(unsigned char *c)
{
LED_PIN ^= 1; /* Toggle the LED pin */
} /* processGeneralMessage() */
/*------------------------------------------------------------------------------
Function : uartRxInterruptHandler
Purpose : Interrupt handler for the UART receiver.
Parameters : None.
Returns : None.
Notes : None.
------------------------------------------------------------------------------*/
//void __ISR(_UART1_RX_IRQ, ipl1auto)uartRxInterruptHandler(void)
void __ISR(_UART1_VECTOR, ipl1auto)uartRxInterruptHandler(void)
{
/* If software FIFO buffer is full */
if (rxFifo.numBytes == FIFO_BUFFER_SIZE)
{
fifoFlagOverflow = TRUE;
}
/* If the software FIFO buffer is not full */
else if (rxFifo.numBytes < FIFO_BUFFER_SIZE)
{
/* Read a byte from the UART receive (hardware) FIFO */
rxFifo.dataBuffer[rxFifo.lastByteIndex] = U1RXREG;
rxFifo.lastByteIndex++;
rxFifo.numBytes++;
}
/* If buffer has now been filled up after the byte read above */
if (rxFifo.numBytes == FIFO_BUFFER_SIZE)
{
fifoFlagFull = TRUE;
/* TODO: Besluit nog wat hier gedoen moet word */
}
/* TODO: skuif hierdie iewers anders heen om reg te hanteer */
fifoFlagBufEmpty = FALSE;
mU1RXClearIntFlag();
} /* ISR */