LPC810M021FN8
http://www.nxp.com/products/microcontrollers/cortex_m0_m0/LPC810M021FN8.html
The LPC810M021FN8 is an ARM Cortex-M0+ based, low-cost 32-bit MCU operating at CPU frequencies of up to 30 MHz.
The peripheral complement of the LPC810M021FN includes 4 kB of flash memory, 1 kB of data memory, CRC engine, one I²C-bus interface, two USARTs, one SPI interface, multi-rate timer, self wakeup timer, state-configurable timer, one comparator, function-configurable I/O ports through a switch matrix, an input pattern match engine and 6 general purpose I/O pins.
Features and benefits
System:
ARM Cortex-M0+ processor, running at frequencies of up to 30 MHz with single-cycle multiplier and fast single-cycle I/O port
ARM Cortex-M0+ built-in Nested Vectored Interrupt Controller (NVIC)
System tick timer
Serial Wire Debug (SWD) and JTAG boundary scan modes supported
Micro Trace Buffer (MTB) supported
Memory:
4 kB on-chip flash programming memory
1 kB SRAM
Boot ROM API support:
Boot loader
USART drivers
I²C drivers
Power profiles
Flash In-Application Programming (IAP) and In-System Programming (ISP)
Digital peripherals:
High-speed GPIO interface connected to the ARM Cortex-M0+ IO bus with 6 General Purpose I/O (GPIO) pins with configurable pull-up/pull-down resistors
GPIO interrupt generation capability with boolean pattern-matching feature on eight GPIO inputs
Switch matrix for flexible configuration of each I/O pin function
State Configurable Timer (SCT) with input and output functions (including capture and match) assigned to pins through the switch matrix
Multiple-channel multi-rate timer for repetitive interrupt generation at up to four programmable, fixed rates
Self Wake-up Timer (WKT) clocked from either the IRC or a low-power, low-frequency internal oscillator
CRC engine
Windowed Watchdog timer
Analog peripherals:
Comparator with external voltage reference with pin functions assigned or enabled through the switch matrix
Serial interfaces:
Two USART interfaces with pin functions assigned through the switch matrix
One SPI controller with pin functions assigned through the switch matrix
One I²C-bus interface with pin functions assigned through the switch matrix
Clock generation:
12 MHz internal RC oscillator trimmed to 1 % accuracy that can optionally be used as a system clock
Crystal oscillator with an operating range of 1 MHz to 25 MHz
Programmable watchdog oscillator with a frequency range of 9.4 kHz to 2.3 MHz
10 kHz low-power oscillator for the WKT
PLL allows CPU operation up to the maximum CPU rate without the need for a high-frequency crystal. May be run from the system oscillator, the external clock input CLKIN, or the internal RC oscillator
Clock output function with divider that can reflect the crystal oscillator, the main clock, the IRC, or the watchdog oscillator
Power control:
Integrated PMU (Power Management Unit) to minimize power consumption
Reduced power modes: Sleep mode, Deep-sleep mode, Power-down mode, and Deep power-down mode
Power-On Reset (POR)
Brownout detect
Unique device serial number for identification
Single power supply
Available as DIP8 package
Applications
8/16-bit applications
Consumer
Climate control
Lighting
Motor control
Fire and security applications
LPC810 Mini Starter Pack - US$12.95
Description
If you like the speed and simplicity of ARM Cortex M0+ chips, but want to just dip a toe in, you'll fall in love with the adorable little LPC810. This particular chip in DIP8 really jumped out at us since it's so different than what people usually think of when they hear 'ARM'. The DIP8 LPC810 is still somewhat of a challenge to use precisely because it's so small (by ARM standards, anyway): 4KB flash and 1KB SRAM. But the low price, 32-bit processor, 12MHz internal oscillator, I2C/SPI/UART with configurable pin-matrix, and built-in serial bootloader is quite nice.
To get you started, this pack contains an LPC810 DIP chip, two LEDs with matching 560 ohm resistors, two tact switches with matching 10K pullups, a MCP1700-3.3V regulator + 2 x 0.1uF capacitors and a USB-serial programming/debug cable. All you need is a breadboard and a computer to follow our step-by-step starter tutorial written by KTOWN (resident ARM Core Master).
.END
LPC1114 LPCXpresso board - LPC Tools
LPC1114 LPCXpresso board
The PC1114 LPCXpresso board with NXP's ARM Cortex-M0 microcontroller has been designed to make it as easy as possible to get started with Cortex-M0. The LPCXpresso comprises a target board combined with a JTAG debugger. A free Eclipse-based IDE from Code Red is also included.
The LPC1114 has 8 kB SRAM, 32 kB Flash, SSP, I2C, UART, ADC, etc. A Prototype board and a Base board that makes it possible to make experiments and prototyping with many peripherals are also available.
Overview
The LPC1114 LPCXpresso board with NXP's ARM Cortex-M0 microcontroller is part of NXP's low-cost development toolchain for LPC families. It has been jointly developed by Embedded Artists, Code Red, and NXP. It is an end-to-end solution for creating applications all the way from evaluation through to production. Here are some of the highlights:
• The target board comes with an integrated JTAG Debugger. No need for a separate emulator! • A free Eclipse-based IDE and compiler is included (128K download limit) • Easy upgrade options to full-blown suites (from Code Red) and hardware kits (from Embedded Artists).
| |||||||||||||||||||||||||||||||||
| Processor | NXP's Cortex-M0 LPC1114 microcontroller in LQFP48 package |
| Flash | 32 kB |
| Data Memory | 8 kB |
| Clock Crystals | 12.000 MHz crystal for CPU |
| Dimensions | 35 x 140 mm |
| Power | 3.15V-3.3V external powering, or from USB via JTAG probe (LPC-LINK) |
| Connectors | All LPC1114 pins available on expansion connector (2x27 pin rows, 100 mil pitch, 900 mil between rows) |
| Other | • Embedded JTAG (LPC-LINK) functionality via LPCXpresso toolchain • LPC-LINK can be connected to external target processor after modifications to the LPCXpresso board • LED on PIO0_7 |
LPC1114 specifications
| Processor | ARM Cortex-M0 core with speed up to 50 MHz |
| Flash | 32 kB |
| RAM | 8 kB |
| Timers | • Two 32-bit counter/timers • Two 16-bit counter/timers • One Watchdog timer |
| PWM | 13 - Using timers 0-3 |
| ADC | 10-bit with input multiplexing among 8 pins |
| Serial interfaces | 1xUART, 1xI2C, 2xSSP |
| Other | • Serial Wire Debug • Integrated PMU to minimize power consumption • Three reduced power modes: Sleep, Deep-sleep, and Deep power-down • Code Read Protection (CRP) with different security levels |
Support
The following resources are available for download.
Documents- LPCXpresso Getting Started Manual (pdf from NXP)
- Board Schematics (in pdf)
Forum and Support
- Visit NXP's LPCXpresso Website
- LPCXpresso Forum
- LPCXpresso Support (lots of code example)
.END
New port pin direction setting function written OK
Now I have written and tested ok a new set port pin direction function with gpio structure pointer as parameter.
void setPortPinDirection01(LPC_GPIO_TypeDef *gpio_struct_ptr, int pinNumber)
.END
// ****************************************************************************
// Program - Blinky
// Description - Blink LED
// Author - TL Fong
// Version - 0.42
// Date - 2013jul12hkt2144
// License - Free
// Hardware - Somy ARM Cortex M0 LPC1114/301 Learning Board
// Software - Keil uVision 4.71.2.0 ARM CC
// Method - Incremental testing
// Hardware configuration notes
// 1. LED1 is connected to PIO1-8
// ****************************************************************************
#include <stdio.h>
#include "LPC11xx.h"
#include "timer.h"
void setPortPinDirection(int portNumber, int pinNumber)
{
LPC_GPIO_TypeDef *gpio_struct_ptr;
gpio_struct_ptr = LPC_GPIO1;
switch( portNumber )
{
case 1:
// The following 4 statements do the same thing.
// LPC_GPIO1->DIR |= (1 << pinNumber);
// (*LPC_GPIO1).DIR |= (1 << pinNumber);
// (*gpio_struct_ptr).DIR |= (1 << pinNumber);
// gpio_struct_ptr->DIR |= (1 << pinNumber);
gpio_struct_ptr->DIR |= (1 << pinNumber);
break;
case 2:
LPC_GPIO2->DIR |= (1 << pinNumber);
break;
default:
break;
}
}
void setPortPinDirection01(LPC_GPIO_TypeDef *gpio_struct_ptr, int pinNumber)
{
gpio_struct_ptr->DIR |= (1 << pinNumber);
}
void setPortPinValue(int portNumber, int pinNumber, int pinValue)
{
switch( portNumber )
{
case 1:
if (pinValue == 1)
LPC_GPIO1->DATA &= ~(1 << pinNumber);
else
LPC_GPIO1->DATA |= (1 << pinNumber);
break;
case 2:
;
break;
default:
break;
}
}
void blinkLed(int ledNumber, int onTime, int offTime, int blinkCount)
{
int count;
switch( ledNumber )
{
case 1:
setPortPinDirection(1, 8);
for (count = 0; count < blinkCount; count--)
{
setPortPinValue(1, 8, 0);
delayTenthSecond(onTime);
setPortPinValue(1, 8, 1);
delayTenthSecond(offTime);
}
break;
case 2:
;
break;
default:
break;
}
}
int main()
{
setPortPinDirection01(LPC_GPIO1, 8);
blinkLed(1, 5, 10, 20); // Led 1, off 0.5 sec, on 1 sec, blink 20 times
}
// ****************************************************************************
// End of Program
// ****************************************************************************
void setPortPinDirection01(LPC_GPIO_TypeDef *gpio_struct_ptr, int pinNumber)
.END
// ****************************************************************************
// Program - Blinky
// Description - Blink LED
// Author - TL Fong
// Version - 0.42
// Date - 2013jul12hkt2144
// License - Free
// Hardware - Somy ARM Cortex M0 LPC1114/301 Learning Board
// Software - Keil uVision 4.71.2.0 ARM CC
// Method - Incremental testing
// Hardware configuration notes
// 1. LED1 is connected to PIO1-8
// ****************************************************************************
#include <stdio.h>
#include "LPC11xx.h"
#include "timer.h"
void setPortPinDirection(int portNumber, int pinNumber)
{
LPC_GPIO_TypeDef *gpio_struct_ptr;
gpio_struct_ptr = LPC_GPIO1;
switch( portNumber )
{
case 1:
// The following 4 statements do the same thing.
// LPC_GPIO1->DIR |= (1 << pinNumber);
// (*LPC_GPIO1).DIR |= (1 << pinNumber);
// (*gpio_struct_ptr).DIR |= (1 << pinNumber);
// gpio_struct_ptr->DIR |= (1 << pinNumber);
gpio_struct_ptr->DIR |= (1 << pinNumber);
break;
case 2:
LPC_GPIO2->DIR |= (1 << pinNumber);
break;
default:
break;
}
}
void setPortPinDirection01(LPC_GPIO_TypeDef *gpio_struct_ptr, int pinNumber)
{
gpio_struct_ptr->DIR |= (1 << pinNumber);
}
void setPortPinValue(int portNumber, int pinNumber, int pinValue)
{
switch( portNumber )
{
case 1:
if (pinValue == 1)
LPC_GPIO1->DATA &= ~(1 << pinNumber);
else
LPC_GPIO1->DATA |= (1 << pinNumber);
break;
case 2:
;
break;
default:
break;
}
}
void blinkLed(int ledNumber, int onTime, int offTime, int blinkCount)
{
int count;
switch( ledNumber )
{
case 1:
setPortPinDirection(1, 8);
for (count = 0; count < blinkCount; count--)
{
setPortPinValue(1, 8, 0);
delayTenthSecond(onTime);
setPortPinValue(1, 8, 1);
delayTenthSecond(offTime);
}
break;
case 2:
;
break;
default:
break;
}
}
int main()
{
setPortPinDirection01(LPC_GPIO1, 8);
blinkLed(1, 5, 10, 20); // Led 1, off 0.5 sec, on 1 sec, blink 20 times
}
// ****************************************************************************
// End of Program
// ****************************************************************************
.END
LPC_GPIO_TypeDef structure pointer problem solved !!!
It took me 2 hours debugging to find out that I made a couple of mistakes.
1. lpc1114 memory address seems to be unsigned long. So if declared just int might
cause trouble.
2. LPC_GPIO_TypeDef is a structure type.
3. LPC_GPIO1 is pointer to LPC_GPIO_TypeDef.
4. LPC_GPIO_TypeDef *p; // p is a pointer to LPC_GPIO_TypeDef.
5. Therefore the following 4 statements are equivalent.
// LPC_GPIO1->DIR |= (1 << pinNumber);
// (*LPC_GPIO1).DIR |= (1 << pinNumber);
// (*gpio_struct_ptr).DIR |= (1 << pinNumber);
// gpio_struct_ptr->DIR |= (1 << pinNumber);
6. The following functions is tested good.
void setPortPinDirection(int portNumber, int pinNumber)
{
LPC_GPIO_TypeDef *gpio_struct_ptr;
gpio_struct_ptr = LPC_GPIO1;
switch( portNumber )
{
case 1:
// The following 4 statements do the same thing.
// LPC_GPIO1->DIR |= (1 << pinNumber);
// (*LPC_GPIO1).DIR |= (1 << pinNumber);
// (*gpio_struct_ptr).DIR |= (1 << pinNumber);
// gpio_struct_ptr->DIR |= (1 << pinNumber);
gpio_struct_ptr->DIR |= (1 << pinNumber);
break;
case 2:
LPC_GPIO2->DIR |= (1 << pinNumber);
break;
default:
break;
}
}
.END
// ****************************************************************************
// Program - Blinky
// Description - Blink LED
// Author - TL Fong
// Version - 0.41
// Date - 2013jul12hkt2126
// License - Free
// Hardware - Somy ARM Cortex M0 LPC1114/301 Learning Board
// Software - Keil uVision 4.71.2.0 ARM CC
// Method - Incremental testing
// Hardware configuration notes
// 1. LED1 is connected to PIO1-8
// ****************************************************************************
#include <stdio.h>
#include "LPC11xx.h"
#include "timer.h"
void setPortPinDirection(int portNumber, int pinNumber)
{
LPC_GPIO_TypeDef *gpio_struct_ptr;
gpio_struct_ptr = LPC_GPIO1;
switch( portNumber )
{
case 1:
// The following 4 statements do the same thing.
// LPC_GPIO1->DIR |= (1 << pinNumber);
// (*LPC_GPIO1).DIR |= (1 << pinNumber);
// (*gpio_struct_ptr).DIR |= (1 << pinNumber);
// gpio_struct_ptr->DIR |= (1 << pinNumber);
gpio_struct_ptr->DIR |= (1 << pinNumber);
break;
case 2:
LPC_GPIO2->DIR |= (1 << pinNumber);
break;
default:
break;
}
}
void setPortPinValue(int portNumber, int pinNumber, int pinValue)
{
switch( portNumber )
{
case 1:
if (pinValue == 1)
LPC_GPIO1->DATA &= ~(1 << pinNumber);
else
LPC_GPIO1->DATA |= (1 << pinNumber);
break;
case 2:
;
break;
default:
break;
}
}
void blinkLed(int ledNumber, int onTime, int offTime, int blinkCount)
{
int count;
switch( ledNumber )
{
case 1:
setPortPinDirection(1, 8);
for (count = 0; count < blinkCount; count--)
{
setPortPinValue(1, 8, 0);
delayTenthSecond(onTime);
setPortPinValue(1, 8, 1);
delayTenthSecond(offTime);
}
break;
case 2:
;
break;
default:
break;
}
}
int main()
{
setPortPinDirection(1, 8);
blinkLed(1, 5, 10, 20); // Led 1, off 0.5 sec, on 1 sec, blink 20 times
}
// ****************************************************************************
// End of Program
// ****************************************************************************
1. lpc1114 memory address seems to be unsigned long. So if declared just int might
cause trouble.
2. LPC_GPIO_TypeDef is a structure type.
3. LPC_GPIO1 is pointer to LPC_GPIO_TypeDef.
4. LPC_GPIO_TypeDef *p; // p is a pointer to LPC_GPIO_TypeDef.
5. Therefore the following 4 statements are equivalent.
// LPC_GPIO1->DIR |= (1 << pinNumber);
// (*LPC_GPIO1).DIR |= (1 << pinNumber);
// (*gpio_struct_ptr).DIR |= (1 << pinNumber);
// gpio_struct_ptr->DIR |= (1 << pinNumber);
6. The following functions is tested good.
void setPortPinDirection(int portNumber, int pinNumber)
{
LPC_GPIO_TypeDef *gpio_struct_ptr;
gpio_struct_ptr = LPC_GPIO1;
switch( portNumber )
{
case 1:
// The following 4 statements do the same thing.
// LPC_GPIO1->DIR |= (1 << pinNumber);
// (*LPC_GPIO1).DIR |= (1 << pinNumber);
// (*gpio_struct_ptr).DIR |= (1 << pinNumber);
// gpio_struct_ptr->DIR |= (1 << pinNumber);
gpio_struct_ptr->DIR |= (1 << pinNumber);
break;
case 2:
LPC_GPIO2->DIR |= (1 << pinNumber);
break;
default:
break;
}
}
.END
// ****************************************************************************
// Program - Blinky
// Description - Blink LED
// Author - TL Fong
// Version - 0.41
// Date - 2013jul12hkt2126
// License - Free
// Hardware - Somy ARM Cortex M0 LPC1114/301 Learning Board
// Software - Keil uVision 4.71.2.0 ARM CC
// Method - Incremental testing
// Hardware configuration notes
// 1. LED1 is connected to PIO1-8
// ****************************************************************************
#include <stdio.h>
#include "LPC11xx.h"
#include "timer.h"
void setPortPinDirection(int portNumber, int pinNumber)
{
LPC_GPIO_TypeDef *gpio_struct_ptr;
gpio_struct_ptr = LPC_GPIO1;
switch( portNumber )
{
case 1:
// The following 4 statements do the same thing.
// LPC_GPIO1->DIR |= (1 << pinNumber);
// (*LPC_GPIO1).DIR |= (1 << pinNumber);
// (*gpio_struct_ptr).DIR |= (1 << pinNumber);
// gpio_struct_ptr->DIR |= (1 << pinNumber);
gpio_struct_ptr->DIR |= (1 << pinNumber);
break;
case 2:
LPC_GPIO2->DIR |= (1 << pinNumber);
break;
default:
break;
}
}
void setPortPinValue(int portNumber, int pinNumber, int pinValue)
{
switch( portNumber )
{
case 1:
if (pinValue == 1)
LPC_GPIO1->DATA &= ~(1 << pinNumber);
else
LPC_GPIO1->DATA |= (1 << pinNumber);
break;
case 2:
;
break;
default:
break;
}
}
void blinkLed(int ledNumber, int onTime, int offTime, int blinkCount)
{
int count;
switch( ledNumber )
{
case 1:
setPortPinDirection(1, 8);
for (count = 0; count < blinkCount; count--)
{
setPortPinValue(1, 8, 0);
delayTenthSecond(onTime);
setPortPinValue(1, 8, 1);
delayTenthSecond(offTime);
}
break;
case 2:
;
break;
default:
break;
}
}
int main()
{
setPortPinDirection(1, 8);
blinkLed(1, 5, 10, 20); // Led 1, off 0.5 sec, on 1 sec, blink 20 times
}
// ****************************************************************************
// End of Program
// ****************************************************************************
.END
pointer to LPC_GPIO1->DIR not working!
I used the following 2 statements to replace LPC_GPIO1->DIR |= (1 << pinNumber);
unsigned int volatile * const lpc_gpio1_dir_ptr = (unsigned int *) LPC_GPIO1->DIR
*lpc_gpio1_dir_ptr|= (1 << pinNumber);
But it does not work.
// ****************************************************************************
// Program - Blinky
// Description - Blink LED
// Author - TL Fong
// Version - 0.40
// Date - 2013jul12hkt2040
// License - Free
// Hardware - Somy ARM Cortex M0 LPC1114/301 Learning Board
// Software - Keil uVision 4.71.2.0 ARM CC
// Method - Incremental testing
// Hardware configuration notes
// 1. LED1 is connected to PIO1-8
// ****************************************************************************
#include <stdio.h>
#include "LPC11xx.h"
#include "timer.h"
void setPortPinDirection(int portNumber, int pinNumber)
{
unsigned int volatile * const lpc_gpio1_dir_ptr = (unsigned int *) LPC_GPIO1-
>DIR;
switch( portNumber )
{
case 1:
LPC_GPIO1->DIR |= (1 << pinNumber);
// *lpc_gpio1_dir_ptr|= (1 << pinNumber);
break;
case 2:
LPC_GPIO2->DIR |= (1 << pinNumber);
break;
default:
break;
}
}
// #define PORTBASE 0x40000000
// unsigned int volatile * const port = (unsigned int *) PORTBASE;
// The variable port is a constant pointer to a volatile unsigned integer, so we can access the memory-mapped register using:
// *port = value; /* write to port */
// value = *port; /* read from port */
void setPortPinValue(int portNumber, int pinNumber, int pinValue)
{
switch( portNumber )
{
case 1:
if (pinValue == 1)
LPC_GPIO1->DATA &= ~(1 << pinNumber);
else
LPC_GPIO1->DATA |= (1 << pinNumber);
break;
case 2:
;
break;
default:
break;
}
}
void blinkLed(int ledNumber, int onTime, int offTime, int blinkCount)
{
int count;
switch( ledNumber )
{
case 1:
setPortPinDirection(1, 8);
for (count = 0; count < blinkCount; count--)
{
setPortPinValue(1, 8, 0);
delayTenthSecond(onTime);
setPortPinValue(1, 8, 1);
delayTenthSecond(offTime);
}
break;
case 2:
;
break;
default:
break;
}
}
int main()
{
setPortPinDirection(1, 8);
blinkLed(1, 5, 10, 20); // Led 1, off 0.5 sec, on 1 sec, blink 20 times
}
// ****************************************************************************
// End of Program
// ****************************************************************************
unsigned int volatile * const lpc_gpio1_dir_ptr = (unsigned int *) LPC_GPIO1->DIR
*lpc_gpio1_dir_ptr|= (1 << pinNumber);
But it does not work.
// ****************************************************************************
// Program - Blinky
// Description - Blink LED
// Author - TL Fong
// Version - 0.40
// Date - 2013jul12hkt2040
// License - Free
// Hardware - Somy ARM Cortex M0 LPC1114/301 Learning Board
// Software - Keil uVision 4.71.2.0 ARM CC
// Method - Incremental testing
// Hardware configuration notes
// 1. LED1 is connected to PIO1-8
// ****************************************************************************
#include <stdio.h>
#include "LPC11xx.h"
#include "timer.h"
void setPortPinDirection(int portNumber, int pinNumber)
{
unsigned int volatile * const lpc_gpio1_dir_ptr = (unsigned int *) LPC_GPIO1-
>DIR;
switch( portNumber )
{
case 1:
LPC_GPIO1->DIR |= (1 << pinNumber);
// *lpc_gpio1_dir_ptr|= (1 << pinNumber);
break;
case 2:
LPC_GPIO2->DIR |= (1 << pinNumber);
break;
default:
break;
}
}
// #define PORTBASE 0x40000000
// unsigned int volatile * const port = (unsigned int *) PORTBASE;
// The variable port is a constant pointer to a volatile unsigned integer, so we can access the memory-mapped register using:
// *port = value; /* write to port */
// value = *port; /* read from port */
void setPortPinValue(int portNumber, int pinNumber, int pinValue)
{
switch( portNumber )
{
case 1:
if (pinValue == 1)
LPC_GPIO1->DATA &= ~(1 << pinNumber);
else
LPC_GPIO1->DATA |= (1 << pinNumber);
break;
case 2:
;
break;
default:
break;
}
}
void blinkLed(int ledNumber, int onTime, int offTime, int blinkCount)
{
int count;
switch( ledNumber )
{
case 1:
setPortPinDirection(1, 8);
for (count = 0; count < blinkCount; count--)
{
setPortPinValue(1, 8, 0);
delayTenthSecond(onTime);
setPortPinValue(1, 8, 1);
delayTenthSecond(offTime);
}
break;
case 2:
;
break;
default:
break;
}
}
int main()
{
setPortPinDirection(1, 8);
blinkLed(1, 5, 10, 20); // Led 1, off 0.5 sec, on 1 sec, blink 20 times
}
// ****************************************************************************
// End of Program
// ****************************************************************************
armcc learning notes - accessing memory mapped peripherals
I found the following #define statement confusing.
#define LPC_GPIO0 ((LPC_GPIO_TypeDef *) LPC_GPIO0_BASE )
I think I don't understand how the pointer thing works. So I googled an article to read.
.END
Placing C variables at specific addresses to access memory-mapped peripherals
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3750.html
In most ARM embedded systems, peripherals are located at specific addresses in memory. It is often convenient to map a C variable onto each register of a memory-mapped peripheral, and then read/write the register via a pointer. In your code, you will need to consider not only the size and address of the register, but also its alignment in memory.
Basic Concepts
The simplest way to implement memory-mapped variables is to use pointers to fixed addresses. If the memory is changeable by 'external factors' (for example, by some hardware), it must be labelled as volatile.
Consider a simple example:
#define PORTBASE 0x40000000
unsigned int volatile * const port = (unsigned int *) PORTBASE;
The variable port is a constant pointer to a volatile unsigned integer, so we can access the memory-mapped register using:
*port = value; /* write to port */
value = *port; /* read from port */
The use of volatile ensures that the compiler always carries out the memory accesses, rather than optimizing them out (for example, if the access is in a loop).
This approach can be used to access 8, 16 or 32 bit registers, but be sure to declare the variable with the appropriate type for its size, i.e., unsigned int for 32-bit registers, unsigned short for 16-bit, and unsigned char for 8-bit. The compiler will then generate the correct single load/store instructions, i.e., LDR/STR, LDRH/STRH, LDB/STRB.
You should also ensure that the memory-mapped registers lie on appropriate address boundaries, e.g. either all word-aligned, or aligned on their natural size boundaries, i.e., 16-bit registers must be aligned on half-word addresses (but note that ARM recommends that all registers, whatever their size, be aligned on word boundaries - see later).
You can also use #define to simplify your code, e.g.:
#define PORTBASE 0x40000000 /* Counter/Timer Base */
#define PortLoad ((volatile unsigned int *) PORTBASE) /* 32 bits */
#define PortValue ((volatile unsigned short *)(PORTBASE + 0x04)) /* 16 bits */
#define PortClear ((volatile unsigned char *)(PORTBASE + 0x08)) /* 8 bits */ void
init_regs(void)
{
unsigned int int_val;
unsigned short short_val;
unsigned char char_val; *PortLoad = (unsigned int) 0xF00FF00F;
int_val = *PortLoad;
*PortValue = (unsigned short) 0x0000;
short_val = *PortValue;
*PortClear = (unsigned char) 0x1F;
char_val = *PortClear;
}
...
.END
#define LPC_GPIO0 ((LPC_GPIO_TypeDef *) LPC_GPIO0_BASE )
I think I don't understand how the pointer thing works. So I googled an article to read.
.END
Placing C variables at specific addresses to access memory-mapped peripherals
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3750.html
In most ARM embedded systems, peripherals are located at specific addresses in memory. It is often convenient to map a C variable onto each register of a memory-mapped peripheral, and then read/write the register via a pointer. In your code, you will need to consider not only the size and address of the register, but also its alignment in memory.
Basic Concepts
The simplest way to implement memory-mapped variables is to use pointers to fixed addresses. If the memory is changeable by 'external factors' (for example, by some hardware), it must be labelled as volatile.
Consider a simple example:
#define PORTBASE 0x40000000
unsigned int volatile * const port = (unsigned int *) PORTBASE;
The variable port is a constant pointer to a volatile unsigned integer, so we can access the memory-mapped register using:
*port = value; /* write to port */
value = *port; /* read from port */
The use of volatile ensures that the compiler always carries out the memory accesses, rather than optimizing them out (for example, if the access is in a loop).
This approach can be used to access 8, 16 or 32 bit registers, but be sure to declare the variable with the appropriate type for its size, i.e., unsigned int for 32-bit registers, unsigned short for 16-bit, and unsigned char for 8-bit. The compiler will then generate the correct single load/store instructions, i.e., LDR/STR, LDRH/STRH, LDB/STRB.
You should also ensure that the memory-mapped registers lie on appropriate address boundaries, e.g. either all word-aligned, or aligned on their natural size boundaries, i.e., 16-bit registers must be aligned on half-word addresses (but note that ARM recommends that all registers, whatever their size, be aligned on word boundaries - see later).
You can also use #define to simplify your code, e.g.:
#define PORTBASE 0x40000000 /* Counter/Timer Base */
#define PortLoad ((volatile unsigned int *) PORTBASE) /* 32 bits */
#define PortValue ((volatile unsigned short *)(PORTBASE + 0x04)) /* 16 bits */
#define PortClear ((volatile unsigned char *)(PORTBASE + 0x08)) /* 8 bits */ void
init_regs(void)
{
unsigned int int_val;
unsigned short short_val;
unsigned char char_val; *PortLoad = (unsigned int) 0xF00FF00F;
int_val = *PortLoad;
*PortValue = (unsigned short) 0x0000;
short_val = *PortValue;
*PortClear = (unsigned char) 0x1F;
char_val = *PortClear;
}
...
.END
