zeruns
Thu, 30 Oct 2025 15:08:17 +0000
开源项目 / Open-Source Project

基于HT32F52352的智能电子负载开源，合泰杯作品开源，含原理图、PCB、程序源码、作品报告。

2023第十届广东省大学生合泰杯单片机应用设计竞赛三等奖作品。

用一个月时间（而且课多没啥时间弄）赶工做出来的，做得很一般，勿喷。

作品演示视频：https://www.bilibili.com/video/BV1sM4y1b7qu/

本开源作品仅供参考学习，不建议复刻，立创开源平台上有更多更好的更完善的电子负载开源作品！

本项目的立创开源平台开源链接：https://url.zeruns.com/xvvF8

合泰HT32单片机开发环境搭建和配置教程：https://blog.zeruns.com/archives/709.html

电子/单片机技术交流群：2169025065

什么是电子负载

电子负载是一种电子设备，用于模拟真实负载环境，以测试电源或电子电路的性能。比起使用大功率可调电阻或者电炉丝等传统的无源负载，电子负载具有参数可调，使用方便等诸多优势。无论是专业的电子工程项目开发还是业余的电子爱好者，电子负载仪都是必备的设备之一。

电子负载从测试电源的种类来分可以分为交流电子负载和直流电子负载。从功能上划分常见的有恒流、恒压、恒阻、恒功率四种类型。因为我们常见的大多数电源都是恒压直流电源。测试这类电源时，主要测试的是其电流输出能力。所以大多数的应用场景中，直流恒流电子负载是最为常见的类型。而电子负载从控制方式上划分则又可以分为数控和模拟两种类型。相较于使用纯模拟电路控制的电子负载，数控电子负载使用数字控制，在参数调节上更为直观，而且功能丰富、扩充简单，还可以方便的实现测试的自动化。

项目简介

本作品采用 HT32F52352 合泰单片机作为主控芯片设计的电子负载，供电方式由18650锂电池供电，方便携带。

控制方式为通过单片机输出PWM波到低通滤波电路滤出一个较为稳定的直流电压（通过PWM+低通滤波实现DAC的功能）作为参考电压给到运放与电流/电压采样放大后的电压做比较，运放输出控制MOS管，从而实现恒压/恒流。

触控屏用的是陶晶驰的2.8寸串口屏，型号为：TJC3224T028_011R。

实物图

当时没拍多少照片，就找到这两张了，可以去看看演示视频。

资料下载地址

下面链接中的资料有：电路原理图、立创EDA工程文件、PCB制板文件、程序源码、串口屏工程文件、芯片手册。

123云盘下载地址：https://www.123pan.com/s/2Y9Djv-ZNevH.html

百度网盘下载地址：https://url.zeruns.com/XP241 提取码：i9bl

元件购买地址

HT32F52352开发板：https://s.click.taobao.com/XmpTGpt

HT32F52352单片机芯片：https://s.click.taobao.com/sG9xeot

INA199A1芯片：https://s.click.taobao.com/XLuweot

0805贴片电阻样品本：https://s.click.taobao.com/p8YSGpt

0805贴片电容样品本：https://u.jd.com/9uvZoBd

XL6007E1芯片：https://s.click.taobao.com/14Oueot

串口屏：https://s.click.taobao.com/pyzleot

建议在立创商城里购买元器件：https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html

原理图

功率板

电源板

控制板

PCB

功率板

顶层

内层1

底层

还有个GND层就不截了

电源板

顶层

底层

控制板

顶层

底层

主要代码

main.c文件

#include "ht32.h"
#include "GPIO.h"
#include "BFTM0.h"
#include "GPTM0.h"
#include "GPTM1.h"
#include "MCTM0.h"
#include "delay.h"
#include "OLED.h"
#include "WDT.h"
#include "ADC.h"
#include "USART.h"
#include "string.h"

#define KEY1 GPIO_ReadInBit(HT_GPIOA, GPIO_PIN_10)

// 定义按键状态的枚举变量类型
typedef enum
{
  KS_RELEASE = 0, // 按键松开
  KS_SHAKE,       // 按键抖动
  KS_PRESS,       // 稳定按下
} KEY_STATUS;

// 当前循环结束的(状态机的)状态
#define g_keyStatus 0
// 当前状态(每次循环后与g_keyStatus保持一致)
#define g_nowKeyStatus 1
// 上次状态(用于记录前一状态以区分状态的来源)
#define g_lastKeyStatus 2

uint8_t KEY_Status[4][3]; // 记录各按键状态
uint8_t key[4];           // 记录各按键是否稳定按下，1表示按键已按下，0表示按键没被按下
char *temp;

// 定义模式页面的枚举变量
enum mode_type
{
  menu = 0, // 菜单
  CC,       // 恒流
  CV,       // 恒压
  CR,       // 恒阻
  CW        // 恒功率
};

uint8_t Eload_Out = 0;                // 电子负载输出开启/关闭状态
uint8_t mode = menu;                  // 当前模式
uint8_t voltage_dw = 0;               // 电压采样档位，0为0.0325倍，2为0.6175倍，1为0.0947倍
float YVF, YIF1, YIF2, YIF;           // 当前电压电流
float ISET, VSET, RSET, PSET;         // 电流、电压、电阻、功率设置值
float VDD = 3.3;                      // 单片机电源电压
uint32_t YVF_SUM, YIF1_SUM, YIF2_SUM; // 电压电流算平均值用的计算总和
uint8_t AVG_count = 0;                // 电流平均值计算累加计数值
uint8_t YVF_AVG_count = 0;            // 电压平均值计算累加计数值
uint8_t Key_ONOFF = 0;                // 电子负载开启关闭按钮是否被按下状态

