锂电池 保护板方案 中颖SH367309方案 原理图 PCB 源代码 保护板方案 中颖SH367309方案 原理图 PCB 源代码 锂电池、保护板方案、中颖SH367309方案、原理图和PCB源代码。 锂电池是一种常见的可充电电池,由锂离子在正负极之间的迁移来储存和释放电能。它们具有高能量密度、长寿命和较低的自放电率等优点,因此在许多电子设备中得到广泛应用。 保护板方案是用于保护锂电池的电路设计方案。它的主要功能是监测电池的电压、电流和温度等参数,并在必要时采取措施来防止电池过充、过放、过流或过温。保护板方案可以提高锂电池的安全性和可靠性。 中颖SH367309方案是一种特定的保护板方案。该方案可能包括特定的电路设计、元件选择和算法等,以实现对锂电池的保护功能。 原理图是电子设备设计中的一种图表,用于展示电路的连接方式和元件之间的关系。它是设计师用来理解和实现电路功能的重要工具。 PCB(Printed Circuit Board,印刷电路板)是电子设备中的一种基础组件,用于支持和连接电子元件。它由一层或多层导电材料构成,通过印刷、蚀刻和穿孔等工艺制成。PCB上的导线和连接点可以实现电路的连接和信号传输。
一、代码背景与整体架构
本次解析的代码来自中颖SH367309锂电池保护板方案,基于STM32F10x系列微控制器,围绕锂电池安全保护核心需求,构建了“内核驱动-硬件适配-功能应用”三层代码架构。代码总数135个,核心文件集中在CORE(内核)与HARDWARE(硬件驱动/应用)目录,通过模块化设计实现电池参数采集、保护控制、数据存储、人机交互等功能。本文将聚焦代码细节,从函数实现、数据结构、寄存器操作等维度,逐一拆解各模块功能逻辑。
二、CORE目录:Cortex-M3内核驱动代码解析
CORE目录包含corecm3.c/.h(内核功能实现)与startupstm32f10x_hd.s/.md.s(启动文件),是整个系统的底层支撑,严格遵循CMSIS标准,适配多编译器环境(ARM Compiler、IAR、GNU、TASKING)。
(一)core_cm3.c:内核核心功能代码实现
该文件通过汇编与C语言混合编程,封装Cortex-M3内核寄存器操作,提供堆栈管理、中断控制、数据处理等底层接口,核心代码片段与功能解析如下:
1. 编译器适配宏定义
代码开篇通过条件编译区分不同编译器的关键字差异,确保底层接口兼容性,例如:
#if defined ( __CC_ARM )
#define __ASM __asm /*!< asm keyword for ARM Compiler */
#define __INLINE __inline /*!< inline keyword for ARM Compiler */
#elif defined ( __ICCARM__ )
#define __ASM __asm /*!< asm keyword for IAR Compiler */
#define __INLINE inline /*!< inline keyword for IAR Compiler */
#elif defined ( __GNUC__ )
#define __ASM __asm /*!< asm keyword for GNU Compiler */
#define __INLINE inline /*!< inline keyword for GNU Compiler */
#endif
功能作用:统一不同编译器的asm(汇编)与inline(内联函数)关键字,避免语法错误,确保代码跨编译器可编译。
2. 堆栈指针操作函数
Cortex-M3内核支持主堆栈(MSP,用于内核异常/特权模式)与进程堆栈(PSP,用于用户线程),代码通过汇编函数实现堆栈指针的读写:
// 读取进程堆栈指针(PSP)
__ASM uint32_t __get_PSP(void)
{
mrs r0, psp // 将PSP寄存器值存入r0
bx lr // 返回,r0作为返回值
}
// 设置进程堆栈指针(PSP)
__ASM void __set_PSP(uint32_t topOfProcStack)
{
msr psp, r0 // 将r0(输入参数)的值写入PSP寄存器
bx lr // 返回
}
代码解析:
mrs(Move Register from Special Register):将特殊寄存器(如PSP、MSP)的值读取到通用寄存器(r0)。
msr(Move Special Register from Register):将通用寄存器的值写入特殊寄存器。
函数返回时,bx lr通过链接寄存器(lr)返回调用处,符合ARM Thumb指令集规范。
功能作用:支持操作系统线程切换(如RTOS)时的堆栈上下文保存与恢复,是多任务运行的基础。
3. 中断与优先级控制函数
代码实现了内核中断屏蔽、优先级配置相关函数,例如基础优先级(BASEPRI)的读写:
// 读取BASEPRI寄存器(控制中断优先级屏蔽)
__ASM uint32_t __get_BASEPRI(void)
{
mrs r0, basepri // 读取BASEPRI值到r0
bx lr
}
// 设置BASEPRI寄存器
__ASM void __set_BASEPRI(uint32_t basePri)
{
msr basepri, r0 // 将输入参数(r0)写入BASEPRI
bx lr
}
关键说明:
BASEPRI寄存器:仅屏蔽优先级低于该值的中断(例如BASEPRI=0x80时,屏蔽优先级≥0x80的中断),用于精细控制中断响应。
代码中basePri & 0xFF的隐含处理(部分编译器实现):确保仅使用低8位(Cortex-M3优先级寄存器共8位,实际使用4位或8位取决于芯片设计)。
功能作用:在关键任务(如电池参数采集、保护逻辑执行)运行时,屏蔽低优先级中断,避免任务被打断导致数据异常。
4. 数据处理 intrinsics函数
针对嵌入式场景中常用的字节/位反转需求,代码封装了硬件加速指令:
// 16位无符号数字节反转(如0x1234 → 0x3412)
__ASM uint32_t __REV16(uint16_t value)
{
rev16 r0, r0 // ARM指令:反转16位数据的字节顺序
bx lr
}
// 16位有符号数字节反转并符号扩展为32位(如0x8000 → 0xFFFF8000)
__ASM int32_t __REVSH(int16_t value)
{
revsh r0, r0 // ARM指令:反转16位数据字节顺序,符号扩展到32位
bx lr
}
代码优势:直接调用ARM硬件指令,比软件实现(如通过移位+掩码)效率提升10倍以上,适用于高速数据处理(如ADC采集数据的端序转换)。
(二)startup_stm32f10x_hd.s:启动代码解析
启动文件为汇编编写,负责STM32F10x芯片上电后的初始化流程,核心代码片段与功能如下:
1. 堆栈空间定义
; 定义堆栈大小(8192字节),NOINIT表示不初始化,READWRITE表示可读写,ALIGN=3表示8字节对齐
Stack_Size EQU 0x00002000
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size ; 分配Stack_Size字节的堆栈空间
__initial_sp ; 定义栈顶指针(堆栈空间的末地址)
; 定义堆大小(1024字节),用于动态内存分配(如malloc)
Heap_Size EQU 0x00000400
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base ; 堆起始地址
Heap_Mem SPACE Heap_Size ; 分配Heap_Size字节的堆空间
__heap_limit ; 堆结束地址
关键说明:
AREA:汇编指令,定义代码/数据段,STACK/HEAP为段名,NOINIT表示上电后不自动初始化(避免浪费启动时间)。
ALIGN=3:8字节对齐(2^3),符合Cortex-M3内核对堆栈指针的对齐要求(否则可能触发硬件异常)。
功能作用:为系统提供堆栈空间,栈用于函数调用时的局部变量存储、寄存器保护;堆用于动态内存分配(如my_cpuset结构体的动态创建)。
2. 中断向量表
中断向量表是启动文件的核心,存储所有异常与外设中断的入口地址:
AREA RESET, DATA, READONLY ; 定义只读数据段(中断向量表)
EXPORT __Vectors ; 导出向量表起始地址,供链接器使用
__Vectors DCD __initial_sp ; 0: 栈顶指针
DCD Reset_Handler ; 1: 复位异常处理函数
DCD NMI_Handler ; 2: NMI异常处理函数
DCD HardFault_Handler ; 3: 硬故障异常处理函数
; ... 省略其他内核异常 ...
DCD SysTick_Handler ; 15: 系统滴答定时器中断
; 外部中断开始
DCD WWDG_IRQHandler ; 16: 窗口看门狗中断
DCD PVD_IRQHandler ; 17: 电源电压检测中断
DCD EXTI0_IRQHandler ; 18: 外部中断0(对应GPIOB0)
; ... 省略其他外设中断 ...
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors ; 计算向量表大小
代码逻辑:
芯片上电后,PC指针自动指向向量表第0项(栈顶指针),随后跳转到第1项(Reset_Handler)执行。
每个中断对应一个DCD(Define Code/Data)指令,存储中断处理函数的地址;未使用的中断默认指向Default_Handler(无限循环)。
功能作用:建立中断与处理函数的映射关系,是系统响应中断的基础(如EXTI0中断触发时,CPU自动跳转到EXTI0_IRQHandler)。
3. 复位处理函数
Reset_Handler PROC ; 定义复位处理函数过程
EXPORT Reset_Handler [WEAK] ; 弱定义,允许用户重写
IMPORT __main ; 导入C库的__main函数
IMPORT SystemInit ; 导入系统初始化函数(如时钟配置)
LDR R0, =SystemInit ; R0 = SystemInit函数地址
BLX R0 ; 调用SystemInit(配置系统时钟)
LDR R0, =__main ; R0 = __main函数地址
BX R0 ; 跳转到__main(最终调用main函数)
ENDP ; 函数结束
执行流程:
调用SystemInit:配置STM32的系统时钟(默认72MHz,通过HSE+PLL实现)、外设时钟使能等。
跳转到main:C库函数,负责初始化堆/栈、全局变量,最终调用用户编写的main函数。
关键细节:
BLX(Branch with Link and Exchange):带链接的跳转,同时切换指令集(ARM/Thumb),适合调用不同指令集的函数。
[WEAK]:弱符号属性,若用户定义了同名的Reset_Handler,则覆盖该实现,增强代码灵活性。
三、HARDWARE目录:硬件驱动与应用代码解析
HARDWARE目录是方案的功能核心,包含ADC(参数采集)、EXTI(中断处理)、KEY(用户输入)、LCD(显示)、CPU_TX(参数管理)等模块,以下聚焦代码实现细节。
(一)ADC模块:adc.c/.h——电池参数采集
ADC模块负责采集锂电池的电压、电流等模拟信号,为保护逻辑提供数据输入,核心代码解析如下:
1. ADC初始化函数`Adc_Init`
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA和ADC1的时钟(APB2总线)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
// 配置ADC时钟分频:PCLK2(72MHz)/6=12MHz(ADC最大时钟不能超过14MHz)
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
// 配置GPIOA0/1/4为模拟输入模式(无上下拉、无推挽)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 复位ADC1(恢复默认配置)
ADC_DeInit(ADC1);
// 配置ADC1工作模式
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 独立模式(仅ADC1工作)
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; // 转换通道数:1
ADC_Init(ADC1, &ADC_InitStructure); // 初始化ADC1
// 使能ADC1
ADC_Cmd(ADC1, ENABLE);
// ADC校准(确保采集精度)
ADC_ResetCalibration(ADC1); // 复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); // 等待复位完成
ADC_StartCalibration(ADC1); // 启动校准
while(ADC_GetCalibrationStatus(ADC1)); // 等待校准完成
}
代码关键逻辑:
时钟配置:RCCAPB2PeriphClockCmd使能对应外设时钟(GPIOA和ADC1均挂载在APB2总线);RCCADCCLKConfig确保ADC时钟在允许范围内,避免采集误差。
引脚配置:GPIOModeAIN(模拟输入)模式下,GPIO引脚断开数字电路,仅保留模拟通路,避免数字信号干扰模拟采集。
校准流程:ADC上电后必须执行校准,通过ADCResetCalibration和ADCStartCalibration消除硬件偏移,提升采集精度(校准后误差可降至±1LSB)。
2. 单通道采集函数`Get_Adc`
u16 Get_Adc(u8 ch)
{
// 配置ADC1的规则通道:通道ch,转换序列1,采样时间239.5周期
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);
// 软件触发ADC转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// 等待转换完成(EOC:End of Conversion)
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
// 返回转换结果(12位ADC,右对齐后范围0~4095)
return ADC_GetConversionValue(ADC1);
}
采集流程:
通道配置:ADC_RegularChannelConfig指定当前采集的ADC通道(ch=0/1/4),采样时间239.5周期(越长,采集越稳定,但速度越慢,适合电池参数这类慢变信号)。
触发转换:ADC_SoftwareStartConvCmd通过软件触发转换(无外部触发源)。
等待完成:ADC_GetFlagStatus检查EOC标志,确保转换完成后再读取数据,避免读取无效值。
数据范围:STM32F10x的ADC为12位,转换结果范围0~4095,对应输入电压范围0~3.3V(若有分压电路,需乘以分压系数得到实际电池电压)。
3. 多通道轮询采集`TIME_to_CAdc`
// 全局变量:采集缓冲区(16个元素,循环存储)
u16 cad_temp1[16], cad_temp2[16], cad_temp3[16];
u8 cad_nox = 0, cad_noy = 0; // 通道索引(0-2)、缓冲区索引(0-15)
void TIME_to_CAdc(void)
{
if(cad_nox == 0)
{
// 读取当前ADC结果,存入cad_temp1(通道0对应的数据)
cad_temp1[cad_noy] = ADC_GetConversionValue(ADC1);
// 配置下一次采集通道1,采样时间239.5周期
ADC_RegularChannelConfig(ADC1, 1, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 触发转换
}
else if(cad_nox == 1)
{
cad_temp2[cad_noy] = ADC_GetConversionValue(ADC1); // 读取通道1结果
ADC_RegularChannelConfig(ADC1, 4, 1, ADC_SampleTime_239Cycles5); // 配置通道4
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
else if(cad_nox == 2)
{
cad_temp3[cad_noy] = ADC_GetConversionValue(ADC1); // 读取通道4结果
ADC_RegularChannelConfig(ADC1, 0, 1, ADC_SampleTime_239Cycles5); // 恢复通道0
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
cad_noy++; // 缓冲区索引递增
if(cad_noy > 15) cad_noy = 0; // 循环覆盖
cad_nox = 0; // 通道索引重置
}
cad_nox++; // 通道索引递增
}
设计思路:
轮询采集:通过cad_nox(0-2)循环切换ADC通道(0→1→4),每20ms触发一次(由定时器中断调用该函数),实现3路信号的周期性采集。
循环缓冲区:cadtemp1/cadtemp2/cadtemp3各有16个元素,cadnoy循环递增,实现数据的滑动存储,支持后续平均滤波(如取16次数据平均值)。
应用场景:通道0/1/4分别对应电池总电压、充电电流、放电电流的采样电路,通过轮询实现多参数同时监测。
(二)EXTI模块:exti.c/.h——中断处理
EXTI模块负责处理外部中断(如AFE芯片的报警信号、唤醒信号),核心代码解析如下:
1. 外部中断初始化`EXTIX_Init`
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能AFIO时钟(外部中断需要AFIO复用功能)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 初始化GPIOB0为上拉输入(中断触发引脚)
alrm_Init();
// 映射GPIOB0到EXTI_Line0
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
// 配置EXTI_Line0
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式(非事件模式)
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能该中断线
EXTI_Init(&EXTI_InitStructure);
// 配置NVIC(嵌套向量中断控制器)
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // EXTI0中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; // 抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; // 子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能该中断通道
NVIC_Init(&NVIC_InitStructure);
}
关键代码解析:
GPIOEXTILineConfig:将GPIO引脚与EXTI中断线绑定(GPIOB0→EXTILine0),STM32的EXTI线与GPIO引脚有固定映射关系(如EXTI_Line0对应所有GPIOx的Pin0)。
中断触发方式:EXTITriggerFalling(下降沿触发),适合检测外部设备的报警信号(如AFE芯片输出低电平报警)。
NVIC配置:PreemptionPriority(抢占优先级)决定中断是否能打断其他中断;SubPriority(子优先级)决定同抢占优先级下的响应顺序,此处配置为中等优先级,避免影响核心中断(如SysTick)。
2. 中断服务函数`EXTI0_IRQHandler`
void EXTI0_IRQHandler(void)
{
// 若系统处于睡眠/空闲模式,唤醒系统
if(bSleep** || bIdle**)
{
// 唤醒相关操作(如重新初始化时钟)
}
bAFE** = 1; // 设置AFE报警标志位,通知上层处理
// 清除中断挂起位(必须清除,否则会重复触发中断)
EXTI_ClearITPendingBit(EXTI_Line0);
}
代码逻辑:
中断响应:当GPIOB0检测到下降沿时,CPU跳转到该函数,设置bAFE**标志位(上层代码会轮询该标志,执行保护动作)。
清除挂起位:EXTI_ClearITPendingBit清除EXTI的中断挂起标志,若不清除,中断控制器会认为中断未处理,导致函数重复执行。
(三)CPU_TX模块:参数管理与Flash存储
CPU_TX模块是方案的“大脑”,负责管理电池保护参数(阈值、校准值),并通过Flash实现参数掉电保存,核心代码解析如下:
1. 核心数据结构`MY_CPU`
typedef struct
{
u16 data; // 数据标识(如0xDC55,用于参数合法性校验)
u16 use_setmaH; // 电流上限阈值(高)
u16 use_setmaL; // 电流下限阈值(低)
u16 adc_maH; // 电流采集校准值(高)
u16 adc_maL; // 电流采集校准值(低)
u16 use_setINVH; // 输入电压上限阈值(高)
u16 use_setINVL; // 输入电压下限阈值(低)
u16 adc_INVH; // 输入电压采集校准值(高)
u16 adc_INVL; // 输入电压采集校准值(低)
u16 use_OUTVH; // 输出电压上限阈值(高)
u16 use_OUTVL; // 输出电压下限阈值(低)
u16 adc_OUTVH; // 输出电压采集校准值(高)
u16 adc_OUTVL; // 输出电压采集校准值(低)
u16 ctr_dianliu; // 电流控制值(如充电电流限制)
} MY_CPU;
MY_CPU my_cpuset; // 全局变量,存储当前参数配置
数据作用:
阈值参数(usesetmaH/usesetINVH等):定义电池的保护边界(如use_setINVH=240对应输入电压上限24V)。
校准参数(adcmaH/adcINVH等):补偿ADC采集误差(如ADC采集值与实际电流的比例系数)。
标识位(data=0xDC55):用于Flash读取时的合法性校验,避免读取无效数据。
2. Flash参数读取`read_sensor_set`
void read_sensor_set(void)
{
u16 save_data[30];
// 从Flash指定地址(FLASH_SAVE_ADDR)读取20个半字(40字节)
STMFLASH_Read(FLASH_SAVE_ADDR, (u16*)save_data, 20);
// 校验数据合法性(通过data字段是否为0xDC55)
if(save_data[0] == 0xDC55)
{
// 从Flash数据加载到my_cpuset
my_cpuset.data = save_data[0];
my_cpuset.use_setmaH = save_data[1];
my_cpuset.use_setmaL = save_data[2];
// ... 省略其他参数加载 ...
// 加载传感器ID
seneor1.sensorID[0] = save_data[14];
seneor1.sensorID[1] = save_data[15];
// ... 省略其他ID加载 ...
}
else
{
// 数据无效,加载默认参数
save_data[0] = my_cpuset.data = 0xDC55;
save_data[1] = my_cpuset.use_setmaH = 265; // 默认电流上限阈值
save_data[2] = my_cpuset.use_setmaL = 72; // 默认电流下限阈值
// ... 省略其他默认参数设置 ...
// 将默认参数写入Flash,下次上电加载
STMFLASH_Write(FLASH_SAVE_ADDR, (u16*)save_data, 20);
}
}
代码逻辑:
数据校验:通过save_data[0] == 0xDC55判断Flash中是否存储了有效参数,避免因Flash擦除或损坏导致的参数错误。
默认参数:若Flash数据无效,加载默认参数(如电流上限265mA),并写入Flash,确保系统正常运行。
Flash操作:调用STMFLASHRead/STMFLASHWrite(封装在stmflash.c中)实现数据读写,后续解析该函数。
3. Flash底层操作`STMFLASH_Write`
void STMFLASH_Write(u32 WriteAddr, u16 *pBuffer, u16 NumToWrite)
{
u32 secpos; // 扇区地址
u16 secoff; // 扇区内偏移(半字单位)
u16 secremain; // 扇区内剩余半字数
u16 i;
u32 offaddr; // 相对于Flash基地址的偏移
// 检查地址合法性(Flash基地址0x08000000,大小STM32_FLASH_SIZE)
if(WriteAddr < STM32_FLASH_BASE || WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE))
return;
FLASH_Unlock(); // 解锁Flash(STM32 Flash默认锁定,防止误写)
offaddr = WriteAddr - STM32_FLASH_BASE; // 计算偏移
secpos = offaddr / STM_SECTOR_SIZE; // 计算扇区地址(STM_SECTOR_SIZE=1024字节)
secoff = (offaddr % STM_SECTOR_SIZE) / 2; // 计算扇区内偏移(半字单位,1半字=2字节)
secremain = STM_SECTOR_SIZE / 2 - secoff; // 扇区内剩余半字数
// 若写入数据超过当前扇区剩余空间,分多次写入
if(NumToWrite <= secremain)
secremain = NumToWrite;
while(1)
{
// 读取当前扇区所有数据到缓冲区(避免擦除扇区导致数据丢失)
STMFLASH_Read(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE, STMFLASH_BUF, STM_SECTOR_SIZE / 2);
// 检查扇区内是否有非0xFFFF的数据(0xFFFF表示未擦除)
for(i = 0; i < secremain; i++)
{
if(STMFLASH_BUF[secoff + i] != 0xFFFF)
break; // 需要擦除扇区
}
if(i < secremain)
{
// 擦除当前扇区(Flash写入前必须擦除,擦除单位为扇区)
FLASH_ErasePage(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE);
// 将待写入数据复制到缓冲区
for(i = 0; i < secremain; i++)
STMFLASH_BUF[secoff + i] = pBuffer;
// 写入整个扇区数据
STMFLASH_Write_NoCheck(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE, STMFLASH_BUF, STM_SECTOR_SIZE / 2);
}
else
{
// 扇区已擦除,直接写入数据
STMFLASH_Write_NoCheck(WriteAddr, pBuffer, secremain);
}
// 判断是否写入完成
if(NumToWrite == secremain)
break;
else
{
// 未完成,处理下一个扇区
secpos++; // 扇区地址递增
secoff = 0; // 偏移重置为0
pBuffer += secremain; // 数据指针偏移
WriteAddr += secremain * 2; // 写入地址偏移(半字=2字节)
NumToWrite -= secremain; // 剩余数据量递减
// 计算下一个扇区的剩余空间
if(NumToWrite > STM_SECTOR_SIZE / 2)
secremain = STM_SECTOR_SIZE / 2;
else
secremain = NumToWrite;
}
}
FLASH_Lock(); // 锁定Flash,防止误写
}
Flash操作核心逻辑:
解锁/锁定:FLASHUnlock/FLASHLock通过写入特定密钥(0x45670123、0xCDEF89AB)解锁,防止意外写入导致参数损坏。
扇区擦除:STM32 Flash的最小擦除单位为扇区(1024字节),写入前需先擦除(擦除后数据为0xFFFF),避免写入错误。
数据保护:通过STMFLASH_BUF缓冲区保存扇区原有数据,仅更新需要修改的部分,再重新写入整个扇区,避免擦除导致其他数据丢失(如其他配置参数)。
跨扇区处理:若写入数据超过当前扇区剩余空间,自动切换到下一个扇区,支持任意长度的数据写入(只要不超过Flash总容量)。
四、代码功能串联与应用场景
(一)核心功能流程
结合上述代码解析,中颖SH367309方案的核心功能流程可总结为:
上电初始化:启动文件执行ResetHandler→调用SystemInit配置时钟→进入main函数→初始化ADC、EXTI、KEY、LCD、Flash模块→readsensor_set加载参数。
参数采集:定时器每20ms调用TIMEtoCAdc→轮询采集ADC通道0/1/4(电压、电流)→数据存入cadtemp1/cadtemp2/cad_temp3缓冲区。
保护判断:主循环中读取采集数据→与mycpuset中的阈值参数(usesetmaH/use_setINVH等)对比→若触发保护条件(如电压超过上限),设置保护标志位→控制硬件关断充放电回路。
中断响应:AFE芯片报警触发EXTI0中断→EXTI0_IRQHandler设置bAFE**→主循环检测到标志位,执行紧急保护动作。
参数配置:用户通过KEY模块输入参数→修改mycpuset结构体→调用saveCPUset写入Flash→下次上电自动加载。
状态显示:LCD模块定期读取mycpuset和采集数据→通过LCDDrawPoint/LCD_ShowChar显示电压、电流、保护状态等信息。
(二)代码设计亮点
分层设计:内核驱动(CORE)、硬件驱动(HARDWARE/ADC/EXTI)、应用逻辑(HARDWARE/CPU_TX)分层隔离,便于维护与扩展(如更换LCD驱动只需修改LCD模块代码)。
兼容性强:支持多编译器、多STM32芯片(HD/MD)、多LCD驱动IC,适配不同硬件方案。
可靠性高:ADC校准、Flash参数校验、多级别保护阈值、中断优先级配置,确保系统稳定运行。
易用性好:提供标准化接口(如GetAdc/saveCPUset),用户无需关注底层细节,可快速二次开发。
五、总结
中颖SH367309锂电池保护板方案代码通过严谨的底层驱动实现、模块化的功能设计、可靠的数据处理逻辑,构建了完整的锂电池保护解决方案。从代码细节看,每个函数、每个寄存器操作均围绕“安全保护”核心需求展开,例如ADC的高精度采集确保保护判断准确,Flash的参数存储确保配置不丢失,EXTI的中断响应确保紧急情况快速处理。
对于开发者而言,理解代码中的内核操作(如堆栈管理、中断控制)、硬件适配(如ADC时钟配置、Flash擦写)、功能逻辑(如参数校准、保护判断),是基于该方案进行二次开发的关键。后续可根据实际需求,扩展通信接口(如UART远程监控)、新增传感器(如温度采集)、优化保护算法(如自适应阈值),进一步提升方案的适用性。
————————————————
版权声明:本文为CSDN博主「Q27699885」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Q27699885/article/details/153964900
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?注册
×
|