这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【Let'sdo第1期-DIY功率检测与控制系统】4、过程贴:过流监测

共4条 1/1 1 跳转至

【Let'sdo第1期-DIY功率检测与控制系统】4、过程贴:过流监测

工程师
2025-05-24 13:46:32     打赏

受远在太平洋的拉尼娜现象影响,今年入夏困难,已经五月天气依然凉爽。风扇基本用不上,如果实在想用最好限制一下转速,免得凉意更浓。

增加几个功能模式,实现对电机转速控制与电流监测形成完整闭环。

一、电子开关控制风扇转速

使用电子开关可以控制风扇的转速,也可以称电子开关为PWM调节电子开关控制板或大功率MOS管模块


1.png

特点:

采用双MOS并联有源输出,内阻低,电流打,功率强,常温下15A,400W,宽电压,支持PWM,工作电压:DC5V-36V

触发信号源:数字量高低电平(DC3.3V-20V),可以接单片机IO口,PLC接口、直流电源等,可以接PWM信号,信号频率0-20khz。

输出能力:直流DC5V-36V,常温下15A,400W,辅助散热条件下,最大电流可达30A

二、旋转编码器改变PWM占空比

电子开关的PWM信号由单片机提供,PWM的占空比决定了电子开关输出功率。

为了控制PWM占空比,再添加一个旋转编码器

2.png

三、整理一下示意图

3.png

1、旋转编码器调节电子开关PWM信号的占空比

2、电子开关在PWM信号作用下,开关,调速控制电机(风扇)旋转

3、INA219测量电压电流功率,如果电流超过电流阈值,启动过流保护(自锁或打嗝)

4、OLED显示相关信息


实物:

电子开关

旋转编码器

风扇

取电5V


四、软件部分

1、电子开关使用TIM1

6.png

频率:32MHZ/(31+1)/(49+1)=200KHZ

占空比:25/50=50%

改变占空比函数,通过这个函数可以控制风扇转速:

void TIM_SetTIM1Compare2(uint32_t compare)  
{  
	if(compare<0||compare>50) return;
	__HAL_TIM_SET_COMPARE(htimPwm, TIM_CHANNEL_2, compare); // compare为新的占空比值  
}

TIM1配置函数,启动PWM。

void pwm_config(TIM_HandleTypeDef *htim)
{
	htimPwm=htim;
	if (HAL_TIM_PWM_Start(htimPwm, TIM_CHANNEL_2) != HAL_OK)  
  {  
    /* PWM Generation Error */  
    Error_Handler();  
  }  
}


2、旋转编码器使用TIM3

4.png

GPIO使用:

5.png


void encoder_config(TIM_HandleTypeDef *htim)
{
  htimEncoder=htim;
 __HAL_TIM_GET_COUNTER(htimEncoder) = 40; //计数器值置位
  HAL_TIM_Encoder_Start(htimEncoder, TIM_CHANNEL_ALL);
}
void encoder_process(void)
{
	CaptureNumber=get_encoder_value();
	if(CaptureNumber<0){
		CaptureNumber=0;
		__HAL_TIM_GET_COUNTER(htimEncoder) = 0;
	}
	
	if(CaptureNumber>50){
		CaptureNumber=50;
		__HAL_TIM_GET_COUNTER(htimEncoder) = 50;
	}
	printf("电子开关PWM占空比:%d%%\r\n",CaptureNumber*2);
	TIM_SetTIM1Compare2(CaptureNumber);
//}
}

设置编码器起始值40(最大50,40对应TIM180%占空比)

调节编码器数值在0-50之间,对应TIM1(0%-100%占空比)

通过get_encoder_value获得当期编码器数值:

int16_t get_encoder_value() {
    return (int16_t)TIM3->CNT;  // 返回当前编码器计数值(带方向)
}

通过__HAL_TIM_GET_COUNTER(htimEncoder) 设置编码器数值。

通过TIM_SetTIM1Compare2(CaptureNumber)以CaptureNumber为参数设置电子开关占空比。


3、INA219过流处理

#define cur_threshold 190    //过流阈值190毫安

