Fast Atomic Access of PIC32 SFRs using the MPLAB® XC32 Compiler

Last modified by Microchip on 2023/11/09 09:12

Like other Microchip devices, PIC32 devices use Special Function Registers (SFRs) to interact with peripherals. These registers can be accessed like ordinary variables in C source code when using the MPLAB® XC32 compiler, but since they directly affect or are directly affected by hardware, there can be read-modify-write problems associated with code that might normally be used to change individual bits with these registers.

To get around this problem, virtually all registers have an alternate register set that can be used to set, clear, or toggle the bits within the primary register. The names of the alternate registers consist of the primary register name appended with SET, CLR, or INV. Each of these alternate registers can be assigned a value in the usual way, but this write operation will instead set, clear, or toggle (respectively) bits within the primary register. Where a bit in the written value is one, this will trigger the appropriate operation in the primary register at the corresponding bit position. Thus, a plain assignment to the alternate registers can be used to replace assignment-OR, assignment-AND, or assignment-XOR (respectively) operations directly on the primary register.

The code to assign a value to an alternate register takes fewer instructions than performing the relevant operation on the primary register, and, importantly, a simple assignment is an atomic operation. Atomic operations cannot be interrupted and are safe to use in situations where the content of the destination register could be changed by interrupt code or by hardware.

Consider, for example, the LATA latch register. It can be read and written in the usual way, but in addition to this register, its alternate registers are defined: LATASET, LATACLR, and LATAINV. The following table shows the operation performed when writing to these alternate registers and the equivalent code which acts on the primary register and which this write replaces.

Operation on LATAAlternate register usageReplaces
set bit #6LATASET = 0x40;LATA |= 0x40;
clear bit #6LATACLR = 0x40;LATA &= ~0x40;
toggle bit #6LATAINV = 0x40;LATA ^= 0x40;

To make these statements more readable, predefined masks (accessible via <xc.h>) can be used rather than you having to use literal constants. The code in the above table could use the macro _LATA_LATA6_MASK instead of the literal constant 0x40.

Since bit masks are used with these registers, you can control as many bits as you like in one operation, as long as it is the same operation to be performed on each bit. To change two bits simultaneously, you could use the following code examples.​

1
2
3
4
// set bits #0 and #2
LATASET = _LATA_LATA0_MASK | _LATA_LATA2_MASK;
// clear bits #1 and #3
LATACLR = _LATA_LATA1_MASK | _LATA_LATA3_MASK;

Where the two masks are first OR-ed together to form the required bit mask. (Do not use the logical OR operator, ||.)

The values read back from the alternate registers are undefined, which means that you must only write to these registers and always read the primary register to obtain its content.