Open-source Intelligent Electronic Load Based on CH32V307VCT6, Including Schematics, PCB, Source Code, and Project Report.
2023 Embedded-Chip & System-Design Contest – Application Track, National Second Prize.
Built in half a month by porting the previous Holtek electronic-load firmware to the CH32 MCU, adding RT-Thread, and tidying up the code—rushed job, nothing fancy, no harsh comments please.
Demo video: https://www.bilibili.com/video/BV1Zu4y1m7Zd/
Holtek HT32F52352 version & “Holtek Cup” files: https://blog.zeruns.com/archives/784.html
This project is shared for study only—replication not recommended; far better electronic-load designs already exist on LCSC Open-Source Platform!
Project page on LCSC: https://url.zeruns.com/Et4x4
MCU/Electronics QQ group: 2169025065
Free CH32 dev-board samples from WCH: https://url.zeruns.com/h9a99
What Is an Electronic Load?
An electronic load simulates real-world loading to test power supplies or circuits. Compared with bulky power resistors or heater wire, it offers adjustable parameters and convenience. Whether for professional development or hobbyists, an electronic load is essential gear.
By input type: AC or DC.
By mode: constant-current, constant-voltage, constant-resistance, constant-power.
Most supplies are DC and constant-voltage, so CC mode is the most common.
Control can be analog or digital; digital (microcontroller-based) gives intuitive adjustment, rich features, easy expansion, and automated testing.
Project Overview
MCU: CH32V307VCT6 (WCH)
Power: one 18650 Li-ion cell, portable.
Control method: MCU DAC generates a reference voltage; op-amp compares it with amplified current/voltage sample and drives the MOSFET for CC/CV regulation.
Touch screen: Taojingchi 2.8" serial TFT, model TJC3224T028_011R.
Heatsink: 2U server cooler for LGA1356/1366, side-blow.
IDE: RT-Thread Studio.
EDA: EasyEDA (LCSC).
Specs: 100 V, 10 A, 200 W max.
Photos
Only a few shots—see the demo video.
Downloads
Package includes: schematics, EasyEDA project, Gerber, source code, HMI project, datasheets.
123 Pan (no speed limit): https://www.123pan.com/ps/2Y9Djv-6NevH.html
Baidu Netdisk: https://pan.baidu.com/s/17YSlBZ6F1M18k7JGa7FlVA?pwd=buxx code: buxx
Part Purchase Links
- CH32V307VCT6 MCU: https://s.click.taobao.com/T8MSZot
- CH32V307 dev board: https://s.click.taobao.com/2JBSZot
- INA199A1 current-sense amp: https://s.click.taobao.com/XLuweot
- 0805 resistor sample book: https://s.click.taobao.com/p8YSGpt
- 0805 capacitor sample book: https://u.jd.com/9uvZoBd
- XL1509 buck chip: https://s.click.taobao.com/DOcRZot
- Serial TFT: https://s.click.taobao.com/pyzleot
Buy parts from LCSC商城: https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html
Schematics
Power Board
Supply Board
Control Board
PCBs
Power Board
Top
Bottom
Supply Board
Top
Bottom
Control Board
Top
Bottom
More Open-Source Projects- Open-sourced a three-phase power collector for easy home electricity monitoring: https://blog.zeruns.com/archives/771.html
- STM32F407 standard-library project template with U8g2 graphics library ported: https://blog.zeruns.com/archives/722.html
- Qinheng CH32V307VCT6 minimum system board open-sourced: https://blog.zeruns.com/archives/726.html
- LM25118 auto buck-boost adjustable DCDC power module: https://blog.zeruns.com/archives/727.html
- EG1164 high-power synchronous-rectification boost module open-sourced, up to 97% efficiency: https://blog.zeruns.com/archives/730.html
- 4G environmental monitoring node based on Air700E (temperature, humidity, barometric pressure, etc.) uploads to Alibaba Cloud IoT via MQTT: https://blog.zeruns.com/archives/747.html
Main Code
main.c file
/********************************** (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"
/* Global typedef */
/* Global define */
#define WDT_DEVICE_NAME "wdt" /* Watchdog device name */
static rt_device_t wdg_dev; /* Watchdog device handle */
/* ADC reference voltage */
#define VREF 3.3
/* Power supply voltage */
#define VCC 3.3
/* Compensation calibration data */
#define V0_COMP 1.000 // 0.0325× voltage range
#define V1_COMP 1.000 // 0.0947× voltage range
#define V2_COMP 1.000 // 0.6175× voltage range
#define YIF1_COMP 1.00 // MOS 1 current-sampling compensation
#define YIF2_COMP 1.00 // MOS 2 current-sampling compensation
#define DAC1_COMP 1.00 // DAC1(VREF) output compensation factor
#define IREF2_COMP 1.00 // IREF2 output compensation factor
#define DAC2_COMP 1.00 // DAC2(IREF1) output compensation
/* ADC sampling average count */
#define ADC_count 3
/* First-order low-pass filter coefficient */
#define dPower1 0.5
/* Pin numbers, determined by checking 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
/* Global Variable */
u8g2_t u8g2; // u8g2 structure variable
rt_uint16_t AD_Value[4]; // ADC sampled data
// Define mode-page enumeration
enum mode_type
{
menu = 0, // Menu
CC, // Constant Current
CV, // Constant Voltage
CR, // Constant Resistance
CW // Constant Power
};
volatile uint8_t Eload_Out = 0; // Electronic load output on/off state
volatile uint8_t mode = menu; // Current mode
volatile uint8_t voltage_dw = 0; // Voltage sampling range: 0=0.0325×, 2=0.6175×, 1=0.0947×
volatile double YVF, YIF1, YIF2, YIF, VBAT; // Present voltage & current
volatile double ISET, VSET, RSET, PSET; // Current, voltage, resistance, power set values
volatile uint32_t YVF_SUM, YIF1_SUM, YIF2_SUM, VBAT_SUM; // Sums for averaging
volatile uint8_t AVG_count = 0; // Current average counter
volatile uint8_t YVF_AVG_count = 0; // Voltage average counter
volatile uint8_t VBAT_count = 0; // Battery-voltage average counter
volatile uint8_t Key_ONOFF = 0; // ON/OFF key pressed state
static qpid_t qpid_CC; // PID control data pointer
static qpid_t qpid_CV; // PID control data pointer
static qpid_t qpid_CR; // PID control data pointer
static qpid_t qpid_CW; // PID control data pointer
static double I_SET, V_SET, R_SET, P_SET;
/* Function declarations */
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 Main program.
*
* @return none
*/
int main(void)
{
rt_kprintf("MCU: CH32V307\n");
rt_kprintf("SysClk: %dHz\n", SystemCoreClock);
SYS_Init();
while (1)
{
// Over-current & over-power protection
if (YIF > 10 | YVF * YIF > 300)
{
if (Eload_Out == 1)
{
Key_ONOFF = 1;
}
}
rt_thread_mdelay(20);
}
}
/* System initialization */
void SYS_Init(void)
{
IWDG_Init(); // Initialize watchdog
UART_Init(); // Initialize UART3
HMILCD_Send("page 0"); // Switch to startup page
Dac_Init(); // Initialize DAC
PWM_Init(); // Initialize PWM
rt_pin_mode(MCU_G0, PIN_MODE_OUTPUT); // Set IO as output
rt_pin_mode(MCU_G1, PIN_MODE_OUTPUT);
rt_pin_write(MCU_G0, PIN_LOW); // Output low
rt_pin_write(MCU_G1, PIN_LOW);
Thread_Init(); // Create threads
}
/* Thread initialization */
void Thread_Init(void)
{
rt_thread_t tid = NULL; //Define a thread control block pointer
/* Create thread */
tid = rt_thread_create("SYS_LED", thread1_sysLED_entry, NULL, 512, 30, 5);
//Create a thread named SYS_LED, entry function thread1_sysLED_entry, parameter NULL, stack 256 bytes, priority 30, time slice 5 ticks
if (tid != RT_NULL) // Check if thread created successfully
{
if (rt_thread_startup(tid) == RT_EOK) // Start thread
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) // Start thread
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) // Start thread
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) // Start thread
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) // Start thread
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) // Start thread
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) // Start thread
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) // Start thread
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) // Start thread
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) // Start thread
LOG_D("thread10 BlueTooth create success");
}
else
{
LOG_E("thread10 BlueTooth create failed...");
}
}/* First-order low-pass filter
* Return value: iData is the filtered sample value */
double lowV1(double com1)
{
static double iLastData1; // Previous value
double iData1; // Current calculated value
iData1 = (com1 * dPower1) + (1 - dPower1) * iLastData1; // Calculate
iLastData1 = iData1; // Store current data
return iData1; // Return data
}
double lowV2(double com1)
{
static double iLastData2; // Previous value
double iData1; // Current calculated value
iData1 = (com1 * dPower1) + (1 - dPower1) * iLastData2; // Calculate
iLastData2 = iData1; // Store current data
return iData1; // Return data
}
u16 lowV3(u16 com1)
{
static u16 iLastData3; // Previous value
u16 iData1; // Current calculated value
iData1 = (com1 * dPower1) + (1 - dPower1) * iLastData3; // Calculate
iLastData3 = iData1; // Store current data
return iData1; // Return data
}
u16 lowV4(u16 com1)
{
static u16 iLastData3; // Previous value
u16 iData1; // Current calculated value
iData1 = (com1 * 0.1) + (1 - 0.1) * iLastData3; // Calculate
iLastData3 = iData1; // Store current data
return iData1; // Return data
}
static void idle_hook(void)
{
/* Feed the watchdog in the idle thread callback */
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; /* Overflow time in seconds */
/* Find watchdog device by name and get device handle */
wdg_dev = rt_device_find(WDT_DEVICE_NAME);
if (!wdg_dev)
{
rt_kprintf("find %s failed!\n", WDT_DEVICE_NAME);
return RT_ERROR;
}
/* Initialize device */
rt_device_init(wdg_dev);
/* Set watchdog timeout */
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;
}
/* Start watchdog */
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;
}
/* Set idle thread callback function */
rt_thread_idle_sethook(idle_hook);
return ret;
}
/* Thread 1 entry function: system status LED blinking */
static void thread1_sysLED_entry(void *parameter)
{
/* Configure LED1 pin as output */
rt_pin_mode(LED1, PIN_MODE_OUTPUT);
/* Default low level */
rt_pin_write(LED1, PIN_LOW);
while (1)
{
/* Thread 1 runs at low priority, keeps blinking LED1 */
rt_pin_write(LED1, !rt_pin_read(LED1));
rt_thread_mdelay(500);
}
}
/* Thread 2 entry function: OLED display information */
static void thread2_OLED_entry(void *parameter)
{
// Initialization
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); // Set Chinese character set
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); // Format string output to string variable
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); // Format string output to string variable
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); // Format string output to string variable
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); // Format string output to string variable
u8g2_DrawStr(&u8g2, 0, 63, String);
u8g2_SendBuffer(&u8g2); // Send buffer data
rt_thread_mdelay(100); // Delay 100 milliseconds
}
}
/* Thread 3 entry function: ADC data processing */
static void thread3_ADC_entry(void *parameter)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); // Enable GPIOA and ADC clocks
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // Enable DMA1 clock
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC clock division configuration, 6 division (72MHz/6=12MHz), ADC clock must not exceed 14MHz
GPIO_InitTypeDef GPIO_InitStructure = { 0 }; // Define structure to configure GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; // Set GPIO pins
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // GPIO mode is analog input
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); // Configure ADC regular group, write channel 0 to sequence 1 of regular group, sampling time 55.5 cycles
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); // Configure ADC regular group, write channel 1 to sequence 2 of regular group, sampling time 55.5 cycles
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5); // Configure ADC regular group, write channel 2 to sequence 3 of regular group, sampling time 55.5 cycles
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5); // Configure ADC regular group, write channel 3 to sequence 4 of regular group, sampling time 55.5 cycles
ADC_InitTypeDef ADC_InitStructure = { 0 };
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // Configure ADC as independent mode
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // Enable scan mode in multi-channel mode
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // Enable continuous conversion mode
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // Set conversion not triggered externally, software trigger
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // Set ADC data right-aligned
ADC_InitStructure.ADC_NbrOfChannel = 4; // Number of ADC channels for regular conversion
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE); // Enable ADC1
ADC_ResetCalibration(ADC1); // Reset ADC1 calibration registers.
while (ADC_GetResetCalibrationStatus(ADC1))
; // Wait for reset calibration to complete
ADC_StartCalibration(ADC1); // Start ADC calibration
while (ADC_GetCalibrationStatus(ADC1))
; // Wait for calibration to complete
DMA_DeInit(DMA1_Channel1); // Reset DMA controller
DMA_InitTypeDef DMA_InitStructure; // Define structure to configure DMA
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32) &ADC1->RDATAR; // Configure peripheral address as ADC data register address
DMA_InitStructure.DMA_MemoryBaseAddr = (u32) AD_Value; // Configure memory address as ADC value read address
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // Configure data source as peripheral, DMA transfer mode is peripheral to memory
DMA_InitStructure.DMA_BufferSize = 4; // Set DMA data buffer size
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Disable DMA peripheral increment mode
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Enable DMA memory increment mode
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // Set peripheral data size as half-word (2 bytes)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // Set memory data size as half-word (2 bytes)
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Set DMA mode as circular transfer
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // Set DMA transfer channel priority as high, priority setting has no effect when using one DMA channel
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // Since DMA transfer mode is peripheral to memory, disable memory-to-memory transfer
DMA_Init(DMA1_Channel1, &DMA_InitStructure); // Initialize DMA1 channel 1, ADC1 hardware trigger is connected to DMA1 channel 1, so DMA1 channel 1 must be used
DMA_Cmd(DMA1_Channel1, ENABLE); // Start DMA1 channel 1
ADC_DMACmd(ADC1, ENABLE); // Enable ADC DMA request
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // Since external trigger is not used, start ADC conversion with software trigger
qpid_init(&qpid_CC); // Initialize PID control data
qpid_set_lmt(&qpid_CC, 0, 10); // Set PID limits
qpid_set_ratio(&qpid_CC, 1, 0.001, 0.1); // Set control ratio coefficients
qpid_init(&qpid_CV); // Initialize PID control data
qpid_set_lmt(&qpid_CV, 31.0, 100); // Set PID limits
qpid_set_ratio(&qpid_CV, 0.35, 0.005, 0.001); // Set control ratio coefficientsqpid_init(&qpid_CW); // Initialize PID control data
qpid_set_lmt(&qpid_CW, 0.1, 200); // Set PID limits
qpid_set_ratio(&qpid_CW, 0.5, 0.003, 0.001); // Set control ratio coefficients
qpid_init(&qpid_CR); // Initialize PID control data
qpid_set_lmt(&qpid_CR, 0.1, 1000); // Set PID limits
qpid_set_ratio(&qpid_CR, 0.35, 0.003, 0.001); // Set control ratio coefficients
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) // When voltage sampling range is 0.0325x
{
if (YVF_AVG_count < ADC_count) // Accumulate samples when less than 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; // Calculate voltage value
YVF_AVG_count = 0;
YVF_SUM = 0;
}
if (YVF <= 31.0) // Switch range when voltage is less than 31V
{
rt_pin_write(MCU_G0, PIN_LOW);
rt_pin_write(MCU_G1, PIN_HIGH); // Voltage sampling range 0.0947x
if (Eload_Out == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5)); // Set DAC1 output value for constant voltage control
}
voltage_dw = 1;
YVF_AVG_count = 0;
YVF_SUM = 0;
qpid_set_lmt(&qpid_CV, 4.6, 33.5); // Set PID limits
qpid_set_ratio(&qpid_CV, 0.38, 0.0035, 0.0005); // Set control ratio coefficients
}
}
else if (voltage_dw == 1) // When voltage range is 0.0947x
{
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) // Switch to 0.0325x range when voltage is greater than 33.5V
{
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)); // Set DAC1 output value for constant voltage control
}
voltage_dw = 0;
YVF_AVG_count = 0;
YVF_SUM = 0;
qpid_set_lmt(&qpid_CV, 31.0, 100); // Set PID limits
qpid_set_ratio(&qpid_CV, 0.5, 0.005, 0.0005); // Set control ratio coefficients
}
else if (YVF <= 4.6) // Switch range when voltage is less than 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); // Set DAC1 output value for constant voltage control
}
voltage_dw = 2;
YVF_AVG_count = 0;
YVF_SUM = 0;
qpid_set_lmt(&qpid_CV, 0.01, 5.1); // Set PID limits
qpid_set_ratio(&qpid_CV, 0.26, 0.0025, 0.0005); // Set control ratio coefficients
}
}
else if (voltage_dw == 2) // When voltage range is 0.6175x
{
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) // Switch range when voltage is greater than 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)); // Set DAC1 output value for constant voltage control
}
voltage_dw = 1;
YVF_AVG_count = 0;
YVF_SUM = 0;
qpid_set_lmt(&qpid_CV, 4.6, 33.5); // Set PID limits
qpid_set_ratio(&qpid_CV, 0.38, 0.0035, 0.0005); // Set control ratio coefficients
}
}
if (AVG_count < ADC_count)
{
YIF1_SUM += AD_Value[1]; // Accumulate MOSFET 1 current
YIF2_SUM += AD_Value[2]; // Accumulate MOSFET 2 current
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]); // Accumulate battery voltage samples
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/* Entry function of thread 4, processes data sent from the serial screen */
static void thread4_HMI_GetDate_entry(void *parameter)
{
HMILCD_Send("CC.x0.val=0"); // Clear the current setpoint displayed on the screen
HMILCD_Send("CV.x0.val=0"); // Clear the voltage setpoint displayed on the screen
HMILCD_Send("CR.x0.val=0"); // Clear the resistance setpoint displayed on the screen
HMILCD_Send("CW.x0.val=0"); // Clear the power setpoint displayed on the screen
while (1)
{
if (Serial3_RxFlag == 1)
{
if (Serial3_RxPacket[0] == 0x01) // Currently on the main menu page
{
if (Serial3_RxPacket[1] == 0x10) // Constant current button pressed
{
HMILCD_Send("page CC"); // Switch to constant current mode page
mode = CC; // Set current mode to constant current
}
else if (Serial3_RxPacket[1] == 0x11) // Constant voltage button pressed
{
HMILCD_Send("page CV"); // Switch to constant voltage page
mode = CV; // Set current mode to constant voltage
}
else if (Serial3_RxPacket[1] == 0x12) // Constant resistance button pressed
{
HMILCD_Send("page CR"); // Switch to constant resistance page
mode = CR; // Set current mode to constant resistance
}
else if (Serial3_RxPacket[1] == 0x13) // Constant power button pressed
{
HMILCD_Send("page CW"); // Switch to constant power page
mode = CW; // Set current mode to constant power
}
}
else if (Serial3_RxPacket[0] == 0x02) // Currently on the constant current mode page
{
if (Serial3_RxPacket[1] == 0x10) // Menu button pressed
{
HMILCD_Send("CC.t1.txt=\"OFF\""); // Display OFF in the top-right title box
HMILCD_Send("CC.b1.txt=\"开启\""); // Display "开启" in the bottom-right button
Eload_Out = 0; // Set load output state to off
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
HMILCD_Send("page menu"); // Switch to menu page
mode = menu;
}
else if (Serial3_RxPacket[1] == 0x11) // ON button pressed and load output is off
{
Key_ONOFF = 1;
}
}
else if (Serial3_RxPacket[0] == 0x03) // Currently on the constant voltage mode page
{
if (Serial3_RxPacket[1] == 0x10) // Menu button pressed
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
HMILCD_Send("page menu");
mode = menu;
}
if (Serial3_RxPacket[1] == 0x11) // ON button pressed and load output is off
{
Key_ONOFF = 1;
}
}
else if (Serial3_RxPacket[0] == 0x04) // Currently on the constant resistance mode page
{
if (Serial3_RxPacket[1] == 0x10) // Menu button pressed
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
HMILCD_Send("page menu");
mode = menu;
}
if (Serial3_RxPacket[1] == 0x11) // ON button pressed and load output is off
{
Key_ONOFF = 1;
}
}
else if (Serial3_RxPacket[0] == 0x05) // Currently on the constant power mode page
{
if (Serial3_RxPacket[1] == 0x10) // Menu button pressed
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
HMILCD_Send("page menu");
mode = menu;
}
if (Serial3_RxPacket[1] == 0x11) // ON button pressed and load output is off
{
Key_ONOFF = 1;
}
}
else if (Serial3_RxPacket[0] == 0xAA) // Currently on the numeric keypad page
{
char *temp = Serial3_RxPacket;
temp++; // Address increment by 1
uint16_t temp2 = atoi(temp); // String to integer
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)
{
// Set DAC2 output to control CC, +0.5 for rounding
DAC_SetChannel2Data(DAC_Align_12b_R,
(uint16_t)(ISET * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4(0); // Set IREF2
}
else
{
// Set DAC2 output to control CC, +0.5 for rounding
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)); // Set 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); // Set IREF1
PWM_SetCCR4(50000); // Set IREF2
if (voltage_dw == 0)
{
DAC_SetChannel1Data(DAC_Align_12b_R,
(uint16_t)(VSET * 0.0325 * 4096 / VREF * DAC1_COMP + 0.5)); // Set DAC1 output to control CV
}
else if (voltage_dw == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R,
(uint16_t)(VSET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5)); // Set DAC1 output to control CV
}
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); // Set DAC1 output to control CV
}
}
}
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);
}
}/*Constant Power Mode*/
void CW_mode(void)
{
double Ptemp = PSET / YVF;
if (Ptemp > 10)
Ptemp = 10;
if (Eload_Out == 1)
{
// Set DAC2 output value to control constant current, +0.5 for rounding
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)); // Set IREF2
}
else
{
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable constant current
PWM_SetCCR4(0); // Set IREF2
}
}
/*Constant Resistance Mode*/
void CR_mode(void)
{
double Rtemp = YVF / RSET;
if (Rtemp > 10)
Rtemp = 10;
if (Eload_Out == 1)
{
// Set DAC2 output value to control constant current, +0.5 for rounding
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)); // Set IREF2
}
else
{
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable constant current
PWM_SetCCR4(0); // Set IREF2
}
}
/* PID Control */
void PID(void)
{
double Ptemp, Rtemp;
if (mode == CC && Eload_Out == 1)
{
qpid_set_dst(&qpid_CC, ISET); // Set PID target value
I_SET = qpid_cal_pos(&qpid_CC, YIF); // PID calculation
if (ISET <= 2.5)
{
// Set DAC2 output value to control constant current, +0.5 for rounding
DAC_SetChannel2Data(DAC_Align_12b_R, (uint16_t)(I_SET * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
}
else
{
// Set DAC2 output value to control constant current, +0.5 for rounding
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)); // Set IREF2
}
}
if (mode == CV && Eload_Out == 1)
{
qpid_set_dst(&qpid_CV, VSET); // Set PID target value
V_SET = qpid_cal_pos(&qpid_CV, YVF); // PID calculation
if (voltage_dw == 0)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(V_SET * 0.0325 * 4096 / VREF * DAC1_COMP + 0.5));
// Set DAC1 output value to control constant voltage
}
else if (voltage_dw == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(V_SET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5));
// Set DAC1 output value to control constant voltage
}
else if (voltage_dw == 2)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(V_SET * 0.6175 * 4096 / VREF * DAC1_COMP + 0.5));
// Set DAC1 output value to control constant voltage
}
}
if (mode == CW && Eload_Out == 1)
{
qpid_set_dst(&qpid_CW, PSET); // Set PID target value
P_SET = qpid_cal_pos(&qpid_CW, YIF * YVF); // PID calculation
Ptemp = P_SET / YVF;
if (Ptemp > 10)
Ptemp = 10;
// Set DAC2 output value to control constant current, +0.5 for rounding
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)); // Set IREF2
}
if (mode == CR && Eload_Out == 1)
{
qpid_set_dst(&qpid_CR, RSET); // Set PID target value
R_SET = qpid_cal_pos(&qpid_CR, YVF / YIF); // PID calculation
Rtemp = YVF / R_SET;
if (Rtemp > 10)
Rtemp = 10;
// Set DAC2 output value to control constant current, +0.5 for rounding
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)); // Set IREF2
}
}
/* Key Handler Function */
void key123(void)
{
if (key[2] == 1) // Key 2, switch key
{
if (mode == menu)
{
HMILCD_Send("page CC"); // Switch to constant current mode page
mode = CC; // Set current mode to constant current
}
else if (mode == CC)
{
if (Eload_Out == 1)
{
HMILCD_Send("CC.t1.txt=\"OFF\"");
HMILCD_Send("CC.b1.txt=\"ON\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable constant current
PWM_SetCCR4(0); // Set IREF2
}
HMILCD_Send("page CV"); // Switch to constant voltage mode page
mode = CV; // Set current mode to constant voltage
}
else if (mode == CV)
{
if (Eload_Out == 1)
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"ON\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable constant current
PWM_SetCCR4(0); // Set IREF2
}
HMILCD_Send("page CR"); // Switch to constant resistance mode page
mode = CR; // Set current mode to constant resistance
}
else if (mode == CR)
{
if (Eload_Out == 1)
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"ON\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable constant current
PWM_SetCCR4(0); // Set IREF2
}
HMILCD_Send("page CW"); // Switch to constant power mode page
mode = CW; // Set current mode to constant power
}
else if (mode == CW)
{
if (Eload_Out == 1)
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"ON\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable constant current
PWM_SetCCR4(0); // Set IREF2
}
HMILCD_Send("page menu"); // Switch to menu page
mode = menu; // Set current mode to menu
}
key[2] = 0;
}
if (key[3] == 1) // Key 3, menu key
{
if (Eload_Out == 1)
Key_ONOFF = 1;
rt_thread_mdelay(15);
HMILCD_Send("page menu"); // Switch to menu page
mode = menu; // Set current mode to menu
key[3] = 0;
}
}/* Thread 5 entry function, electronic load ON/OFF key handling */
static void thread5_ONOFF_entry(void *parameter)
{
/* LED2 pin set to output mode */
rt_pin_mode(LED2, PIN_MODE_OUTPUT);
/* Default high level */
rt_pin_write(LED2, PIN_HIGH);
while (1)
{
if (Key_ONOFF == 1 | key[1] == 1) // ON button pressed
{
if (mode == CC) // Constant current mode
{
if (Eload_Out == 0) // When load output is currently off
{
HMILCD_Send("CC.t1.txt=\"ON\""); // Display ON in top-right title box
HMILCD_Send("CC.b1.txt=\"关闭\""); // Display “关闭” in bottom-right button
Eload_Out = 1; // Set load output state to on
DAC_SetChannel1Data(DAC_Align_12b_R, 0);
if (ISET <= 2.5)
{
// Set DAC2 output to control constant current, +0.5 for rounding
DAC_SetChannel2Data(DAC_Align_12b_R,
(uint16_t)(ISET * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4(0); // Set IREF2
}
else
{
// Set DAC2 output to control constant current, +0.5 for rounding
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)); // Set IREF2
}
}
else if (Eload_Out == 1) // When load output is currently on
{
HMILCD_Send("CC.t1.txt=\"OFF\"");
HMILCD_Send("CC.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low, disable constant current
PWM_SetCCR4(0); // Set IREF2
}
}
else if (mode == CV) // Constant voltage mode
{
if (Eload_Out == 0) // When load output is currently off
{
HMILCD_Send("CV.t1.txt=\"ON\"");
HMILCD_Send("CV.b1.txt=\"关闭\"");
Eload_Out = 1;
DAC_SetChannel2Data(DAC_Align_12b_R, 4095); // Set IREF1
PWM_SetCCR4(50000); // Set IREF2
if (voltage_dw == 0)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.0325 * 4096 / VREF * DAC1_COMP + 0.5));
// Set DAC1 output to control constant voltage
}
else if (voltage_dw == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5));
// Set DAC1 output to control constant voltage
}
else if (voltage_dw == 2)
{
DAC_SetChannel1Data(DAC_Align_12b_R, (uint16_t)(VSET * 0.6175 * 4096 / VREF * DAC1_COMP + 0.5));
// Set DAC1 output to control constant voltage
}
}
else if (Eload_Out == 1) // When load output is currently on
{
HMILCD_Send("CV.t1.txt=\"OFF\"");
HMILCD_Send("CV.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low, disable constant current
PWM_SetCCR4(0); // Set IREF2
}
}
else if (mode == CR) // Constant resistance mode
{
if (Eload_Out == 0) // When load output is currently 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) // When load output is currently on
{
HMILCD_Send("CR.t1.txt=\"OFF\"");
HMILCD_Send("CR.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low, disable constant current
PWM_SetCCR4(0); // Set IREF2
}
}
else if (mode == CW) // Constant power mode
{
if (Eload_Out == 0) // When load output is currently 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) // When load output is currently on
{
HMILCD_Send("CW.t1.txt=\"OFF\"");
HMILCD_Send("CW.b1.txt=\"开启\"");
Eload_Out = 0;
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high, disable constant voltage
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low, disable constant current
PWM_SetCCR4(0); // Set 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);
}
}
/* Thread 7 entry function, serial screen parameter display */
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)); // Display voltage
HMILCD_Send("x2.val=%d", (uint16_t)(I * 1000)); // Display current
HMILCD_Send("x3.val=%d", (uint32_t)(I * V * 100)); // Display power
HMILCD_Send("x4.val=%d", (uint32_t)(V / I * 100)); // Display resistance
HMILCD_Send("x5.val=%d", (uint32_t)(VBAT * 100)); // Display resistance
}
rt_thread_mdelay(50);
}
}
/* Thread 8 entry function, cooling fan control */
static void thread8_FAN_entry(void *parameter)
{
while (1)
{
uint16_t P = (uint16_t)(YIF * YVF + 0.5);
if (P >= 13) // Start fan when power exceeds 13 W
{
FAN_PWM_ON();
if (P < 20)
{
FAN_PWM_SetCCR(20); // Fan duty cycle 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); // Turn off fan PWM and output low
FAN_PWM_OFF();
}
rt_thread_mdelay(200);
}
}
/* Thread 9 entry function, constant power and constant resistance mode control */
static void thread9_CWCR_entry(void *parameter)
{
while (1)
{
if (mode == CW)
{
CW_mode();
}
if (mode == CR)
{
CR_mode();
}
rt_thread_mdelay(50);
}
}/* Thread 10 entry function, processes data received via Bluetooth */
static void thread10_BlueTooth_entry(void *parameter)
{
HMILCD_Send("CC.x0.val=0"); // Clear current setpoint displayed on screen
HMILCD_Send("CV.x0.val=0"); // Clear voltage setpoint displayed on screen
HMILCD_Send("CR.x0.val=0"); // Clear resistance setpoint displayed on screen
HMILCD_Send("CW.x0.val=0"); // Clear power setpoint displayed on screen
while (1)
{
if (Serial5_RxFlag == 1)
{
if (Serial5_RxPacket[0] == 0x01) // Currently on main menu page
{
if (Serial5_RxPacket[1] == 0x10) // Constant-current button pressed
{
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 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
Eload_Out = 0;
HMILCD_Send("page CC"); // Switch to constant-current mode page
mode = CC; // Set current mode to constant-current
}
else if (Serial5_RxPacket[1] == 0x11) // Constant-voltage button pressed
{
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 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
Eload_Out = 0;
HMILCD_Send("page CV"); // Switch to constant-voltage page
mode = CV; // Set current mode to constant-voltage
}
else if (Serial5_RxPacket[1] == 0x12) // Constant-resistance button pressed
{
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 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
Eload_Out = 0;
HMILCD_Send("page CR"); // Switch to constant-resistance page
mode = CR; // Set current mode to constant-resistance
}
else if (Serial5_RxPacket[1] == 0x13) // Constant-power button pressed
{
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 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
Eload_Out = 0;
HMILCD_Send("page CW"); // Switch to constant-power page
mode = CW; // Set current mode to constant-power
}
}
else if (Serial5_RxPacket[0] == 0x08) // Return to menu
{
if (mode == CC)
{
HMILCD_Send("CC.t1.txt=\"OFF\""); // Display OFF in top-right title box
HMILCD_Send("CC.b1.txt=\"开启\""); // Display "开启" in bottom-right button
Eload_Out = 0; // Set load output state to off
DAC_SetChannel1Data(DAC_Align_12b_R, 4095); // DAC1 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
HMILCD_Send("page menu"); // Switch to menu page
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 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set 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 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set 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 outputs high level, disable CV
DAC_SetChannel2Data(DAC_Align_12b_R, 0); // DAC2 outputs low level, disable CC
PWM_SetCCR4(0); // Set IREF2
HMILCD_Send("page menu");
mode = menu;
}
}
else if (Serial5_RxPacket[0] == 0x09) // ON/OFF button
{
Key_ONOFF = 1;
}
else if (Serial5_RxPacket[0] == 0xAA) // Currently on numeric keypad page
{
char *temp = Serial5_RxPacket;
temp++; // Address increment by 1
uint16_t temp2 = atoi(temp); // String to integer
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)
{
// Set DAC2 output value to control constant current; +0.5 for rounding
DAC_SetChannel2Data(DAC_Align_12b_R,
(uint16_t)(ISET * 0.01 * 50 * 4096 / VREF * DAC2_COMP + 0.5));
PWM_SetCCR4(0); // Set IREF2
}
else
{
// Set DAC2 output value to control constant current; +0.5 for rounding
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)); // Set 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); // Set IREF1
PWM_SetCCR4(50000); // Set IREF2
if (voltage_dw == 0)
{
DAC_SetChannel1Data(DAC_Align_12b_R,
(uint16_t)(VSET * 0.0325 * 4096 / VREF * DAC1_COMP + 0.5)); // Set DAC1 output value to control constant voltage
}
else if (voltage_dw == 1)
{
DAC_SetChannel1Data(DAC_Align_12b_R,
(uint16_t)(VSET * 0.0947 * 4096 / VREF * DAC1_COMP + 0.5)); // Set DAC1 output value to control constant voltage
}
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); // Set DAC1 output value to control constant voltage
}
}
}
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);
}
}
Recommended Reading
- Cost-effective and Cheap VPS/Cloud Server Recommendations: https://blog.zeruns.com/archives/383.html
- Minecraft Server Setup Tutorial: https://blog.zeruns.com/tag/mc/
- Build a Blog Site Without Coding! Ultra-Detailed Personal Blog Setup Tutorial: https://blog.zeruns.com/archives/783.html
- Intranet Penetration Server Setup Tutorial, NPS Setup and Usage Guide: https://blog.zeruns.com/archives/741.html
- Rain Cloud GPU Server Setup SD (Stable Diffusion) Tutorial, Build Your Own AI Art Website: https://blog.zeruns.com/archives/768.html
- 2.5G Switch TL-SE2109 Quick Unboxing Review, 8×2.5G RJ45 + 1×10G SFP+: https://blog.zeruns.com/archives/780.html















