### Initialize Trigonometric Look-Up Tables Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Call this function once at startup to pre-fill sin/cos LUTs. Subsequent calls to fast_sin/fast_cos use these tables for efficient calculations in ISRs. ```c #include "FOC_math.h" // Call once at startup, before any FOC or transform function init_trig_lut(); // Subsequent fast trig usage (replaces sinf/cosf in ISR context) float theta = 1.2345f; // electrical angle in radians float s = fast_sin(theta); // interpolated from LUT float c = fast_cos(theta); // interpolated from LUT // Pre-calculate both sin and cos in one call (saves redundant LUT lookup) float sin_t, cos_t; pre_calc_sin_cos(theta, &sin_t, &cos_t); // sin_t ≈ sinf(1.2345f), cos_t ≈ cosf(1.2345f) ``` -------------------------------- ### Trigonometric Look-Up Table Initialization and Usage Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Initializes the sine and cosine look-up tables (LUTs) for fast trigonometric calculations. `init_trig_lut` must be called once at startup. `fast_sin` and `fast_cos` provide interpolated LUT lookups, while `pre_calc_sin_cos` computes both simultaneously. ```APIDOC ## Trigonometric Look-Up Table Initialization — `init_trig_lut` Fills 4096-entry sin/cos LUTs used by all real-time math. Must be called once before any FOC operation. `fast_sin` and `fast_cos` perform linearly-interpolated LUT lookups to avoid costly `sinf`/`cosf` calls in the ISR. ```c #include "FOC_math.h" // Call once at startup, before any FOC or transform function init_trig_lut(); // Subsequent fast trig usage (replaces sinf/cosf in ISR context) float theta = 1.2345f; // electrical angle in radians float s = fast_sin(theta); // interpolated from LUT float c = fast_cos(theta); // interpolated from LUT // Pre-calculate both sin and cos in one call (saves redundant LUT lookup) float sin_t, cos_t; pre_calc_sin_cos(theta, &sin_t, &cos_t); // sin_t ≈ sinf(1.2345f), cos_t ≈ cosf(1.2345f) ``` ``` -------------------------------- ### FreeRTOS Control Task for Calibration and Heartbeat Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Handles calibration sequencing and provides a heartbeat blink. Runs with normal priority and a 512-word stack. ```c // Task 1: controlTask (Normal priority, 512-word stack) // Handles calibration sequencing and LED heartbeat blink void StartControlTask(void const *argument) { for (;;) { if (hfoc.control_mode == CALIBRATION_MODE && start_cal == 1) { calibration_seq(); // encoder cal → measure_R → measure_L → auto-tune start_cal = 0; } HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 500 ms blink = alive indicator osDelay(1); } } ``` -------------------------------- ### PID Controller Configuration and Usage Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Configures and uses a generic discrete-time PI/PID controller with anti-windup, derivative filtering, deadband, and output clamping. Used for various control loops. ```c #include "pid_utils.h" PID_Controller_t my_ctrl; // Initialize / reset state pid_reset(&my_ctrl); // Set sample time (seconds) pid_set_ts(&my_ctrl, FOC_TS); // 50 µs for current loops // Set gains pid_set_kp(&my_ctrl, 2.5f); pid_set_ki(&my_ctrl, 150.0f); pid_set_kd(&my_ctrl, 0.0001f); // optional derivative // Derivative filter cutoff frequency (Hz) and max derivative magnitude pid_set_d_filter_fc(&my_ctrl, 100.0f); pid_set_max_d(&my_ctrl, 10.0f); // Output saturation limits (anti-windup via clamping) pid_set_out_constraint(&my_ctrl, 24.0f, -24.0f); // ±24 V output limit // Error deadband (ignore errors smaller than this) pid_set_deadband(&my_ctrl, 0.05f); // Run PI control each period float error = id_ref - id_measured; float vd_out = pi_control(&my_ctrl, error); // vd_out is clamped to [-24, 24] V // Run full PID (includes filtered derivative) float vq_out = pid_control(&my_ctrl, iq_ref - iq_measured); ``` -------------------------------- ### FOC Handle Initialization Sequence Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Initializes the FOC control structure, including motor parameters, sensor configuration, and control modes. Must be called before enabling the gate driver. ```c #include "FOC_utils.h" #include "flash.h" motor_config_t m_config; foc_t hfoc; // 1. Load saved config from Flash (or use defaults) flash_read_config(&m_config); // 2. Pre-compute trig LUT init_trig_lut(); // 3. Motor electrical parameters foc_motor_init(&hfoc, 7, 360.0f); // 7 pole pairs, Kv = 360 RPM/V foc_sensor_init(&hfoc, m_config.encd_offset, REVERSE_DIR); foc_gear_reducer_init(&hfoc, 1.0f); // direct drive (no reducer) foc_set_limit_current(&hfoc, 20.0f); // hard current limit: 20 A hfoc.Rs = m_config.Rs; // stator resistance (Ohm) hfoc.Ld = m_config.Ld; // d-axis inductance (H) hfoc.Lq = m_config.Lq; // q-axis inductance (H) hfoc.flux_linkage = 0.003789f; // 4. Choose sensor fusion mode foc_set_mode(&hfoc, FOC_MODE_HYBRID); // sensored low-speed, SMO high-speed // 5. Initialize sensorless estimators (SMO + HFI) foc_sensorless_init(&hfoc, BLDC_PWM_FREQ); // BLDC_PWM_FREQ = 20000 Hz // 6. Set up current control bandwidth (auto-calculates kp/ki) foc_set_torque_control_bandwidth(&hfoc, m_config.I_ctrl_bandwidth); ``` -------------------------------- ### FreeRTOS Communication Task for USB, Telemetry, and CAN Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Manages USB CLI parsing, real-time telemetry streaming, and CAN transmission. Runs with low priority and a 512-word stack. ```c // Task 2: comTask (Low priority, 512-word stack) // Handles USB CLI parsing, real-time telemetry streaming, CAN transmission void StartComTask(void const *argument) { for (;;) { if (usb_recv_flag) { usb_recv_flag = 0; parse_command((char *)usb_recv); // parse and execute CLI command } // Stream selected telemetry variables to serial plotter at 200 Hz if (HAL_GetTick() - debug_tick >= 5) { float data[MAX_DATA_PLOT]; uint8_t len = 0; for (int i = 0; source_plot[i].addr != NULL && i < MAX_DATA_PLOT; i++) { data[len++] = *source_plot[i].addr; } send_data_float(data, len); debug_tick = HAL_GetTick(); } // Transmit actual_angle over CAN bus at 100 Hz if (HAL_GetTick() - transmit_tick >= 10) { uint8_t can_tx[5]; union { float f; uint8_t b[4]; } u = { .f = hfoc.actual_angle }; can_tx[0] = 0x30; // frame ID byte memcpy(&can_tx[1], u.b, 4); CAN_Send(&hcan1, TRANSMITTER_ID, can_tx, 5); transmit_tick = HAL_GetTick(); } osDelay(1); } } ``` -------------------------------- ### Save and Load Motor Configuration to Flash Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Persists motor configuration parameters, including PID gains and motor parameters, to Flash sector 11 using HAL Flash erase and program operations. Includes functions for reading, saving, and setting default configurations. ```c #include "flash.h" motor_config_t m_config; // Load config at startup (falls back to defaults if no valid config in Flash) flash_read_config(&m_config); // Checks SOF_FLAG (0xAA) and EOF_FLAG (0x55) for data integrity // Modify a parameter m_config.speed_kp = 0.05f; m_config.speed_ki = 2.0f; m_config.speed_out_max = 20.0f; // max 20 A current output from speed loop // Sync live controller state into config struct copy_from_local(&m_config, &hfoc); // Erase sector 11 and write new config HAL_StatusTypeDef status = flash_save_config(&m_config); if (status == HAL_OK) { usb_print("Configuration saved successfully\r\n"); } // Reset all parameters to factory defaults flash_default_config(&m_config); // Resets all PID gains, clears encoder calibration, etc. ``` -------------------------------- ### Auto-Tune PI Gains for Torque Control Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Calculates optimal kp and ki for current controllers using standard symmetrical-optimum tuning formulas based on Rs, Ld/Lq, and a user-defined closed-loop bandwidth. Applies and saves the new gains. ```c // Set desired closed-loop bandwidth (Hz) m_config.I_ctrl_bandwidth = 500.0f; // 500 Hz current loop bandwidth // Auto-compute kp and ki (writes results into m_config) flash_auto_tuning_torque_control(&m_config); // Apply new gains to live controllers hfoc.id_ctrl.kp = m_config.id_kp; hfoc.id_ctrl.ki = m_config.id_ki; hfoc.iq_ctrl.kp = m_config.iq_kp; hfoc.iq_ctrl.ki = m_config.iq_ki; // Save to Flash so gains survive power-cycle flash_save_config(&m_config); // Expected output: "success save configuration" ``` -------------------------------- ### SMO Initialization and Angle Estimation Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Initializes and uses a Sliding Mode Observer for sensorless rotor angle and speed estimation, reliable above ~300 RPM. Updates Rs/Ls after self-commissioning. ```c #include "sliding_mode_observer.h" smo_t my_smo; // Initialize with motor parameters and sample time float Rs = 0.15f; // stator resistance (Ohm) float Ls = 0.0001f; // average inductance = (Ld + Lq) / 2 (H) float pole_pairs = 7.0f; float Ts = FOC_TS; // 50 µs smo_init(&my_smo, Rs, Ls, pole_pairs, Ts); smo_set_min_emf(&my_smo, 0.5f); // minimum EMF magnitude to trust estimate // Update observer each current loop cycle int valid = smo_update_arctan(&my_smo, hfoc.v_alpha, hfoc.v_beta, // applied voltages hfoc.i_alpha, hfoc.i_beta); // measured currents if (valid) { float e_rad = smo_get_rotor_angle(&my_smo); // electrical angle (rad) float omega = smo_get_omega(&my_smo); // electrical angular velocity (rad/s) float rpm = (omega * 60.0f / TWO_PI) / pole_pairs; // mechanical RPM // Use e_rad as hfoc.e_rad for Park/inverse-Park transforms } else { // EMF too low (low speed) — fall back to HFI estimator } // Update Rs/Ls after self-commissioning smo_update_R_L(&my_smo, new_Rs, (new_Ld + new_Lq) * 0.5f); ``` -------------------------------- ### Inverse Park Transform for PWM Generation Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Converts PI controller output voltages (vd, vq) from the dq-frame back to the stationary αβ-frame. These values are then used for Space Vector PWM generation. ```c float vd = 0.5f, vq = 2.3f; // PI controller outputs (Volts) float sin_t = 0.7071f, cos_t = 0.7071f; float v_alpha, v_beta; inverse_park_transform(vd, vq, sin_t, cos_t, &v_alpha, &v_beta); // v_alpha = vd*cos - vq*sin = 0.5*0.707 - 2.3*0.707 ≈ -1.273 V // v_beta = vd*sin + vq*cos = 0.5*0.707 + 2.3*0.707 ≈ 1.980 V ``` -------------------------------- ### HFI Initialization and Rotor Position Estimation Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Initializes and uses a High Frequency Injection estimator for sensorless position detection at low speed and standstill. Updates the position estimator with current measurements and can force a known position. ```c #include "hfi_sdft.h" hfi_t my_hfi; // Initialize: injection amplitude (V), frequency (Hz), sampling frequency (Hz) hfi_init(&my_hfi, HFI_AMP, HFI_FREQ, BLDC_PWM_FREQ); // HFI_AMP = 3.0 V, HFI_FREQ = 2000 Hz, BLDC_PWM_FREQ = 20000 Hz // Get injection voltage to superimpose on vd_ref each current cycle float v_inj = hfi_get_v_inj(&my_hfi); // v_inj alternates as a 2 kHz injection signal // Update the position estimator with current iq measurement hfi_update_estimate_position(&my_hfi, iq_measured, FOC_TS); // Read estimated rotor position and speed float theta_est = hfi_get_estimate_position(&my_hfi); // electrical angle (rad) float omega_est = hfi_get_estimate_omega(&my_hfi); // electrical angular velocity (rad/s) // Force a known position (used for HFI→SMO handoff) hfi_force_estimate_position(&my_hfi, smo_angle); // Reset estimator (called on foc_disable) hfi_reset(&my_hfi); ``` -------------------------------- ### PID Controller Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Generic discrete-time PI/PID controller with anti-windup, derivative filtering, deadband, and output clamping. Used for id, iq, speed, position, and field-weakening control loops. ```APIDOC ## PID Controller (`lib/PID/pid_utils.h` / `pid_utils.c`) ### PID Configuration and Usage Generic discrete-time PI/PID controller with anti-windup, derivative filtering, deadband, and output clamping. Used for id, iq, speed, position, and field-weakening control loops. ### Initialize / Reset PID Controller ```c pid_reset(&my_ctrl); ``` ### Set Sample Time ```c pid_set_ts(&my_ctrl, FOC_TS); // 50 µs for current loops ``` ### Set Gains ```c pid_set_kp(&my_ctrl, 2.5f); pid_set_ki(&my_ctrl, 150.0f); pid_set_kd(&my_ctrl, 0.0001f); // optional derivative ``` ### Set Derivative Filter Cutoff Frequency and Max Derivative ```c pid_set_d_filter_fc(&my_ctrl, 100.0f); pid_set_max_d(&my_ctrl, 10.0f); ``` ### Set Output Constraints (Anti-windup) ```c pid_set_out_constraint(&my_ctrl, 24.0f, -24.0f); // ±24 V output limit ``` ### Set Error Deadband ```c pid_set_deadband(&my_ctrl, 0.05f); ``` ### Run PI Control ```c float error = id_ref - id_measured; float vd_out = pi_control(&my_ctrl, error); // vd_out is clamped to [-24, 24] V ``` ### Run Full PID Control ```c float vq_out = pid_control(&my_ctrl, iq_ref - iq_measured); ``` ``` -------------------------------- ### Parse ASCII Commands for Real-Time Tuning Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Parses ASCII commands received over USB CDC, terminated by newline characters. Use for real-time tuning of FOC parameters, mode switching, and calibration. ```c // Commands sent from PC serial terminal (e.g., minicom, PuTTY) at any baud (USB CDC) // Switch to speed control mode parse_command("mode 1"); // Output: "Control Mode[1]: Speed Control\n" // Set speed setpoint to 300 RPM parse_command("sp 300"); // Read all PID parameters for the speed controller parse_command("pid speed"); // Output: "speed control param:\r\n kp:0.050000\r\n ki:2.000000\r\n ..." // Update speed controller kp parse_command("pid speed kp 0.08"); // Output: " OK\r\n" // Auto-tune current loop bandwidth to 600 Hz parse_command("pid id bw 600"); // Output: " new bw:600.00\r\n" // Start full self-commissioning (encoder offset + Rs + Ld/Lq + auto-tune) parse_command("calib"); // Output: "start callibration\r\n" then measurement results // Add rpm to real-time serial plotter parse_command("plot add rpm"); // Save current configuration to Flash parse_command("save"); // Output: " success save configuration\r\n" // Print motor parameters parse_command("motor_param"); // Output: "motor param:\r\n Rs:0.152000\r\n Ld:0.000098\r\n ..." // Disable motor (safe stop) parse_command("motor off"); // Output: "disable motor\r\n" ``` -------------------------------- ### Park Transform for Rotating Frame Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Rotates stationary αβ currents into the synchronously rotating dq reference frame using the electrical angle's sine and cosine. Requires pre-computed sin/cos values. ```c float i_alpha = 1.5f, i_beta = -0.058f; float e_angle = 0.785f; // electrical angle in radians (~45°) float sin_t, cos_t, id, iq; pre_calc_sin_cos(e_angle, &sin_t, &cos_t); park_transform(i_alpha, i_beta, sin_t, cos_t, &id, &iq); // id = i_alpha*cos + i_beta*sin (flux-axis current) // iq = i_beta*cos - i_alpha*sin (torque-axis current) ``` -------------------------------- ### ADC Injected Conversion Complete Callback for FOC Loop Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt This callback is triggered upon completion of injected ADC conversions. It initiates the FOC pipeline by calling `foc_loop`. ```c // ADC ISR (highest priority, 50 µs period): runs entire FOC pipeline void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *hadc) { if (hadc->Instance == ADC3) { foc_loop(); // reads v_bus, dispatches to foc_current_control_update } } ``` -------------------------------- ### Park Transform Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Executes the Park transform, rotating the stationary αβ currents into the synchronously rotating dq reference frame. This requires the pre-computed sine and cosine of the electrical angle. ```APIDOC ## Park Transform — `park_transform` Rotates the stationary αβ currents into the synchronously rotating dq reference frame aligned with the rotor flux. Requires the pre-computed sin/cos of the electrical angle. ```c float i_alpha = 1.5f, i_beta = -0.058f; float e_angle = 0.785f; // electrical angle in radians (~45°) float sin_t, cos_t, id, iq; pre_calc_sin_cos(e_angle, &sin_t, &cos_t); park_transform(i_alpha, i_beta, sin_t, cos_t, &id, &iq); // id = i_alpha*cos + i_beta*sin (flux-axis current) // iq = i_beta*cos - i_alpha*sin (torque-axis current) ``` ``` -------------------------------- ### Configure and Read AS5047P Encoder Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Configures the SPI peripheral and chip-select GPIO for the AS5047P encoder. Reads angle and speed using DMA-based non-blocking SPI transfers. The angle is processed in the HAL_SPI_TxRxCpltCallback. ```c #include "AS5047P.h" AS5047P_t my_encoder; // Configure SPI peripheral and chip-select GPIO AS5047P_config(&my_encoder, &hspi1, SPI_CS_GPIO_Port, SPI_CS_Pin); // Trigger a non-blocking SPI read (result available in callback) AS5047P_start(&my_encoder); HAL_Delay(10); // allow first read to complete AS5047P_start(&my_encoder); // In HAL_SPI_TxRxCpltCallback, angle is updated automatically: void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi->Instance == SPI1) { foc_sensored_calc_electric_angle(&hfoc); // uses AS5047P data inside hfoc // Triggers next DMA transfer for continuous streaming } } // Read filtered mechanical angle (degrees, 0–360) float angle_deg = AS5047P_get_degree(&my_encoder); // Read speed (RPM), computed from angle difference over Ts float rpm = AS5047P_get_rpm(&my_encoder, FOC_TS); // New-value flag (set in callback, cleared after consumption) if (AS5047P_get_val_flag()) { AS5047P_reset_val_flag(); AS5047P_start(&my_encoder); // trigger next read } ``` -------------------------------- ### Inverse Park Transform Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Performs the inverse Park transform, converting voltage commands (vd, vq) from the dq-frame back into the stationary αβ-frame. This is typically used after PI controller outputs are determined. ```APIDOC ## Inverse Park Transform — `inverse_park_transform` Converts PI controller output voltages (vd, vq) from the dq-frame back to the stationary αβ-frame. ```c float vd = 0.5f, vq = 2.3f; // PI controller outputs (Volts) float sin_t = 0.7071f, cos_t = 0.7071f; float v_alpha, v_beta; inverse_park_transform(vd, vq, sin_t, cos_t, &v_alpha, &v_beta); // v_alpha = vd*cos - vq*sin = 0.5*0.707 - 2.3*0.707 ≈ -1.273 V // v_beta = vd*sin + vq*cos = 0.5*0.707 + 2.3*0.707 ≈ 1.980 V ``` ``` -------------------------------- ### Estimate Stator Inductance (Ld, Lq) Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Estimates Ld and Lq by injecting sinusoidal voltages at a known frequency and computing impedance magnitude and phase angle via single-frequency DFT. Measurements are performed sequentially for Ld and Lq. ```c // Inject at 1000 Hz, 1.2 V amplitude on both d and q axes sequentially hfoc.meas_inj_freq = 1000.0f; hfoc.meas_inj_amp = 1.2f; hfoc.meas_inj_omega = TWO_PI * 1000.0f; // Trigger Ld measurement hfoc.meas_inj_target = LD; hfoc.meas_inj_start_flag = 1; while (hfoc.meas_inj_start_flag) { osDelay(1); } // Trigger Lq measurement hfoc.meas_inj_target = LQ; hfoc.meas_inj_start_flag = 1; while (hfoc.meas_inj_start_flag) { osDelay(1); } // Compute Ld/Lq from DFT of sampled Vd/Vq and Id/Iq buffers estimate_inductance(&hfoc, FOC_TS); // hfoc.Ld = |Zd| * sin(phi_d) / omega (Henry) // hfoc.Lq = |Zq| * sin(phi_q) / omega (Henry) // Example: "Estimate Inductance @(f=1000.00Hz)\r\nLd: 0.000098\r\nLq: 0.000115\r\n" ``` -------------------------------- ### Send Binary Telemetry Data via USB CDC Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Sends float values as a binary framed packet over USB CDC for visualization. Includes commands to control plotter titles, legends, and display. ```c // Send two float values (id_ref and iq) as a binary framed packet float data[2] = { hfoc.id_ref, hfoc.iq_filtered }; send_data_float(data, 2); // Wire format: [0x55][0xAA][f0_b0][f0_b1][f0_b2][f0_b3][f1_b0]...[0xAA][0x55] // Total size: 2 + 2*4 + 2 = 12 bytes // Plotter control commands change_title("SPEED CONTROL"); // 0x04 0x00 + ASCII title change_legend(0, "rpm_ref"); // 0x03 index + ASCII label change_legend(1, "actual_rpm"); erase_graph(); // 0x02 0x00 — clears plotter display ``` -------------------------------- ### Estimate Stator Resistance (Rs) Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Measures stator resistance by injecting a known DC voltage on the d-axis and averaging the resulting current response. Requires entering calibration mode and setting injection parameters. ```c // Enter calibration mode to allow signal injection hfoc.control_mode = CALIBRATION_MODE; // Set injection amplitude and trigger the measurement hfoc.meas_inj_amp = 1.0f; // 1 V DC injection on d-axis hfoc.meas_inj_target = RS; hfoc.meas_inj_start_flag = 1; // Wait for acquisition to complete (128 + 256 wait cycles = ~19 ms at 20 kHz) while (hfoc.meas_inj_start_flag) { osDelay(1); } // Compute Rs from averaged Vd/Id estimate_resistance(&hfoc); // hfoc.Rs = mean(Vd_buff) / mean(Id_buff) // Example output via USB: "Estimate Resistance @(V=1.00)\r\nRs: 0.152000\r\n" usb_print("Rs = %f Ohm\r\n", hfoc.Rs); ``` -------------------------------- ### FOC Speed Control Update Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Implements the outer speed loop PI controller. Computes the required current reference (Is_ref) to track a speed setpoint. Called periodically at a slower rate than the current loop. ```c // Set control mode and speed setpoint via CLI or directly hfoc.control_mode = SPEED_CONTROL_MODE; float target_rpm = 500.0f; // desired speed in RPM // Called in the control task / slower interrupt foc_speed_control_update(&hfoc, target_rpm); // Internally: hfoc.Is_ref = pi_control(&hfoc.speed_ctrl, target_rpm - hfoc.actual_rpm) // hfoc.Is_ref is then used by foc_current_control_update() as the torque reference ``` -------------------------------- ### High Frequency Injection (HFI) Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Single-bin Sliding DFT-based HFI estimator for sensorless position detection at low speed and standstill. Injects a high-frequency voltage on the d-axis and extracts rotor position from the resulting iq current ripple using a Sliding DFT and PLL. ```APIDOC ## High Frequency Injection (`lib/HFI/hfi_sdft.h`) ### HFI Initialization and Rotor Position Estimation at Standstill Single-bin Sliding DFT-based HFI estimator for sensorless position detection at low speed and standstill. Injects a high-frequency voltage on the d-axis and extracts rotor position from the resulting iq current ripple using a Sliding DFT and PLL. ### Initialize HFI ```c // Initialize: injection amplitude (V), frequency (Hz), sampling frequency (Hz) hfi_init(&my_hfi, HFI_AMP, HFI_FREQ, BLDC_PWM_FREQ); // HFI_AMP = 3.0 V, HFI_FREQ = 2000 Hz, BLDC_PWM_FREQ = 20000 Hz ``` ### Get Injection Voltage ```c float v_inj = hfi_get_v_inj(&my_hfi); // v_inj alternates as a 2 kHz injection signal ``` ### Update HFI Estimate ```c // Update the position estimator with current iq measurement hfi_update_estimate_position(&my_hfi, iq_measured, FOC_TS); ``` ### Read Estimated Position and Speed ```c float theta_est = hfi_get_estimate_position(&my_hfi); // electrical angle (rad) float omega_est = hfi_get_estimate_omega(&my_hfi); // electrical angular velocity (rad/s) ``` ### Force Estimated Position ```c // Force a known position (used for HFI→SMO handoff) hfi_force_estimate_position(&my_hfi, smo_angle); ``` ### Reset HFI Estimator ```c // Reset estimator (called on foc_disable) hfi_reset(&my_hfi); ``` ``` -------------------------------- ### Motor Enable / Disable Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Safe enable and disable of the gate driver and PWM outputs. `foc_disable` resets all PI integrators to prevent integrator wind-up and re-initializes sensorless estimators. ```APIDOC ## Motor Enable / Disable — `foc_enable` / `foc_disable` Safe enable and disable of the gate driver and PWM outputs. `foc_disable` resets all PI integrators to prevent integrator wind-up and re-initializes sensorless estimators. ### Enable Motor Drive ```c // Enable motor drive (start PWM and enable gate) foc_enable(&hfoc); hfoc.control_mode = TORQUE_CONTROL_MODE; sp_input = 0.0f; // start with zero torque reference ``` ### Disable Motor Drive ```c // Safe disable (resets all PID state, stops PWM, disables gate) foc_disable(&hfoc); // After foc_disable: // - DRV8302 gate is disabled (all switches open) // - All PID integrators = 0 // - HFI/SMO estimators reset // - hfoc.control_mode = POWER_UP_MODE ``` ``` -------------------------------- ### FOC Position Control Update Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Implements cascaded position control loop. Runs at a slower rate (e.g., 10 ms), outputs an RPM reference to the speed loop, which then commands current. Handles angle wrap-around. ```c hfoc.control_mode = POSITION_CONTROL_MODE; float target_deg = 90.0f; // desired mechanical angle in degrees // Called at position loop rate (every 100 current cycles = 5 ms) foc_position_control_update(&hfoc, target_deg); // Normalizes target_deg to [0, 360] // Computes shortest-path angle error with wrap-around handling // Outputs hfoc.rpm_ref → foc_speed_control_update → hfoc.Is_ref // Expected: hfoc.actual_angle converges to 90.0 degrees ``` -------------------------------- ### Clarke Transform for Stationary Frame Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Converts two phase currents (ia, ib) into the stationary αβ reference frame. Assumes a balanced three-phase system where ia + ib + ic = 0. ```c float ia = 1.5f; // Phase A current in Amperes (from ADC) float ib = -0.8f; // Phase B current in Amperes (from ADC) float i_alpha, i_beta; clarke_transform(ia, ib, &i_alpha, &i_beta); // i_alpha = ia = 1.5 // i_beta = (1/√3)*ia + (2/√3)*ib ≈ 0.866 + (-0.924) = -0.058 ``` -------------------------------- ### Sliding Mode Observer (SMO) Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Back-EMF observer for sensorless rotor angle and speed estimation. Works reliably above ~300 RPM. Used as the high-speed estimator in the HFI+SMO hybrid sensorless scheme. ```APIDOC ## Sliding Mode Observer (`lib/SMO/sliding_mode_observer.h`) ### SMO Initialization and Angle Estimation Back-EMF observer for sensorless rotor angle and speed estimation. Works reliably above ~300 RPM. Used as the high-speed estimator in the HFI+SMO hybrid sensorless scheme. ### Initialize SMO ```c float Rs = 0.15f; // stator resistance (Ohm) float Ls = 0.0001f; // average inductance = (Ld + Lq) / 2 (H) float pole_pairs = 7.0f; float Ts = FOC_TS; // 50 µs smo_init(&my_smo, Rs, Ls, pole_pairs, Ts); smo_set_min_emf(&my_smo, 0.5f); // minimum EMF magnitude to trust estimate ``` ### Update SMO ```c int valid = smo_update_arctan(&my_smo, hfoc.v_alpha, hfoc.v_beta, // applied voltages hfoc.i_alpha, hfoc.i_beta); // measured currents ``` ### Get Estimated Rotor Angle and Speed ```c if (valid) { float e_rad = smo_get_rotor_angle(&my_smo); // electrical angle (rad) float omega = smo_get_omega(&my_smo); // electrical angular velocity (rad/s) float rpm = (omega * 60.0f / TWO_PI) / pole_pairs; // mechanical RPM // Use e_rad as hfoc.e_rad for Park/inverse-Park transforms } else { // EMF too low (low speed) — fall back to HFI estimator } ``` ### Update Stator Resistance and Inductance ```c smo_update_R_L(&my_smo, new_Rs, (new_Ld + new_Lq) * 0.5f); ``` ``` -------------------------------- ### Space Vector PWM (SVPWM) Calculation Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Converts alpha-beta voltage vectors to PWM duty cycles for the DRV8302 driver. Maximizes DC bus utilization and minimizes switching losses. ```c float v_alpha = -1.273f, v_beta = 1.980f; float vbus = 24.0f; // DC bus voltage (Volts) uint32_t pwm_period = 1024; // TIM1 auto-reload register value uint32_t pwm_u, pwm_v, pwm_w; svpwm(v_alpha, v_beta, vbus, pwm_period, &pwm_u, &pwm_v, &pwm_w); // pwm_u, pwm_v, pwm_w: compare values written to TIM1 CCR1/CCR2/CCR3 // Range: [0, pwm_period], clamped if out of bounds DRV8302_set_pwm(&hfoc.drv8302, pwm_u, pwm_v, pwm_w); ``` -------------------------------- ### Combined Clarke and Park Transform Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt An optimized function that performs both Clarke and Park transforms in a single call. This is used in the high-frequency current control ISR to minimize computation time by avoiding intermediate storage of i_alpha/i_beta. ```c float ia = 1.5f, ib = -0.8f; float sin_t = 0.7071f, cos_t = 0.7071f; // 45° electrical angle float id, iq; clarke_park_transform(ia, ib, sin_t, cos_t, &id, &iq); // Equivalent to clarke_transform() followed by park_transform() // but avoids storing intermediate i_alpha/i_beta ``` -------------------------------- ### Enable/Disable Motor Drive Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Safely enables and disables the gate driver and PWM outputs for motor control. `foc_disable` resets PI integrators and re-initializes sensorless estimators. ```c // Enable motor drive (start PWM and enable gate) foc_enable(&hfoc); hfoc.control_mode = TORQUE_CONTROL_MODE; sp_input = 0.0f; // start with zero torque reference // ... motor running ... // Safe disable (resets all PID state, stops PWM, disables gate) foc_disable(&hfoc); // After foc_disable: // - DRV8302 gate is disabled (all switches open) // - All PID integrators = 0 // - HFI/SMO estimators reset // - hfoc.control_mode = POWER_UP_MODE ``` -------------------------------- ### Combined Clarke + Park Transform Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt An optimized single function that combines both the Clarke and Park transforms. This is used in the high-frequency current control ISR to minimize computation time by avoiding intermediate storage of αβ values. ```APIDOC ## Combined Clarke + Park Transform — `clarke_park_transform` Single optimized call combining both transforms. Used in the high-frequency current control ISR to minimize computation time. ```c float ia = 1.5f, ib = -0.8f; float sin_t = 0.7071f, cos_t = 0.7071f; // 45° electrical angle float id, iq; clarke_park_transform(ia, ib, sin_t, cos_t, &id, &iq); // Equivalent to clarke_transform() followed by park_transform() // but avoids storing intermediate i_alpha/i_beta ``` ``` -------------------------------- ### FOC Current Control Update ISR Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Core ISR function for current control, executed every PWM period. Handles ADC reading, FOC transformations, PI control, and PWM output. Called from ADC completion callback. ```c // Called from ADC injected conversion complete ISR (every 50 µs) void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc) { if (hadc->Instance == ADC3) { hfoc.v_bus = get_power_voltage(); // read filtered bus voltage switch (hfoc.control_mode) { case TORQUE_CONTROL_MODE: hfoc.Is_ref = sp_input; // set torque reference (A) foc_current_control_update(&hfoc, FOC_TS); // FOC_TS = 50µs foc_get_mech_degree(&hfoc); break; case SPEED_CONTROL_MODE: // Inner current loop every cycle foc_current_control_update(&hfoc, FOC_TS); // Outer speed loop every SPEED_CONTROL_CYCLE=10 cycles (2 ms) if (torque_control_update() == 1) { foc_speed_control_update(&hfoc, sp_input); // sp_input in RPM } break; default: break; } } } // After foc_current_control_update: // hfoc.id, hfoc.iq — measured dq currents (A) // hfoc.vd, hfoc.vq — applied dq voltages (V) // hfoc.actual_rpm — estimated/measured speed (RPM) // hfoc.actual_angle — mechanical angle (degrees) ``` -------------------------------- ### Clarke Transform Source: https://context7.com/sirojudinmunir/stm32-foc/llms.txt Performs the Clarke transform to convert two measured phase currents (ia, ib) into the stationary αβ reference frame. This function assumes a balanced three-phase system where `ia + ib + ic = 0`. ```APIDOC ## Clarke Transform — `clarke_transform` Converts two measured phase currents (ia, ib) into the stationary αβ reference frame. Assumes balanced three-phase system (ia + ib + ic = 0), so only two ADC channels are needed. ```c float ia = 1.5f; // Phase A current in Amperes (from ADC) float ib = -0.8f; // Phase B current in Amperes (from ADC) float i_alpha, i_beta; clarke_transform(ia, ib, &i_alpha, &i_beta); // i_alpha = ia = 1.5 // i_beta = (1/√3)*ia + (2/√3)*ib ≈ 0.866 + (-0.924) = -0.058 ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.