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
部品購入先
- 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
- シリアルLCD:https://s.click.taobao.com/pyzleot
立創商城で部品を購入することをおすすめします: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];
}
}
その他のオープンソースプロジェクトおすすめ
- 三相電力計測器を作ってオープンにしました。家庭の電力状況を簡単に監視できます: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でアリババクラウドIoTプラットフォームへアップロード: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













