单片机基础功能汇总——以STM32为例

引言

本文以STM32F103C8T6芯片为基础,汇总了基础的单片机知识,基于HAL库,编译软件选择STM32CubeIDE,便于配置引脚。

当然了,现在是AI的时代,Clion等强大的编译软件能大幅简化我们的工作,但是知其然知其所以然,AI只是助手,决策,规划和调试修正仍然需要人的参与,理解寄存器和数据手册才是真正学会。

这篇文章仅仅提供我在嵌入式开发学习中的一点点思考和总结,后续的FreeRtos,pid,上位机等知识仍然需要探索。

HAL库

HAL库是ST官方主推的STM32开发库,它以句柄、超时、回调、多模式等机制,大大降低了外设驱动的开发难度,最大的特点是抽象和可移植,同时保持了代码的可移植性和可维护性。

设计哲学

  • 分层:将外设驱动分为三层:用户应用程序 → HAL API → 硬件寄存器。

  • 面向对象风格:使用“句柄”(Handle)结构体来封装外设的状态、配置和底层资源。

  • 超时保护:所有可能阻塞的操作(如等待标志位)都提供了超时参数,防止程序卡死。

  • 中断与轮询统一:支持轮询、中断、DMA三种模式,切换模式只需改变初始化参数,API命名规范一致。

  • 弱定义回调:允许用户在应用程序中轻松扩展中断处理,无需修改库代码。

单片机结构

定义

单片机(Microcontroller Unit,MCU)是一种将中央处理器(CPU)、存储器(RAM/ROM)、输入输出接口(GPIO)、定时器/计数器、通信接口(如串口、I2C、SPI)等集成在一个芯片上的微型计算机。简单来说,它就是一台单芯片的计算机,专门用于控制电子设备的运行。

特点

集成度高:单芯片集成计算机核心部件,体积小、功耗低。

专用性强:针对特定控制任务设计。

实时性:能快速响应外部事件(中断机制)。

成本低:适合大批量嵌入式应用。

工作流程

  1. 上电复位后,CPU从ROM读取第一条指令。
  2. 根据程序逻辑,通过GPIO控制外部设备(如LED、电机)。
  3. 通过外设采集数据(如ADC读取传感器电压,串口接收数据)。
  4. 处理数据并做出决策(如通过PWM调整电机转速)。
  5. 使用定时器和中断实现精确时序控制和多任务处理。

单片机的核心运行模式就是:上电复位 → 配置时钟树 → 初始化外设 → 进入无限循环,并随时响应中断。时钟树并非与上电同时启动,而是复位后先使用内部默认时钟,再通过软件配置最终时钟;无限循环是主程序的结构,但中断机制使其能够实时处理外部事件。

底层逻辑

单片机上电启动后的工作流程可以总结为以下几个核心步骤:

1. 硬件复位与启动

上电/复位:电源稳定后,芯片内部复位电路释放复位信号,CPU内核从固定地址(如0x00000000)加载主堆栈指针(MSP),并从0x00000004加载复位向量(即复位程序的入口地址),然后跳转执行。

