项目概述与技术架构
本代码库实现了一个基于STM32F103微控制器和中颖SH367309方案的锂电池保护板系统。从代码结构来看,这是一个典型的嵌入式实时系统,采用了CMSIS-Cortex-M3标准架构,包含完整的硬件抽象层、驱动层和应用逻辑层。
核心代码模块深度分析
1. Cortex-M3系统底层代码分析
在core_cm3.c中,我们看到针对不同编译器的内联汇编实现:
// ARM Compiler版本的内联汇编实现
__ASM uint32_t __get_PSP(void)
{
mrs r0, psp
bx lr
}
// GNU Compiler版本带有属性修饰
uint32_t __get_PSP(void) __attribute__( ( naked ) );
uint32_t __get_PSP(void)
{
uint32_t result=0;
__ASM volatile ("MRS %0, psp\n\t"
"MOV r0, %0 \n\t"
"BX lr \n\t" : "=r" (result) );
return(result);
}
这种多编译器支持的设计体现了代码的移植性考虑。naked属性确保函数没有编译器生成的序言和结语,完全由汇编指令控制。
2. 存储器映射与寄存器定义
在core_cm3.h中,定义了完整的Cortex-M3存储器映射:
#define SCS_BASE (0xE000E000)
#define ITM_BASE (0xE0000000)
#define CoreDebug_BASE (0xE000EDF0)
#define SysTick_BASE (SCS_BASE + 0x0010)
#define NVIC_BASE (SCS_BASE + 0x0100)
#define SCB_BASE (SCS_BASE + 0x0D00)
// 寄存器结构体定义
typedef struct {
__IO uint32_t ISER[8]; // 中断使能寄存器
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; // 中断禁用寄存器
// ... 更多寄存器定义
} NVIC_Type;
这种结构体映射的方式使得寄存器访问更加类型安全,代码可读性更强。
3. ADC采样系统实现
ADC模块的初始化代码展示了精心的时序控制:
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 时钟配置与分频
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 72M/6=12MHz,符合ADC最大14MHz限制
// GPIO配置为模拟输入
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);
// 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_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)); // 等待复位校准结束
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)); // 等待校准结束
}
多通道轮流采样机制:
void TIME_to_CAdc(void)
{
if(cad_nox==0) {
cad_temp1[cad_noy] = ADC_GetConversionValue(ADC1);
ADC_RegularChannelConfig(ADC1, 1, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
if(cad_nox==1) {
cad_temp2[cad_noy] = ADC_GetConversionValue(ADC1);
ADC_RegularChannelConfig(ADC1, 4, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
if(cad_nox==2) {
cad_nox=0;
cad_temp3[cad_noy] = ADC_GetConversionValue(ADC1);
ADC_RegularChannelConfig(ADC1, 0, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
cad_noy++;
if(cad_noy>15) cad_noy=0; // 16元素循环缓冲区
} else {
cad_nox++;
}
}
这个设计实现了三通道ADC的时分复用,使用循环缓冲区来存储历史数据,为后续的数字滤波算法提供数据基础。
4. Flash存储管理系统
Flash驱动代码展示了扇区管理的完整实现:
#define STM_SECTOR_SIZE 2048 // STM32F103的扇区大小
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2]; // 扇区缓冲区
void STMFLASH_Write(u32 WriteAddr, u16 *pBuffer, u16 NumToWrite)
{
u32 secpos; // 扇区地址
u16 secoff; // 扇区内偏移
u16 secremain; // 扇区剩余空间
FLASH_Unlock(); // Flash解锁
// 地址计算和校验
secpos = (WriteAddr-STM32_FLASH_BASE)/STM_SECTOR_SIZE;
secoff = (WriteAddr-STM32_FLASH_BASE)%STM_SECTOR_SIZE/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);
// 检查是否需要擦除
for(i=0; i<secremain; i++) {
if(STMFLASH_BUF[secoff+i] != 0XFFFF) break;
}
if(i < secremain) { // 需要擦除
FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);
// 更新缓冲区并写入整个扇区
for(i=0; i<secremain; i++) {
STMFLASH_BUF[i+secoff] = pBuffer[i];
}
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;
// 更新指针和计数器,处理下一个扇区
// ...
}
FLASH_Lock(); // Flash上锁
}
这个Flash写入算法体现了嵌入式系统中对Flash寿命的考虑:尽量减少擦除次数,通过先读取再比较的方式避免不必要的擦除操作。
5. 系统参数数据结构
参数存储使用了复杂的数据结构组织:
typedef struct {
u16 data; // 数据标识
u16 use_setmaH; // 电流高阈值设置
u16 use_setmaL; // 电流低阈值设置
u16 adc_maH; // 电流ADC高值
u16 adc_maL; // 电流ADC低值
u16 use_setINVH; // 输入电压高阈值
u16 use_setINVL; // 输入电压低阈值
u16 adc_INVH; // 输入电压ADC高值
u16 adc_INVL; // 输入电压ADC低值
u16 use_OUTVH; // 输出电压高阈值
u16 use_OUTVL; // 输出电压低阈值
u16 adc_OUTVH; // 输出电压ADC高值
u16 adc_OUTVL; // 输出电压ADC低值
u16 ctr_dianliu; // 控制电流
} MY_CPU;
这种设计将校准参数、保护阈值和ADC原始值统一管理,便于系统的校准和保护逻辑实现。
6. 外部中断与唤醒机制
外部中断配置代码展示了低功耗唤醒机制:
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// GPIO配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 外部中断线配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
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;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler(void)
{
if(bSleep** || bIdle**) { // 从睡眠模式唤醒
// InitClk(); 可能需要重新初始化时钟
}
bAFE** = 1; // 设置AFE标志
EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断挂起位
}
这个中断服务函数的设计体现了状态机思想,通过标志位来通知主循环处理AFE相关事件,而不是在ISR中直接处理复杂逻辑。
7. 启动文件与向量表
启动文件startup_stm32f10x_hd.s中定义了完整的异常向量表:
__Vectors DCD __initial_sp ; 栈顶地址
DCD Reset_Handler ; 复位处理器
DCD NMI_Handler ; NMI处理器
DCD HardFault_Handler ; 硬Fault处理器
DCD MemManage_Handler ; MPU Fault处理器
DCD BusFault_Handler ; Bus Fault处理器
DCD UsageFault_Handler ; Usage Fault处理器
; ... 更多异常向量
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
这个启动序列确保了系统从复位到C语言环境的正确初始化。
代码设计特点与工程实践
1. 错误处理与鲁棒性
代码中大量使用状态检查:
while(ADC_GetResetCalibrationStatus(ADC1)); // 等待操作完成
2. 资源管理
Flash操作严格遵守解锁-操作-上锁的序列,防止意外写入。
3. 性能优化
ADC采样使用单次模式降低功耗
Flash写入使用扇区缓冲区减少擦除次数
中断处理使用标志位传递,减少ISR执行时间
4. 可维护性
模块化设计,功能分离清晰
使用结构体封装相关参数
提供完整的硬件抽象层
总结
这套锂电池保护板方案代码体现了嵌入式系统开发的多个最佳实践:从底层的Cortex-M3内核操作到应用层的电池管理算法,代码结构清晰,资源管理严谨,错误处理完善。特别是Flash存储管理和ADC采样系统的实现,展示了在资源受限的嵌入式环境中如何平衡性能、功耗和可靠性的设计考量。
代码中采用的模块化架构和多编译器支持也体现了良好的软件工程实践,为后续的功能扩展和维护提供了坚实的基础。
————————————————
版权声明:本文为CSDN博主「qq68823886」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2503_93853778/article/details/153752023
|
|