4. 从dSPACE原型到嵌入式目标处理器的代码移植

任务定义

将dSPACE/Simulink环境中验证通过的控制算法模型,移植为可在嵌入式目标处理器(以STM32F407为例)上独立运行的C代码。核心目标:完成从模型到嵌入式C代码的映射转换,确保控制周期、I/O接口与原型系统一致,并能在目标硬件上编译运行。

移植前准备

原型系统基于dSPACE DS1104配合MATLAB/Simulink搭建,控制对象为一阶惯性环节(模拟被控对象),采样周期T=1ms。原型模型中包含:ADC输入模块(读取模拟电压)、PID控制器(离散化)、DAC输出模块(输出电压)。移植目标为STM32F407VET6开发板,使用其内置ADC1(12位)与DAC1(12位),主频168MHz,定时器TIM6产生1ms中断作为控制节拍。

代码实现


/* main.c - STM32F407 PID控制移植示例 */
#include "stm32f4xx.h"
#include <math.h>

/* 控制参数(与dSPACE原型一致) */
#define KP  2.5f
#define KI  0.8f
#define KD  0.1f
#define TS  0.001f   /* 采样周期1ms */

/* 全局变量 */
static float err_prev = 0.0f;
static float integral = 0.0f;
static volatile uint16_t adc_val = 0;
static volatile uint16_t dac_val = 0;

/* 参考输入(由DAC输出模拟给定) */
#define REF_VALUE  2048   /* 12位DAC中间值,对应1.65V */

/* PID计算函数 */
float PID_Calculate(float setpoint, float measurement)
{
    float err = setpoint - measurement;
    float output;
    
    /* 积分分离:误差过大时停止积分 */
    if (fabs(err) < 100.0f) {
        integral += KI * err * TS;
    }
    
    /* 限幅积分 */
    if (integral > 3000.0f) integral = 3000.0f;
    if (integral < -3000.0f) integral = -3000.0f;
    
    output = KP * err + integral + KD * (err - err_prev) / TS;
    err_prev = err;
    
    /* 输出限幅到12位DAC范围 */
    if (output > 4095.0f) output = 4095.0f;
    if (output < 0.0f) output = 0.0f;
    
    return output;
}

/* TIM6中断服务函数 - 1ms控制周期 */
void TIM6_DAC_IRQHandler(void)
{
    if (TIM6->SR & TIM_SR_UIF) {
        TIM6->SR = ~TIM_SR_UIF;
        
        /* 读取ADC值(实际测量值) */
        ADC1->CR2 |= ADC_CR2_SWSTART;
        while (!(ADC1->SR & ADC_SR_EOC));
        adc_val = ADC1->DR & 0x0FFF;
        
        /* 执行PID计算 */
        float control = PID_Calculate((float)REF_VALUE, (float)adc_val);
        dac_val = (uint16_t)control;
        
        /* 输出到DAC */
        DAC->DHR12R1 = dac_val;
    }
}

/* 系统初始化 */
void SystemInit(void)
{
    /* 时钟配置:168MHz主频 */
    RCC->CR |= RCC_CR_HSEON;
    while (!(RCC->CR & RCC_CR_HSERDY));
    
    RCC->CFGR |= RCC_CFGR_PLLSRC_HSE;
    RCC->CFGR |= RCC_CFGR_PLLM(8) | RCC_CFGR_PLLN(336) | 
                 RCC_CFGR_PLLP(2) | RCC_CFGR_PLLQ(7);
    RCC->CR |= RCC_CR_PLLON;
    while (!(RCC->CR & RCC_CR_PLLRDY));
    
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
    
    /* 使能外设时钟 */
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM6EN | RCC_APB1ENR_DACEN;
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    
    /* GPIO配置:PA0-ADC输入,PA4-DAC输出 */
    GPIOA->MODER |= GPIO_MODER_MODER0 | GPIO_MODER_MODER4;
    GPIOA->PUPDR |= GPIO_PUPDR_PUPD0_0;  /* PA0上拉 */
    
    /* ADC1配置:12位,通道0 */
    ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_SWSTART;
    ADC1->SQR3 = 0;  /* 通道0 */
    
    /* DAC配置:通道1使能 */
    DAC->CR |= DAC_CR_EN1;
    
    /* TIM6配置:1ms中断 */
    TIM6->PSC = 168 - 1;   /* 168MHz/168 = 1MHz */
    TIM6->ARR = 1000 - 1;  /* 1MHz/1000 = 1kHz */
    TIM6->DIER |= TIM_DIER_UIE;
    NVIC_EnableIRQ(TIM6_DAC_IRQn);
    TIM6->CR1 |= TIM_CR1_CEN;
}

int main(void)
{
    SystemInit();
    
    while (1) {
        /* 主循环空闲,控制由中断驱动 */
        __WFI();
    }
}

移植要点说明

上述代码将dSPACE原型中的离散PID控制器直接映射为C语言实现。采样周期由TIM6定时器中断保证1ms精度,与原型系统一致。ADC1通道0读取被控对象反馈电压,DAC1通道1输出控制量。PID参数(KP、KI、KD)与原型模型中的参数完全一致,积分分离和输出限幅逻辑也保持相同策略。

验证方法

编译验证:使用arm-none-eabi-gcc工具链编译,命令如下:


arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 \
  -mfloat-abi=hard -DSTM32F407xx -I./Inc \
  -c main.c -o main.o
arm-none-eabi-ld -T stm32f407.ld main.o -o control.elf
arm-none-eabi-objcopy -O binary control.elf control.bin

硬件验证:将control.bin通过ST-Link烧录至STM32F407开发板。用信号发生器在PA0引脚输入0~3.3V正弦波(频率1Hz),用示波器观察PA4引脚输出波形。判定标准:输出波形应为输入波形的反相放大(因PID负反馈),且无明显滞后(相位差小于采样周期对应的0.36°)。

dSPACE原型系统 ADC输入 PID控制器 DAC输出 采样周期 T = 1ms 控制参数: KP=2.5, KI=0.8, KD=0.1 代码移植 STM32F407嵌入式目标 ADC1 通道0 PID C代码 DAC1 TIM6中断 → 1ms控制周期 主频168MHz, 12位ADC/DAC 验证路径 1. 编译链接 arm-none-eabi-gcc + ld 2. 烧录运行 ST-Link → control.bin 3. 信号观测 示波器: PA4输出波形 判定通过: 输出为输入反相放大波形,相位滞后 < 0.36° 输入: 1Hz正弦波 0~3.3V | 输出: 反相PID控制信号

判定标准

当示波器显示PA4输出波形与PA0输入波形呈反相关系(相位差180°±0.36°),且幅值满足PID控制规律(稳态时输出稳定在参考值附近),即判定移植成功。若输出波形出现明显失真或振荡,需检查PID参数是否与原型一致、采样周期是否准确为1ms。