void HMI_GetData(void);
void HMI_Display(void);
void AdcFb(void);
void ONOFF(void);
void FAN(void);
void key_status_check(uint8_t key_num, uint8_t KEY);
void CW_mode(void);
void CR_mode(void);
void OFF(void);//关闭
void ON(void);//开启

int main(void)
{
  GPIO_Configuration();     // 初始化GPIO
  BFTM0_Configuration();    // 初始化BFTM0定时器
  GPTM0_Configuration();    // 初始化GPTM0定时器
  GPTM1_Configuration();    // 初始化GPTM1定时器
  MCTM0_Configuration();    // 初始化MCTM0定时器
  WDT_Configuration();      // 初始化看门狗
  OLED_Init();              // 初始化OLED
  ADC_Configuration();      // 初始化ADC
  RETARGET_Configuration(); // 重定向printf函数到串口
  USART1_Configuration();   // 初始化串口1
  USART0_Configuration();   // 初始化串口0

  uint16_t count1 = 0;

  Serial_SendHMILCD("page 0");      // 切到启动页
  GPTM0_CH0_DisablePWMOutput(1);    // 关闭CH0(恒压)通道的PWM输出,并输出高电平
  GPTM0_CH2_DisablePWMOutput(0);    // 关闭CH2(恒流1)通道的PWM输出,并输出低电平
  GPTM0_CH3_DisablePWMOutput(0);    // 关闭CH3(恒流2)通道的PWM输出,并输出低电平
  MCTM0_CH0_DisablePWMOutput(0);    // 关闭MCTM0_CH0(风扇)通道的PWM输出,并输出低电平
  Serial_SendHMILCD("CC.x0.val=0"); // 屏幕显示的电流设定值清零
  Serial_SendHMILCD("CV.x0.val=0"); // 屏幕显示的电压设定值清零
  Serial_SendHMILCD("CR.x0.val=0"); // 屏幕显示的电阻设定值清零
  Serial_SendHMILCD("CW.x0.val=0"); // 屏幕显示的功率设定值清零

  GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_10, RESET); // MCU_G0，电压采样挡0.6175倍
  GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_11, RESET); // MCU_G1，电压采样挡0.0947倍

  while (1)
  {
    if (HT_CKCU->APBCCR1 & (1 << 4)) // 判断看门狗时钟是否使能了
      WDT_Restart();                 // 复位看门狗计数值

    GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_14, RESET);

    if (bftm0_ct3 >= 1) // 每1ms执行一次
    {
      AdcFb(); // ADC数据处理
      bftm0_ct3 = 0;
    }

    HMI_GetData();

    if (bftm0_ct2 >= 40) // 每40ms执行一次
    {
      HMI_Display(); // 串口屏显示数据
      bftm0_ct2 = 0;
    }
    ONOFF();
    FAN();

    if (bftm0_ct >= 50) // 每50ms执行一次
    {
      key_status_check(0, KEY1); // 按键扫描
      if (mode == CW)
      {
        CW_mode();
      }
      if (mode == CR)
      {
        CR_mode();
      }
      bftm0_ct = 0;
    }

    if (YIF > 10 | YVF * YIF > 100)
    {
      if (Eload_Out == 1)
      {
        Key_ONOFF = 1;
      }
    }

    GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_14, SET);
    /*
        float Voltage0 = (AD_Value[0] & 0x0000FFFF) / 4096.0 * 3.3; // ADC采样值转换为电压值
        OLED_ShowNum(1, 4, (uint8_t)Voltage0, 1);                   // 显示整数部分
        OLED_ShowString(1, 5, ".");
        OLED_ShowString(1, 9, "V");
        OLED_ShowNum(1, 6, (uint16_t)(Voltage0 * 1000) % 1000, 3); // 显示小数部分

        float Voltage1 = (AD_Value[1] & 0x0000FFFF) / 4096.0 * 3.3; // ADC采样值转换为电压值
        OLED_ShowNum(2, 4, (uint8_t)Voltage1, 1);                   // 显示整数部分
        OLED_ShowString(2, 5, ".");
        OLED_ShowString(2, 9, "V");
        OLED_ShowNum(2, 6, (uint16_t)(Voltage1 * 1000) % 1000, 3); // 显示小数部分

        float Voltage2 = (AD_Value[2] & 0x0000FFFF) / 4096.0 * 3.3; // ADC采样值转换为电压值
        OLED_ShowNum(3, 4, (uint8_t)Voltage2, 1);                   // 显示整数部分
        OLED_ShowString(3, 5, ".");
        OLED_ShowString(3, 9, "V");
        OLED_ShowNum(3, 6, (uint16_t)(Voltage2 * 1000) % 1000, 3); // 显示小数部分

        VDD = (AD_Value[3] & 0x0000FFFF) / 4096.0 * 3.3; // ADC采样值转换为电压值
        OLED_ShowNum(4, 4, (uint8_t)VDD, 1);             // 显示整数部分
        OLED_ShowString(4, 5, ".");
        OLED_ShowString(4, 9, "V");
        OLED_ShowNum(4, 6, (uint16_t)(VDD * 1000) % 1000, 3); // 显示小数部分

        VDD = (AD_Value[4] & 0x0000FFFF) / 4096.0 * 3.3; // ADC采样值转换为电压值
        OLED_ShowNum(4, 11, (uint8_t)VDD, 1);            // 显示整数部分
        OLED_ShowString(4, 12, ".");
        OLED_ShowString(4, 16, "V");
        OLED_ShowNum(4, 13, (uint16_t)(VDD * 1000) % 1000, 3); // 显示小数部分
        count1++;*/

			if(flag_start==1)//串口1数据处理
		{
			if(++cishu==7)  {flag_over=1;}
						  Delay_ms(1);
		}
		if(flag_over==1)
	{
				flag_over=0;
			 if (Data[0] == 'A') // 当前为数字键盘页面
    {

			char *temp = Data;
      temp++;                      // 地址自增1
      uint16_t temp2 = atoi(temp); // 字符串转整形
//      if (mode == CC)
//      {
//        if (temp2 > 10000)
//          temp2 = 10000;
//        ISET = temp2 / 1000.0;
//        Serial_SendHMILCD("CC.x0.val=%d", temp2);

//        if (ISET <= 2.5)
//        {
//          if (Eload_Out == 1)
//          {
//            GPTM0_CH3_DisablePWMOutput(0);
//            GPTM0_CH2_SetOnduty((uint16_t)(ISET * 0.01 * 50 / VDD * 50000));
//          }
//          else
//          {
//            GPTM0_CH2_SetOnduty((uint16_t)(ISET * 0.01 * 50 / VDD * 50000));
//          }
//        }
//        else
//        {
//          if (Eload_Out == 1)
//          {
//            GPTM0_CH2_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
//            GPTM0_CH3_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
//            GPTM0_CH3_EnablePWMOutput();
//          }
//          else
//          {
//            GPTM0_CH2_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
//            GPTM0_CH3_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
//          }
//        }
//      }
//      else if (mode == CV)
//      {
//        VSET = temp2 / 100.0;
//        Serial_SendHMILCD("CV.x0.val=%d", temp2);
//        if (voltage_dw == 0)
//        {
//          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0325 / VDD * 50000));
//        }
//        else if (voltage_dw == 1)
//        {
//          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0947 / VDD * 50000));
//        }
//        else if (voltage_dw == 2)
//        {
//          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.6175 / VDD * 50000));
//        }
//      }
//      else if (mode == CR)
//      {
//        RSET = temp2 / 100.0;
//        Serial_SendHMILCD("CR.x0.val=%d", temp2);
//        CR_mode();
//      }
       if (mode == CW)
      {
        PSET = temp2/100.0;
        Serial_SendHMILCD("CW.x0.val=%d", temp2);
        CW_mode();
      }

    }
			switch (Data[0]){
				case '6':
				Serial_SendHMILCD("page menu");          // 切换到菜单页面
        mode = menu;
				break;
				case '5':
		              Serial_SendHMILCD("page CW"); // 切换到恒功率页面
                  mode = CW;                    // 设置当前模式为恒功率模式
				break;
				case '4':
		              Serial_SendHMILCD("page CR"); // 切换到恒阻页面
                  mode = CR;                    // 设置当前模式为恒阻模式
				break;
				case '3':
		              Serial_SendHMILCD("page CV"); // 切换到恒压页面
                  mode = CV;                    // 设置当前模式为恒压模式
				break;
        case '2':
		              Serial_SendHMILCD("page CC"); // 切换到恒流模式页面
                  mode = CC;                    // 设置当前模式恒流模式
					break;
				case '1':
		      ON();//开启
					break;
				case '0':
          OFF();//关闭
					break;
				default:
					break;
			}

			  cishu=0;
				len=0;
				flag_start=0;
	 }
  }
}

