/** * @brief GPIO Port Definition */ typedefenum { GPIO_PORT_A, /**< GPIO Port A */ GPIO_PORT_B, /**< GPIO Port B */ GPIO_PORT_C, /**< GPIO Port C */ // ... Add more ports if needed GPIO_PORT_MAX } gpio_port_t;
// --- Mock Hardware Registers --- // In a real system, these would be memory-mapped hardware registers uint32_t GPIO_MODER[GPIO_PORT_MAX]; uint32_t GPIO_OTYPER[GPIO_PORT_MAX]; uint32_t GPIO_PUPDR[GPIO_PORT_MAX]; uint32_t GPIO_ODR[GPIO_PORT_MAX]; uint32_t GPIO_IDR[GPIO_PORT_MAX];
if (mode == GPIO_MODE_OUTPUT) { // Configure Output Type GPIO_OTYPER[port] &= ~(0x01 << pin); // Clear output type bit GPIO_OTYPER[port] |= (otype << pin); // Set new output type }
// --- Clock Source Configuration (Simplified) --- // In a real system, you would configure clock sources based on clk_src uint32_t timer_clock_frequency = 16000000; // Example: 16MHz internal clock
// Calculate Prescaler and Period (Auto-Reload Register - ARR) uint32_t prescaler = 1; // Start with no prescaler for simplicity uint32_t period = (timer_clock_frequency / (prescaler * frequency)) - 1;
// --- Basic Error Handling and Period Limit --- if (period > 0xFFFF) { // Example: 16-bit timer limit period = 0xFFFF; // Limit period prescaler = timer_clock_frequency / (frequency * (period + 1)); // Recalculate prescaler if (prescaler > 0xFFFF) { // Prescaler limit, frequency too low prescaler = 0xFFFF; frequency = timer_clock_frequency / (prescaler * (period + 1)); // Approximate freq. // Handle error: frequency too low to achieve with current clock return; } }
PWM_ARR[channel] = period;
// Set Initial Duty Cycle HAL_PWM_SetDutyCycle(channel, duty_cycle);
/** * @brief Read ADC value from a specific channel * @param channel ADC channel (ADC_CHANNEL_0, ADC_CHANNEL_1, ...) * @return uint16_t ADC raw value (0 - max resolution value) */ uint16_tHAL_ADC_ReadChannel(adc_channel_t channel);
// --- Configure Resolution --- ADC_CR1 &= ~(0x03 << 24); // Clear RES bits (Example bits, check datasheet) switch (resolution) { case ADC_RESOLUTION_12BIT: // Default, no need to set bits in this example if default is 12-bit break; case ADC_RESOLUTION_10BIT: ADC_CR1 |= (0x01 << 24); break; case ADC_RESOLUTION_8BIT: ADC_CR1 |= (0x02 << 24); break; default: // Handle invalid resolution return; }
// --- Power ON ADC (Example) --- ADC_CR2 |= (1 << 0); // ADON bit (Example bit, check datasheet) // Add delay for ADC to stabilize if needed }
/** * @brief Read ADC value from a specific channel (Mock Implementation) * @param channel ADC channel * @return uint16_t ADC raw value (0 - max resolution value) */ uint16_tHAL_ADC_ReadChannel(adc_channel_t channel) { if (channel >= ADC_CHANNEL_MAX) { // Handle invalid channel return0; // Or throw an error }
// --- Wait for Conversion to Complete (Polling) --- while (!(ADC_CR2 & (1 << 2))) { // EOC bit - End of Conversion (Example bit) // Wait until conversion is complete // In a real system, consider timeout to prevent infinite loop }
// --- Read Conversion Result --- uint16_t raw_value = (uint16_t)ADC_DR; // Assuming DR is the Data Register
// --- Clear EOC Flag (Optional, depends on ADC configuration) --- ADC_CR2 &= ~(1 << 2);
/** * @brief Read voltage feedback value * @return uint16_t Raw ADC value of voltage feedback */ uint16_tUC3842_ReadVoltageFeedback(void) { return HAL_ADC_ReadChannel(current_config.v_feedback_channel); }
/** * @brief Read current sense value (optional) * @return uint16_t Raw ADC value of current sense */ uint16_tUC3842_ReadCurrentSense(void) { if (current_config.i_sense_channel != ADC_CHANNEL_MAX) { // Check if current sense channel is configured return HAL_ADC_ReadChannel(current_config.i_sense_channel); } else { return0; // Or return an error value/code } }
/** * @brief Enable UC3842 output (if enable pin is configured) * @return void */ voidUC3842_EnableOutput(void) { if (current_config.enable_port != GPIO_PORT_MAX) { HAL_GPIO_WritePin(current_config.enable_port, current_config.enable_pin, true); // Set enable pin high } else { HAL_PWM_Start(current_config.pwm_channel); // If no enable pin, just start PWM directly } }
/** * @brief Disable UC3842 output (if enable pin is configured) * @return void */ voidUC3842_DisableOutput(void) { if (current_config.enable_port != GPIO_PORT_MAX) { HAL_GPIO_WritePin(current_config.enable_port, current_config.enable_pin, false); // Set enable pin low } else { HAL_PWM_Stop(current_config.pwm_channel); // If no enable pin, just stop PWM directly } }
/** * @file power_control.h * @brief Power Control Algorithm Header File * @author Your Name * @date 2023-10-27 * @version 1.0 */
#ifndef POWER_CONTROL_H #define POWER_CONTROL_H
#include<stdint.h> #include<stdbool.h>
/** * @brief Power Control Configuration Structure */ typedefstruct { float v_setpoint; /**< Target output voltage (in Volts) */ float v_feedback_scale; /**< Scaling factor for voltage feedback ADC value to Volts */ float i_sense_scale; /**< Scaling factor for current sense ADC value to Amps (optional) */ float pid_kp; /**< PID Proportional gain */ float pid_ki; /**< PID Integral gain */ float pid_kd; /**< PID Derivative gain */ uint16_t over_voltage_threshold_adc; /**< Over voltage threshold in ADC raw value */ uint16_t over_current_threshold_adc; /**< Over current threshold in ADC raw value (optional) */ } power_control_config_t;
/** * @brief Initialize Power Control Module * @param config Pointer to power control configuration structure * @return bool True if initialization successful, false otherwise */ boolPowerControl_Init(power_control_config_t *config);
/** * @brief Run Power Control Loop (PID Control) * @return void */ voidPowerControl_RunLoop(void);
/** * @brief Get Current Output Voltage (in Volts) * @return float Current output voltage */ floatPowerControl_GetOutputVoltage(void);
/** * @brief Get Current Output Current (in Amps, optional) * @return float Current output current */ floatPowerControl_GetOutputCurrent(void);
/** * @brief Check if Over Voltage Protection is active * @return bool True if over voltage, false otherwise */ boolPowerControl_IsOverVoltage(void);
/** * @brief Check if Over Current Protection is active (optional) * @return bool True if over current, false otherwise */ boolPowerControl_IsOverCurrent(void);
/** * @brief Initialize Power Control Module * @param config Pointer to power control configuration structure * @return bool True if initialization successful, false otherwise */ boolPowerControl_Init(power_control_config_t *config) { if (config == NULL) { returnfalse; // Invalid configuration }
current_config = *config; pid_integral_term = 0; // Reset integral term last_error = 0; // Reset last error over_voltage_flag = false; // Reset flags over_current_flag = false;
returntrue; }
/** * @brief Run Power Control Loop (PID Control) * @return void */ voidPowerControl_RunLoop(void) { // 1. Read Feedback Voltage uint16_t v_feedback_adc = UC3842_ReadVoltageFeedback(); float v_feedback_volts = (float)v_feedback_adc * current_config.v_feedback_scale;
// 2. Check Over Voltage Protection if (v_feedback_adc > current_config.over_voltage_threshold_adc) { over_voltage_flag = true; UC3842_DisableOutput(); // Disable output immediately return; // Exit control loop to prevent further PWM changes } else { over_voltage_flag = false; // Clear flag if voltage is back to normal }
// 3. Read Current Sense (Optional) and Check Over Current Protection uint16_t i_sense_adc = UC3842_ReadCurrentSense(); if (current_config.i_sense_scale > 0) { // Only if current sense is configured if (i_sense_adc > current_config.over_current_threshold_adc) { over_current_flag = true; UC3842_DisableOutput(); // Disable output immediately return; // Exit control loop } else { over_current_flag = false; // Clear flag if current is back to normal } }
// 4. PID Control Calculation float error = current_config.v_setpoint - v_feedback_volts; pid_integral_term += error; // Integral term accumulation
// --- Integral Windup Prevention (Simple Clamping) --- float integral_limit = 100.0f; // Example limit, adjust as needed if (pid_integral_term > integral_limit) { pid_integral_term = integral_limit; } elseif (pid_integral_term < -integral_limit) { pid_integral_term = -integral_limit; }
// 5. PWM Duty Cycle Control uint8_t duty_cycle = (uint8_t)control_output;
// --- Duty Cycle Clamping (0-100%) --- if (duty_cycle > 95) duty_cycle = 95; // Avoid 100% to ensure switching operation if (duty_cycle < 0) duty_cycle = 0;
UC3842_SetDutyCycle(duty_cycle); }
/** * @brief Get Current Output Voltage (in Volts) * @return float Current output voltage */ floatPowerControl_GetOutputVoltage(void) { uint16_t v_feedback_adc = UC3842_ReadVoltageFeedback(); return (float)v_feedback_adc * current_config.v_feedback_scale; }
/** * @brief Get Current Output Current (in Amps, optional) * @return float Current output current */ floatPowerControl_GetOutputCurrent(void) { if (current_config.i_sense_scale > 0) { uint16_t i_sense_adc = UC3842_ReadCurrentSense(); return (float)i_sense_adc * current_config.i_sense_scale; } else { return0.0f; // Or return an error value/code } }
/** * @brief Check if Over Voltage Protection is active * @return bool True if over voltage, false otherwise */ boolPowerControl_IsOverVoltage(void) { return over_voltage_flag; }
/** * @brief Check if Over Current Protection is active (optional) * @return bool True if over current, false otherwise */ boolPowerControl_IsOverCurrent(void) { return over_current_flag; }
// --- Define GPIO and Peripheral Configurations --- #define PWM_CONTROL_CHANNEL PWM_CHANNEL_1 #define VOLTAGE_FEEDBACK_ADC_CHANNEL ADC_CHANNEL_0 #define CURRENT_SENSE_ADC_CHANNEL ADC_CHANNEL_1 // Optional, use ADC_CHANNEL_MAX if not used #define ENABLE_GPIO_PORT GPIO_PORT_B // Example port, use GPIO_PORT_MAX if not used #define ENABLE_GPIO_PIN GPIO_PIN_0 // Example pin, use GPIO_PIN_MAX if not used
// --- Define Power Supply Parameters --- #define TARGET_OUTPUT_VOLTAGE 5.0f // 5V Output #define VOLTAGE_FEEDBACK_SCALE (5.0f / 4095.0f) // Example: Assuming 12-bit ADC, 5V full range #define CURRENT_SENSE_SCALE (0.1f / 4095.0f) // Example: 0.1V/A sense resistor, 12-bit ADC, 5V range #define PID_KP 0.5f #define PID_KI 0.01f #define PID_KD 0.001f #define OVER_VOLTAGE_THRESHOLD_ADC (uint16_t)(5.5f / VOLTAGE_FEEDBACK_SCALE) // 5.5V Over voltage threshold #define OVER_CURRENT_THRESHOLD_ADC (uint16_t)(2.5f / CURRENT_SENSE_SCALE) // 2.5A Over current threshold (example)
// --- Function Prototypes --- voidSystemClock_Initialize(void); // Mock system clock initialization voidDelay_ms(uint32_t ms); // Mock delay function
intmain() { // 1. System Initialization SystemClock_Initialize(); // Initialize system clock (mock) printf("System Initialized.\r\n");
if (!PowerControl_Init(&power_config)) { printf("Power Control Initialization Failed!\r\n"); return-1; // Error } printf("Power Control Initialized.\r\n");
Delay_ms(100); // Control loop frequency, adjust as needed }
return0; // Normal exit }
// --- Mock System Clock Initialization --- voidSystemClock_Initialize(void) { // In a real system, this would configure the microcontroller's clock system printf("System Clock Initialized (Mock).\r\n"); }
// --- Mock Delay Function --- voidDelay_ms(uint32_t ms) { // In a real system, use a hardware timer for accurate delays // For simulation, a simple loop might suffice for basic testing volatileuint32_t count = ms * 10000; // Adjust loop count for approximate delay while (count--) { __asm("nop"); // No operation } }
5. 测试代码示例 (单元测试)
为了进行单元测试,我们可以为 HAL 层和驱动层编写简单的测试函数。 以下是 hal_gpio.c 的一个简单的单元测试示例。 更完善的单元测试会使用专门的测试框架。
HAL_GPIO_WritePin(port, pin, true); if (!HAL_GPIO_ReadPin(port, pin)) { printf("GPIO Write High and Read Test Failed!\r\n"); } else { printf("GPIO Write High and Read Test Passed!\r\n"); }
HAL_GPIO_WritePin(port, pin, false); if (HAL_GPIO_ReadPin(port, pin)) { printf("GPIO Write Low and Read Test Failed!\r\n"); } else { printf("GPIO Write Low and Read Test Passed!\r\n"); } }
intmain() { printf("--- GPIO Unit Tests ---\r\n"); test_gpio_init_output(); test_gpio_write_read(); printf("--- GPIO Unit Tests Completed ---\r\n"); return0; }