You are on page 1of 6

Lesson 13

C Review

1. Overview

In this lesson, we will start writing program in C language. C programs provide some abstraction level that is
not available with assembly language. For example, when you write a C function, you don’t need to know
exactly what general purpose registers to use, or to keep track of the return addresses, or to maintain the stack
memory, etc. However, in order communicate with various peripherals and devices of the microcontroller, we
will need to directly access registers of these peripherals and devices. And since these registers are mapped into
the system memory address space, we will need to use C pointers extensively.

2. Review of C Pointer

The pointer data type corresponds to a memory address. A pointer variable is designated with a *. For example,

int x = 1, y = 5, z = 8, *ptr;

*ptr indicates that ptr is a pointer (i.e., a memory address) and it points to a location with the int data type. x, y,
and z are integer variables.

A conceptual diagram of the memory space after the compilation is shown in the figure below. An arrow is
used to indicate a pointer variable. Note that the arrow points to nowhere (i.e. null) because it is unassigned
initially. Variable x is assigned to locations 0x1000 to 0x1003 (4-byte integer); variable y is assigned to
locations 0x100C to 0x100F; and variable z is assigned to 0x1020 to 0x1023 as shown in the figure.

Address

PTR NULL

0x1000 X

?
0x100C Y

0x1020 Z

The & operator returns the address of a variable and is known as the address-of operator. For example,

ptr = &x ;

For the statement above, the address of x is assigned to ptr. The result is
1
Address

PTR NULL

0x1000 X

0x100C Y

0x1020 Z

The * operator returns the content pointed by the pointer and is known as the dereference operator. For
example,

y = *ptr ;
*ptr = z;

For the “y = *ptr” statement above, the content pointed by ptr is assigned to y. For the “*ptr = z” statement, the
value of z is stored to the location pointed by ptr. The result is

Address

PTR NULL

0x1000 X

0x100C Y

0x1020 Z

3. Register Access Using C

The memory addresses assigned to the registers of the I/O devices can be thought of as values of pointers. So,
we can use pointer operators to directly manipulate these registers. For example, address of the PINSEL4
register is 0x4002C010. We can define a pointer to point to this register as:
2
volatile unsigned int* PINSEL4_ptr = (unsigned int *) 0x4002C010;

The keyword volatile instructs the compiler that the value of the object may be modified without processor
interaction and thus optimizations should not be performed. When some level of optimization is done, the
pointer may be optimized away (removed).

To make your code more readable, you may want to use symbolic constants to replace the hard numbers by
using the #define directive. For example, we can define a pointer to point to PINSEL4 register as

#define PINSEL4_ADR 0x4002C010


volatile unsigned int* PINSEL4_ptr = (unsigned int *) PINSEL4_ADR;

We can read the content of PINSEL4 register as

unsigned int PINSEL4_value;


PINSEL4_value = *(PINSEL4_ptr);

4. C Techniques for Low-Level I/O Operations

C supports many bitwise operations:


~ operator: inverts bits,
& operator: and bits of two operands,
| operator: or bits of two operands,
^ operator: xor bits of two operands,

Bit Manipulation:

We can use the bitwise operators to manipulate a bit or a group of bits in a data object with a mask operand. For
example,

char mask = 0x60; // 0b01100000; will look at bit 6 and bit 5


char data = 0xb3; // 0b10110011; data
char a0, a1, a2, a3;

Question: What will be the results of a0, a1, a2 and a3 after the following statements are executed?

a0 = data & mask;

a0 = 0b00100000  isolate bits 6 and 5 from data


a1 = data & ~mask; // note ~mask = 0b10011111

a0 = 0b10010011  clear bits 6 and 5 of data to 0


a2 = data | mask;

a0 = 0b11110011  set bits 6 and 5 of data to 1


a3 = data ^ mask;

3
a0 = 0b11010011  invert bits 6 and 5 of data
5. Example: EINT0 Interrupt

Write a C code to setup EINT0 interrupt. Keep a count of the number of interrupt occurrences.

/*----------------------------------------------------------------------------
Lesson 13: C review (low level access): set up EINT0 interrupt
*---------------------------------------------------------------------------*/
#include "LPC17xx.h" // Device header

