Using C To Create Interrupt-Driven Systems On Blackfin® Processors
Using C To Create Interrupt-Driven Systems On Blackfin® Processors
a
Technical Notes on using Analog Devices' DSP components and development tools
Contact our technical support by phone: (800) ANALOG-D or e-mail: [email protected]
Or visit our on-line resources http://www.analog.com/dsp and http://www.analog.com/dsp/EZAnswers
Using C To Create Interrupt-Driven Systems On Blackfin Processors
Contributed by Joe B. May 28, 2003
Introduction
This Engineer-to-Engineer note will describe the
process for implementing an interrupt-driven
general-purpose timer routine for the Blackfin
Processor family using the C programming
language.
The referenced code in this application note was
verified using VisualDSP++ 3.1 for Blackfin
and the project was run on the EZ-KIT Lite
evaluation systems for both the ADSP-BF535
and the ADSP-BF533 Blackfin Processors
(ADDS-BF535-EZLITE, Rev 1.0 and ADDS-
BF533-EZLITE, Rev 1.0).
Timer Programming Model
When coding in a pure assembly environment,
configuration of the timers and use of interrupts
is fairly straightforward, as described in the
Hardware Reference Manuals. However, when
programming in C, using interrupts and
accessing the timer registers requires knowledge
of some of the system header files and an
understanding of the C run-time environment as
it relates to embedded system programming. The
process explained herein describes how to
implement an interrupt-driven timer routine in C,
but the methods employed can be applied to any
C-coded interrupt routines for the Blackfin
processors.
Two test platforms are necessary for this paper
because the demonstration utilizes the on-board
LEDs on the Blackfin evaluation platforms. On
the ADDS-BF535-EZKIT, the four LEDs are
mapped directly to the general purpose flag pins,
whereas, on the ADDS-BF533-EZKIT, the six
LEDs are accessible only through the on-board
flash port, which requires an alternate set of
instructions in the Interrupt Service Routine
(ISR) in order to update the LED display. The
differences in the software will be discussed
later.
Using Features In VisualDSP++
VisualDSP++ 3.1 comes with a set of header
files that makes programming the Blackfin
processors in a C environment simpler. These
headers can be found under the Blackfin include
directory and are named using the cdef prefix
to denote C definitions.
Memory-mapped registers are accessed in C via
a referencing-dereferencing scheme using casted
pointers, like this:
vol at i l e l ong *pI MASK = ( l ong *) I MASK;
IMASK is defined for the ADSP-BF535 in
defblackfin.h and for the ADSP-BF533 in
def_LPblackfin.h to be the address of the
IMASK register in Blackfin address space
(0xFFE02104). If the user wanted to set bit 0 of
the IMASK register in C, a proper 32-bit read-
modify-write would be performed indirectly
through the pointer defined above:
*pI MASK | = 0x1;
Using this convention, the label pIMASK is a
data element of type l ong i nt *, which
Copyright 2003, Analog Devices, Inc. All rights reserved. Analog Devices assumes no responsibility for customer product design or the use or application of
customers products or for any infringements of patents or rights of others which may result from Analog Devices assistance. All trademarks and logos are property
of their respective holders. Information furnished by Analog Devices Applications and Development Tools Engineers is believed to be accurate and reliable, however
no responsibility is assumed by Analog Devices regarding technical accuracy and topicality of the content provided in Analog Devices Engineer-to-Engineer Notes.
a
consumes 4 bytes of memory and whose sole
purpose is to hold the address of IMASK so that
the user can indirectly write a 1 to the register by
accessing it through its mapped address. This
convention wastes 4 bytes of memory for every
register accessed in this fashion!
An alternate method of manipulating the IMASK
register without consuming memory for the
pointer is to force a casted pointer in the code to
modify the address directly:
*( vol at i l e l ong *) I MASK | = 0x1;
In the Blackfin C-defines header files, all of the
memory-mapped registers have already been set
up for access in C in a series of #def i ne
statements, like this:
#def i ne pI MASK ( ( vol at i l e l ong *) I MASK)
This method allows the user to use the same C
syntax in their code, *pI MASK | = 0x1, to modify
the registers contents without dedicating
memory for the pointer. Instead, the compilers
pre-processor substitutes the casted pointer
syntax for what appears to be pointer-modifying
code. So, if one knows the name of the register,
one can easily access that register using standard
C code for pointer modification:
*pREGISTER_NAME = VALUE;
These cdef header files were created for each
individual processor based upon the widths of
the registers being represented (i.e., a 16-bit
register uses the shor t i nt type, which the
Blackfin compiler treats as 16-bit data, whereas a
32-bit register is represented as a l ong i nt or i nt
type).
Blackfin Interrupts (Hardware)
The Blackfin family of processors has an
intricate interrupt system that includes core
interrupts, enabled in the IMASK register, and
peripheral interrupts, which are configured in the
System Interrupt Controller (SIC). These
system-level peripheral interrupts are optionally
user-configurable in a set of System Interrupt
Assignment Registers: SIC_IAR0, SIC_IAR1,
and SIC_IAR2. Refer to Chapter 4 (Program
Sequencer) of the appropriate Hardware
Reference Manual (either ADSP-BF535 or
ADSP-BF533) for more information regarding
interrupt priority assignments.
Several peripheral sources can share the same
SIC interrupt assignment. Those sources with
common interrupt assignments share the same
General-purpose interrupt (IVGx). Table 4-8 of
the Hardware Reference Manual depicts the
IVG-Select Definitions, which is where the
values programmed into the specific fields of the
SIC_IARx registers are translated into an IVG
priority. Use the IVG priority detailed here to
determine which IMASK bit to set in the Core
Event Controller (CEC) to enable interrupt
handling for this group of peripherals.
In addition to having a group of interrupt sources
enabled in the CEC, handling for each sources
individual interrupt is enabled separately on the
SIC level in the SIC_IMASK register. For
example, one could set each peripherals IVG
priority to the same level, thus making one group
of 24 potential interrupt sources. If only one of
those peripheral interrupts is enabled in
SIC_IMASK, then the IVG effectively describes
that single interrupt source because that single
source is the only source enabled at the SIC level
to be recognized by the core as a peripheral
interrupt source.
An abridged overview of how to set peripheral
interrupts up correctly goes like this:
1. Enable peripherals individual interrupt in
SIC_IMASK register.
2. Program the interrupt priority levels into the
SIC_IARx registers. This step is optional.
3. Use the values in the SIC_IARx registers
with Table 4-8 of the Hardware Reference
Manual to determine which interrupt group
(IVGx) the peripheral of interest is assigned
to.
4. Set the appropriate IVGx bit in the IMASK
register.
Using C To Create Interrupt-Driven Systems On Blackfin Processors (EE-192) Page 2 of 9
a
This example uses the interrupt of the General
Purpose Timer 0 to generate LED activity on the
evaluation boards. Following the process
detailed above, the first step is to configure the
SIC_IMASK register. The Timer0 interrupt is
enabled in SIC_IMASK by setting either bit 14
(ADSP-BF535) or bit 16 (ADSP-BF533):
*pSI C_I MASK = 0x00004000; / / ADSP- BF535
*pSI C_I MASK = 0x00010000; / / ADSP- BF533