HT32F52352ベースのスマート電子負荷、合泰杯作品をオープンソース化

HT32F52352ベースのスマート電子負荷オープンソース、Holtek Cup作品オープンソース、回路図・PCB・プログラムソース・作品報告書含む。

2023年第10回広東省大学生Holtek Cupマイコン応用設計コンテスト 三等賞作品。

1ヶ月(しかも授業が多くてほとんど時間が無かった)で急いで作った、出来はイマイチ、厳しいことは言わないでください

作品デモ動画:https://www.bilibili.com/video/BV1sM4y1b7qu/

本オープンソース作品は参考・学習用のみで、複製は推奨しません。立創オープンソースプラットフォームにはもっと良くて完成度の高い電子負荷オープンソース作品がたくさんあります!

本プロジェクトの立創オープンソースプラットフォームリンク:https://url.zeruns.com/xvvF8

Holtek HT32マイコン開発環境構築・設定チュートリアル:https://blog.zeruns.com/archives/709.html

電子・マイコン技術交流グループ:2169025065

電子負荷とは

電子負荷は、実際の負荷環境をシミュレートして電源や電子回路の性能をテストするための電子機器です。大出力可変抵抗やニクロム線などの従来の無源負荷と比べ、電子負荷はパラメータ可調・使いやすさなどの利点があります。プロの電子エンジニアリング開発でもアマチュアの電子工作でも、電子負荷装置は必須の機器の一つです。

電子負荷はテストする電源の種類により、交流電子負荷と直流電子負荷に分類されます。機能別には、定電流・定電圧・定抵抗・定電力の4種類が一般的です。私たちがよく使うのは定電圧直流電源なので、このような電源をテストする際には主に電流出力能力をテストします。そのため、多くの応用シーンでは直流定電流電子負荷が最も一般的です。また、制御方式別には数値制御とアナログ制御の2種類があります。純粋なアナログ回路で制御する電子負荷と比べ、数値制御電子負荷はデジタル制御によりパラメータ調整が直感的で、機能が豊富・拡張が簡単で、テストの自動化も容易に実現できます。

プロジェクト概要

本作品はHT32F52352 Holtekマイコンをメインコントローラーとして設計した電子負荷で、電源は18650リチウム電池で動作し、持ち運びが簡単です。

制御方式は、マイコンがPWM波を出力しローパスフィルタ回路で比較的安定した直流電圧を生成(PWM+ローパスフィルタでDAC機能を実現)、これをリファレンス電圧としてオペアンプに入力し、電流・電圧サンプリング後の増幅電圧と比較し、オペアンプ出力でMOS-FETを制御することで定電圧・定電流を実現します。

タッチディスプレイは陶晶馳の2.8インチシリアルLCD、型番:TJC3224T028_011Rを使用。

実物写真

当時あまり撮影していなくて、これしか見つかりませんでした。デモ動画をご覧ください。


資料ダウンロード先

以下のリンクには回路図・立創EDAプロジェクトファイル・PCB製造ファイル・プログラムソース・シリアルLCDプロジェクトファイル・チップデータシートが含まれています。

123クラウドディスク:https://www.123pan.com/s/2Y9Djv-ZNevH.html

Baidu NetDisk:https://url.zeruns.com/XP241 抽出コード:i9bl

部品購入先

立創商城で部品を購入することをおすすめします:https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html

回路図

電源板



電源基板

コントロール基板

PCB

電源板

Top Layer

Inner Layer 1

Bottom Layer

GNDレイヤーは省略

電源基板

Top Layer

Bottom Layer

コントロール基板

Top Layer

Bottom Layer

メインコード

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;                // 電子負荷出力ON/OFF状態
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;                // 電子負荷ON/OFFボタン押下状態

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関数をUARTにリダイレクト
  USART1_Configuration();   // UART1初期化
  USART0_Configuration();   // UART0初期化

  uint16_t count1 = 0;

  Serial_SendHMILCD("page 0");      // 起動ページへ
  GPTM0_CH0_DisablePWMOutput(1);    // CH0(定電圧)PWM出力停止、HIGH出力
  GPTM0_CH2_DisablePWMOutput(0);    // CH2(定電流1)PWM出力停止、LOW出力
  GPTM0_CH3_DisablePWMOutput(0);    // CH3(定電流2)PWM出力停止、LOW出力
  MCTM0_CH0_DisablePWMOutput(0);    // MCTM0_CH0(ファン)PWM出力停止、LOW出力
  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;
        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;
        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;
        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;
        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)// UART1データ処理
			{
				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未満の時は1個の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;
      }
    }

   ```c
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); // MOS1電流累積
    YIF2_SUM += (AD_Value[2] & 0x0000FFFF); // MOS2電流累積
    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;
}
```/*電子負荷ON/OFFボタン*/
void ONOFF(void)
{
  if (Key_ONOFF == 1 | key[0] == 1) // 電源ボタンが押された
  {
    if (mode == CC) // 定電流モード
    {
      if (Eload_Out == 0) // 現在の負荷出力がOFFのとき
      {
        Serial_SendHMILCD("CC.t1.txt=\"ON\"");   // 画面右上タイトル欄にON表示
        Serial_SendHMILCD("CC.b1.txt=\"オフ\""); // 画面右下ボタンにオフ表示
        Eload_Out = 1;                           // 負荷出力をONに設定
        if (ISET <= 2.5)                         // 電流設定値が2.5A以下のとき1個のMOS管のみ使用
        {
          GPTM0_CH2_SetOnduty((uint16_t)(ISET * 0.01 * 50 / VDD * 50000));
          GPTM0_CH2_EnablePWMOutput();   // CH2(IREF1)チャンネルのPWMをON
          GPTM0_CH3_DisablePWMOutput(0); // CH3(IREF2)チャンネルのPWMをOFFしローレベル出力
        }
        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をON
          GPTM0_CH3_EnablePWMOutput(); // CH3チャンネルのPWMをON
        }
        GPTM0_CH0_DisablePWMOutput(0); // VREF ローレベル出力
      }
      else if (Eload_Out == 1) // 現在の負荷出力がONのとき
      {
        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) // 現在の負荷出力がOFFのとき
      {
        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) // 現在の負荷出力がONのとき
      {
        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) // 現在の負荷出力がOFFのとき
      {
        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をON
        GPTM0_CH3_EnablePWMOutput(); // CH3チャンネルのPWMをON

        GPTM0_CH0_DisablePWMOutput(0); // VREF ローレベル出力
      }
      else if (Eload_Out == 1) // 現在の負荷出力がONのとき
      {
        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) // 現在の負荷出力がOFFのとき
      {
        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をON
        GPTM0_CH3_EnablePWMOutput(); // CH3チャンネルのPWMをON

        GPTM0_CH0_DisablePWMOutput(0); // VREF ローレベル出力
      }
      else if (Eload_Out == 1) // 現在の負荷出力がONのとき
      {
        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(); // ファンON
    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出力をOFFしローレベル出力
  }
}

/*定電力モード*/
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];
  }
}

その他のオープンソースプロジェクトおすすめ

おすすめ記事- 高コスパで安いVPS/クラウドサーバーおすすめ: https://blog.zeruns.com/archives/383.html