Introduction to ARM Bare-Metal Programming and Embedded Systems

Introduction to ARM Bare-Metal Programming and Embedded Systems

Getting Started with ARM Bare-Metal Programming: From Zero to Embedded Development

Hello everyone, I’m Daodao. Today, let’s talk about ARM bare-metal development. Many beginners feel overwhelmed when they see ARM, thinking it’s particularly difficult. In fact, it’s not; once you grasp the essentials, writing a simple LED blinking program is easier than knitting (just kidding).

What Exactly is ARM?

Speaking of ARM, let me tell you an interesting story. In the 1980s, a British company called Acorn wanted to create a processor for their computers but found that the processors available on the market were either too expensive or too slow. So they decided to create their own, and that’s how ARM came to be. Nowadays, most processors in your smartphones are based on the ARM architecture.

The biggest feature of ARM processors is their Reduced Instruction Set Computing (RISC). To put it simply, if we compare processors to chefs, the X86 processor is like a chef who knows hundreds of dishes, while the ARM processor is a cook who specializes in just a few basic recipes. Although there are fewer recipes, each one can be completed quickly and accurately.

Basic Concepts of Bare-Metal Programming

Bare-metal programming, as the name suggests, involves directly interacting with hardware without an operating system. It’s like buying a piece of land and building a house yourself, where you have to manage every brick and tile from the foundation up.

The most basic components of a bare-metal program are:

  1. Startup file (startup.s): equivalent to the foundation of the house

  2. Linker script (*.ld): like the architectural blueprint of the house

  3. Main program (main.c): which is the house itself

Setting Up the Development Environment

To do a good job, one must first sharpen their tools. Bare-metal development requires the following tools:

  • Cross-compilation toolchain: used to convert the C code we write into machine code that ARM can understand

  • Debugger: such as J-Link or ST-Link, used to find bugs in the program

  • Editor: recommended VSCode or Keil MDK

Note: Make sure to correctly configure the environment variables for the toolchain under Windows; otherwise, the commands will not be found during compilation. I’ve stumbled into this pitfall several times in the past.

Understanding the Hardware

Taking the STM32F103 series as an example, its core is an ARM processor with a CM3 core. The most basic peripherals on the board include:

- Clock system

- GPIO ports

- NVIC interrupt controller

- Various buses (AHB/APB)

Clock configuration is crucial, just like a human heartbeat; if configured incorrectly, the entire system will fail.

First Program: Blinking an LED

Let’s take a look at the most basic LED blinking program:

#define LED_PIN  GPIO_PIN_13

#define LED_PORT GPIOC


int main(void)
{
    // 1. Enable clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    
    // 2. Configure GPIO
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = LED_PIN;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_PORT, &GPIO_InitStruct);
    
    while(1)
    {
        GPIO_SetBits(LED_PORT, LED_PIN);    // LED on
        delay_ms(500);                      // Delay 500ms
        GPIO_ResetBits(LED_PORT, LED_PIN);  // LED off
        delay_ms(500);                      // Delay 500ms
    }
}

Case Analysis:

Last year, I encountered an interesting problem where the customer’s LED wouldn’t light up. The code was fine, but I eventually discovered that the GPIO port clock wasn’t enabled. It’s like wanting to turn on the TV but forgetting to plug it in.

Interrupt Handling

Speaking of interrupts, I like to compare them to doorbells. When you’re watching TV and the doorbell rings (interrupt signal), you have to go answer the door (interrupt service routine).

void EXTI0_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        // Interrupt handling code
        LED_Toggle();  // Toggle LED state
        
        // Clear interrupt flag
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

Notes:

  • Keep the interrupt service routine as short and efficient as possible, just like a delivery person ringing the doorbell—you can’t make them wait too long.

  • Remember to clear the interrupt flag, otherwise it will trigger repeatedly.

  • Priority settings should be reasonable to avoid delaying critical interrupts.

Debugging Techniques

  1. The most commonly used debugging methods:

  • Serial printing

  • LED status indication

  • Waveform observation

  • Common Problem Solutions:

    • Program runaway: usually due to stack overflow or wild pointers.

    • Infinite loop: check if the while condition is reasonable.

    • Peripheral not working: check clock and pin configuration.

    Practical Suggestions

    1. Prepare a development board, I recommend the STM32F103 series.

    2. Start with the simplest GPIO control.

    3. Use the HAL library or standard library to avoid direct register manipulation.

    4. Use serial printing frequently during debugging for easier problem localization.

    5. Establish good coding standards and add reasonable comments.

    Memory Management Suggestions:

    • Avoid allocating memory in interrupts.

    • Be mindful of stack space size settings.

    • Use static variables judiciously.

    Lastly, let me say that patience is the most important thing in embedded development. When the code doesn’t work, brew a cup of coffee, take a break, and come back with a fresh perspective. After all, Rome wasn’t built in a day, and neither was a chip debugged in a day.

    That’s all for now; I hope this article helps you get started with ARM bare-metal development. Feel free to discuss any questions in the comments section.

    Note: The code mentioned in this article is for reference only and should be adjusted according to specific project requirements. Remember to take precautions and avoid turning your development board into a brick.

    Leave a Comment