void ON(void)//开启
{
		if (mode == CC) // 恒流模式
    {
      if (Eload_Out == 0) // 当前负载输出状态为关闭时
      {
        Serial_SendHMILCD("CC.t1.txt=\"ON\"");   // 屏幕右上角标题框显示ON
        Serial_SendHMILCD("CC.b1.txt=\"关闭\""); // 屏幕右下角按钮显示关闭
        Eload_Out = 1;                           // 负载输出状态设置为开启
        if (ISET <= 2.5)                         // 电流设定值小于2.5A时只启用一个MOS管
        {
          GPTM0_CH2_SetOnduty((uint16_t)(ISET * 0.01 * 50 / VDD * 50000));
          GPTM0_CH2_EnablePWMOutput();   // 开启CH2(IREF1)通道的PWM
          GPTM0_CH3_DisablePWMOutput(0); // 关闭CH3(IREF2)通道的PWM，并输出低电平
        }
        else
        {
          GPTM0_CH2_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
          GPTM0_CH3_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
          GPTM0_CH2_EnablePWMOutput(); // 开启CH2通道的PWM
          GPTM0_CH3_EnablePWMOutput(); // 开启CH3通道的PWM
        }
        GPTM0_CH0_DisablePWMOutput(0); // VREF 输出低电平
      }

    }

		else if (mode == CV) // 恒压模式
    {
      if (Eload_Out == 0) // 当前负载输出状态为关闭时
      {
        Serial_SendHMILCD("CV.t1.txt=\"ON\"");
        Serial_SendHMILCD("CV.b1.txt=\"关闭\"");
        Eload_Out = 1;
        if (voltage_dw == 0)
        {
          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0325 / VDD * 50000));
        }
        else if (voltage_dw == 1)
        {
          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0947 / VDD * 50000));
        }
        else if (voltage_dw == 2)
        {
          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.6175 / VDD * 50000));
        }
        GPTM0_CH0_EnablePWMOutput();
        GPTM0_CH2_DisablePWMOutput(1);
        GPTM0_CH3_DisablePWMOutput(1);
      }
    }

		else if (mode == CR) // 恒阻模式
    {
      if (Eload_Out == 0) // 当前负载输出状态为关闭时
      {
        Serial_SendHMILCD("CR.t1.txt=\"ON\"");
        Serial_SendHMILCD("CR.b1.txt=\"关闭\"");
        Eload_Out = 1;
        float Rtemp = YVF / RSET;
        if (Rtemp > 10)
          Rtemp = 10;
        GPTM0_CH2_SetOnduty((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VDD * 50000));
        GPTM0_CH3_SetOnduty((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VDD * 50000));
        GPTM0_CH2_EnablePWMOutput(); // 开启CH2通道的PWM
        GPTM0_CH3_EnablePWMOutput(); // 开启CH3通道的PWM

        GPTM0_CH0_DisablePWMOutput(0); // VREF 输出低电平
      }
    }

		else if (mode == CW) // 恒功率模式
    {
      if (Eload_Out == 0) // 当前负载输出状态为关闭时
      {
        Serial_SendHMILCD("CW.t1.txt=\"ON\"");
        Serial_SendHMILCD("CW.b1.txt=\"关闭\"");
        Eload_Out = 1;
        float Ptemp = PSET / YVF;
        if (Ptemp > 10)
          Ptemp = 10;
        GPTM0_CH2_SetOnduty((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VDD * 50000));
        GPTM0_CH3_SetOnduty((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VDD * 50000));
        GPTM0_CH2_EnablePWMOutput(); // 开启CH2通道的PWM
        GPTM0_CH3_EnablePWMOutput(); // 开启CH3通道的PWM

        GPTM0_CH0_DisablePWMOutput(0); // VREF 输出低电平
      }
    }
}