#define PINSEL4_ADR 0x4002C010


#define EXTMODE_ADR 0x4002C148
#define EXTPOLAR_ADR 0x4002C14C
#define ISER0_ADR 0xE000E100
#define EXTINT_ADR 0x400FC140

void EINT0_IRQHandler(void);
void EINT0_Init(void);
int EINT0_Interrupt_count; // variable stored at address 0x10000000

// Define pointers to registers


volatile unsigned int* PINSEL4_ptr = (unsigned int *) PINSEL4_ADR;
volatile unsigned int* EXTMODE_ptr = (unsigned int *) EXTMODE_ADR;
volatile unsigned int* EXTPOLAR_ptr = (unsigned int *) EXTPOLAR_ADR;
volatile unsigned int* EXTINT_ptr = (unsigned int *) EXTINT_ADR;
volatile unsigned int* ISER0_ptr = (unsigned int *) ISER0_ADR;

// Define global variables


volatile unsigned int PINSEL4_value, EXTMODE_value, EXTPOLAR_value;
volatile unsigned int ISER0_value, EXTINT_value;

/*----------------------------------------------------------------------------
Main: Initialize
*---------------------------------------------------------------------------*/
int main (void) {

EINT0_Interrupt_count = 16; // Initialize a count


EINT0_Init (); // Initialize EINT0 interrupt

while (1){} // endless loop -- do nothing, wait for interrupt


}

4
/*----------------------------------------------------------------------------
EINT0_Init: set up EINT0 to request interrup on falling edge
*---------------------------------------------------------------------------*/
void EINT0_Init(void)
{
// ------------------- Step 1 -------------------------------------------
//Setup pin to be EINT0 in PINSEL4 register (0x4002C010)
PINSEL4_value = *(PINSEL4_ptr); // read current value of PINSEL4
PINSEL4_value |= 0x00100000; // set bit 20
PINSEL4_value &= 0xffdfffff; // clear bit 21
*(PINSEL4_ptr) = PINSEL4_value; // write back to PINSEL4

//Setup Mode to be edge in EXTMODE register (0x400FC148)


EXTMODE_value = *(EXTMODE_ptr); // read current value of EXTMODE
EXTMODE_value |= 0x1; // set bit 0
*(EXTMODE_ptr) = EXTMODE_value; // write back to EXTMODE

//Setup Polarity to be falling edge in EXTPOLAR register (0x400FC14C)


EXTPOLAR_value = *(EXTPOLAR_ptr); // read current value of EXTPOLAR
EXTPOLAR_value &= 0xfffffffe; // clear bit 0
*(EXTPOLAR_ptr) = EXTPOLAR_value; // write back to EXTPOLAR

// -----Step 2: Enable interrupt for peripheral (EINT0) in ISER0 register


ISER0_value = *(ISER0_ptr); // read current value of ISER0
ISER0_value |= 0x00040000; // set bit 18
*(ISER0_ptr) = ISER0_value; // write back to ISER0

// -------- Step 3: Setup priority for peripheral (EINT0) -- default ok

// -------- Step 4: Enable global interrupt bit -- default ok

return;
}
/*----------------------------------------------------------------------------
EINT0_IRQHandler: EINT0 ISR (Step 5) --> keep a count
*---------------------------------------------------------------------------*/
void EINT0_IRQHandler(void){

// Clear EINT0 interrupt flag in EXTINT register (0x400FC140)


EXTINT_value = *(EXTINT_ptr); // read current value of EXTINT
EXTINT_value |= 0x1; // set bit 0
*(EXTINT_ptr) = EXTINT_value; // write back to EXTINT

// Processing: increment a count


EINT0_Interrupt_count += 1;

return;
}

5
Exercise: Write a program to verify the example above.

6. References

[1]. Joseph Yiu, The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors, Elsevier, 3rd ed, 2014.
[2]. Jonathan Valvano, Introduction to ARM Cortex-M Microcontroller, 4nd ed, 2013.
[3]. ARMv7-M Architecture Reference Manual, ARM Limited, 2010.
[4]. LPC17xx User manual, NXP Semiconductors, 2010.
[5]. Cortex-M3 Technical Reference Manual, ARM Limited, 2010.

You might also like