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 7.2 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 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., p30F6014.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 – for example PSV
for the PSV bit of the CORCON register.
Here is the complete definition of CORCON
(subject to
change):
/* CORCON: CPU Mode control Register */
extern volatile unsigned int CORCON __attribute__((__sfr__));
typedef struct tagCORCONBITS {
unsigned IF :1; /* Integer/Fractional mode */
unsigned RND :1; /* Rounding mode */
unsigned PSV :1; /* Program Space Visibility enable */
unsigned IPL3 :1;
unsigned ACCSAT :1; /* Acc saturation mode */
unsigned SATDW :1; /* Data space write saturation enable */
unsigned SATB :1; /* Acc B saturation enable */
unsigned SATA :1; /* Acc A saturation enable */
unsigned DL :3; /* DO loop nesting level status */
unsigned :4;
} 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® XC-DSC Assembler, Linker and Utilities User’s Guide” (DS-50003590) 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
p30F6014.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
p30F6014.gld
.
Sample Real-Time Clock
/*
** Sample Real Time Clock for dsPIC
**
** Uses Timer1, TCY clock timer mode
** and interrupt on period match
*/
#include <xc.h>
/* Timer1 period for 1 ms with FOSC = 20 MHz */
#define TMR1_PERIOD 0x1388
struct clockType
{
unsigned int timer; /* countdown timer, milliseconds */
unsigned int ticks; /* absolute time, milliseconds */
unsigned int seconds; /* absolute time, seconds */
} volatile RTclock;
void reset_clock(void)
{
RTclock.timer = 0; /* clear software registers */
RTclock.ticks = 0;
RTclock.seconds = 0;
TMR1 = 0; /* clear timer1 register */
PR1 = TMR1_PERIOD; /* set period1 register */
T1CONbits.TCS = 0; /* set internal clock source */
IPC0bits.T1IP = 4; /* set priority level */
IFS0bits.T1IF = 0; /* clear interrupt flag */
IEC0bits.T1IE = 1; /* enable interrupts */
SRbits.IPL = 3; /* enable CPU priority levels 4-7*/
T1CONbits.TON = 1; /* start the timer*/
}
void __attribute__((__interrupt__,__auto_psv__)) _T1Interrupt(void)
{ static int sticks=0;
if (RTclock.timer > 0) /* if countdown timer is active */
RTclock.timer -= 1; /* decrement it */
RTclock.ticks++; /* increment ticks counter */
if (sticks++ > 1000)
{ /* if time to rollover */
sticks = 0; /* clear seconds ticks */
RTclock.seconds++; /* and increment seconds */
}
IFS0bits.T1IF = 0; /* clear interrupt flag */
return;
}