void OFF(void)//关闭
{
	      	if (mode == CC) // 恒流模式
				{
							if (Eload_Out == 1) // 当前负载输出状态为开启时
					{
						Serial_SendHMILCD("CC.t1.txt=\"OFF\"");
						Serial_SendHMILCD("CC.b1.txt=\"开启\"");
						Eload_Out = 0;
						GPTM0_CH0_DisablePWMOutput(1); // VREF 输出高电平
						GPTM0_CH2_DisablePWMOutput(0); // IREF1 输出低电平
						GPTM0_CH3_DisablePWMOutput(0); // IREF2 输出低电平
					}
				}

				else if (mode == CV) // 恒压模式
     {

			  if (Eload_Out == 1) // 当前负载输出状态为开启时
      {
        Serial_SendHMILCD("CV.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CV.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // VREF 输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // IREF1 输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // IREF2 输出低电平
      }
    }

		 	else if (mode == CR) // 恒阻模式
     {
			 if (Eload_Out == 1) // 当前负载输出状态为开启时
      {
        Serial_SendHMILCD("CR.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CR.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // VREF 输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // IREF1 输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // IREF2 输出低电平
      }
		}
		else if (mode == CW) // 恒功率模式
    {
		 if (Eload_Out == 1) // 当前负载输出状态为开启时
      {
        Serial_SendHMILCD("CW.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CW.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // VREF 输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // IREF1 输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // IREF2 输出低电平
      }
		}
}

/*对串口屏发来的数据进行处理*/
void HMI_GetData(void)
{
  if (Serial_RxFlag == 1)
  {
    if (Serial_RxPacket[0] == 0x01) // 当前是主菜单页面
    {
      if (Serial_RxPacket[1] == 0x10) // 恒流按钮按下
      {
        Serial_SendHMILCD("page CC"); // 切换到恒流模式页面
        mode = CC;                    // 设置当前模式恒流模式
      }
      else if (Serial_RxPacket[1] == 0x11) // 恒压按钮按下
      {
        Serial_SendHMILCD("page CV"); // 切换到恒压页面
        mode = CV;                    // 设置当前模式为恒压模式
      }
      else if (Serial_RxPacket[1] == 0x12) // 恒阻按钮按下
      {
        Serial_SendHMILCD("page CR"); // 切换到恒阻页面
        mode = CR;                    // 设置当前模式为恒阻模式
      }
      else if (Serial_RxPacket[1] == 0x13) // 恒功率按钮按下
      {
        Serial_SendHMILCD("page CW"); // 切换到恒功率页面
        mode = CW;                    // 设置当前模式为恒功率模式
      }
    }
    else if (Serial_RxPacket[0] == 0x02) // 当前是恒流模式页面
    {
      if (Serial_RxPacket[1] == 0x10) // 菜单按钮按下
      {
        Serial_SendHMILCD("CC.t1.txt=\"OFF\"");  // 屏幕右上角标题框显示OFF
        Serial_SendHMILCD("CC.b1.txt=\"开启\""); // 屏幕右下角按钮显示开启
        Eload_Out = 0;                           // 负载输出状态设置为关闭
        GPTM0_CH0_DisablePWMOutput(1);           // 关闭CH0(恒压)通道的PWM输出,并输出高电平
        GPTM0_CH2_DisablePWMOutput(0);           // 关闭CH2(恒流1)通道的PWM输出,并输出低电平
        GPTM0_CH3_DisablePWMOutput(0);           // 关闭CH3(恒流2)通道的PWM输出,并输出低电平
        Serial_SendHMILCD("page menu");          // 切换到菜单页面
        mode = menu;
      }
      else if (Serial_RxPacket[1] == 0x11) // 开启按钮按下并且当前负载输出状态为关闭
      {
        Key_ONOFF = 1;
      }
    }
    else if (Serial_RxPacket[0] == 0x03) // 当前是恒压模式页面
    {
      if (Serial_RxPacket[1] == 0x10) // 菜单按钮按下
      {
        Serial_SendHMILCD("CV.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CV.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // 关闭CH0(恒压)通道的PWM输出,并输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // 关闭CH2(恒流1)通道的PWM输出,并输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // 关闭CH3(恒流2)通道的PWM输出,并输出低电平
        Serial_SendHMILCD("page menu");
        mode = menu;
      }
      if (Serial_RxPacket[1] == 0x11) // 开启按钮按下并且当前负载输出状态为关闭
      {
        Key_ONOFF = 1;
      }
    }
    else if (Serial_RxPacket[0] == 0x04) // 当前是恒阻模式页面
    {
      if (Serial_RxPacket[1] == 0x10) // 菜单按钮按下
      {
        Serial_SendHMILCD("CR.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CR.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // 关闭CH0(恒压)通道的PWM输出,并输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // 关闭CH2(恒流1)通道的PWM输出,并输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // 关闭CH3(恒流2)通道的PWM输出,并输出低电平
        Serial_SendHMILCD("page menu");
        mode = menu;
      }
      if (Serial_RxPacket[1] == 0x11) // 开启按钮按下并且当前负载输出状态为关闭
      {
        Key_ONOFF = 1;
      }
    }
    else if (Serial_RxPacket[0] == 0x05) // 当前是恒功率模式页面
    {
      if (Serial_RxPacket[1] == 0x10) // 菜单按钮按下
      {
        Serial_SendHMILCD("CW.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CW.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // 关闭CH0(恒压)通道的PWM输出,并输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // 关闭CH2(恒流1)通道的PWM输出,并输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // 关闭CH3(恒流2)通道的PWM输出,并输出低电平
        Serial_SendHMILCD("page menu");
        mode = menu;
      }
      if (Serial_RxPacket[1] == 0x11) // 开启按钮按下并且当前负载输出状态为关闭
      {
        Key_ONOFF = 1;
      }
    }

    else if (Serial_RxPacket[0] == 0xAA) // 当前为数字键盘页面
    {
      char *temp = Serial_RxPacket;
      temp++;                      // 地址自增1
      uint16_t temp2 = atoi(temp); // 字符串转整形
      if (mode == CC)
      {
        if (temp2 > 10000)
          temp2 = 10000;
        ISET = temp2 / 1000.0;
        Serial_SendHMILCD("CC.x0.val=%d", temp2);

        if (ISET <= 2.5)
        {
          if (Eload_Out == 1)
          {
            GPTM0_CH3_DisablePWMOutput(0);
            GPTM0_CH2_SetOnduty((uint16_t)(ISET * 0.01 * 50 / VDD * 50000));
          }
          else
          {
            GPTM0_CH2_SetOnduty((uint16_t)(ISET * 0.01 * 50 / VDD * 50000));
          }
        }
        else
        {
          if (Eload_Out == 1)
          {
            GPTM0_CH2_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
            GPTM0_CH3_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
            GPTM0_CH3_EnablePWMOutput();
          }
          else
          {
            GPTM0_CH2_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
            GPTM0_CH3_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
          }
        }
      }
      else if (mode == CV)
      {
        VSET = temp2 / 100.0;
        Serial_SendHMILCD("CV.x0.val=%d", temp2);
        if (voltage_dw == 0)
        {
          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0325 / VDD * 50000));
        }
        else if (voltage_dw == 1)
        {
          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0947 / VDD * 50000));
        }
        else if (voltage_dw == 2)
        {
          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.6175 / VDD * 50000));
        }
      }
      else if (mode == CR)
      {
        RSET = temp2 / 100.0;
        Serial_SendHMILCD("CR.x0.val=%d", temp2);
        CR_mode();
      }
      else if (mode == CW)
      {
        PSET = temp2 / 100.0;
        Serial_SendHMILCD("CW.x0.val=%d", temp2);
        CW_mode();
      }
    }
    Serial_RxFlag = 0;
  }
}

