本次DIY要实现的是INA219过流保护的应用,分别是打嗝模式和自锁模式。我使用的方案是定时器每半秒检测一次主循环标志位,如果过流状态发生标志位置1反之置0。这个方案可以避免电机启动电流过大,一直触发过流的情况。
我定义的标志位:
接线图:
继电器控制电机开关,继电器和片上的LED共用同一个GPIO口便于观察继电器状态。按钮可以控制这个io口就当开关了
ps:制作了GIF图结果发现不知道为什么传不上来,只能放图片凑活看了
打嗝模式
打嗝模式需要实现的需求是:当检测到处于过流状态时电机停机,等待3s电机重新开机,当检测到过流状态继续停机,反之开机正常运行。
主循环:
中断:
自锁模式
自锁模式需要实现的需求是:当检测到处于过流状态时电机停机,外部开机按键不能在停机状态使用,在排除故障后由串口发送恢复指令电机重新开机。
主循环:
中断:
电流阈值写入片内Flash
F411的flash有整整512k有7个扇区容量可以说是非常充足了。
下面是串口修改flash的过程。
下面是代码部分:
#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(" Reset\r\n"); // 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_Measurement\r\n"); 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_Burp\r\n"); 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_Recovery\r\n"); 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(" ERROR\r\n"); 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; } } } }