这里是功率监测与控制系统DIY活动的成果帖
本文将通过“系统框图、电路原理图、如何开启运行、主要参数情况、实现步骤”等方面记录开发过程中的硬件设计、软件调试、项目代码等;
由于在测试过程中使用活动提供的开关电源空载电机烧坏了INA219,保险起见使用学生电源对项目进行供电。一方面防止电流过大再次烧坏INA219,另一方面可以实时监测电流判断本次DIY数值准确与否。
系统框图
系统主要分为以下几个部分:
1.微控制器
STM32F411
2. 外设模块:
- OLED显示- INA219电流检测- 继电器控制(通过GPIO)- 按键输入(PC13)- 串口通信
3. 存储模块:
内部Flash存储电流阈值
4. 软件功能模块:
- 模式控制(4种工作模式)- 电流保护逻辑(打嗝模式、自锁模式)- 串口命令解析- 定时器中断服务

电路原理图


在接线图上面你可能会发现:欸?
怎么没有接usb转ttl模块这是怎么实现串口通信的
没错!这块板子把串口通信模块也集成在STLINK上面了

在原理图里面也有体现
所以只需要简单配置一下PA2和PA3引脚就可以使用mini-usb来通信了!

如何开启运行
1.系统初始化:初始化所有外设。
从Flash读取电流阈值存入Current_Limiting变量。
2. 进入主循环,根据当前模式执行相应的操作(测量、保护、显示等)。
主循环默认Mode 0:电流测量无保护模式。
3. 串口接收中断接收数据,当收到完整数据包后,在主循环中解析并执行命令。
数据包固定格式:‘@’开头,‘回车’结束。
4. 定时器中断每500ms触发一次,更新打嗝模式的状态和继电器控制。
项目要求过流半秒内响应。
ps:精度可调节,但是精度过高启动电流可能会触发过流保护。可以通过PWM来实现软启动
5. 按键触发或串口命令可以控制继电器状态,同时保护逻辑也会根据电流情况自动控制继电器。
在过流保护状态时需通过串口发送过流恢复指令来重新启动电机或者直接复位程序。
主要参数情况
![]()

电流限制标准位:
int Current_Limiting; -> 电流限制变量
int Current_Limiting_temp; -> 电流限制临时变量
IO控制标志位:
unsigned char IO_Control = 0; -> IO控制位
unsigned char IO = 0; -> IO模式位
unsigned char IO_First = 0;-> IO单次变化
打嗝模式状态标志位:
ps:由于本程序可以通过按键控制电机开关需要判断电机是正常停机还是过流停机
unsigned char MODE1_Burp;-> 打嗝模式标志位
unsigned char MODE1_Stop_Burp;-> 打嗝停止情况标志位:正常停止/打嗝停止
过流保护模式状态标志位:
unsigned char MODE2_Recovery;-> 过流模式标志位
unsigned char Stop_Recovery;-> 过流停止模式标志位
程序模式标志位:
unsigned char MODE_Flag;-> 模式标志位(0~3)
MODE0:电流测量无保护模式。
MODE1:打嗝模式。
MODE2:过流保护模式。
MODE3:Flash电流阈值修改模式。
实现步骤
详细的实验步骤以及实验现象请见开箱贴和过程贴汇总,以下只使用流程图展示实现步骤。(文末会展示主函数代码以及开箱贴和过程贴汇总)
1. 初始化阶段
![]()
2. 模式处理逻辑

3. 串口命令处理

4. 继电器控制逻辑

5. 定时器中断逻辑