uint8_t overcur_flag=0;        //过流自锁生效标志

uint8_t hiccup_flag=0;          //过流打嗝生效标志

volatile uint8_t wait_flag=0; //过流自锁后等待标志

#define Self_Locking_Mode 0 //自锁模式

#define Hiccup_Mode 1       //打嗝模式

static uint8_t CTR_Mode=Self_Locking_Mode; //当期过流模式选择:缺省自锁

用USER BUTTON(PC13)外部中断方式切换过流处理模式:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == B1_Pin) {  // 检查是哪个引脚触发的中断
       if(CTR_Mode==Self_Locking_Mode){
					CTR_Mode=Hiccup_Mode;
					printf("Change mode to Hiccup_Mode\r\n");
			 }else
			 {
					CTR_Mode=Self_Locking_Mode;
				  printf("Change mode to Self_Locking_Mode\r\n");
			 }
    }
		
}

处理INA219传感器数据并显示:

void INA219_process(void)
{
    static uint32_t Hiccup_Time;
    static uint32_t lastDisplayUpdate = 0;
    const uint32_t DISPLAY_UPDATE_INTERVAL = 250; // 显示更新间隔(ms)
    
    // 读取传感器数据
    float busVoltage = INA219_ReadBusVoltage();
    float shuntVoltage = INA219_ReadShuntVoltage();
    float current = INA219_ReadCurrent();
    float power = busVoltage * current;
    
    // 调试信息输出
    printf("Bus Voltage: %.2f V\n", busVoltage);
    printf("Shunt Voltage: %.2f mV\n", shuntVoltage * 1000);
    printf("Current: %.2f mA\n", current * 1000);
    printf("Power: %.2f mW\n", power * 1000);
    printf("\r\n");
    
    // Hiccup模式处理
    if((HAL_GetTick() - Hiccup_Time) >= 3000 && hiccup_flag == 1 && CTR_Mode == Hiccup_Mode)
    {
        hiccup_flag = 0;    
        Hiccup_Time = HAL_GetTick();
        __HAL_TIM_SET_COUNTER(htimEncoder, 40);
        TIM_SetTIM1Compare2(40);
    }
    
    // 过流处理
    if(current * 1000 > cur_threshold) 
    {
        if(CTR_Mode == Self_Locking_Mode)
        {
            overcur_flag = 1;
        }
        else if(CTR_Mode == Hiccup_Mode)
        {
            hiccup_flag = 1;
        }
        __HAL_TIM_SET_COUNTER(htimEncoder, 0);
        TIM_SetTIM1Compare2(0);
    }
    
    // 显示处理
    if(wait_flag == 1)
    {
        App_Show_Clock_Info();
        HAL_Delay(200);
    }
    
    if(CaptureNumber > 0 && wait_flag == 1)
    {
        wait_flag = 0;
        overcur_flag = 0;
        OLED_ShowChineseString16(0, 0, (uint8_t*)"DIY功率监测与控制", 1);
    }
    
    if(wait_flag != 1)
    {
        if(overcur_flag == 1 || hiccup_flag == 1)
        {
            App_Show_Clock_Info();
            HAL_Delay(500);
            wait_flag = 1;
        }
        else
        {    
            // 仅在需要更新时刷新显示
            if(HAL_GetTick() - lastDisplayUpdate >= DISPLAY_UPDATE_INTERVAL)
            {
                lastDisplayUpdate = HAL_GetTick();
                
                char buff[20] = {0};
                
                // 显示电流、电压、功率
                sprintf(buff, "电流:%5.2fmA ", current * 1000);
                OLED_ShowChineseString16(0, 16, (uint8_t*)buff, 1);
                
                sprintf(buff, "电压:%5.2fV ", busVoltage);
                OLED_ShowChineseString16(0, 32, (uint8_t*)buff, 1);
                
                sprintf(buff, "功率:%5.2fmW  ", power * 1000);
                OLED_ShowChineseString16(0, 48, (uint8_t*)buff, 1);
                
                // 绘制条形图边框
                OLED_DrawRectangle(117, 16, 127, 63);
                
                // 更新垂直条形图(高度根据CaptureNumber计算)
                OLED_DrawVerticalBar(117, 16, 10, 47, 2 * CaptureNumber);
                
                // 根据控制模式显示不同图标
                if(CTR_Mode == Self_Locking_Mode)
                {
                    OLED_DrawFillRectangle(111, 17, 115, 40);
                    OLED_DrawFillRectangleClear(111, 41, 115, 63);
                }
                else if(CTR_Mode == Hiccup_Mode)
                {
                    OLED_DrawFillRectangle(111, 41, 115, 63);
                    OLED_DrawFillRectangleClear(111, 17, 115, 40);
                }
                
                // 优化后的滚动效果
                for(uint8_t i = 0; i < 4; i++)
                {
                    OLED_ShiftTwoRowsLeft(0, 3);
                    HAL_Delay(DISPLAY_UPDATE_INTERVAL);
                }
                
                OLED_Refresh();
            }
        }
    }
}

