7.5 Using SFRs
The Special Function Registers (SFRs) are registers which control aspects of the MCU operation or that of peripheral modules on the device. These registers are device memory mapped, which means that they appear at, and can be accessed using, specific addresses in the device’s data memory space. Individual bits within some registers control independent features. Some registers are read-only; some are write-only. See your device data sheet for more information.
Memory-mapped SFRs are accessed by special C variables that are placed at the address of the register. These variables can be accessed like any ordinary C variable so that no special syntax is required to access SFRs.
The SFR variable identifiers are predefined in header files and are
accessible once you have included the <xc.h>
header file (see Device Header Files) into your source code.
Structures with bit-fields are also defined so you may access bits within a register in
your source code.
A linker script file for the appropriate device must be linked into your
project to ensure the SFR variable identifiers are linked to the correct address. MPLAB X IDE will link in a default linker script, but a linker script file
must be explicitly specified if you are driving the command-line toolchain. Linker scripts
have a .gld
extension (e.g., p32AK1216GC41064.gld
).
The convention in the processor header files is that each SFR is named,
using the same name that appears in the data sheet for the part – for example,
CORCON
for the Core Control register. If the register has individual bits
that might be of interest, then there will also be a structure defined for that SFR and the
name of the structure will be the same as the SFR name, with “bits” appended. For example,
CORCONbits
for the Core Control register. The individual bits (or
bit-fields) are named in the structure using the names in the data sheet.
Here is the complete definition of CORCON
(subject to
change):
#define CORCON CORCON
extern volatile uint32_t CORCON __attribute__((__sfr__));
typedef struct tagCORCONBITS {
uint8_t IF:1;
uint8_t RND:1;
uint8_t :2;
uint8_t ACCSAT:1;
uint8_t SATDW:1;
uint8_t SATB:1;
uint8_t SATA:1;
uint8_t IPLST:3;
uint8_t :1;
uint8_t US:1;
uint8_t :3;
uint8_t :8;
uint8_t :8;
} CORCONBITS;
extern volatile CORCONBITS CORCONbits __attribute__((__sfr__));
CORCON
and
CORCONbits
refer to the same register and will resolve to the same
address at link time.See “MPLAB® XC32 Assembler, Linker and Utilities User’s Guide for PIC32A MCU” (DS-50003839) for more information on using linker scripts.
For example, the following is a sample real-time clock. It uses an SFR,
e.g., TMR1
, as well as bits within an SFR, e.g.,
T1CONbits.TCS
. Descriptions for these SFRs are found in the p32AK1216GC41064.h
file (this file will
automatically be included by <xc.h>
so you do not need to include this
into your source code). This file would be linked with the device specific linker script which
is p32AK1216GC41064.gld
.
Real-Time Clock
/*
** Configure TMR1 for 1ms ticks - PIC32A
*/
#pragma config FWDT_WDTEN = SW // Watchdog Timer Enable bit (WDT is controlled by software, use WDTCON.ON bit)
#include <xc.h>
#define SYS_CLK 8000000UL // running from FRC
#define STD_PERIPHERAL_CLK (SYS_CLK / 2) // standard peripheral clock is 1:2 to system clock
#define MS_PER_TICK 1
#define PR1_VAL (MS_PER_TICK * 1000) / (1000000.0 / STD_PERIPHERAL_CLK)
void tmr1_init(uint32_t ms_per_tick)
{
T1CONbits.TCS = 0; // select standard peripheral clock (1:2)
T1CONbits.TCKPS = 0; // do not divide clock source
// reset timer count
TMR1 = 0;
// set timer period
// time per tick / timer per clock tick
PR1 = (uint32_t)PR1_VAL;
// enable interrupts
_T1IF = 0;
_T1IE = 1;
// start the timer
T1CONbits.ON = 1;
}
void __attribute__((interrupt, no_auto_psv)) _T1Interrupt(void)
{
static uint32_t tick_count = 0;
tick_count++;
if (tick_count >= 1000) {
tick_count = 0;
_LATC3 ^= 1; // toggle LED for indication
}
// ack the IRQ
_T1IF = 0;
}
int main(void)
{
// LED indicator
_TRISC3 = 0;
_LATC3 = 0;
// initialize TMR1 for 1ms tick
tmr1_init(1);
// do nothing..
while (1) {
Nop();
}
}