各模块通过标志变量(如MODE_Flag、IO_Control等)进行通信,实现完整的电流保护功能。
主函数代码展示
#include "stm32f4xx.h" // Device header
#include "stm32f4xx_conf.h"
#include "string.h"
#include "gpio.h"
#include "oled.h"
#include "ina219.h"
#include "Timer.h"
#include "delay.h"
#include "Serial.h"
#include "Store.h"
#include "MyFLASH.h"
//extern uint8_t Serial_RxFlag;
int Current_Limiting;//!!电流限制变量临时存储后续写到flash里!!
int Current_Limiting_temp;
unsigned char time = 0;
unsigned char IO_Control = 0;//IO控制位
unsigned char IO = 0; //IO模式位
unsigned char IO_First = 0;//IO首次变化
unsigned char MODE1_Burp;//打嗝模式标志位
unsigned char MODE1_Stop_Burp;//打嗝停止情况标志位:正常停止/打嗝停止
unsigned char MODE2_Recovery;//过流模式标志位
unsigned char Stop_Recovery;//过流停止模式标志位
unsigned char MODE_Flag;//模式标志位
int main()
{
gpio_init();
OLED_Init();
INA_Init();
OLED_MAP();
Timer_Init();
Serial_Init();
Store_Init();
Serial_SendString(" Resetrn");
// Store_Data[1] = 100; //过流阈值强制修改接口
// Store_Save();
unsigned char MODE_First_Flag = 0;
unsigned char i = 0;
while(1)
{
Current_Limiting = *((__IO uint32_t *)(0x08060002));
if(MODE_Flag == 0) //模式0,电流测量无特殊模式
{
if(MODE_First_Flag == 1)
{
OLED_Clear();
OLED_MAP();
Serial_SendString(" MODE0_Measurementrn");
MODE_First_Flag = 0;
}
show_v();
show_w();
show_i();
}
if(MODE_Flag == 1) //打嗝模式
{
if(MODE_First_Flag == 1)
{
OLED_Clear();
OLED_MAP();
Serial_SendString(" MODE1_Burprn");
MODE_First_Flag = 0;
}
show_v();
show_w();
show_i();
if((INA_GET_Current_MA())>=Current_Limiting)
{
MODE1_Burp = 1;
}
else
{
MODE1_Burp = 0;
}
}
if(MODE_Flag == 2) //过流保护模式
{
if(MODE_First_Flag == 1)
{
OLED_Clear();
OLED_MAP();
Serial_SendString(" MODE2_Recoveryrn");
MODE_First_Flag = 0;
}
show_v();
show_w();
show_i();
if((INA_GET_Current_MA())>=Current_Limiting)
{
MODE2_Recovery = 1;
}
else if(Stop_Recovery == 0)
{
MODE2_Recovery = 0;
}
}
if(MODE_Flag == 3) //显示片内flash存储电流值
{
if(MODE_First_Flag == 1)
{
OLED_Clear();
OLED_MAP2();
MODE_First_Flag = 0;
}
//OLED_ShowNum(3,1,Current_Limiting,10);
OLED_ShowNum(2,10,Current_Limiting,4);
if((INA_GET_Current_MA())>=Current_Limiting)
{
MODE2_Recovery = 1;
}
else if(Stop_Recovery == 0)
{
MODE2_Recovery = 0;
}
if(MODE2_Recovery||MODE1_Burp)
{
OLED_ShowChiness(1,11,23);
OLED_ShowChiness(1,13,24);
}
else
{
OLED_ShowChiness(1,11,22);
OLED_ShowChiness(1,13,24);
}
}
if(Serial_RxFlag == 1) //接收到一个数据包到Serial_RxPacket数组里以'/0'结尾
{
Serial_RxFlag = 0; //先清空标志位
MODE_First_Flag = 1; //首次循环标志位
if (strcmp(Serial_RxPacket, "CurrentMode0") == 0) //MODE0电流测量无特殊模式
{
MODE_Flag = 0;
}
else if (strcmp(Serial_RxPacket, "CurrentMode1") == 0) //MODE1 //打嗝模式
{
MODE_Flag = 1;
}
else if (strcmp(Serial_RxPacket, "CurrentMode2") == 0) //MODE2 //自锁模式
{
MODE_Flag = 2;
}
else if (strcmp(Serial_RxPacket, "CurrentMode3") == 0) //MODE3 //显示片内flash存储电流值
{
MODE_Flag = 3;
}
else if (strcmp(Serial_RxPacket, "IO") == 0) //IO反转
{
IO_Control = 1;
MODE_First_Flag = 0; //非模式控制清空首次循环标志
}
else if (strncmp(Serial_RxPacket, "CurrentThreshold:",17) == 0) //修改电流阈值
{
MODE_First_Flag = 0; //非模式控制清空首次循环标志
Current_Limiting_temp = 0;
i = 0;
//Serial_RxPacket[100];
while(1)
{
if(Serial_RxPacket[17+i]>'9')
{break;}
Current_Limiting_temp = Current_Limiting_temp*10 + (Serial_RxPacket[17+i]-'0');
i++;
}
Store_Data[1] = Current_Limiting_temp; //过流阈值强制修改接口
Store_Save();
}
else if (strcmp(Serial_RxPacket, "RecoveryControl") == 0) //IO反转
{
if(MODE2_Recovery) //在过流保护时生效
{
MODE2_Recovery = 0;
IO_Control = 1;
Stop_Recovery = 0;
}
MODE_First_Flag = 0; //非模式控制清空首次循环标志
}
else if (strcmp(Serial_RxPacket, "CanRun") == 0) //IO 1
{
IO = 1;
IO_First = 1;
MODE_First_Flag = 0; //非模式控制清空首次循环标志
}
else if (strcmp(Serial_RxPacket, "StopRun") == 0) //IO 0
{
IO = 0;
IO_First = 1;
MODE_First_Flag = 0; //非模式控制清空首次循环标志
}
else //非控制数据包
{
Serial_SendString(" ERRORrn");
MODE_First_Flag = 0; //非模式控制清空首次循环标志
}
}
if(((0==GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13))||IO_Control)&&(!MODE2_Recovery)) //继电器控制
{
GPIO_ToggleBits(GPIOA,GPIO_Pin_5);
IO_Control = 0;
if(MODE1_Stop_Burp)
{
time = 2;
}
while(0==GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13))
{
delay_ms(20);
}
}
if((IO_First)&&(!MODE2_Recovery)) //继电器单次控制
{
IO_First = 0;
GPIO_WriteBit(GPIOA,GPIO_Pin_5, IO);
}
}
}
void TIM3_IRQHandler(void) //每半秒进一次中断
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
time++;
time = time%6;
if(MODE_Flag == 0) //模式0,电流测量无特殊模式
{
//发送电流电压数据
}
if(MODE_Flag == 1) //模式1
{
if(MODE1_Burp)
{
IO_Control = 1; //打嗝模式
MODE1_Stop_Burp = 1; //打嗝停止
}
else if(((INA_GET_Current_MA())<=1)&&(MODE1_Stop_Burp)&&(time==0))
{
IO_Control = 1;
MODE1_Stop_Burp = 0;
}
else
{
IO_Control = 0;
}
}
if(MODE_Flag == 2) //模式2,电流过流保护模式
{
if(MODE2_Recovery)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_5, 0);
Stop_Recovery = 1;
}
}
}
}
开箱贴和过程贴汇总
1.【Let'sdo第1期-功率检测与控制系统DIY】—【开箱帖】!!!-电子产品世界论坛
2.【Let'sdo第1期-功率检测与控制系统DIY】-【过程贴】-1.标准库开发环境,oled屏幕测试-电子产品世界论坛
3.【Let'sdo第1期-功率检测与控制系统DIY】-【过程贴】-2.INA219,串口接收数据包-电子产品世界论坛
4.【Let'sdo第1期-功率检测与控制系统DIY】-【过程贴】-3.INA219过流保护,电流阈值写入flash-电子产品世界论坛
我要赚赏金