4、OLED显示相关

在第一行显示过流状态:

void App_Show_Clock_Info(void)
{
	static uint8_t i=0;
	if(CTR_Mode==Self_Locking_Mode)
	{
		OLED_ShowChineseString16(0,0,(uint8_t*)"过流状态:Locking   ",i);
	}else
	OLED_ShowChineseString16(0,0,(uint8_t*)"过流状态:Hiccup   ",i);
	OLED_Refresh();
	i=~i;
}

分段显示Bmp:从上到下分四段显示bmp1,最后显示Title

void App_Show_Title_Info(void)
{
	OLED_Clear();

	OLED_Copy_Bmp(0,0,128,16,bmp1,0,0);
	OLED_Refresh();
	HAL_Delay(100);
	OLED_Copy_Bmp(0,16,128,16,bmp1,0,16);
	OLED_Refresh();
	HAL_Delay(100);
	OLED_Copy_Bmp(0,32,128,16,bmp1,0,32);
	OLED_Refresh();
	HAL_Delay(100);
	OLED_Copy_Bmp(0,48,128,16,bmp1,0,48);
	OLED_Refresh();
	HAL_Delay(100);
	
	OLED_Clear();
	OLED_Clear_Buffer();
	OLED_ShowChineseString16(0,0,(uint8_t*)"DIY功率监测与控制",1);
	OLED_Refresh();
}

1.jpg

绘制垂直百分比条形图函数,用来绘制TIM1占空比示意

/**
  * @brief  绘制垂直百分比条形图
  * @param  x: 条形图左上角x坐标
  * @param  y: 条形图左上角y坐标
  * @param  width: 条形图宽度
  * @param  height: 条形图总高度(100%对应的高度)
  * @param  percent: 要显示的百分比(0-100)
  * @param  filled: 是否填充(1=填充,0=空心)
  * @retval 无
  */
void OLED_DrawVerticalBar(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t percent)
{
    // 计算实际填充高度
    uint8_t bar_height = (height * percent) / 100;
    
    // 确保不超过最大高度
    if(bar_height > height) {
        bar_height = height;
    }
    
    // 计算条形图底部y坐标
    uint8_t bar_bottom = y + height - 1;
    
    // 计算填充部分的顶部y坐标
    uint8_t fill_top = bar_bottom - bar_height + 1;
    
	 OLED_DrawFillRectangleClear(x,y,x+width,bar_bottom-bar_height);
   // 绘制填充条形图
   OLED_DrawFillRectangle(x, fill_top, x + width - 1, bar_bottom);
   

}


2.jpg

五、运行效果

1、自锁

tutieshi_480x270_7s.gif

2、打嗝

tutieshi_480x270_9s.gif

3、调节电子开关输入信号占空比

tutieshi_480x272_11s.gif

4、切换过流模式

tutieshi_480x272_5s.gif




关键词: Let'sdo第1期-DIY功率检测与控制系统    

专家
2025-05-24 21:50:10     打赏
2楼

感谢分享


专家
2025-05-24 21:55:29     打赏
3楼

感谢分享


工程师
2025-05-25 07:56:35     打赏
4楼

大佬,这作品比较秀呀!


共4条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]