ARM单片机C语言综合教程
第一章:引言与准备
1 什么是ARM单片机?
- ARM (Advanced RISC Machines):是一种处理器架构,而不是一个具体的芯片公司,它定义了处理器的指令集、功能等“设计蓝图”。
- RISC (Reduced Instruction Set Computer):精简指令集计算机,它的特点是指令数量少、执行速度快、效率高,与x86(复杂指令集,如你的电脑CPU)相对。
- 单片机:将CPU、内存、定时器、I/O端口等集成在一块芯片上的微型计算机系统。
- ARM单片机:采用了ARM架构的单片机,目前市面上最主流、最流行的就是ARM Cortex-M系列,例如STM32、NXP Kinetis、TI Tiva等。
为什么学习ARM单片机?

- 市场占有率高:从消费电子到工业控制,ARM无处不在,就业需求大。
- 性能与功耗平衡:Cortex-M系列在提供强大性能的同时,功耗控制得非常好,适合电池供电的设备。
- 生态系统完善:有大量的开发工具、开源库、社区支持和学习资料。
2 开发环境搭建
要进行ARM单片机开发,你需要一套工具链,对于初学者,强烈推荐使用集成开发环境。
核心三要素:
-
IDE (集成开发环境):编写、编译、调试代码的图形化软件。
- 推荐:Keil MDK (ARM):老牌、稳定、文档齐全,非常适合初学者,有免费版(代码量限制)。
- 推荐:STM32CubeIDE:ST官方推出的免费IDE,集成了代码生成器(CubeMX),非常强大。
- 其他选项:IAR Embedded Workbench(商业,非常强大)、VS Code + PlatformIO(开源,灵活)。
-
编译器:将你写的C语言代码翻译成ARM CPU能理解的机器码。
(图片来源网络,侵删)- ARMCC/ARMCLANG:Keil和STM32CubeIDE默认使用的编译器。
- GCC (GNU Compiler Collection):开源,被广泛使用,例如在PlatformIO中。
-
调试器/下载器:将编译好的程序下载到单片机中,并可以进行在线调试。
- ST-Link:ST公司官方的调试器,用于STM32单片机。
- J-Link:SEGGER公司出品,支持多种ARM芯片,功能强大。
- U-Link:Keil配套的调试器。
第一步:安装IDE和调试器驱动 以Keil MDK为例:
- 下载并安装Keil MDK(例如MDK523)。
- 安装对应的芯片包(例如STM32F1 Series Pack)。
- 安装ST-Link的USB驱动。
第二章:ARM Cortex-M核心概念
在学习C语言编程前,理解Cortex-M内核的几个关键特性至关重要。
1 寄存器
CPU通过寄存器与内存交互,Cortex-M内核有多个特殊功能寄存器,最核心的是:

- 通用寄存器:
R0~R15,用于临时存储数据、地址等。 - 特殊功能寄存器:
- APSR (Application Program Status Register):应用程序状态寄存器,存放运算结果的标志(如进位、零、负等)。
- PSR (Program Status Register):程序状态寄存器,包含APSR和中断优先级等。
- SP (Stack Pointer):堆栈指针,指向栈顶。
- PC (Program Counter):程序计数器,指向下一条要执行的指令地址。
2 存储器映射
ARM单片机内部有不同功能的存储区域,每个区域都有固定的地址。
- 代码区:存放你的程序代码。
- SRAM (静态随机存取存储器):存放全局变量、静态变量、堆栈,断电后数据丢失。
- 寄存器区:控制所有外设(GPIO、串口、定时器等)的寄存器都集中在这里。
- 片上Flash:用于程序存储,可以长期保存代码。
- 外设区:虽然寄存器在寄存器区,但通常我们通过访问外设的“基地址 + 偏移量”来操作它们。
示例:STM32F103的GPIOA端口的寄存器基地址是 0x40010800,要操作GPIOA的输出数据寄存器(ODR),地址就是 0x4001080C。
3 启动文件与系统初始化
当你按下复位键,单片机并不会直接运行你的main函数,它会先执行一段启动代码。
- 启动文件:由汇编语言编写,完成以下工作:
- 设置堆栈指针。
- 将已初始化的全局变量从Flash复制到SRAM中。
- 清零未初始化的全局变量(BSS段)。
- 调用
SystemInit()函数,配置系统时钟(将内部8MHz的时钟倍频到72MHz)。 - 调用
main()函数,跳转到你的C语言主程序。
重要:你通常不需要修改启动文件,但需要理解它的存在。
4 中断与异常
这是ARM单片机的强大功能。
- 异常:由内部事件引起,如系统复位、时钟故障。
- 中断:由外部事件引起,如按键按下、串口收到数据。
- NVIC (Nested Vectored Interrupt Controller):嵌套向量中断控制器,它是Cortex-M内核的一部分,负责管理所有的中断,可以设置中断的优先级、使能/失能中断。
第三章:ARM单片机C语言编程
这部分是核心,我们将结合STM32的HAL库(或标准外设库)来讲解。
1 点亮一个LED - GPIO操作
GPIO(General-Purpose Input/Output)是最基础的外设。
步骤:
- 使能GPIO时钟:ARM单片机为了省电,所有外设的时钟默认是关闭的,必须先开启对应GPIO端口的时钟。
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;// 使能GPIOA时钟
- 配置GPIO模式:将引脚设置为输出模式(推挽、开漏等)。
GPIOA->CRL &= ~(0xF << 4*5);// 清空PA5的配置位GPIOA->CRL |= (0x3 << 4*5);// 设置PA5为推挽输出模式
- 输出高低电平:通过设置或清除端口输出数据寄存器来控制引脚。
GPIOA->BSRR = (1 << 5);// PA5输出高电平GPIOA->BRR = (1 << 5);// PA5输出低电平
现代方法:使用HAL库 STM32的HAL库封装了这些复杂的寄存器操作,代码更简洁、可移植性更好。
// 在 main.c 中
#include "main.h" // 包含了所有外设的头文件
void SystemClock_Config(void);
int main(void)
{
// 1. HAL库初始化
HAL_Init();
// 2. 配置系统时钟
SystemClock_Config();
// 3. 初始化GPIO
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitStruct.Pin = GPIO_PIN_5; // 选择PA5引脚
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 不上下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 调用HAL函数完成初始化
while (1)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // PA5 = 1, LED亮
HAL_Delay(500); // 延时500ms
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // PA5 = 0, LED灭
HAL_Delay(500); // 延时500ms
}
}
HAL库的优势:你只需要关心“做什么”,而不是“怎么做”,代码可读性极高。
2 串口通信 - UART
串口是单片机与PC或其他设备通信的常用方式。
步骤(HAL库):
- 使能时钟:使能GPIOA(用于TX, RX引脚)和USART1的时钟。
- 配置GPIO:将TX引脚(如PA9)设置为复用功能推挽输出,RX引脚(如PA10)设置为复用功能输入。
- 配置USART:设置波特率、数据位、停止位、校验位等。
- 初始化USART:调用
HAL_UART_Init()。 - 收发数据:
- 发送:
HAL_UART_Transmit(&huart1, (uint8_t*)"Hello", 5, 100); - 接收:
HAL_UART_Receive(&huart1, rx_buffer, 1, 100);
- 发送:
3 定时器与中断
定时器可以用于精确延时、产生PWM波、或周期性地执行任务。
步骤(HAL库 + 中断):
- 使能定时器时钟。
- 配置定时器参数:设置预分频器、自动重装载值,来计算中断周期。
htim.Instance = TIM2;htim.Init.Prescaler = 8000 - 1;// 8MHz / 8000 = 1kHzhtim.Init.Period = 1000 - 1;// 1kHz / 1000 = 1Hz (1秒中断一次)
- 使能定时器中断:在
HAL_TIM_Base_Init之后,调用HAL_NVIC_SetPriority和HAL_NVIC_EnableIRQ来配置NVIC。 - 启动定时器:
HAL_TIM_Base_Start_IT(&htim2); - 编写中断服务函数:HAL库会自动调用这个函数。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM2) { // 每隔1秒,翻转一次LED HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); } }
第四章:进阶主题
1 DMA (Direct Memory Access)
作用:让数据在内存和外设之间直接传输,无需CPU参与,解放CPU,提高效率。 应用场景:高速数据采集(如ADC)、大量数据发送(如通过串口发送文件)。
2 FreeRTOS实时操作系统
作用:当一个任务变得复杂(需要同时处理按键、串口、定时器等多个事件),裸机编程会变得非常困难,RTOS可以帮你管理多个任务,让它们“看起来”在同时运行。 核心概念:任务、调度器、信号量、队列、互斥锁。
第五章:学习路径与资源推荐
1 推荐的学习路径
-
基础阶段:
- 掌握C语言基础(指针、结构体、位操作是重中之重)。
- 学会点亮一个LED,控制蜂鸣器。
- 学会使用串口打印信息,实现与PC的通信。
- 学会使用定时器进行精确延时和中断。
- 目标:完成一个“呼吸灯”项目。
-
进阶阶段:
- 学习使用ADC采集模拟信号(如电位器)。
- 学习使用PWM控制舵机或电机转速。
- 学习使用I2C、SPI等通信协议驱动OLED屏、传感器(如温湿度)。
- 目标:完成一个“环境监测器”项目(显示温湿度)。
-
高级阶段:
- 学习使用DMA优化串口或ADC数据传输。
- 学习移植和使用FreeRTOS,实现多任务并发。
- 学习低功耗模式,延长电池寿命。
- 目标:完成一个复杂的综合项目,如“智能小车”或“无线遥控器”。
2 推荐资源
- 开发板:
- 正点原子:教程和配套资料极其丰富,非常适合新手入门。
- 野火:同样提供非常棒的教程和开发板。
- ST官方:可以购买Nucleo系列开发板,价格便宜,自带ST-Link调试器。
- 在线教程:
- B站/YouTube:搜索“STM32入门”、“正点原子”、“野火”,有大量视频教程。
- ST官方网站:提供数据手册、参考手册、应用笔记和例程,是最终的技术权威。
- 书籍:
- 《STM32库开发实战指南》- 野火/正点原子
- 《Cortex-M3权威指南》- Joseph Yiu (如果对底层原理感兴趣)
学习ARM单片机C语言是一个“理论+实践”相结合的过程,不要害怕寄存器,因为HAL库已经帮你处理了大部分复杂性,但理解底层原理(时钟、中断、存储器映射)会让你在遇到问题时更有底气。
动手!动手!再动手! 从点亮第一个LED开始,一步步搭建你的项目,这是最快的学习方式,祝你学习顺利!