/*串口屏显示参数*/
void HMI_Display(void)
{
  if (mode != menu)
  {
    Serial_SendHMILCD("x1.val=%d", (uint16_t)(YVF * 100));       // 显示电压
    Serial_SendHMILCD("x2.val=%d", (uint16_t)(YIF * 1000));      // 显示电流
    Serial_SendHMILCD("x3.val=%d", (uint32_t)(YIF * YVF * 100)); // 显示功率
    Serial_SendHMILCD("x4.val=%d", (uint32_t)(YVF / YIF * 100)); // 显示电阻
  }
}

/*ADC数据处理*/
void AdcFb(void)
{
  VDD = (AD_Value[4] & 0x0000FFFF) * 1.2482 / (AD_Value[3] & 0x0000FFFF); // ADC采样值转换为电压值
                                                                          // VDD = (AD_Value[4] & 0x0000FFFF)  / 4096.0*3.3; // ADC采样值转换为电压值
  // VDD = 3.3;

  if (voltage_dw == 0) // 电压采样档位为0.0325倍时
  {
    if (YVF_AVG_count < 15)
    {
      YVF_SUM += (AD_Value[0] & 0x0000FFFF);
      YVF_AVG_count++;
    }
    if (YVF_AVG_count == 15)
    {
      YVF = YVF_SUM / YVF_AVG_count / 4096.0 * VDD / 0.0325;
      YVF_AVG_count = 0;
      YVF_SUM = 0;
    }
    // YVF = (AD_Value[0] & 0x0000FFFF) / 4096.0 * VDD / 0.0325;
    if (YVF <= 32) // 电压小于32V时，切换档位
    {
      GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_10, RESET);
      GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_11, SET);
      GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0947 / VDD * 50000));
      voltage_dw = 1;
      YVF_AVG_count = 0;
      YVF_SUM = 0;
      // YVF = (AD_Value[0] & 0x0000FFFF) / 4096.0 * VDD / 0.0947;
    }
  }
  else if (voltage_dw == 1) // 电压档位为0.0947倍时
  {
    if (YVF_AVG_count < 15)
    {
      YVF_SUM += (AD_Value[0] & 0x0000FFFF);
      YVF_AVG_count++;
    }
    if (YVF_AVG_count == 15)
    {
      YVF = YVF_SUM / YVF_AVG_count / 4096.0 * VDD / 0.0947;
      YVF_AVG_count = 0;
      YVF_SUM = 0;
    }
    // YVF = (AD_Value[0] & 0x0000FFFF) / 4096.0 * VDD / 0.0947;

    if (YVF >= 34)
    {
      GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_10, RESET);
      GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_11, RESET);
      GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0325 / VDD * 50000));
      voltage_dw = 0;
      YVF_AVG_count = 0;
      YVF_SUM = 0;
      // YVF = (AD_Value[0] & 0x0000FFFF) / 4096.0 * VDD / 0.0325;
    }
    else if (YVF <= 5.0) // 电压小于5V时切换档位
    {
      GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_10, SET);
      GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_11, RESET);
      GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.6175 / VDD * 50000));
      voltage_dw = 2;
      YVF_AVG_count = 0;
      YVF_SUM = 0;
      // YVF = (AD_Value[0] & 0x0000FFFF) / 4096.0 * VDD / 0.6175;
    }
  }
  else if (voltage_dw == 2) // 电压档位为0.6175倍时
  {
    if (YVF_AVG_count < 15)
    {
      YVF_SUM += (AD_Value[0] & 0x0000FFFF);
      YVF_AVG_count++;
    }
    if (YVF_AVG_count == 15)
    {
      YVF = YVF_SUM / YVF_AVG_count / 4096.0 * VDD / 0.6175;
      if (YVF < 0.2)
      {
        YVF = 0;
      }
      YVF_AVG_count = 0;
      YVF_SUM = 0;
    }
    // YVF = (AD_Value[0] & 0x0000FFFF) / 4096.0 * VDD / 0.6175;

    if (YVF > 5.1) // 电压大于5.1V时，切换档位
    {
      GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_10, RESET);
      GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_11, SET);
      GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0947 / VDD * 50000));
      voltage_dw = 1;
      YVF_AVG_count = 0;
      YVF_SUM = 0;
      // YVF = (AD_Value[0] & 0x0000FFFF) / 4096.0 * VDD / 0.0947;
    }
  }

  if (AVG_count < 15)
  {
    YIF1_SUM += (AD_Value[1] & 0x0000FFFF); // MOS管1电流累加
    YIF2_SUM += (AD_Value[2] & 0x0000FFFF); // MOS管2电流累加
    AVG_count++;
  }
  if (AVG_count == 15)
  {
    YIF1 = YIF1_SUM / AVG_count / 4096.0 * VDD / 50 / 0.01;
    YIF2 = YIF2_SUM / AVG_count / 4096.0 * VDD / 50 / 0.01;
    YIF = YIF1 + YIF2;
    AVG_count = 0;
    YIF1_SUM = 0;
    YIF2_SUM = 0;
  }
  // YIF1 = (AD_Value[1] & 0x0000FFFF) / 4096.0 * VDD / 50 / 0.01;
  // YIF2 = (AD_Value[2] & 0x0000FFFF) / 4096.0 * VDD / 50 / 0.01;
  // YIF = YIF1 + YIF2;
}

