1. 嵌入式系统I/O接口深度解析
嵌入式系统的I/O接口是连接处理器与外部世界的桥梁,其设计直接影响系统的功能扩展性和性能表现。作为一名嵌入式开发工程师,我经常需要根据项目需求选择合适的接口方案。下面我将从实际应用角度,详细剖析各类常见接口的工作原理和设计要点。
1.1 GPIO接口的实战应用
GPIO(通用输入输出)是嵌入式系统中最基础的接口类型,几乎所有微控制器都配备GPIO引脚。在STM32系列MCU中,GPIO的配置涉及多个寄存器操作,需要开发者深入理解其底层机制。
以STM32F103为例,其GPIO主要包含四种寄存器:
- 端口配置寄存器(GPIOx_CRL/CRH):设置引脚工作模式
- 端口输入数据寄存器(GPIOx_IDR):读取输入状态
- 端口输出数据寄存器(GPIOx_ODR):控制输出电平
- 端口位设置/清除寄存器(GPIOx_BSRR):原子操作输出状态
关键提示:在配置GPIO时,务必先使能对应端口的时钟(RCC_APB2ENR寄存器),这是新手最容易忽略的步骤。我曾在一个项目中花了半天时间排查GPIO不工作的问题,最终发现就是漏了时钟使能。
GPIO的工作模式主要分为:
- 输入模式
- 浮空输入:用于数字信号输入,如按键检测
- 上拉/下拉输入:避免引脚悬空导致的不确定状态
- 输出模式
- 推挽输出:标准数字输出,可驱动LED等负载
- 开漏输出:支持线与逻辑,常用于I2C总线
在实际项目中,GPIO的配置流程通常为:
c复制// STM32 GPIO配置示例
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置PB5为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
1.2 模数转换接口技术详解
模数转换器(ADC)是将模拟信号转换为数字信号的关键接口,在传感器数据采集等场景中不可或缺。STM32内置的ADC模块通常支持12位分辨率,有多种工作模式可供选择。
ADC的主要性能指标包括:
- 分辨率:12位ADC可表示4096个离散值
- 采样率:STM32F103的ADC最高采样率可达1MHz
- 输入通道:多路复用,支持单次或连续转换
ADC的转换过程可分为四个阶段:
- 采样保持:采集输入信号并保持稳定
- 逐次逼近:通过DAC和比较器逐位确定数字值
- 数据转换:完成全部位数的转换
- 数据输出:将结果存入数据寄存器
在电机控制项目中,我曾使用STM32的ADC采集电流信号,配置要点包括:
c复制// ADC配置示例
void ADC_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
// 使能ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// ADC基本配置
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
// 使能ADC
ADC_Cmd(ADC1, ENABLE);
// ADC校准
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
经验分享:ADC采样时要注意抗干扰设计。我曾遇到采样值跳动大的问题,通过添加RC滤波(10kΩ电阻+100nF电容)和软件均值滤波(采样10次取平均)显著提高了稳定性。
1.3 人机交互接口设计
人机交互接口包括键盘、显示和触摸屏等,是嵌入式系统与用户沟通的重要渠道。
1.3.1 矩阵键盘扫描技术
矩阵键盘可有效减少I/O占用,常见的4x4矩阵只需8个GPIO即可支持16个按键。扫描方法主要有:
- 行扫描法:逐行输出低电平,检测列输入
- 列扫描法:逐列输出低电平,检测行输入
- 反转法:行列角色互换进行二次扫描
在智能家居控制面板项目中,我采用行扫描法实现按键检测:
c复制// 矩阵键盘扫描示例
uint8_t Key_Scan(void)
{
uint8_t row, col;
// 所有行输出低电平
for(row=0; row<4; row++) {
KEY_ROW_OUT(row, 0);
}
// 检测列输入
for(col=0; col<4; col++) {
if(!KEY_COL_IN(col)) {
// 有按键按下,进行行扫描
for(row=0; row<4; row++) {
KEY_ROW_OUT(row, 0);
if(!KEY_COL_IN(col)) {
KEY_ROW_OUT(row, 1); // 恢复行输出
return row*4 + col + 1; // 返回键值
}
KEY_ROW_OUT(row, 1);
}
}
}
return 0; // 无按键按下
}
1.3.2 LCD显示接口实战
TFT-LCD是目前嵌入式系统最常用的显示设备,其接口主要有:
- 8080并行接口:传输速度快,但占用I/O多
- SPI接口:引脚少但速度较慢,适合小屏
- RGB接口:高端应用,需要专用控制器
在智能手表项目中,我使用SPI接口驱动1.3寸TFT屏,关键配置如下:
c复制// LCD SPI初始化
void LCD_Init(void)
{
// 硬件复位
LCD_RST_LOW();
DelayMs(100);
LCD_RST_HIGH();
DelayMs(100);
// SPI配置
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
// 发送初始化命令序列
LCD_WriteCmd(0x11); // Sleep out
DelayMs(120);
LCD_WriteCmd(0x36); // MEMORY_ACCESS_CONTROL
LCD_WriteData(0x00);
// 更多初始化命令...
}
避坑指南:LCD初始化时序非常关键,不同型号的初始化序列可能差异很大。务必仔细阅读数据手册,并预留足够的延时。我曾因初始化延时不足导致显示异常,增加延时后问题解决。
2. 定时器系统深度剖析
定时器是嵌入式系统的"心脏",负责时间管理、PWM生成、输入捕获等重要功能。STM32系列提供了丰富的定时器资源,包括基本定时器、通用定时器和高级定时器。
2.1 硬件定时器配置实战
以STM32F103的TIM2通用定时器为例,其典型配置流程如下:
c复制void TIM_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 使能TIM2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 时基配置
TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 使能定时器中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// 启动定时器
TIM_Cmd(TIM2, ENABLE);
}
定时器参数计算公式:
- 定时周期 = (TIM_Period + 1) × (TIM_Prescaler + 1) / 定时器时钟频率
- 例如:72MHz时钟,TIM_Prescaler=71,TIM_Period=999,则定时周期为1ms
2.2 PWM输出应用实例
PWM(脉宽调制)广泛用于电机控制、LED调光等场景。配置TIM3通道1输出PWM的代码如下:
c复制void PWM_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 使能GPIOA和TIM3时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 配置PA6为复用推挽输出(TIM3_CH1)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// TIM3时基配置
TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// PWM模式配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500; // 初始占空比50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
// 启动TIM3
TIM_Cmd(TIM3, ENABLE);
}
调参技巧:PWM频率选择需权衡分辨率和响应速度。电机控制通常使用10-20kHz,可避免人耳听到的噪声;LED调光则可用100-1kHz,降低开关损耗。
2.3 输入捕获测量频率
定时器的输入捕获功能可用于测量脉冲宽度或频率。以下是测量PA0输入信号频率的配置:
c复制void IC_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能GPIOA和TIM2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 配置PA0为浮空输入(TIM2_CH1)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// TIM2时基配置
TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 1MHz计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 输入捕获配置
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
// 使能捕获中断
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 启动TIM2
TIM_Cmd(TIM2, ENABLE);
}
频率计算公式:
- 频率 = 定时器时钟 / (TIM_Prescaler+1) / 捕获差值
- 例如:72MHz时钟,预分频71,捕获差值36000,则频率=72MHz/72/36000=27.78Hz
3. 通信接口技术详解
嵌入式系统的通信接口可分为板级通信和远程通信两大类,各有其适用场景和特点。
3.1 UART串口通信实战
UART是最常用的异步串行通信接口,配置STM32的USART1如下:
c复制void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 使能GPIOA和USART1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// 配置PA9为复用推挽输出(USART1_TX)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置PA10为浮空输入(USART1_RX)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// USART参数配置
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART1
USART_Cmd(USART1, ENABLE);
}
调试经验:串口通信常见问题包括波特率不匹配、电平不兼容等。建议使用逻辑分析仪抓取波形,确认实际波特率与配置一致。我曾遇到RS485通信不稳定的问题,最终通过添加120Ω终端电阻解决。
3.2 SPI接口应用详解
SPI是一种高速全双工同步串行总线,适用于存储器、显示屏等设备。STM32的SP1配置示例:
c复制void SPI_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
// 使能GPIOA和SPI1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);
// 配置PA5/6/7为复用推挽输出(SPI1_SCK/MISO/MOSI)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// SPI参数配置
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &SPI_InitStructure);
// 使能SPI1
SPI_Cmd(SPI1, ENABLE);
}
SPI通信的四种模式由CPOL和CPHA组合决定:
- 模式0:CPOL=0,CPHA=0
- 模式1:CPOL=0,CPHA=1
- 模式2:CPOL=1,CPHA=0
- 模式3:CPOL=1,CPHA=1
设备兼容性提示:不同SPI设备可能支持不同的模式,必须严格匹配。我曾因模式设置错误导致Flash芯片无法识别,调整模式后正常工作。
3.3 I2C总线应用技巧
I2C是一种两线制串行总线,适合连接多个低速设备。STM32的I2C配置示例:
c复制void I2C_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
// 使能GPIOB和I2C1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
// 配置PB6/7为复用开漏输出(I2C1_SCL/SDA)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// I2C参数配置
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0xA0;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000; // 100kHz
I2C_Init(I2C1, &I2C_InitStructure);
// 使能I2C1
I2C_Cmd(I2C1, ENABLE);
}
I2C总线常见问题及解决方法:
- 总线锁死:通常因从设备异常导致,可通过多次发送STOP条件恢复
- 应答超时:检查设备地址是否正确,上拉电阻是否合适(通常4.7kΩ)
- 数据错误:确保时钟频率不超过从设备支持的最大值
3.4 CAN总线通信实现
CAN总线广泛应用于汽车电子和工业控制,STM32的CAN配置较为复杂:
c复制void CAN_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
// 使能GPIOA和CAN1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
// 配置PA11/12为复用推挽输出(CAN1_RX/TX)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// CAN参数配置
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = DISABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq;
CAN_InitStructure.CAN_Prescaler = 4;
CAN_Init(CAN1, &CAN_InitStructure);
// CAN过滤器配置
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
}
CAN总线波特率计算公式:
- 波特率 = APB1时钟 / (Prescaler × (1 + BS1 + BS2))
- 例如:APB1时钟36MHz,Prescaler=4,BS1=8tq,BS2=7tq,则波特率=36MHz/(4×16)=562.5kHz
工程经验:CAN总线终端必须添加120Ω匹配电阻,且布线应避免分支。在工业现场应用中,建议使用带隔离的CAN收发器(如ADM3053)以提高抗干扰能力。
4. 嵌入式SoC设计要点
现代嵌入式系统正朝着SoC(System on Chip)方向发展,集成了处理器核心、存储器和各种外设接口。选择适合的SoC需要考虑多方面因素。
4.1 SoC选型关键指标
-
处理器性能
- 主频:从几十MHz到几百MHz不等
- 架构:Cortex-M0/M3/M4/M7等
- 指令集:Thumb/Thumb-2等
-
存储资源
- Flash容量:从16KB到2MB
- SRAM容量:从4KB到512KB
- 外部存储器接口:支持NOR Flash、SDRAM等
-
外设接口
- 通信接口:UART、SPI、I2C、USB、CAN等
- 模拟接口:ADC、DAC、比较器等
- 定时器资源:基本定时器、PWM、编码器接口等
-
功耗特性
- 工作电压:1.8V-3.6V
- 低功耗模式:睡眠、停止、待机等
- 动态功耗调节:电压调节、时钟门控等
4.2 典型应用场景选型建议
-
智能家居控制
- 推荐型号:STM32F103系列
- 理由:性价比高,外设丰富,开发资源多
-
工业控制
- 推荐型号:STM32F407系列
- 理由:带FPU,高性能,丰富通信接口
-
可穿戴设备
- 推荐型号:STM32L4系列
- 理由:超低功耗,小封装,集成LCD驱动
-
电机控制
- 推荐型号:STM32F3系列
- 理由:高精度ADC,定时器支持PWM互补输出
4.3 硬件设计注意事项
-
电源设计
- 使用LDO或DC-DC提供稳定电压
- 添加足够的去耦电容(通常0.1μF+10μF组合)
- 注意模拟和数字电源的隔离
-
时钟电路
- 外部晶振选择合适负载电容
- 高频信号线尽量短,避免干扰
- 备用低速晶振(32.768kHz)用于RTC
-
复位电路
- 使用专用复位芯片或RC电路
- 复位时间通常需要大于20ms
- 添加手动复位按钮方便调试
-
调试接口
- SWD接口占用引脚少,推荐使用
- 预留测试点方便信号测量
- 考虑添加Bootloader跳线
设计经验:PCB布局时,模拟部分应远离数字高速信号;晶振下方不要走线;电源走线足够宽;敏感信号使用地线保护。我曾因晶振布局不当导致系统不稳定,重新布局后问题解决。
