首先来张硬件全家福和软件项目构成图。主要用了几个模块:
1.F411主控板 2.正点原子便携电源 3.INA219模块 4. OLED模块 5.不知道从哪遗留的电机模块(直流电机通过皮带带动旋转平台,上面放了一卷电工胶带当负载) 5.USB转mini usb数据线(和stlink相连) 6.串口通信模块


系统框图
电路中主要用了2个模拟IIC,分别与OLED和INA219通信,一个串口和2个GPIO,F411IO资源分配如下。
F411 IO分配 | 信号 | IO方向 | IO功能 |
PA5 | GPIO_LED | O | LED控制 |
PC13 | GPIO_KEY | I | 按键控制 |
PC10 | SCL1 | 0 | OLED模拟IIC 时钟 |
PC12 | SDA1 | IO | OLED模拟IIC 数据 |
PB8 | SCL2 | O | INA模拟IIC时钟 |
PB9 | SDA2 | IO | INA模拟IIC 数据 |
PA9 | UART_TX | AF | 串口发送 |
PA10 | UART_RX | AF | 串口接收 |

实现的任务:
1. led定时翻转
2. OLED切换显示
3. INA219测量电压电流功率,并经过滑动平均滤波,电阻值10mR,电流检测范围0-4A,总线电压32V.
4. 串口不定长命令超时检测机制和解析,并存储电流阈值和校验
5. 不同电流模式的切换。
下面是成果。
第一张图是5V电压时电机负载的测量值。

第二张图是12V电压时电机负载的测量值。

第三张图是电机带负载转动的画面。负载是一个电工胶带。电机通过皮带带动旋转平台转动。

第四张图是过流切换。

第五张图是发送命令和解析命令。

第六张图是将收到的阈值数据保存在flash中,flash地址为0x8060000,在扇区7。收到的阈值电流为1000mA,对应16进制数据为0x3e8。

第七张图是VOFA上位机读取的滑动平均滤波后的电压,电流和功率值。看起来还不错。