/*电子负载开启关闭按键*/
void ONOFF(void)
{
  if (Key_ONOFF == 1 | key[0] == 1) // 开启按钮按下
  {
    if (mode == CC) // 恒流模式
    {
      if (Eload_Out == 0) // 当前负载输出状态为关闭时
      {
        Serial_SendHMILCD("CC.t1.txt=\"ON\"");   // 屏幕右上角标题框显示ON
        Serial_SendHMILCD("CC.b1.txt=\"关闭\""); // 屏幕右下角按钮显示关闭
        Eload_Out = 1;                           // 负载输出状态设置为开启
        if (ISET <= 2.5)                         // 电流设定值小于2.5A时只启用一个MOS管
        {
          GPTM0_CH2_SetOnduty((uint16_t)(ISET * 0.01 * 50 / VDD * 50000));
          GPTM0_CH2_EnablePWMOutput();   // 开启CH2(IREF1)通道的PWM
          GPTM0_CH3_DisablePWMOutput(0); // 关闭CH3(IREF2)通道的PWM，并输出低电平
        }
        else
        {
          GPTM0_CH2_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
          GPTM0_CH3_SetOnduty((uint16_t)(ISET / 2.0 * 0.01 * 50 / VDD * 50000));
          GPTM0_CH2_EnablePWMOutput(); // 开启CH2通道的PWM
          GPTM0_CH3_EnablePWMOutput(); // 开启CH3通道的PWM
        }
        GPTM0_CH0_DisablePWMOutput(0); // VREF 输出低电平
      }
      else if (Eload_Out == 1) // 当前负载输出状态为开启时
      {
        Serial_SendHMILCD("CC.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CC.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // VREF 输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // IREF1 输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // IREF2 输出低电平
      }
    }
    else if (mode == CV) // 恒压模式
    {
      if (Eload_Out == 0) // 当前负载输出状态为关闭时
      {
        Serial_SendHMILCD("CV.t1.txt=\"ON\"");
        Serial_SendHMILCD("CV.b1.txt=\"关闭\"");
        Eload_Out = 1;
        if (voltage_dw == 0)
        {
          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0325 / VDD * 50000));
        }
        else if (voltage_dw == 1)
        {
          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.0947 / VDD * 50000));
        }
        else if (voltage_dw == 2)
        {
          GPTM0_CH0_SetOnduty((uint16_t)(VSET * 0.6175 / VDD * 50000));
        }
        GPTM0_CH0_EnablePWMOutput();
        GPTM0_CH2_DisablePWMOutput(1);
        GPTM0_CH3_DisablePWMOutput(1);
      }
      else if (Eload_Out == 1) // 当前负载输出状态为开启时
      {
        Serial_SendHMILCD("CV.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CV.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // VREF 输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // IREF1 输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // IREF2 输出低电平
      }
    }
    else if (mode == CR) // 恒阻模式
    {
      if (Eload_Out == 0) // 当前负载输出状态为关闭时
      {
        Serial_SendHMILCD("CR.t1.txt=\"ON\"");
        Serial_SendHMILCD("CR.b1.txt=\"关闭\"");
        Eload_Out = 1;
        float Rtemp = YVF / RSET;
        if (Rtemp > 10)
          Rtemp = 10;
        GPTM0_CH2_SetOnduty((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VDD * 50000));
        GPTM0_CH3_SetOnduty((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VDD * 50000));
        GPTM0_CH2_EnablePWMOutput(); // 开启CH2通道的PWM
        GPTM0_CH3_EnablePWMOutput(); // 开启CH3通道的PWM

        GPTM0_CH0_DisablePWMOutput(0); // VREF 输出低电平
      }
      else if (Eload_Out == 1) // 当前负载输出状态为开启时
      {
        Serial_SendHMILCD("CR.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CR.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // VREF 输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // IREF1 输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // IREF2 输出低电平
      }
    }
    else if (mode == CW) // 恒功率模式
    {
      if (Eload_Out == 0) // 当前负载输出状态为关闭时
      {
        Serial_SendHMILCD("CW.t1.txt=\"ON\"");
        Serial_SendHMILCD("CW.b1.txt=\"关闭\"");
        Eload_Out = 1;
        float Ptemp = PSET / YVF;
        if (Ptemp > 10)
          Ptemp = 10;
        GPTM0_CH2_SetOnduty((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VDD * 50000));
        GPTM0_CH3_SetOnduty((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VDD * 50000));
        GPTM0_CH2_EnablePWMOutput(); // 开启CH2通道的PWM
        GPTM0_CH3_EnablePWMOutput(); // 开启CH3通道的PWM

        GPTM0_CH0_DisablePWMOutput(0); // VREF 输出低电平
      }
      else if (Eload_Out == 1) // 当前负载输出状态为开启时
      {
        Serial_SendHMILCD("CW.t1.txt=\"OFF\"");
        Serial_SendHMILCD("CW.b1.txt=\"开启\"");
        Eload_Out = 0;
        GPTM0_CH0_DisablePWMOutput(1); // VREF 输出高电平
        GPTM0_CH2_DisablePWMOutput(0); // IREF1 输出低电平
        GPTM0_CH3_DisablePWMOutput(0); // IREF2 输出低电平
      }
    }

    Key_ONOFF = 0;
    key[0] = 0;
  }
