CH32V307VCT6ベースのスマート電子負荷オープンソース、組込みコンテスト作品オープンソース、回路図・PCB・プログラムソース・作品レポート付き。
2023年組込みチップ&システム設計コンテスト応用部門、全国2位作品。
以前作ったHoltek版電子負荷をベースに半月で改造し、プログラムをCH32マイコンへ移植、RT-Threadシステムを導入してコードを最適化、急ピッチで制作。作りは大したことないので厳しいコメントはご遠慮ください。
作品デモ動画:https://www.bilibili.com/video/BV1Zu4y1m7Zd/
HT32F52352ベースのスマート電子負荷オープンソース、Holtek Cup作品オープンソース:https://blog.zeruns.com/archives/784.html
本オープンソース作品は参考・学習用のみで、複製は推奨しません。LCSCオープンプラットフォームにはもっと優れた電子負荷オープンソース作品が多数あります!
本プロジェクトのLCSCオープンソースリンク:https://url.zeruns.com/Et4x4
電子・マイコン技術交流QQグループ:2169025065
WCH公式サイトで開発ボードサンプルを無料申請:https://url.zeruns.com/h9a99
電子負荷とは
電子負荷は、実際の負荷環境をシミュレートして電源や電子回路の性能をテストする電子機器です。大電力可変抵抗やニクロム線などの従来の無源負荷と比べ、パラメータ可変・使いやすさなどの利点があります。プロ開発者もアマチュアも電子負荷装置は必須です。
電源種類により交流電子負荷と直流電子負荷に分類され、機能面では定電流・定電圧・定抵抗・定電力の4タイプが一般的です。恒圧直流電源のテストでは電流供給能力が主なので、直流定電流電子負荷が最も使われます。制御方式では数値制御とアナログ制御があり、数値制御はパラメータ調整が直感的で機能拡張も簡単、自動テストも容易です。
プロジェクト概要
本作品はCH32V307VCT6(WCHマイコン)をメインコントローラとし、18650リチウム電池で駆動し携帯性を確保。
制御方式はマイコンDACで直流基準電圧を生成しオペアンプで電流/電圧検出増幅電圧と比較、オペアンプ出力でMOS-FETを制御し恒圧/恒電流を実現。
タッチディスプレイは陶晶馳2.8インチシリアルLCD、型番TJC3224T028_011R。
ヒートシンクは2Uサーバー用1356/1366ピンサイドフロー型。
プログラムはRT-Thread Studioで開発、回路設計はLCSC EDA。
最大入力100V/10A、最大200W。
実物写真
撮影枚数少ないのでデモ動画をご覧ください。
資料ダウンロード
以下リンクに回路図・LCSC EDAプロジェクト・PCB製造データ・ソースコード・シリアルLCDプロジェクト・チップマニュアル含む。
123パン不限速ダウンロード:https://www.123pan.com/ps/2Y9Djv-6NevH.html
百度网盘ダウンロード:https://pan.baidu.com/s/17YSlBZ6F1M18k7JGa7FlVA?pwd=buxx パスワード:buxx
部品購入先
- CH32V307VCT6チップ:https://s.click.taobao.com/T8MSZot
- CH32V307VCT6開発ボード:https://s.click.taobao.com/2JBSZot
- INA199A1チップ:https://s.click.taobao.com/XLuweot
- 0805チップ抵抗サンプル帳:https://s.click.taobao.com/p8YSGpt
- 0805チップコンデンササンプル帳:https://u.jd.com/9uvZoBd
- XL1509チップ:https://s.click.taobao.com/DOcRZot
- シリアルLCD:https://s.click.taobao.com/pyzleot
部品はLCSC商城で購入することをおすすめ:https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html
回路図
電源板
電源基板
コントロール基板
PCB
電源板
Top Layer
Bottom Layer
電源基板
Top Layer
Bottom Layer
コントロール基板
Top Layer
Bottom Layer
その他のオープンソースプロジェクトおすすめ- 三相電力計測器を作ってオープンソース化しました。家庭の電力使用状況を簡単に監視できます: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
主要コード
main.cファイル
/********************************** (C) COPYRIGHT *******************************
* File Name : main.c
* Author : WCH
* Version : V1.0.0
* Date : 2021/06/06
* Description : Main program body.
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
* https://blog.zeruns.com
*******************************************************************************/
#include "ch32v30x.h"
#include<rtthread.h>
#include <rtdevice.h>
#include <stdlib.h>
#include <rthw.h>
#include "drivers/pin.h"
#include <board.h>
#include <rtdbg.h>
#include <u8g2_port.h>
#include <qpid.h>
#include "USART.h"
#include "KEY.h"
#include "DAC.h"
#include "PWM.h"
/* グローバルtypedef */
/* グローバルdefine */
#define WDT_DEVICE_NAME "wdt" /* ウォッチドッグデバイス名 */
static rt_device_t wdg_dev; /* ウォッチドッグデバイスハンドル */
/* ADC基準電圧 */
#define VREF 3.3
/* 電源電圧 */
#define VCC 3.3
/* 補正キャリブレーションデータ */
#define V0_COMP 1.000 // 0.0325倍電圧レンジ
#define V1_COMP 1.000 // 0.0947倍電圧レンジ
#define V2_COMP 1.000 // 0.6175倍電圧レンジ
#define YIF1_COMP 1.00 // MOS管1電流サンプリング補償
#define YIF2_COMP 1.00 // MOS管2電流サンプリング補償
#define DAC1_COMP 1.00 // DAC1(VREF)出力補償係数
#define IREF2_COMP 1.00 // IREF2出力補償係数
#define DAC2_COMP 1.00 // DAC2(IREF1)出力補償
/* ADCサンプリング平均値計算回数 */
#define ADC_count 3
/* 一次ローパスフィルタフィルタ係数*/
#define dPower1 0.5
/* ピン番号、ドライバファイルdrv_gpio.cで確認 */
#define OLED_I2C_PIN_SCL 22 //PB6
#define OLED_I2C_PIN_SDA 23 //PB7
#define LED2 59 //PD11
#define LED1 60 //PD12
#define MCU_G0 62 //PD14
#define MCU_G1 63 //PD14
/* グローバル変数 */
u8g2_t u8g2; // u8g2の構造体変数
rt_uint16_t AD_Value[4]; //ADCサンプリングデータ
// モードページの列挙変数を定義
enum mode_type
{
menu = 0, // メニュー
CC, // 定電流
CV, // 定電圧
CR, // 定抵抗
CW // 定電力
};
volatile uint8_t Eload_Out = 0; // 電子負荷出力ON/OFF状態
volatile uint8_t mode = menu; // 現在のモード
volatile uint8_t voltage_dw = 0; // 電圧サンプリングレンジ、0は0.0325倍、2は0.6175倍、1は0.0947倍
volatile double YVF, YIF1, YIF2, YIF, VBAT; // 現在の電圧電流
volatile double ISET, VSET, RSET, PSET; // 電流、電圧、抵抗、電力設定値
volatile uint32_t YVF_SUM, YIF1_SUM, YIF2_SUM, VBAT_SUM; // 電圧電流平均値計算用合計
volatile uint8_t AVG_count = 0; // 電流平均値計算累積カウント値
volatile uint8_t YVF_AVG_count = 0; // 電圧平均値計算累積カウント値
volatile uint8_t VBAT_count = 0; // バッテリ電圧平均値計算累積カウント値
volatile uint8_t Key_ONOFF = 0; // 電子負荷ON/OFFボタン押下状態
static qpid_t qpid_CC; // PID制御データポインタ
static qpid_t qpid_CV; // PID制御データポインタ
static qpid_t qpid_CR; // PID制御データポインタ
static qpid_t qpid_CW; // PID制御データポインタ
static double I_SET, V_SET, R_SET, P_SET;
/* 関数宣言 */
void OLED_Init(void);
static int IWDG_Init();
static void thread1_sysLED_entry(void *parameter);
static void thread2_OLED_entry(void *parameter);
static void thread3_ADC_entry(void *parameter);
static void thread4_HMI_GetDate_entry(void *parameter);
static void thread5_ONOFF_entry(void *parameter);
static void thread7_HMI_Display_entry(void *parameter);
static void thread8_FAN_entry(void *parameter);
static void thread9_CWCR_entry(void *parameter);
static void thread10_BlueTooth_entry(void *parameter);
void CW_mode(void);
void CR_mode(void);
void key123(void);
void Thread_Init(void);
void SYS_Init(void);
void PID(void);
/*********************************************************************
* @fn main
*
* @brief メインプログラム。
*
* @return none
*/
int main(void)
{
rt_kprintf("MCU: CH32V307\n");
rt_kprintf("SysClk: %dHz\n", SystemCoreClock);
SYS_Init();
while (1)
{
// 過電流および過電力保護
if (YIF > 10 | YVF * YIF > 300)
{
if (Eload_Out == 1)
{
Key_ONOFF = 1;
}
}
rt_thread_mdelay(20);
}
}
/* システム初期化 */
void SYS_Init(void)
{
IWDG_Init(); // ウォッチドッグ初期化
UART_Init(); // シリアル3初期化
HMILCD_Send("page 0"); // 起動ページへ切替
Dac_Init(); // DAC初期化
PWM_Init(); // PWM初期化
rt_pin_mode(MCU_G0, PIN_MODE_OUTPUT); // IO口を出力モードに設定
rt_pin_mode(MCU_G1, PIN_MODE_OUTPUT);
rt_pin_write(MCU_G0, PIN_LOW); // ローレベル出力
rt_pin_write(MCU_G1, PIN_LOW);
Thread_Init(); // スレッド作成
}
/* スレッド初期化 */
void Thread_Init(void)
{
rt_thread_t tid = NULL; //スレッド制御ブロックポインタを定義
/* スレッド作成 */
tid = rt_thread_create("SYS_LED", thread1_sysLED_entry, NULL, 512, 30, 5);
//"SYS_LED"という名前のスレッドを作成、エントリ関数はthread1_sysLED_entry、パラメータはNULL、スタックサイズは256バイト、優先度は30、タイムスライスは5tick
if (tid != RT_NULL) // スレッド作成成功か判定
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread sysLED create success");
}
else
{
LOG_E("thread1 sysLED create failed...");
}
/*
tid = rt_thread_create("OLED_Display", thread2_OLED_entry, NULL, 2048, 25, 30);
if (tid != RT_NULL)
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread2 OLED create success");
}
else
{
LOG_E("thread2_OLED create failed...");
}*/
tid = rt_thread_create("ADC", thread3_ADC_entry, NULL, 1536, 18, 30);
if (tid != RT_NULL)
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread3 ADC create success");
}
else
{
LOG_E("thread3 ADC create failed...");
}
tid = rt_thread_create("HMI_GetDate", thread4_HMI_GetDate_entry, NULL, 2048, 23, 30);
if (tid != RT_NULL)
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread4 HMI_GetDate create success");
}
else
{
LOG_E("thread4 HMI_GetDate create failed...");
}
tid = rt_thread_create("ONOFF", thread5_ONOFF_entry, NULL, 1024, 15, 25);
if (tid != RT_NULL)
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread5 ONOFF create success");
}
else
{
LOG_E("thread5 ONOFF create failed...");
}
tid = rt_thread_create("KEY", thread6_KEY_entry, NULL, 512, 20, 20);
if (tid != RT_NULL)
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread6 KEY create success");
}
else
{
LOG_E("thread6 KEY create failed...");
}
tid = rt_thread_create("HMI_Display", thread7_HMI_Display_entry, NULL, 1024, 25, 30);
if (tid != RT_NULL)
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread7 HMI_Display create success");
}
else
{
LOG_E("thread7 HMI_Display create failed...");
}
tid = rt_thread_create("FAN", thread8_FAN_entry, NULL, 512, 26, 15);
if (tid != RT_NULL)
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread8 FAN create success");
}
else
{
LOG_E("thread8 FAN create failed...");
}
tid = rt_thread_create("CWCR", thread9_CWCR_entry, NULL, 512, 19, 15);
if (tid != RT_NULL)
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread9 CWCR create success");
}
else
{
LOG_E("thread9 CWCR create failed...");
}
tid = rt_thread_create("BlueTooth", thread10_BlueTooth_entry, NULL, 2048, 23, 30);
if (tid != RT_NULL)
{
if (rt_thread_startup(tid) == RT_EOK) // スレッド起動
LOG_D("thread10 BlueTooth create success");
}
else
{
LOG_E("thread10 BlueTooth create failed...");
}
}/* 一次ローパスフィルタ
* 戻り値:iData 一次フィルタ後のサンプル値 */
double lowV1(double com1)
{
static double iLastData1; //前回値
double iData1; //今回計算値
iData1 = (com1 * dPower1) + (1 - dPower1) * iLastData1; //計算
iLastData1 = iData1; //今回データを保存
return iData1; //データを返す
}
double lowV2(double com1)
{
static double iLastData2; //前回値
double iData1; //今回計算値
iData1 = (com1 * dPower1) + (1 - dPower1) * iLastData2; //計算
iLastData2 = iData1; //今回データを保存
return iData1; //データを返す
}
u16 lowV3(u16 com1)
{
static u16 iLastData3; //前回値
u16 iData1; //今回計算値
iData1 = (com1 * dPower1) + (1 - dPower1) * iLastData3; //計算
iLastData3 = iData1; //今回データを保存
return iData1; //データを返す
}
u16 lowV4(u16 com1)
{
static u16 iLastData3; //前回値
u16 iData1; //今回計算値
iData1 = (com1 * 0.1) + (1 - 0.1) * iLastData3; //計算
iLastData3 = iData1; //今回データを保存
return iData1; //データを返す
}
static void idle_hook(void)
{
/* アイドルスレッドのコールバック関数でウォッチドッグにエサをやる */
rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
//rt_kprintf("feed the dog!\n ");
}
static int IWDG_Init()
{
rt_err_t ret = RT_EOK;
rt_uint32_t timeout = 1; /* オーバーフロー時間、単位:秒 */
/* デバイス名でウォッチドッグデバイスを検索し、デバイスハンドルを取得 */
wdg_dev = rt_device_find(WDT_DEVICE_NAME);
if (!wdg_dev)
{
rt_kprintf("find %s failed!\n", WDT_DEVICE_NAME);
return RT_ERROR;
}
/* デバイスを初期化 */
rt_device_init(wdg_dev);
/* ウォッチドッグのオーバーフロー時間を設定 */
ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
if (ret != RT_EOK)
{
rt_kprintf("set %s timeout failed!\n", WDT_DEVICE_NAME);
return RT_ERROR;
}
/* ウォッチドッグを起動 */
ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_START, RT_NULL);
if (ret != RT_EOK)
{
rt_kprintf("start %s failed!\n", WDT_DEVICE_NAME);
return -RT_ERROR;
}
/* アイドルスレッドコールバック関数を設定 */
rt_thread_idle_sethook(idle_hook);
return ret;
}
/* スレッド1のエントリ関数、システム稼働状態LED点滅 */
static void thread1_sysLED_entry(void *parameter)
{
/* LED1ピンを出力モードに設定 */
rt_pin_mode(LED1, PIN_MODE_OUTPUT);
/* デフォルトでローレベル */
rt_pin_write(LED1, PIN_LOW);
while (1)
{
/* スレッド1は低優先度で動作し、LED1を常に点滅させる */
rt_pin_write(LED1, !rt_pin_read(LED1));
rt_thread_mdelay(500);
}
}
/* スレッド2のエントリ関数、OLED画面に情報を表示 */
static void thread2_OLED_entry(void *parameter)
{
// 初期化
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_rtthread_hw_i2c, u8x8_gpio_and_delay_rtthread);
u8g2_InitDisplay(&u8g2);
u8g2_SetPowerSave(&u8g2, 0);
u8g2_InitDisplay(&u8g2);
u8g2_SetPowerSave(&u8g2, 0);
while (1)
{
char String[26];
u8g2_ClearBuffer(&u8g2);
u8g2_SetFont(&u8g2, u8g2_font_wqy15_t_chinese3); // 中文字キャラクタセットを設定
float V0 = AD_Value[0] * VREF / 4096.0;
sprintf(String, "AD0:%d V:%d.%d%d%d", AD_Value[0], (uint8_t) V0, (uint16_t)(V0 * 10.0) % 10,
(uint16_t)(V0 * 100.0) % 100 % 10, (uint16_t)(V0 * 1000.0) % 1000 % 100 % 10); // フォーマット文字列を文字列変数に出力
u8g2_DrawStr(&u8g2, 0, 15, String);
float V1 = AD_Value[1] * VREF / 4096.0;
sprintf(String, "AD1:%d V:%d.%d%d%d", AD_Value[1], (uint8_t) V1, (uint16_t)(V1 * 10.0) % 10,
(uint16_t)(V1 * 100.0) % 100 % 10, (uint16_t)(V1 * 1000.0) % 1000 % 100 % 10); // フォーマット文字列を文字列変数に出力
u8g2_DrawStr(&u8g2, 0, 31, String);
float V2 = AD_Value[2] * VREF / 4096.0;
sprintf(String, "AD2:%d V:%d.%d%d%d", AD_Value[2], (uint8_t) V2, (uint16_t)(V2 * 10.0) % 10,
(uint16_t)(V2 * 100.0) % 100 % 10, (uint16_t)(V2 * 1000.0) % 1000 % 100 % 10); // フォーマット文字列を文字列変数に出力
u8g2_DrawStr(&u8g2, 0, 47, String);
float V3 = AD_Value[3] * VREF / 4096.0;
sprintf(String, "AD3:%d V:%d.%d%d%d", AD_Value[3], (uint8_t) V3, (uint16_t)(V3 * 10.0) % 10,
(uint16_t)(V3 * 100.0) % 100 % 10, (uint16_t)(V3 * 1000.0) % 1000 % 100 % 10); // フォーマット文字列を文字列変数に出力
u8g2_DrawStr(&u8g2, 0, 63, String);
u8g2_SendBuffer(&u8g2); // バッファデータを送信
rt_thread_mdelay(100); // 100ミリ秒遅延
}
}
/* スレッド3のエントリ関数、ADCデータ処理 */
static void thread3_ADC_entry(void *parameter)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); //GPIOAクロックとADCを有効化
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //DMA1クロックを有効化
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //ADCクロック分配設定、6分周(72MHz/6=12MHz)、ADCクロック周波数は14MHzを超えてはならない
GPIO_InitTypeDef GPIO_InitStructure = { 0 }; //GPIO設定用構造体を定義
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; //GPIOピンを設定
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //GPIOモードをアナログ入力に設定
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); //ADCレギュラーグループを設定、レギュラーグループのシーケンス1にチャネル0を書き込み、サンプリング時間239.5サイクル
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); //ADCレギュラーグループを設定、レギュラーグループのシーケンス2にチャネル1を書き込み、サンプリング時間239.5サイクル
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5); //ADCレギュラーグループを設定、レギュラーグループのシーケンス3にチャネル2を書き込み、サンプリング時間239.5サイクル
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5); //ADCレギュラーグループを設定、レギュラーグループのシーケンス4にチャネル3を書き込み、サンプリング時間239.5サイクル
ADC_InitTypeDef ADC_InitStructure = { 0 };
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADCを独立モードに設定
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //マルチチャネルモードでスキャンモードを有効化
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //連続変換モードを有効化
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //変換が外部トリガーで開始されないよう設定、ソフトウェアトリガーで開始
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADCデータを右揃えに設定
ADC_InitStructure.ADC_NbrOfChannel = 4; //レギュラー変換のADCチャネル数
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE); //ADC1を有効化
ADC_ResetCalibration(ADC1); //ADC1キャリブレーションレジスタをリセット
while (ADC_GetResetCalibrationStatus(ADC1))
; //リセットキャリブレーション終了を待つ
ADC_StartCalibration(ADC1); //ADキャリブレーションを開始
while (ADC_GetCalibrationStatus(ADC1))
; //キャリブレーション終了を待つ
DMA_DeInit(DMA1_Channel1); //DMAコントローラをリセット
DMA_InitTypeDef DMA_InitStructure; //DMA設定用構造体を定義
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32) &ADC1->RDATAR; //ペリフェラルアドレスをADCデータレジスタアドレスに設定
DMA_InitStructure.DMA_MemoryBaseAddr = (u32) AD_Value; //メモリアドレスをADC値読み取りアドレスに設定
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //データソースをペリフェラルに設定、DMA転送方式をペリフェラルからメモリに設定
DMA_InitStructure.DMA_BufferSize = 4; //DMAデータバッファサイズを設定
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //DMAペリフェラルインクリメントモードを無効化
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //DMAメモリインクリメントモードを有効化
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //ペリフェラルデータサイズをハーフワード(2バイト)に設定
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //メモリデータサイズをハーフワード(2バイト)に設定
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMAモードを循環転送モードに設定
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA転送チャネル優先度を高に設定、単一DMAチャネル使用時は優先度設定は影響しない
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA転送方式がペリフェラルからメモリのため、メモリ間転送方式を無効化
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //DMA1チャネル1を初期化、ADC1のハードウェアトリガーはDMA1チャネル1に接続されているため、DMA1チャネル1を使用する必要がある
DMA_Cmd(DMA1_Channel1, ENABLE); //DMA1チャネル1を起動
ADC_DMACmd(ADC1, ENABLE); // ADC DMA リクエストを有効化
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 外部トリガーを使用していないため、ソフトウェアでADC変換をトリガー
qpid_init(&qpid_CC); // PID制御データを初期化
qpid_set_lmt(&qpid_CC, 0, 10); // PID制限値を設定
qpid_set_ratio(&qpid_CC, 1, 0.001, 0.1); // 制御比率係数を設定
qpid_init(&qpid_CV); // PID制御データを初期化
qpid_set_lmt(&qpid_CV, 31.0, 100); // PID制限値を設定
qpid_set_ratio(&qpid_CV, 0.35, 0.005, 0.001); // 制御比率係数を設定
qpid_init(&qpid_CW); // PID制御データを初期化
qpid_set_lmt(&qpid_CW, 0.1, 200); // PID制限値を設定
qpid_set_ratio(&qpid_CW, 0.5, 0.003, 0.001); // 制御比率係数を設定
qpid_init(&qpid_CR); // PID制御データを初期化
qpid_set_lmt(&qpid_CR, 0.1, 1000); // PID制限値を設定
qpid_set_ratio(&qpid_CR, 0.35, 0.003, 0.001); // 制御比率係数を設定
while (1)
{
/*rt_kprintf("AD0:%d V:%f\n", AD_Value[0], AD_Value[0] / 4095.0 * VREF);
rt_kprintf("AD1:%d V:%4.3f\n", AD_Value[1], (float) AD_Value[1] / 4095.0 * VREF);
rt_kprintf("AD2:%d V:%4.3f\n", AD_Value[2], AD_Value[2] / 4095.0 * VREF);*/
if (voltage_dw == 0) // 電圧サンプリングレンジが0.0325倍の場合
{
if (YVF_AVG_count < ADC_count) // 15未満のときサンプリング値を累積
{
YVF_SUM += AD_Value[0];
YVF_AVG_count++;
}
if (YVF_AVG_count == ADC_count)
{
YVF = YVF_SUM / YVF_AVG_count * VREF / 4096.0 / 0.0325 * V0_COMP; // 電圧値を計算
YVF_AVG_count = 0;
YVF_SUM = 0;
}
if (YVF <= 31.0) // 電圧が31V未満のとき、レンジを切り替え
{
rt_pin_write(MCU_G0, PIN_LOW);
rt_pin_write(MCU_G1, PIN_HIGH); // 電圧サンプリングレンジ0.0947倍
if (Eload_Out == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5)); // DAC1出力値を設定し、定電圧を制御
}
voltage_dw = 1;
YVF_AVG_count = 0;
YVF_SUM = 0;
qpid_set_lmt(&qpid_CV, 4.6, 33.5); // PID制限値を設定
qpid_set_ratio(&qpid_CV, 0.38, 0.0035, 0.0005); // 制御比率係数を設定
}
}
else if (voltage_dw == 1) // 電圧レンジが0.0947倍の場合
{
if (YVF_AVG_count < ADC_count)
{
YVF_SUM += AD_Value[0];
YVF_AVG_count++;
}
if (YVF_AVG_count == ADC_count)
{
YVF = YVF_SUM / YVF_AVG_count * VREF / 4096.0 / 0.0947 * V1_COMP;
YVF_AVG_count = 0;
YVF_SUM = 0;
}
if (YVF >= 33.5) // 電圧が33.5V以上で0.0325倍レンジに切り替え
{
rt_pin_write(MCU_G0, PIN_LOW);
rt_pin_write(MCU_G1, PIN_LOW);
if (Eload_Out == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.0325 * 4096 / VREF * DAC1_COMP + 0.5)); // DAC1出力値を設定し、定電圧を制御
}
voltage_dw = 0;
YVF_AVG_count = 0;
YVF_SUM = 0;
qpid_set_lmt(&qpid_CV, 31.0, 100); // PID制限値を設定
qpid_set_ratio(&qpid_CV, 0.5, 0.005, 0.0005); // 制御比率係数を設定
}
else if (YVF <= 4.6) // 電圧が4.6V未満のときレンジを切り替え
{
rt_pin_write(MCU_G0, PIN_HIGH);
rt_pin_write(MCU_G1, PIN_LOW);
if (Eload_Out == 1)
{
uint16_t vset_pwm = (uint16_t)(VSET * 0.6175 * 4096 / VREF * DAC1_COMP + 0.5);
if (vset_pwm > 4065)
vset_pwm = 4095;
DAC_SetChannel1Data(DAC_Align_12b_R, vset_pwm); // DAC1出力値を設定し、定電圧を制御
}
voltage_dw = 2;
YVF_AVG_count = 0;
YVF_SUM = 0;
qpid_set_lmt(&qpid_CV, 0.01, 5.1); // PID制限値を設定
qpid_set_ratio(&qpid_CV, 0.26, 0.0025, 0.0005); // 制御比率係数を設定
}
}
else if (voltage_dw == 2) // 電圧レンジが0.6175倍の場合
{
if (YVF_AVG_count < ADC_count)
{
YVF_SUM += AD_Value[0];
YVF_AVG_count++;
}
if (YVF_AVG_count == ADC_count)
{
YVF = YVF_SUM / YVF_AVG_count * VREF / 4096.0 / 0.6175 * V2_COMP;
if (YVF < 0.15)
YVF = 0;
YVF_AVG_count = 0;
YVF_SUM = 0;
}
if (YVF >= 5.1) // 電圧が5.1V以上のとき、レンジを切り替え
{
rt_pin_write(MCU_G0, PIN_LOW);
rt_pin_write(MCU_G1, PIN_HIGH);
if (Eload_Out == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5)); // DAC1出力値を設定し、定電圧を制御
}
voltage_dw = 1;
YVF_AVG_count = 0;
YVF_SUM = 0;
qpid_set_lmt(&qpid_CV, 4.6, 33.5); // PID制限値を設定
qpid_set_ratio(&qpid_CV, 0.38, 0.0035, 0.0005); // 制御比率係数を設定
}
}
if (AVG_count < ADC_count)
{
YIF1_SUM += AD_Value[1]; // MOS管1電流を累積
YIF2_SUM += AD_Value[2]; // MOS管2電流を累積
AVG_count++;
}
if (AVG_count == ADC_count)
{
YIF1 = YIF1_SUM / AVG_count * VREF / 4096.0 / 50 / 0.01 * YIF1_COMP - 0.007;
YIF2 = YIF2_SUM / AVG_count * VREF / 4096.0 / 50 / 0.01 * YIF2_COMP - 0.007;
YIF = YIF1 + YIF2;
if (YIF < 0.008)
YIF = 0;
AVG_count = 0;
YIF1_SUM = 0;
YIF2_SUM = 0;
PID();
}
if (VBAT_count < 5)
{
VBAT_SUM += lowV4(AD_Value[3]); // バッテリ電圧サンプリング値を累積
VBAT_count++;
}
if (VBAT_count == 5)
{
VBAT = VBAT_SUM / VBAT_count * VREF / 4096.0 / 0.3535;
VBAT_count = 0;
VBAT_SUM = 0;
}
rt_thread_mdelay(5);
}
}
// https://blog.zeruns.com/* スレッド4のエントリ関数。シリアル画面からのデータを処理 */
static void thread4_HMI_GetDate_entry(void *parameter)
{
HMILCD_Send("CC.x0.val=0"); // 画面に表示される電流設定値をクリア
HMILCD_Send("CV.x0.val=0"); // 画面に表示される電圧設定値をクリア
HMILCD_Send("CR.x0.val=0"); // 画面に表示される抵抗設定値をクリア
HMILCD_Send("CW.x0.val=0"); // 画面に表示される電力設定値をクリア
while (1)
{
if (Serial3_RxFlag == 1)
{
if (Serial3_RxPacket[0] == 0x01) // 現在はメインメニューページ
{
if (Serial3_RxPacket[1] == 0x10) // 定電流ボタンが押された
{
HMILCD_Send("page CC"); // 定電流モードページへ切り替え
mode = CC; // 現在のモードを定電流モードに設定
}
else if (Serial3_RxPacket[1] == 0x11) // 定電圧ボタンが押された
{
HMILCD_Send("page CV"); // 定電圧ページへ切り替え
mode = CV; // 現在のモードを定電圧モードに設定
}
else if (Serial3_RxPacket[1] == 0x12) // 定抵抗ボタンが押された
{
HMILCD_Send("page CR"); // 定抵抗ページへ切り替え
mode = CR; // 現在のモードを定抵抗モードに設定
}
else if (Serial3_RxPacket[1] == 0x13) // 定電力ボタンが押された
{
HMILCD_Send("page CW"); // 定電力ページへ切り替え
mode = CW; // 現在のモードを定電力モードに設定
}
}
else if (Serial3_RxPacket[0] == 0x02) // 現在は定電流モードページ
{
if (Serial3_RxPacket[1] == 0x10) // メニューボタンが押された
{
HMILCD_Send("CC.t1.txt=\"OFF\""); // 画面右上のタイトル欄にOFFを表示
HMILCD_Send("CC.b1.txt=\"開啓\""); // 画面右下のボタンに「開啓」を表示
Eload_Out = 0; // 負荷出力状態をオフに設定
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力で定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力で定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
HMILCD_Send("page menu"); // メニューページへ切り替え
mode = menu;
}
else if (Serial3_RxPacket[1] == 0x11) // 開啓ボタンが押され、現在の負荷出力状態がオフ
{
Key_ONOFF = 1;
}
}
else if (Serial3_RxPacket[0] == 0x03) // 現在は定電圧モードページ
{
if (Serial3_RxPacket[1] == 0x10) // メニューボタンが押された
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"開啓\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力で定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力で定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
HMILCD_Send("page menu");
mode = menu;
}
if (Serial3_RxPacket[1] == 0x11) // 開啓ボタンが押され、現在の負荷出力状態がオフ
{
Key_ONOFF = 1;
}
}
else if (Serial3_RxPacket[0] == 0x04) // 現在は定抵抗モードページ
{
if (Serial3_RxPacket[1] == 0x10) // メニューボタンが押された
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"開啓\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力で定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力で定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
HMILCD_Send("page menu");
mode = menu;
}
if (Serial3_RxPacket[1] == 0x11) // 開啓ボタンが押され、現在の負荷出力状態がオフ
{
Key_ONOFF = 1;
}
}
else if (Serial3_RxPacket[0] == 0x05) // 現在は定電力モードページ
{
if (Serial3_RxPacket[1] == 0x10) // メニューボタンが押された
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"開啓\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力で定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力で定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
HMILCD_Send("page menu");
mode = menu;
}
if (Serial3_RxPacket[1] == 0x11) // 開啓ボタンが押され、現在の負荷出力状態がオフ
{
Key_ONOFF = 1;
}
}
else if (Serial3_RxPacket[0] == 0xAA) // 現在は数字キーボードページ
{
char *temp = Serial3_RxPacket;
temp++; // アドレスを1増加
uint16_t temp2 = atoi(temp); // 文字列を整数に変換
if (mode == CC)
{
if (temp2 > 1000)
temp2 = 1000;
ISET = temp2 / 100.0;
HMILCD_Send("CC.x0.val=%d", temp2);
if (Eload_Out == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
if (ISET <= 2.5)
{
// DAC2出力値を設定し定電流を制御、+0.5は四捨五入用
DAC_SetChannel2Data(DAC_Align_12b_R,
(uint16_t)(ISET * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4(0); // IREF2を設定
}
else
{
// DAC2出力値を設定し定電流を制御、+0.5は四捨五入用
DAC_SetChannel2Data(DAC_Align_12b_R,
(uint16_t)(ISET / 2.0 * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4((uint16_t)(ISET / 2.0 * 0.01 * 50 / VCC * 50000 * IREF2_COMP + 0.5)); // IREF2を設定
}
}
}
else if (mode == CV)
{
VSET = temp2 / 100.0;
HMILCD_Send("CV.x0.val=%d", temp2);
if (Eload_Out == 1)
{
DAC_SetChannel2Data(DAC_Align_12b_R, 4095); // IREF1を設定
PWM_SetCCR4(50000); // IREF2を設定
if (voltage_dw == 0)
{
DAC_SetChannel1Data(DAC_Align_12b_R,
(uint16_t)(VSET * 0.0325 * 4096 / VREF * DAC1_COMP + 0.5)); // DAC1出力値を設定し定電圧を制御
}
else if (voltage_dw == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R,
(uint16_t)(VSET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5)); // DAC1出力値を設定し定電圧を制御
}
else if (voltage_dw == 2)
{
uint16_t vset_pwm = (uint16_t)(VSET * 0.6175 * 4096 / VREF * DAC1_COMP + 0.5);
if (vset_pwm > 4065)
vset_pwm = 4095;
DAC_SetChannel1Data(DAC_Align_12b_R, vset_pwm); // DAC1出力値を設定し定電圧を制御
}
}
}
else if (mode == CR)
{
RSET = temp2 / 100.0;
HMILCD_Send("CR.x0.val=%d", temp2);
//CR_mode();
}
else if (mode == CW)
{
PSET = temp2 / 100.0;
HMILCD_Send("CW.x0.val=%d", temp2);
//CW_mode();
}
}
Serial3_RxFlag = 0;
}
key123();
rt_thread_mdelay(35);
}
}/*恒功率モード*/
void CW_mode(void)
{
double Ptemp = PSET / YVF;
if (Ptemp > 10)
Ptemp = 10;
if (Eload_Out == 1)
{
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入のため
DAC_SetChannel2Data(DAC_Align_12b_R, (uint16_t)(Ptemp / 2.0 * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VCC * 50000 * IREF2_COMP + 0.5)); // IREF2を設定
}
else
{
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
}
}
/*恒抵抗モード*/
void CR_mode(void)
{
double Rtemp = YVF / RSET;
if (Rtemp > 10)
Rtemp = 10;
if (Eload_Out == 1)
{
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入のため
DAC_SetChannel2Data(DAC_Align_12b_R, (uint16_t)(Rtemp / 2.0 * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VCC * 50000 * IREF2_COMP + 0.5)); // IREF2を設定
}
else
{
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
}
}
/* PID制御 */
void PID(void)
{
double Ptemp, Rtemp;
if (mode == CC && Eload_Out == 1)
{
qpid_set_dst(&qpid_CC, ISET); // PID目標値を設定
I_SET = qpid_cal_pos(&qpid_CC, YIF); // PID計算
if (ISET <= 2.5)
{
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入のため
DAC_SetChannel2Data(DAC_Align_12b_R, (uint16_t)(I_SET * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
}
else
{
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入のため
DAC_SetChannel2Data(DAC_Align_12b_R, (uint16_t)(I_SET / 2.0 * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4((uint16_t)(I_SET / 2.0 * 0.01 * 50 / VCC * 50000 * IREF2_COMP + 0.5)); // IREF2を設定
}
}
if (mode == CV && Eload_Out == 1)
{
qpid_set_dst(&qpid_CV, VSET); // PID目標値を設定
V_SET = qpid_cal_pos(&qpid_CV, YVF); // PID計算
if (voltage_dw == 0)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(V_SET * 0.0325 * 4096 / VREF * DAC1_COMP + 0.5));
// DAC1出力値を設定し、定電圧を制御
}
else if (voltage_dw == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(V_SET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5));
// DAC1出力値を設定し、定電圧を制御
}
else if (voltage_dw == 2)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(V_SET * 0.6175 * 4096 / VREF * DAC1_COMP + 0.5));
// DAC1出力値を設定し、定電圧を制御
}
}
if (mode == CW && Eload_Out == 1)
{
qpid_set_dst(&qpid_CW, PSET); // PID目標値を設定
P_SET = qpid_cal_pos(&qpid_CW, YIF * YVF); // PID計算
Ptemp = P_SET / YVF;
if (Ptemp > 10)
Ptemp = 10;
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入のため
DAC_SetChannel2Data(DAC_Align_12b_R, (uint16_t)(Ptemp / 2.0 * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4((uint16_t)(Ptemp / 2.0 * 0.01 * 50 / VCC * 50000 * IREF2_COMP + 0.5)); // IREF2を設定
}
if (mode == CR && Eload_Out == 1)
{
qpid_set_dst(&qpid_CR, RSET); // PID目標値を設定
R_SET = qpid_cal_pos(&qpid_CR, YVF / YIF); // PID計算
Rtemp = YVF / R_SET;
if (Rtemp > 10)
Rtemp = 10;
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入のため
DAC_SetChannel2Data(DAC_Align_12b_R, (uint16_t)(Rtemp / 2.0 * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4((uint16_t)(Rtemp / 2.0 * 0.01 * 50 / VCC * 50000 * IREF2_COMP + 0.5)); // IREF2を設定
}
}
/* キー処理関数 */
void key123(void)
{
if (key[2] == 1) // キー2、切替キー
{
if (mode == menu)
{
HMILCD_Send("page CC"); // 定電流モードページに切替
mode = CC; // 現在のモードを定電流モードに設定
}
else if (mode == CC)
{
if (Eload_Out == 1)
{
HMILCD_Send("CC.t1.txt=\"OFF\"");
HMILCD_Send("CC.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
}
HMILCD_Send("page CV"); // 定電圧モードページに切替
mode = CV; // 現在のモードを定電圧モードに設定
}
else if (mode == CV)
{
if (Eload_Out == 1)
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
}
HMILCD_Send("page CR"); // 定抵抗モードページに切替
mode = CR; // 現在のモードを定抵抗モードに設定
}
else if (mode == CR)
{
if (Eload_Out == 1)
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
}
HMILCD_Send("page CW"); // 定電力モードページに切替
mode = CW; // 現在のモードを定電力モードに設定
}
else if (mode == CW)
{
if (Eload_Out == 1)
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
}
HMILCD_Send("page menu"); // メニューページに切替
mode = menu; // 現在のモードをメニューモードに設定
}
key[2] = 0;
}
if (key[3] == 1) // キー3、メニューキー
{
if (Eload_Out == 1)
Key_ONOFF = 1;
rt_thread_mdelay(15);
HMILCD_Send("page menu"); // メニューページに切替
mode = menu; // 現在のモードをメニューモードに設定
key[3] = 0;
}
}/* スレッド5のエントリ関数、電子負荷のON/OFFボタン処理 */
static void thread5_ONOFF_entry(void *parameter)
{
/* LED2ピンを出力モードに設定 */
rt_pin_mode(LED2, PIN_MODE_OUTPUT);
/* デフォルトでハイレベル */
rt_pin_write(LED2, PIN_HIGH);
while (1)
{
if (Key_ONOFF == 1 | key[1] == 1) // 起動ボタンが押された
{
if (mode == CC) // 定電流モード
{
if (Eload_Out == 0) // 現在の負荷出力状態がOFFの場合
{
HMILCD_Send("CC.t1.txt=\"ON\""); // 画面右上のタイトルボックスにONを表示
HMILCD_Send("CC.b1.txt=\"关闭\""); // 画面右下のボタンに「关闭」と表示
Eload_Out = 1; // 負荷出力状態をONに設定
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
if (ISET <= 2.5)
{
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入用
DAC_SetChannel2Data(DAC_Align_12b_R,
(uint16_t)(ISET * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4(0); // IREF2を設定
}
else
{
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入用
DAC_SetChannel2Data(DAC_Align_12b_R,
(uint16_t)(ISET / 2.0 * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4((uint16_t)(ISET / 2.0 * 0.01 * 50 / VCC * 50000 * IREF2_COMP + 0.5)); // IREF2を設定
}
}
else if (Eload_Out == 1) // 現在の負荷出力状態がONの場合
{
HMILCD_Send("CC.t1.txt=\"OFF\"");
HMILCD_Send("CC.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1をハイレベル出力、定電圧をOFF
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2をローレベル出力、定電流をOFF
PWM_SetCCR4(0); // IREF2を設定
}
}
else if (mode == CV) // 定電圧モード
{
if (Eload_Out == 0) // 現在の負荷出力状態がOFFの場合
{
HMILCD_Send("CV.t1.txt=\"ON\"");
HMILCD_Send("CV.b1.txt=\"关闭\"");
Eload_Out = 1;
DAC_SetChannel2Data(DAC_Align_12b_R, 4095); // IREF1を設定
PWM_SetCCR4(50000); // IREF2を設定
if (voltage_dw == 0)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.0325 * 4096 / VREF * DAC1_COMP + 0.5));
// DAC1出力値を設定し、定電圧を制御
}
else if (voltage_dw == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5));
// DAC1出力値を設定し、定電圧を制御
}
else if (voltage_dw == 2)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.6175 * 4096 / VREF * DAC1_COMP + 0.5));
// DAC1出力値を設定し、定電圧を制御
}
}
else if (Eload_Out == 1) // 現在の負荷出力状態がONの場合
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1をハイレベル出力、定電圧をOFF
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2をローレベル出力、定電流をOFF
PWM_SetCCR4(0); // IREF2を設定
}
}
else if (mode == CR) // 定抵抗モード
{
if (Eload_Out == 0) // 現在の負荷出力状態がOFFの場合
{
HMILCD_Send("CR.t1.txt=\"ON\"");
HMILCD_Send("CR.b1.txt=\"关闭\"");
Eload_Out = 1;
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
CR_mode();
}
else if (Eload_Out == 1) // 現在の負荷出力状態がONの場合
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1をハイレベル出力、定電圧をOFF
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2をローレベル出力、定電流をOFF
PWM_SetCCR4(0); // IREF2を設定
}
}
else if (mode == CW) // 定電力モード
{
if (Eload_Out == 0) // 現在の負荷出力状態がOFFの場合
{
HMILCD_Send("CW.t1.txt=\"ON\"");
HMILCD_Send("CW.b1.txt=\"关闭\"");
Eload_Out = 1;
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
CW_mode();
}
else if (Eload_Out == 1) // 現在の負荷出力状態がONの場合
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1をハイレベル出力、定電圧をOFF
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2をローレベル出力、定電流をOFF
PWM_SetCCR4(0); // IREF2を設定
}
}
Key_ONOFF = 0;
key[1] = 0;
}
if (Eload_Out == 0)
{
rt_pin_write(LED2, SET);
}
else if (Eload_Out == 1)
{
rt_pin_write(LED2, RESET);
}
rt_thread_mdelay(35);
}
}
/* スレッド7のエントリ関数、シリアル画面へのパラメータ表示 */
static void thread7_HMI_Display_entry(void *parameter)
{
while (1)
{
if (mode != menu)
{
double V = lowV1(YVF);
double I = lowV2(YIF);
HMILCD_Send("x1.val=%d", (uint16_t)(V * 100)); // 電圧を表示
HMILCD_Send("x2.val=%d", (uint16_t)(I * 1000)); // 電流を表示
HMILCD_Send("x3.val=%d", (uint32_t)(I * V * 100)); // 電力を表示
HMILCD_Send("x4.val=%d", (uint32_t)(V / I * 100)); // 抵抗を表示
HMILCD_Send("x5.val=%d", (uint32_t)(VBAT * 100)); // 抵抗を表示
}
rt_thread_mdelay(50);
}
}
/* スレッド8のエントリ関数、冷却ファン制御 */
static void thread8_FAN_entry(void *parameter)
{
while (1)
{
uint16_t P = (uint16_t)(YIF * YVF + 0.5);
if (P >= 13) // 電力が13W以上の場合ファンを起動
{
FAN_PWM_ON();
if (P < 20)
{
FAN_PWM_SetCCR(20); // ファン制御デューティ比20%
}
else if (P >= 20 && P < 25)
{
FAN_PWM_SetCCR(30);
}
else if (P >= 25 && P < 30)
{
FAN_PWM_SetCCR(40);
}
else if (P >= 30 && P < 35)
{
FAN_PWM_SetCCR(50);
}
else if (P >= 35 && P < 40)
{
FAN_PWM_SetCCR(60);
}
else if (P >= 40 && P < 45)
{
FAN_PWM_SetCCR(70);
}
else if (P >= 45 && P < 50)
{
FAN_PWM_SetCCR(80);
}
else if (P >= 50 && P < 60)
{
FAN_PWM_SetCCR(90);
}
else if (P >= 60)
{
FAN_PWM_SetCCR(100);
}
}
else if (P <= 8)
{
FAN_PWM_SetCCR(0); // ファンのPWM出力をOFF、ローレベル出力
FAN_PWM_OFF();
}
rt_thread_mdelay(200);
}
}
/* スレッド9のエントリ関数、定電力・定抵抗モード制御 */
static void thread9_CWCR_entry(void *parameter)
{
while (1)
{
if (mode == CW)
{
CW_mode();
}
if (mode == CR)
{
CR_mode();
}
rt_thread_mdelay(50);
}
}/* スレッド10のエントリ関数、Bluetoothからのデータを処理 */
static void thread10_BlueTooth_entry(void *parameter)
{
HMILCD_Send("CC.x0.val=0"); // 画面に表示される電流設定値をクリア
HMILCD_Send("CV.x0.val=0"); // 画面に表示される電圧設定値をクリア
HMILCD_Send("CR.x0.val=0"); // 画面に表示される抵抗設定値をクリア
HMILCD_Send("CW.x0.val=0"); // 画面に表示される電力設定値をクリア
while (1)
{
if (Serial5_RxFlag == 1)
{
if (Serial5_RxPacket[0] == 0x01) // 現在はメインメニューページ
{
if (Serial5_RxPacket[1] == 0x10) // 定電流ボタンが押された
{
if (mode == CV)
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"开启\"");
}
else if (mode == CR)
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"开启\"");
}
else if (mode == CW)
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"开启\"");
}
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
Eload_Out = 0;
HMILCD_Send("page CC"); // 定電流モードページに切り替え
mode = CC; // 現在のモードを定電流モードに設定
}
else if (Serial5_RxPacket[1] == 0x11) // 定電圧ボタンが押された
{
if (mode == CC)
{
HMILCD_Send("CC.t1.txt=\"OFF\"");
HMILCD_Send("CC.b1.txt=\"开启\"");
}
else if (mode == CR)
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"开启\"");
}
else if (mode == CW)
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"开启\"");
}
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
Eload_Out = 0;
HMILCD_Send("page CV"); // 定電圧ページに切り替え
mode = CV; // 現在のモードを定電圧モードに設定
}
else if (Serial5_RxPacket[1] == 0x12) // 定抵抗ボタンが押された
{
if (mode == CC)
{
HMILCD_Send("CC.t1.txt=\"OFF\"");
HMILCD_Send("CC.b1.txt=\"开启\"");
}
else if (mode == CV)
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"开启\"");
}
else if (mode == CW)
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"开启\"");
}
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
Eload_Out = 0;
HMILCD_Send("page CR"); // 定抵抗ページに切り替え
mode = CR; // 現在のモードを定抵抗モードに設定
}
else if (Serial5_RxPacket[1] == 0x13) // 定電力ボタンが押された
{
if (mode == CV)
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"开启\"");
}
else if (mode == CR)
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"开启\"");
}
else if (mode == CC)
{
HMILCD_Send("CC.t1.txt=\"OFF\"");
HMILCD_Send("CC.b1.txt=\"开启\"");
}
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
Eload_Out = 0;
HMILCD_Send("page CW"); // 定電力ページに切り替え
mode = CW; // 現在のモードを定電力モードに設定
}
}
else if (Serial5_RxPacket[0] == 0x08) // メニューに戻る
{
if (mode == CC)
{
HMILCD_Send("CC.t1.txt=\"OFF\""); // 画面右上のタイトルボックスにOFFを表示
HMILCD_Send("CC.b1.txt=\"开启\""); // 画面右下のボタンに开启を表示
Eload_Out = 0; // 負荷出力状態をオフに設定
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
HMILCD_Send("page menu"); // メニューページに切り替え
mode = menu;
}
else if (mode == CV)
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
HMILCD_Send("page menu");
mode = menu;
}
else if (mode == CR)
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
HMILCD_Send("page menu");
mode = menu;
}
else if (mode == CW)
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1はハイレベル出力、定電圧をオフ
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2はローレベル出力、定電流をオフ
PWM_SetCCR4(0); // IREF2を設定
HMILCD_Send("page menu");
mode = menu;
}
}
else if (Serial5_RxPacket[0] == 0x09) // オンボタン
{
Key_ONOFF = 1;
}
else if (Serial5_RxPacket[0] == 0xAA) // 現在は数字キーボードページ
{
char *temp = Serial5_RxPacket;
temp++; // アドレスを1増加
uint16_t temp2 = atoi(temp); // 文字列を整数に変換
if (mode == CC)
{
if (temp2 > 1000)
temp2 = 1000;
ISET = temp2 / 100.0;
HMILCD_Send("CC.x0.val=%d", temp2);if (Eload_Out == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
if (ISET <= 2.5)
{
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入用
DAC_SetChannel2Data(DAC_Align_12b_R,
(uint16_t)(ISET * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4(0); // IREF2を設定
}
else
{
// DAC2出力値を設定し、定電流を制御、+0.5は四捨五入用
DAC_SetChannel2Data(DAC_Align_12b_R,
(uint16_t)(ISET / 2.0 * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4((uint16_t)(ISET / 2.0 * 0.01 * 50 / VCC * 50000 * IREF2_COMP + 0.5)); // IREF2を設定
}
}
}
else if (mode == CV)
{
VSET = temp2 / 100.0;
HMILCD_Send("CV.x0.val=%d", temp2);
if (Eload_Out == 1)
{
DAC_SetChannel2Data(DAC_Align_12b_R, 4095); // IREF1を設定
PWM_SetCCR4(50000); // IREF2を設定
if (voltage_dw == 0)
{
DAC_SetChannel1Data(DAC_Align_12b_R,
(uint16_t)(VSET * 0.0325 * 4096 / VREF * DAC1_COMP + 0.5)); // DAC1出力値を設定し、定電圧を制御
}
else if (voltage_dw == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R,
(uint16_t)(VSET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5)); // DAC1出力値を設定し、定電圧を制御
}
else if (voltage_dw == 2)
{
uint16_t vset_pwm = (uint16_t)(VSET * 0.6175 * 4096 / VREF * DAC1_COMP + 0.5);
if (vset_pwm > 4065)
vset_pwm = 4095;
DAC_SetChannel1Data(DAC_Align_12b_R, vset_pwm); // DAC1出力値を設定し、定電圧を制御
}
}
}
else if (mode == CR)
{
RSET = temp2 / 100.0;
HMILCD_Send("CR.x0.val=%d", temp2);
CR_mode();
}
else if (mode == CW)
{
PSET = temp2 / 100.0;
HMILCD_Send("CW.x0.val=%d", temp2);
CW_mode();
}
}
Serial5_RxFlag = 0;
}
rt_thread_mdelay(40);
}
}
おすすめ記事
- 高コスパで安い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
- 2.5Gスイッチ TL-SE2109 簡易開封レビュー、8ポート2.5G電気ポート+1ポート10G光ポート(SFP+):https://blog.zeruns.com/archives/780.html