2. 初始化
  • 基础时钟启动:复位后,CPU默认使用内部RC振荡器(如HSI,上电后默认启用,速度较快(如8MHz或16MHz),但精度一般。优点是无需外部晶振。作为系统时钟源,以保证最基本运行。

  • 执行复位程序:运行启动文件(如startup_xx.s)中的Reset_Handler,完成:

    • 全局/静态变量初始化(复制.data段、清零.bss段)。

    • 调用SystemInit函数,配置PLL锁相环(一个倍频器,可以把较低的输入时钟(如8MHz)乘以一个系数(如9倍),生成一个高频时钟(如72MHz)供CPU内核和高速外设使用。,将系统时钟切换到外部晶振(HSE,需外接石英晶体(如8MHz),精度高,通常用于需要精准时序的场合并倍频至目标频率(如72MHz),同时配置总线时钟(通过AHB预分频器APB预分频器,将SYSCLK分频后,供给各个外设总线)——此时时钟树完整建立

    • 跳转到main函数。

3. 主程序执行
  • 外设初始化:在main函数开头,使能各外设时钟(通过RCC寄存器),配置GPIO、UART、定时器等的工作模式。

  • 进入无限循环:执行while(1)for(;;),循环处理应用程序逻辑(如轮询标志位、状态机等)。这是单片机工作的主体,除非断电或复位,否则永不退出。

4. 中断并发处理
  • 异步事件响应:在无限循环执行过程中,若外设触发中断(如UART接收完成、定时器溢出),CPU在当前指令完成后,由NVIC(嵌套向量中断控制器,Cortex-M内核中专门管理中断的模块)硬件自动保存现场查找向量表并跳转到对应的中断服务函数(ISR)。ISR执行完毕后硬件恢复现场,回到主循环被打断的位置继续执行。

外设模块

输入/输出类
  • GPIO:复习输入模式(浮空/上拉/下拉)、输出模式(推挽/开漏)、复用功能配置。
  • PWM:要素是占空比、频率与定时器,例如通过PWM控制电机/灯光亮度。
  • ADC:分辨率(如12位)、采样时间、参考电压、多通道扫描、DMA传输(可选)。
通信接口类
  • UART(串口):波特率、数据位/停止位/校验位配置,与蓝牙模块(HC-05/HC-06)的交互。
  • I2C/SPI:总线协议、时序、与OLED/传感器(如MPU6050)的通信。
  • 蓝牙:无线串口,通过AT指令配置模块,实现无线数据传输。
时间与事件类
  • 定时器:基本定时、输入捕获(测频率/脉宽)、输出比较(PWM)、编码器接口。
  • RTC:理解时钟源(外部晶振/内部RC)、时间寄存器操作、闹钟/唤醒功能。
  • 中断:中断优先级、嵌套向量中断控制器(NVIC)、中断服务函数编写注意事项(快进快出)。
显示类
  • OLED:通常通过I2C/SPI驱动,复习显示初始化、显存操作、字符/图形绘制。

GPIO

定义

即通用输入输出引脚,是单片机与外界交换数字信号的最基本途径。

引脚可独立配置

  • 输入模式:读取外部信号(如按键)。

  • 输出模式:驱动LED、蜂鸣器等(推挽/开漏)。

  • 复用功能:作为UART、I2C等外设的引脚。

  • 模拟模式:用于ADC输入。

核心操作:时钟使能 → 模式配置 → 数据读写。

内部结构

STM32的GPIO内部包含保护二极管、施密特触发器、输出驱动电路及一系列控制寄存器(CRL/CRH、IDR、ODR、BSRR等)。

GPIO引脚内部

包含

  • 保护二极管(防止过高/过低电压损坏芯片)。

  • 输出驱动器:可配置为推挽输出(能输出强高、低电平)或开漏输出(需要外接上拉电阻)。

  • 输入施密特触发器:对输入信号进行整形,避免抖动。

  • 复用功能选择器:将引脚连接到其他外设模块。

核心寄存器

(以STM32F1系列为例,每组GPIO有7个寄存器):

  • CRL/CRH(端口配置寄存器低/高):配置每个引脚的模式(输入、输出、复用、模拟)和输出类型/速度。

  • IDR(输入数据寄存器):只读,反映引脚当前的电平状态。

  • ODR(输出数据寄存器):读写,控制引脚输出电平(但修改时会可能引起中间态,一般用BSRR替代)。

  • BSRR(位设置/清除寄存器):写1对应位置位(输出高)或复位(输出低),原子操作,无中断风险。

  • BRR(位清除寄存器):仅用于复位。

  • LCKR(锁定寄存器):锁定配置,防止误改。

开发流程

新建工程且选择芯片

  • 打开STM32CubeIDE,新建STM32项目,选择MCU型号(如STM32F103C8)。

  • 系统会自动初始化时钟和基本外设。

配置GPIO

在图形界面中:

  • 找到要控制的引脚(如PA5),在Pinout视图中将其设置为GPIO_Output

  • Configuration > GPIO中,可以详细设置:

    • GPIO output level:初始电平(High/Low)。

    • GPIO mode:推挽输出(Push-pull)或开漏输出(Open-drain)。

    • GPIO Pull-up/Pull-down:是否使能内部上拉/下拉(输出模式通常不使能)。

    • Maximum output speed:根据应用选择(如Low, Medium, High),影响EMI和功耗。

配置时钟

  • Clock Configuration标签页,设置系统时钟(如HSE+PLL倍频至72MHz),HAL库会自动生成SystemClock_Config()函数。

编译代码

以PA5控制LED为例

点击Ctrl+S保存,CubeIDE会生成初始化代码,包括:

  • main.c:包含main函数和初始化调用。

  • gpio.c/.h:GPIO初始化函数MX_GPIO_Init()

时钟使能

MX_GPIO_Init()中,首先使能GPIOA时钟:

__HAL_RCC_GPIOA_CLK_ENABLE();

(向RCC寄存器(RCC_APB2ENR)对应位写1,打开GPIOA模块的时钟门。只有时钟开启,才能访问GPIO的寄存器。)

GPIO初始化结构体
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;          // 引脚号
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_GPIO_Init()会根据结构体配置,设置GPIOx的CRL/CRH寄存器(低8位引脚用CRL,高8位用CRH):

  • 对于PA5(属于低8位),CRL寄存器的[23:20]位被写入MODE5CNF5

    • MODE设置速度(01=10MHz, 10=2MHz, 11=50MHz),对应结构体中的Speed

    • CNF设置模式(00=推挽输出,01=开漏输出等),对应ModePull的组合。

控制输出电平
  • 置高HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
    底层:向BSRR寄存器的低16位(BS5)写1,引脚输出高电平。

  • 置低HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
    底层:向BSRR寄存器的高16位(BR5)写1,引脚输出低电平(实际是BSRR的高16位用于复位,也可用BRR寄存器)。

  • 翻转HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
    底层:读取ODR寄存器当前状态,取反后写回ODR(或使用BSRR原子操作实现)。注意翻转非原子操作,但HAL实现中用了BSRR来避免风险。

以下是LED闪烁的代码参考

int main(void)
{
    HAL_Init();                 // 初始化HAL库(配置SysTick定时器作为时基)
    SystemClock_Config();       // 配置系统时钟(由CubeMX生成)
    MX_GPIO_Init();             // 初始化GPIO

    while (1)
    {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);   // 翻转LED
        HAL_Delay(500);         // 延时500ms(依赖SysTick中断)
    }
}

一般配置时钟和初始化HAL,GPIO系统会自动完成,编译.h件和while(1)函数中的的代码即可

工作流程

  • 上电复位后,CPU从Flash加载复位向量,执行启动文件中的Reset_Handler

  • Reset_Handler调用SystemInit(设置基本的时钟,如Flash等待周期),然后跳转到main

  • main中调用HAL_Init(初始化SysTick定时器,用于HAL_Delay),接着SystemClock_Config配置PLL得到72MHz系统时钟,然后MX_GPIO_Init配置PA5为推挽输出。

  • 进入无限循环,每500ms翻转LED状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值