// https://blog.zeruns.com
  if (Eload_Out == 0)
  {
    GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_15, SET);
  }
  else if (Eload_Out == 1)
  {
    GPIO_WriteOutBits(HT_GPIOC, GPIO_PIN_15, RESET);
  }
}

/*散热风扇控制*/
void FAN(void)
{
  uint16_t P = (uint16_t)(YIF * YVF);
  if (P >= 6) // 功率大于6W时启动风扇
  {
    MCTM0_CH0_EnablePWMOutput(); // 开启风扇
    if (P < 15)
    {
      MCTM0_CH0_SetOnduty(50); // 风扇控制占空比50%
    }
    else if (P >= 15 && P < 20)
    {
      MCTM0_CH0_SetOnduty(60);
    }
    else if (P >= 20 && P < 25)
    {
      MCTM0_CH0_SetOnduty(70);
    }
    else if (P >= 25 && P < 30)
    {
      MCTM0_CH0_SetOnduty(80);
    }
    else if (P >= 30 && P < 35)
    {
      MCTM0_CH0_SetOnduty(90);
    }
    else if (P >= 35)
    {
      MCTM0_CH0_SetOnduty(100);
    }
  }
  else if (P <= 5)
  {
    MCTM0_CH0_DisablePWMOutput(0); // 关闭MCTM0_CH0(风扇)通道的PWM输出,并输出低电平
  }
}

