The ARM Cortex-M3 is a 32-bit RISC processor core based on the ARMv7-M architecture. It runs the Thumb-2 instruction set, uses a 3-stage pipeline with Harvard-style buses, and includes a built-in Nested Vectored Interrupt Controller (NVIC) with 12-cycle interrupt latency. At 1.25 DMIPS/MHz, it powers microcontrollers such as the STM32F103, LPC1768 and SAM3X8E in real-time embedded control.If you have ever blinked an LED on an STM32F103 “Blue Pill”, flashed code onto an LPC1768, or run a sketch on the Arduino Due, you have already worked with the ARM Cortex-M3, even if the datasheet mentioned it only once.
Introduced by ARM in 2004, the ARM Cortex M3 became the engine behind industrial controllers, smart meters, medical devices, 3D printers and countless IoT products. It strikes a rare balance: solid performance, very fast interrupts and low power, at a price that suits everyday products.
This guide takes you from “What is it?” to “How do I program it?” in small, clear steps, with tables, working C code and the practical gotchas that usually cost people hours.
What you will learn:
| Parameter | Details |
|---|---|
| Designer | ARM (licensed to chip makers such as ST, NXP, Microchip and TI) |
| Architecture | ARMv7-M |
| Data width | 32-bit RISC core |
| Instruction set | Thumb and Thumb-2 only (no 32-bit ARM state) |
| Pipeline | 3 stages: fetch, decode, execute |
| Bus design | Harvard-style: separate I-Code, D-Code and System buses |
| Performance | 1.25 DMIPS/MHz |
| Interrupt controller | NVIC: up to 240 interrupts, 8 to 256 priority levels |
| Interrupt latency | 12 cycles (zero-wait-state memory) |
| Memory Protection Unit | Optional, up to 8 regions |
| FPU and DSP | Not included |
| Debug | SWD or JTAG with DWT, FPB and ITM (ETM optional) |
| Typical clock speed | Roughly 24 MHz to 120 MHz, depending on the chip |
The ARM Cortex-M3 is a licensable 32-bit processor core, built on the ARM architecture and designed for microcontrollers. ARM does not manufacture chips. It sells the design, and companies such as STMicroelectronics, NXP, Microchip and Texas Instruments wrap Flash, SRAM and peripherals around it.
Think of ARM as an architect who sells building plans. Every chip maker builds the same core “floor plan” but adds its own rooms: timers, UART, ADC, USB and CAN.
Key points to remember:
Let’s open the hood. The core looks compact on paper, but every block has a clear job.
The ARM Cortex-M3 processes instructions through a three-stage pipeline, much like an assembly line.
The Cortex-M3 can fetch an instruction and read data at the same time because they travel on separate buses.
| Bus | What it carries | Address region |
|---|---|---|
| I-Code | Instruction fetches | 0x00000000 to 0x1FFFFFFF |
| D-Code | Data reads in the code region (constants, tables) | 0x00000000 to 0x1FFFFFFF |
| System | Instructions and data in SRAM, peripherals and external memory | 0x20000000 to 0xDFFFFFFF and 0xE0100000 to 0xFFFFFFFF |
| PPB | Core-private blocks: NVIC, SysTick, debug | 0xE0000000 to 0xE00FFFFF |
I-Code, D-Code and System are AHB-Lite buses, while the PPB is an APB bus.
The Cortex-M3 register set is small. You rarely touch it directly in C, but every debugger shows it, so it pays to know what each register does.
| Register | Role |
|---|---|
| R0-R7 | Low registers: general purpose, usable by all instructions |
| R8-R12 | High registers: general purpose, mainly for 32-bit Thumb-2 instructions |
| R13 (SP) | Two banked stack pointers: MSP (main) and PSP (process) |
| R14 (LR) | Link register: return address for calls and exceptions |
| R15 (PC) | Program counter |
| xPSR | Flags (N, Z, C, V, Q), active exception number and Thumb state bit |
| PRIMASK | Blocks all configurable interrupts (used by __disable_irq()) |
| FAULTMASK | Blocks everything except NMI |
| BASEPRI | Blocks interrupts at or below a chosen priority level |
| CONTROL | Selects the privilege level and the stack pointer used in Thread mode |
Pro tip: FreeRTOS on the Cortex-M3 uses BASEPRI for its critical sections, so urgent interrupts keep running while the kernel stays protected. PRIMASK is the blunt tool, BASEPRI the precise one.
| Mode | Runs | Privilege | Stack used |
|---|---|---|---|
| Thread mode | Your application code | Privileged or unprivileged (CONTROL bit 0) | MSP or PSP (CONTROL bit 1) |
| Handler mode | Interrupt and exception handlers | Always privileged | MSP |
The Cortex-M3 has a fixed 4 GB linear address space. ARM defines the regions, and each chip maker decides what to place inside them.
| Region | Address range | Typical contents |
|---|---|---|
| Code | 0x00000000 to 0x1FFFFFFF | Flash, boot memory |
| SRAM | 0x20000000 to 0x3FFFFFFF | On-chip RAM |
| Peripheral | 0x40000000 to 0x5FFFFFFF | GPIO, timers, UART, ADC |
| External RAM | 0x60000000 to 0x9FFFFFFF | Off-chip memory |
| External device | 0xA0000000 to 0xDFFFFFFF | Off-chip peripherals |
| Private peripheral bus | 0xE0000000 to 0xE00FFFFF | NVIC, SysTick, SCB, debug |
| Vendor-specific | 0xE0100000 to 0xFFFFFFFF | Chip-specific features |
Example: on the STM32F103, Flash sits at 0x08000000 (mirrored to 0x00000000 at boot), SRAM starts at 0x20000000 and peripherals start at 0x40000000.
Bit-banding is a Cortex-M3 feature that lets you read or write a single bit with one ordinary memory access.
Normally, changing one bit takes three steps: read the register, modify the bit, write it back. If an interrupt strikes in the middle and touches the same register, you lose data. Bit-banding removes that risk. Every bit in two 1 MB regions gets its own 32-bit alias address, and writing 0 or 1 to that alias changes exactly that bit in a single atomic operation.
| Region | Bit-band area | Alias area |
|---|---|---|
| SRAM | 0x20000000 to 0x200FFFFF (1 MB) | 0x22000000 to 0x23FFFFFF (32 MB) |
| Peripheral | 0x40000000 to 0x400FFFFF (1 MB) | 0x42000000 to 0x43FFFFFF (32 MB) |
Formula: alias_address = alias_base + (byte_offset x 32) + (bit_number x 4)
A ready-to-use C macro is in the programming section below.
These are the features that make the Cortex-M3 such a dependable choice for real-time work:
WFI and WFE instructions, sleep and deep-sleep modes, and sleep-on-exit for fully interrupt-driven designs.LDREX and STREX for lock-free semaphores and mutexes.printf-style output over a single SWO pin.The ARM Cortex-M3 works by reading a vector table, running startup code, executing instructions through its pipeline and reacting to interrupts in hardware. Here is the life of a program, step by step.
0x00000000 it finds the initial Main Stack Pointer value, and at 0x00000004 the address of Reset_Handler. These are the first two entries of the vector table, which bootloaders can relocate with the VTOR register..data) from Flash to SRAM, clears .bss, calls SystemInit() to set up the clocks, and finally jumps to main().EXC_RETURN in LR tells the hardware which stack and mode to go back to, and the eight registers are popped automatically.__WFI() pauses the core until the next interrupt, saving power.Two tricks that make Cortex-M3 interrupts so quick:
Picture the NVIC as a smart receptionist: she knows every visitor’s urgency, lets the most urgent one in first and keeps the queue moving without making anyone fill in the same form twice.
| Exception | Number | Priority |
|---|---|---|
| Reset | 1 | -3 (fixed, highest) |
| NMI | 2 | -2 (fixed) |
| HardFault | 3 | -1 (fixed) |
| MemManage, BusFault, UsageFault | 4, 5, 6 | Programmable |
| SVCall | 11 | Programmable |
| PendSV | 14 | Programmable |
| SysTick | 15 | Programmable |
| External interrupts (IRQ0 onwards) | 16 and above | Programmable |
PRIGROUP field splits the priority value. Only the group priority decides pre-emption, while sub-priority just orders pending interrupts.The Cortex-M3 executes only Thumb and Thumb-2 instructions, so the compiler can mix 16-bit and 32-bit encodings freely. These are the ones worth knowing:
switch statements compact and fast.| Feature | Cortex-M0 | Cortex-M3 | Cortex-M4 |
|---|---|---|---|
| Architecture | ARMv6-M | ARMv7-M | ARMv7E-M |
| Instruction set | Thumb + small Thumb-2 subset | Thumb + full Thumb-2 | Thumb-2 + DSP extensions |
| Performance | About 0.9 DMIPS/MHz | 1.25 DMIPS/MHz | 1.25 DMIPS/MHz |
| Hardware divide | No | Yes | Yes |
| Interrupts / priority levels | 32 / 4 | 240 / 8 to 256 | 240 / 8 to 256 |
| MPU | No | Optional | Optional |
| Bit-banding | No | Yes | Yes |
| FPU | No | No | Optional (single precision) |
| DSP (SIMD, MAC) | No | No | Yes |
| Best for | Low-cost, low-power sensors | Real-time control, RTOS apps | Audio, filters, motor control |
For pure integer code, the M3 and M4 perform the same. The M4’s edge appears in signal processing and floating-point work.
| Vendor | Chip family | Notes |
|---|---|---|
| STMicroelectronics | STM32F103, STM32F2, STM32L1 | The F103 runs at 72 MHz and has a huge community (the “Blue Pill” board) |
| NXP | LPC1768 / LPC17xx | Up to 100 MHz with Ethernet, USB and CAN |
| Microchip (Atmel) | SAM3X8E | 84 MHz, the chip on the Arduino Due |
| Texas Instruments | Stellaris LM3S | Early Cortex-M3 devices, originally from Luminary Micro |
| Silicon Labs | EFM32 Gecko families | Energy-efficient designs |
| Infineon (Cypress) | PSoC 5LP | Cortex-M3 plus programmable analog and digital blocks |
The ARM Cortex-M3 shows up wherever a product needs reliable timing, decent processing power and low energy use:
arm-none-eabi-gcc) with OpenOCD and GDB.SystemInit() normally configures 72 MHz on the STM32F103).#include /* use with STM32Cube */
volatile uint32_t ms_ticks = 0;
void SysTick_Handler(void) /* runs every 1 ms */
{
ms_ticks++;
}
static void delay_ms(uint32_t ms)
{
uint32_t start = ms_ticks;
while ((ms_ticks - start) < ms) { } /* safe even when the counter wraps */ } int main(void) { SystemCoreClockUpdate(); SysTick_Config(SystemCoreClock / 1000); /* 1 ms tick */ RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; /* clock for GPIOC */
GPIOC->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13);
GPIOC->CRH |= GPIO_CRH_MODE13_1; /* PC13: output, 2 MHz, push-pull */
while (1)
{
GPIOC->ODR ^= GPIO_ODR_ODR13; /* toggle the on-board LED */
delay_ms(500);
}
}
Why it works:
SysTick_Config() comes from CMSIS and sets up a 1 ms interrupt.volatile stops the compiler from caching a variable that an ISR changes.start from ms_ticks stays correct even when the counter overflows.#define BB_ALIAS(base, alias, addr, bit) (*(volatile uint32_t *)((alias) + (((uint32_t)(addr) - (base)) << 5) + ((uint32_t)(bit) << 2))) #define BITBAND_PERI(addr, bit) BB_ALIAS(0x40000000UL, 0x42000000UL, addr, bit) #define BITBAND_SRAM(addr, bit) BB_ALIAS(0x20000000UL, 0x22000000UL, addr, bit) BITBAND_PERI(&GPIOC->ODR, 13) = 1; /* set PC13 in one atomic write */
BITBAND_PERI(&GPIOC->ODR, 13) = 0; /* clear PC13 */
volatile uint32_t flags;
BITBAND_SRAM(&flags, 3) = 1; /* set bit 3 of a shared flag word */
Bit-banding is available on the Cortex-M3 and Cortex-M4, so code that uses it will not port to cores without it, such as the Cortex-M0.
Every interrupt follows the same pattern: configure the peripheral to raise it, set the priority, enable it in the NVIC and keep the ISR short. This example assumes PA0 and EXTI line 0 are already configured.
volatile uint8_t button_pressed = 0;
void EXTI0_IRQHandler(void)
{
if (EXTI->PR & EXTI_PR_PR0)
{
EXTI->PR = EXTI_PR_PR0; /* clear the pending flag first */
button_pressed = 1; /* set a flag, do the work in main() */
}
}
void nvic_setup(void)
{
NVIC_SetPriority(EXTI0_IRQn, 5); /* smaller number = higher priority */
NVIC_EnableIRQ(EXTI0_IRQn);
}
SCB->CFSR) and the stacked PC to find the exact instruction that failed.printf-style output if your debug probe and board expose the SWO pin.DWT->CYCCNT) measures code in clock cycles.volatile: variables shared between an ISR and main() need it.For more habits that keep firmware reliable, see embedded C best practices.
The ARM Cortex-M3 earned its place by doing the basics extremely well. Keep these points handy:
main().Your next step: flash the SysTick example on a Cortex-M3 board, then extend it with a button interrupt. Once that blink works, you are already thinking like a firmware engineer.
It powers real-time embedded control in industrial automation, automotive modules, consumer appliances, medical devices, smart meters and IoT nodes.
It is a processor core, not a complete chip. Microcontrollers such as the STM32F103 and LPC1768 combine a Cortex-M3 core with Flash, SRAM and peripherals.
The Cortex-M4 adds DSP instructions and an optional single-precision FPU. For integer code, both deliver about 1.25 DMIPS/MHz.
No. Floating-point math runs in software. For hardware floating point, choose a Cortex-M4F, M7 or M33.
Yes. The STM32F103 is a Cortex-M3 device that runs at up to 72 MHz.
Yes. The NVIC, registers, memory map and startup flow carry over to every Cortex-M chip, and Cortex-M3 parts remain common in cost-sensitive designs.
Indian Institute of Embedded Systems – IIES