Detailed Explanation of the Cortex-M3 Embedded System Programming Model (Part 2)

This is a further refinement of the Cortex-M3 programming model, covering more specific implementation details and key operations:

1. Refinement of Register Groups

1. Detailed Explanation of Special Registers

CONTROL Register (32 bits, modifiable only at privileged level)

Bit 0 (`nPRIV`):

0 = Privileged mode (can access all resources)

1 = Unprivileged mode (restricted access)

Bit 1 (`SPSEL`):

0 = Use MSP (default)

1 = Use PSP (must be in unprivileged mode)

Example code (switching stack pointer in privileged mode):

__asm void SwitchToPSP(void) {

MRS R0, CONTROL

ORR R0, R0, 0x02 // Set SPSEL=1

MSR CONTROL, R0

BX LR

}

APSR (Application Status Register)

Flags:

N (Negative): Set to 1 when the result of the operation is negative.

Z (Zero): Set to 1 when the result of the operation is 0.

C (Carry): Set to 1 when there is an overflow in unsigned operations.

V (oVerflow): Set to 1 when there is an overflow in signed operations.

Usage: Conditional branches (such as `BEQ`, `BNE`) and data processing instructions depend on these flags.

2. Code Practice for Operating Modes and Privilege Levels

1. Privilege Level Switching

To elevate from unprivileged to privileged level:

Must be done through an exception (such as SVC call).

__asm void TriggerSVC(void) {

SVC 0x01 // Trigger SVC exception, parameter is 0x01

BX LR

}

// SVC Handler (privileged level)

void SVC_Handler(void) {

uint32_t *stack_frame;

__asm {

TST LR, 4 // Check EXC_RETURN Bit 2

ITE EQ

MRSEQ stack_frame, MSP // Use MSP

MRSNE stack_frame, PSP // Use PSP

}

uint8_t svc_number = ((uint8_t*)stack_frame[6])[-2]; // Extract SVC parameter

// Execute privileged operations

}

3. Memory Mapping and MPU Configuration

1. Example of MPU Region Setup

Objective: Protect a certain area of SRAM (e.g., `0x20001000~0x20001FFF`) as read-only.

void ConfigureMPU(void) {

MPU->RNR = 0; // Select region 0

MPU->RBAR = 0x20001000 | (1 << 4); // Base address + Enable region

MPU->RASR = (0x01 << 1) | // 1KB Size (2^(1+1)=4)

(0x3 << 24) | // AP=011 (privileged read/write, user no access)

(1 << 0); // Enable region

SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; // Enable memory protection exception

}

4. Refinement of Exception and Interrupt Handling

1. Interrupt Priority Configuration

NVIC Register Operations:

Setting priority: Each interrupt has 4 bit priority (actual chip may only support high bits, e.g., only high 3 bits).

// Configure UART1 interrupt priority to 2 (0 highest, 15 lowest)

NVIC_SetPriority(UART1_IRQn, 2 << (8 – __NVIC_PRIO_BITS));

NVIC_EnableIRQ(UART1_IRQn); // Enable interrupt

2. Best Practices for Interrupt Service Routines (ISR)

Clear interrupt flags:

void TIM2_IRQHandler(void) {

if (TIM2->SR & TIM_SR_UIF) { // Check update interrupt flag

TIM2->SR &= ~TIM_SR_UIF; // Manually clear flag

// Execute processing logic

}

}

5. Key Operations of the Instruction Set

1. Implementation of Atomic Operations

Use `LDREX` and `STREX` to implement atomic read/write:

uint32_t atomic_increment(volatile uint32_t *value) {

uint32_t result;

do {

result = __LDREXW(value) + 1;

} while (__STREXW(result, value) != 0);

return result;

}

2. Low Power Instructions

Enter sleep mode:

__WFI(); // Wait for interrupt (enter sleep)

__WFE(); // Wait for event (requires wake-up source configuration)

6. Startup Process and Initialization Code

1. Key Steps in the Startup File (startup.s)

Initialize stack:

assembly

Reset_Handler:

LDR SP, =_estack // Load main stack pointer

BL SystemInit // Initialize clock, Flash, etc.

BL __libc_init_array // Initialize C++ global objects

BL main // Jump to main()

B . // Prevent runaway

2. Data Segment Initialization (.data and .bss)

Manually copy initialized data (when no startup file):

extern uint32_t _sdata, _edata, _sidata; // Linker script defined symbols

extern uint32_t _sbss, _ebss;

void SystemInit(void) {

// Initialize .data segment (copy from Flash to RAM)

uint32_t *src = &_sidata;

uint32_t *dst = &_sdata;

while (dst < &_edata) *dst++ = *src++;

// Zero out .bss segment

dst = &_sbss;

while (dst < &_ebss) *dst++ = 0;

}

7. Debugging and Troubleshooting Practices

1. Hard Fault Analysis

Capture HardFault LR and PC:

void HardFault_Handler(void) {

__asm(“TST LR, 4”); // Check stack frame source (MSP/PSP)

__asm(“ITE EQ”);

__asm(“MRSEQ R0, MSP”);

__asm(“MRSNE R0, PSP”);

uint32_t *stack = (uint32_t*)__get_MSP(); // Get stack pointer

uint32_t pc = stack[6]; // PC that triggered the exception

uint32_t lr = stack[5]; // LR that triggered the exception

while(1); // Breakpoint here to analyze pc and lr

}

2. Stack Overflow Detection

Use stack top fill pattern (Stack Canary):

#define STACK_CANARY 0xDEADBEEF

uint32_t *stack_end = (uint32_t*)&_estack;

*stack_end = STACK_CANARY;

// Regularly check if the stack top has been corrupted

if (*stack_end != STACK_CANARY) {

// Trigger error handling

}

8. CMSIS Core Function Examples

1. Accessing Core Registers

Directly manipulate NVIC:

// Disable global interrupts

__disable_irq();

// Modify system tick

SysTick->LOAD = 0xFFFFFF; // Set reload value

__enable_irq();

2. Using CMSIS-RTOS for Task Scheduling

Create task (requires CMSIS-RTOS support):

osThreadId_t ledTaskHandle;

void led_task(void *arg) {

while(1) {

GPIOA->ODR ^= GPIO_ODR_OD5; // Toggle LED

osDelay(500); // Delay 500ms

}

}

int main(void) {

osKernelInitialize();

osThreadNew(led_task, NULL, NULL);

osKernelStart();

}

The programming model of Cortex-M3 requires developers to have a deep understanding of register operations, exception mechanisms, memory management, and low-level instructions. In actual development, attention should be paid to:

1. Privilege level switching must be done through exceptions or resets.

2. Interrupt service routines need to be efficient and timely in clearing flags.

3. Stack management is key to stability (especially in RTOS multi-tasking scenarios).

4. The CMSIS standard library can greatly simplify cross-platform development.

5. Debugging tools (such as J-Link) combined with HardFault analysis can quickly locate underlying issues.

It is recommended to combine with specific chip manuals (such as STM32F1xx) and ARMv7-M architecture reference manuals to deepen the practice of peripheral drivers and RTOS integration.

Leave a Comment