/*恒功率模式*/
void CW_mode(void)
{
  float Ptemp = PSET / YVF;
  if (Ptemp > 10)
    Ptemp = 10;
  if (Eload_Out == 1)
  {
    GPTM0_CH2_SetOnduty((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VDD * 50000));
    GPTM0_CH3_SetOnduty((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VDD * 50000));
  }
  else
  {
    GPTM0_CH2_SetOnduty((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VDD * 50000));
    GPTM0_CH3_SetOnduty((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VDD * 50000));
  }
}

/*恒阻模式*/
void CR_mode(void)
{
  float Rtemp = YVF / RSET;
  if (Rtemp > 10)
    Rtemp = 10;
  if (Eload_Out == 1)
  {
    GPTM0_CH2_SetOnduty((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VDD * 50000));
    GPTM0_CH3_SetOnduty((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VDD * 50000));
  }
  else
  {
    GPTM0_CH2_SetOnduty((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VDD * 50000));
    GPTM0_CH3_SetOnduty((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VDD * 50000));
  }
}

// 按键状态机程序
void key_status_check(uint8_t key_num, uint8_t KEY)
{
  switch (KEY_Status[key_num][g_keyStatus])
  {
  // 按键释放(初始状态)
  case KS_RELEASE:
  {
    // 检测到低电平，先进行消抖
    if (KEY == 0)
    {
      KEY_Status[key_num][g_keyStatus] = KS_SHAKE;
    }
  }
  break;

  // 抖动
  case KS_SHAKE:
  {
    if (KEY == 1)
    {
      KEY_Status[key_num][g_keyStatus] = KS_RELEASE;
    }
    else
    {
      KEY_Status[key_num][g_keyStatus] = KS_PRESS;
    }
  }
  break;

  // 稳定短按
  case KS_PRESS:
  {
    // 检测到高电平，先进行消抖
    if (KEY == 1)
    {
      KEY_Status[key_num][g_keyStatus] = KS_SHAKE;
    }
  }
  break;

  default:
    break;
  }

  if (KEY_Status[key_num][g_keyStatus] != KEY_Status[key_num][g_nowKeyStatus])
  {
    // 当前状态为松开 并且 前一次状态为按下
    if ((KEY_Status[key_num][g_keyStatus] == KS_RELEASE) && (KEY_Status[key_num][g_lastKeyStatus] == KS_PRESS))
    {
      key[key_num] = 1;
    }
    KEY_Status[key_num][g_lastKeyStatus] = KEY_Status[key_num][g_nowKeyStatus];
    KEY_Status[key_num][g_nowKeyStatus] = KEY_Status[key_num][g_keyStatus];
  }
}

其他开源项目推荐

做了个三相电量采集器开源出来，可以方便监测家里用电情况：https://blog.zeruns.com/archives/771.html

移植好U8g2图形库的STM32F407标准库工程模板：https://blog.zeruns.com/archives/722.html

沁恒CH32V307VCT6最小系统板开源：https://blog.zeruns.com/archives/726.html

LM25118自动升降压可调DCDC电源模块：https://blog.zeruns.com/archives/727.html

EG1164大功率同步整流升压模块开源，最高效率97%：https://blog.zeruns.com/archives/730.html

基于合宙Air700E的4G环境监测节点（温湿度、气压等数据），通过MQTT上传阿里云物联网平台：https://blog.zeruns.com/archives/747.html

推荐阅读

高性价比和便宜的VPS/云服务器推荐: https://blog.zeruns.com/archives/383.html

我的世界开服教程：https://blog.zeruns.com/tag/mc/

免代码搭建博客网站！超详细个人博客搭建教程：https://blog.zeruns.com/archives/783.html

内网穿透服务器搭建教程，NPS搭建和使用教程：https://blog.zeruns.com/archives/741.html

雨云GPU云服务器搭建SD(Stable Diffusion)的教程，搭建自己的AI绘画网站：https://blog.zeruns.com/archives/768.html

华为Pura70Pro+拍照测评，与Mate40Pro对比：https://blog.zeruns.com/archives/782.html

1 个帖子 - 1 位参与者

阅读完整话题