下面是主要的功能代码。
在数据结构上,定义了PowerInfoDef(功率信息)、CommandInfoDef(命令信息)、HiccupInfoDef(打嗝模式信息)、SelfLockingInfoDef(自锁模式信息)、OverCurrentInfoDef(过流信息)五个结构体。PowerInfoDef(功率信息)结构体用来记录电压、电流、功率、过流阈值等属性,CommandInfoDef(命令信息)结构体用来记录串口发送的各种命令号和过流阈值,HiccupInfoDef(打嗝模式信息)结构体用来记录打嗝模式状态和计数值,SelfLockingInfoDef(自锁模式信息)结构体用来记录自锁模式状态和计数值,OverCurrentInfoDef(过流信息)结构体用来记录过流标志、过流恢复标志和过流次数。
头文件如下。
#ifndef __APP_H
#define __APP_H
#include "sys.h"
typedef struct
{
int Current;
uint32_t Voltage;
uint32_t CurThreshold;
uint32_t Power;
} PowerInfoDef;
typedef struct
{
u8 CmdNum;
u32 CurrentThreshold;
}CommandInfoDef;
typedef enum {CurrentThresholdCMD = 1,\
RecoveryControlCMD = 2,\
CanRunCMD =3,\
StopRunCMD = 4,\
CurrentMode1CMD = 5,\
CurrentMode2CMD =6
}Command;
//自锁模式:当过流保护产生时,必须使用按键或者串口指令恢复控制;
//打嗝模式:当过流保护产生时,停止负载,过流消失1s(可自行设定)后自行恢复控制
typedef enum {
SelfLockingMode = 1,\
HiccupMode =2
}ControlMode;
typedef struct
{
u8 HiccupStatus;
u32 HiccupCount;
}HiccupInfoDef;//打嗝模式
typedef struct
{
u8 SelfLockingStatus;
u32 SelfLockingCount;
}SelfLockingInfoDef;//自锁模式
typedef struct
{
u8 OverCurrentFlag;
u8 OverCurrentRecoveryFlag;
u32 OverCurrentCount;
}OverCurrentInfoDef;
#define HICCUP_START 0
#define HICCUP_FINISH 1
#define HICCUP_NUM 5
#define OVER_CURRENT_NUM 5//过流次数,预防启动瞬间可能过流
ErrorStatus ParseCMD(u8* buf,CommandInfoDef* command);
void ControlProcess(u8* ControlMode);
#endifC文件如下
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "oled.h"
#include "ina219.h"
#include "stmflash.h"
#include "app.h"
#include "string.h"
#include "filter.h"
#include "key.h"
PowerInfoDef PowerInfo = {0,0,0,0};
HiccupInfoDef HiccupInfo= {0,0};
SelfLockingInfoDef SelfLockingInfo= {0,0};
OverCurrentInfoDef OverCurrentInfo= {0,0,0};
CommandInfoDef CommandInfo = {0,0};
u8 CurrentControlMode = SelfLockingMode;//默认自锁模式
SlidAvgFilterDef SlidAvgFilterInfo_Votage;
SlidAvgFilterDef SlidAvgFilterInfo_Current;
SlidAvgFilterDef SlidAvgFilterInfo_Power;
bool OLED_page = 0;
//将电流阈值保存在扇区7 0x8060000地址
//扇区 0 0x0800 0000 - 0x0800 3FFF 16 KB
//扇区 1 0x0800 4000 - 0x0800 7FFF 16 KB
//扇区 2 0x0800 8000 - 0x0800 BFFF 16 KB
//扇区 3 0x0800 C000 - 0x0800 FFFF 16 KB
//扇区 4 0x0801 0000 - 0x0801 FFFF 64 KB
//扇区 5 0x0802 0000 - 0x0803 FFFF 128 KB
//扇区 6 0x0804 0000 - 0x0805 FFFF 128 KB
//扇区 7 0x0806 0000 - 0x0807 FFFF 128 KB
#define CURRENT_THRESHOLD_POS 0x8060000 //扇区7的起始地址
u32* pCurrentThreshold = (u32*)CURRENT_THRESHOLD_POS;
int main(void)
{
static u8 INA_Init_Status = 0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(100); //初始化延时函数
LED_Init(); //初始化LED端口
uart_init(115200);
KEY_Init();
TIM3_Int_Init(10-1,10000-1); //定时器时钟100M,分频系数10000,所以100M/10000=10Khz的计数频率,计数10次为1ms
INA_Init_Status = INA_Init();
if(INA_Init_Status == 1)
printf("INA219_init complete\r\n");
else
printf("INA219_init fail\r\n");
OLED_Init();
printf("uart_init complete\r\n");
SlidAvgFilterInit(&SlidAvgFilterInfo_Votage);
SlidAvgFilterInit(&SlidAvgFilterInfo_Current);
SlidAvgFilterInit(&SlidAvgFilterInfo_Power);
PowerInfo.CurThreshold = *pCurrentThreshold;//将flash存的数据取出来复制
OLED_Show_Power_Info();
while(1)
{
if(GetTickCount() >= 1000)
{
LED0=!LED0;//DS0翻转
TickReset();
}
//直接采集电流电压功率信息,不滤波
//PowerInfo.Voltage = INA_GET_BusVoltage_MV();
//PowerInfo.Current = INA_GET_Current_MA();
//PowerInfo.Power = INA_GET_Power_MW();
//采集电流电压功率信息并滑动平均滤波
PowerInfo.Voltage = (u32)SlidAvgFilter(&SlidAvgFilterInfo_Votage,(float)INA_GET_BusVoltage_MV());
PowerInfo.Current = (u32)SlidAvgFilter(&SlidAvgFilterInfo_Current,(float)INA_GET_Current_MA());
PowerInfo.Power = (u32)SlidAvgFilter(&SlidAvgFilterInfo_Power,(float)INA_GET_Power_MW());
if(KEY_Scan(0) != 0){
OLED_page = !OLED_page;//如果按下,切换页面
}
if(OLED_page == 0)
OLED_Show_Power_Info();
else
OLED_Show_Threshold_Info();
printf("%4d,%4d,%5d\r\n",PowerInfo.Voltage,PowerInfo.Current,PowerInfo.Power);
//查询串口数据是否接收完成,如果超时未收到新数据,则认为接收完成。
if(REC_PACK_FINSH == UsartRecTime())
{
if(ParseCMD(ext_usart_rx_buf,&CommandInfo) != ERROR)
{
switch(CommandInfo.CmdNum)
{
case CurrentThresholdCMD :
{
printf("CurrentThresholdCMD\r\n");
//存阈值数据
STMFLASH_Write(CURRENT_THRESHOLD_POS,&CommandInfo.CurrentThreshold,1);
//校验阈值数据,校验通过,将阈值数据赋值给power_info
if(CommandInfo.CurrentThreshold == *pCurrentThreshold)
{
PowerInfo.CurThreshold = *pCurrentThreshold;
}
printf("CurrentThreshold verify success\r\n");
break;
}
case RecoveryControlCMD://过流恢复命令
{
printf("RecoveryControlCMD\r\n");
OverCurrentInfo.OverCurrentRecoveryFlag = 1;
break;
}
case CanRunCMD://电机启动
{
printf("CanRunCMD\r\n");
break;
}
case StopRunCMD://电机停止
{
printf("StopRunCMD\r\n");
break;
}
case CurrentMode1CMD://切换自锁模式
{
printf("CurrentMode1CMD\r\n");
CurrentControlMode = SelfLockingMode;
break;
}
case CurrentMode2CMD://切换打嗝模式
{
printf("CurrentMode2CMD\r\n");
CurrentControlMode = HiccupMode;
break;
}
default:
{
printf("NoneCMD\r\n");
;
break;
}
}
}
}
ControlProcess(&CurrentControlMode);
};
}
/**************************************************
*简介:解析命令
*参数:
*****参数1:数据缓冲区
*****参数2:命令结构体指针,在函数内部被更改
*返回值:无
**************************************************/
ErrorStatus ParseCMD(u8 *buf,CommandInfoDef *command)
{
char* endptr = NULL;
char* pCommand = NULL;
u32 threshold=0;
command->CmdNum = 0;
command->CurrentThreshold = 0;
//查找字符串
pCommand = strstr(buf,"CurrentThreshold:");//找到电流阈值并返回所在位置指针
if(pCommand != NULL)
{
pCommand += strlen("CurrentThreshold:");//指向mA
//提取电流值大小
command->CurrentThreshold = strtol(pCommand,&endptr,10);
if(*endptr != 'm' && *(endptr++) != 'A')
return ERROR;
command->CmdNum = CurrentThresholdCMD;
return SUCCESS;
}
pCommand = strstr(buf,"RecoveryControl");//找到恢复控制命令并返回所在位置指针
if(pCommand != NULL)
{
command->CmdNum = RecoveryControlCMD;
return SUCCESS;
}
pCommand = strstr(buf,"CanRun");//找到运行并返回所在位置指针
if(pCommand != NULL)
{
command->CmdNum = CanRunCMD;
return SUCCESS;
}
pCommand = strstr(buf,"StopRun");//找到停止运行命令并返回所在位置指针
if(pCommand != NULL)
{
command->CmdNum = StopRunCMD;
return SUCCESS;
}
pCommand = strstr(buf,"CurrentMode1");//找到电流模式1并返回所在位置指针
if(pCommand != NULL)
{
command->CmdNum = CurrentMode1CMD;
return SUCCESS;
}
pCommand = strstr(buf,"CurrentMode2");//找到电流模式2并返回所在位置指针
if(pCommand != NULL)
{
command->CmdNum = CurrentMode2CMD;
return SUCCESS;
}
return ERROR;
}
/**************************************************
*简介:电流控制模式处理
*参数:
*****参数1:电流控制模式指针
*返回值:无
**************************************************/
void ControlProcess(u8* ControlMode)
{
u8* pControlMode = ControlMode;
//控制处理
switch(*pControlMode)
{
case SelfLockingMode:
{
if(PowerInfo.Current > PowerInfo.CurThreshold)
{
OverCurrentInfo.OverCurrentCount++;
if(OverCurrentInfo.OverCurrentCount >= OVER_CURRENT_NUM)
{
OverCurrentInfo.OverCurrentFlag = 1;
printf("负载过流\r\n");
}
}else
{
OverCurrentInfo.OverCurrentCount = 0;
}
if(OverCurrentInfo.OverCurrentRecoveryFlag == 1 && OverCurrentInfo.OverCurrentFlag == 1)
{
OverCurrentInfo.OverCurrentRecoveryFlag = 0;
OverCurrentInfo.OverCurrentFlag = 0;
printf("过流解除\r\n");
}
break;
}
case HiccupMode:
{
if(PowerInfo.Current > PowerInfo.CurThreshold)
{
OverCurrentInfo.OverCurrentCount++;
if(OverCurrentInfo.OverCurrentCount >= OVER_CURRENT_NUM)
{
OverCurrentInfo.OverCurrentFlag = 1;
HiccupInfo.HiccupStatus = HICCUP_START;
printf("负载过流\r\n");
}
}else
{
OverCurrentInfo.OverCurrentCount = 0;
}
if(HiccupInfo.HiccupStatus == HICCUP_FINISH && OverCurrentInfo.OverCurrentFlag ==1)
{
HiccupInfo.HiccupStatus = HICCUP_START;
OverCurrentInfo.OverCurrentCount = 0;
printf("过流解除\r\n");
}
break;
}
default:
break;
}
}
我要赚赏金
