电机的电流闭环

1原理:

直流电机的扭矩和电流成正比,直流电机的转速和电压成正比关系。什么是扭矩呢?电机可以带动外部部件旋转的力量,在物理上用转矩来描述,单位为:N.m(牛每米)(常用单位有Kg.cm)。大扭矩可以带动比较重的东西。扭矩一般不太好测量,所以可以测量其电流从而去控制扭矩。当我设置一个目标电流当负载变化一直维持目标电流的稳定。

2.位置式PID电流闭环控制列子:

首先还是ADC采集电压从而计算采样电流。

ADC.h

#ifndef __ADC_H__
#define	__ADC_H__
#include "stm32f4xx_hal.h"

#define ADCx_RCC_CLK_ENABLE()            __HAL_RCC_ADC3_CLK_ENABLE()
#define ADCx_RCC_CLK_DISABLE()           __HAL_RCC_ADC3_CLK_DISABLE()
#define ADCx                             ADC3
#define ADC_CURRENT_CHANNEL              ADC_CHANNEL_8   
#define ADC_OVP_IRQx                     ADC_IRQn
#define ADC_OVP_IRQHandler               ADC_IRQHandler

#define DMAx_RCC_CLK_ENABLE()            __HAL_RCC_DMA2_CLK_ENABLE()
#define ADCx_DMA_IRQx                    DMA2_Stream0_IRQn
#define ADCx_DMA_IRQx_Handler            DMA2_Stream0_IRQHandler
#define DMAx_Stream_x                    DMA2_Stream0
#define DMAx_CHANNEL_x                   DMA_CHANNEL_2

#define ADC_CUR_GPIO_ClK_ENABLE()        __HAL_RCC_GPIOF_CLK_ENABLE()
#define ADC_CUR_GPIO                     GPIOF
#define ADC_CUR_GPIO_PIN                 GPIO_PIN_10        


                                 
#define VOLT_REF      3.3f                    // ADC参考电压
/* 根据驱动板设置 放大倍数 采样电阻 */
#define GAIN          6.8f                     // 放大倍数 7.5k/1.068k
#define SAMPLING_RES  0.01f                     // 采样电阻

/** 电压分辨率 =  ADC(Hex) * 3.3 / 2^n * 1000(mV) 单位是mV 
  * STM32的ADC分辨率是n = 12bit,电机控制脉冲是20KHz,理论上采样率40Kz就可以正确采集到波形计算电流,
  * 根据过采样理论:实际采样率 > 40KHz * 4^2,所以可以提高分辨率到14bit.所以这里用 2^14 = 16384
  */
#define VOLT_RESOLUTION     ((float)((VOLT_REF/(float)(16384))*(float)1000)) // ADC 电压
  
/* 总线电压参数相关 */
#define VOLT_MAX                         60.0f // 最大电压值
#define VOLT_MIN                         5.0f  // 最小电压值

/* 总线分压电阻:3.9 kΩ,80.4 kΩ */
#define VOLT_LIMIT_MAX                   (int32_t)((((VOLT_MAX * 3.9f) / (3.9f+80.4f) ) /3.3f) *4096.0f) 
#define VOLT_LIMIT_MIN                   (int32_t)((((VOLT_MIN * 3.9f) / (3.9f+80.4f) ) /3.3f) *4096.0f)

/* 扩展变量 ------------------------------------------------------------------*/
extern ADC_HandleTypeDef hadcx;
extern DMA_HandleTypeDef hdma_adcx;

/* 函数声明 ------------------------------------------------------------------*/
void MX_ADCx_Init(void);
void MX_DMA_Init(void) ;
void SetChannel_Rank1(ADC_HandleTypeDef* hadc,uint32_t Channel);

#endif /* __ADC_H__ */

ADC.c

#include "adc/bsp_adc.h"

ADC_HandleTypeDef hadcx;          /* ADC初始化结构体 */
DMA_HandleTypeDef hdma_adcx;      /* DMA结构体 */

void MX_ADCx_Init(void)
{ 
  ADC_ChannelConfTypeDef sConfig = {0};
 
  /* 外设时钟使能 */
  ADCx_RCC_CLK_ENABLE();
 
  hadcx.Instance = ADCx;
  hadcx.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadcx.Init.Resolution = ADC_RESOLUTION_12B;
  hadcx.Init.ScanConvMode = DISABLE;
  hadcx.Init.ContinuousConvMode = ENABLE;
  hadcx.Init.DiscontinuousConvMode = DISABLE;
  hadcx.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadcx.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadcx.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadcx.Init.NbrOfConversion = 1;
  hadcx.Init.DMAContinuousRequests = ENABLE;
  hadcx.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  HAL_ADC_Init(&hadcx);
  
  /* 配置电流采样通道 */
  sConfig.Channel = ADC_CURRENT_CHANNEL;
  sConfig.Offset = 0;
  sConfig.Rank = 0x01;
  sConfig.SamplingTime = ADC_SAMPLETIME_15CYCLES;
  HAL_ADC_ConfigChannel(&hadcx,&sConfig);

}

void MX_DMA_Init(void) 
{ 
  /* 使能外设时钟 */
  DMAx_RCC_CLK_ENABLE();
  hdma_adcx.Instance = DMAx_Stream_x;
  hdma_adcx.Init.Channel = DMAx_CHANNEL_x;
  hdma_adcx.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdma_adcx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_adcx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_adcx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  hdma_adcx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  hdma_adcx.Init.Mode = DMA_CIRCULAR;
  hdma_adcx.Init.Priority = DMA_PRIORITY_HIGH;
  hdma_adcx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  HAL_DMA_Init(&hdma_adcx);
  
  __HAL_LINKDMA(&hadcx,DMA_Handle,hdma_adcx); /* DMA与ADC与联系 */
  
  /* 外设中断优先级配置和使能中断 */
  HAL_NVIC_SetPriority(ADCx_DMA_IRQx, 1, 1);
  HAL_NVIC_EnableIRQ(ADCx_DMA_IRQx); 
}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  if(hadc->Instance==ADCx)
  {
    /* AD转换通道引脚时钟使能 */
    ADC_CUR_GPIO_ClK_ENABLE();
    
    /* AD转换通道引脚初始化 */
    GPIO_InitStruct.Pin = ADC_CUR_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(ADC_CUR_GPIO, &GPIO_InitStruct);
  }
}



timer.h

#ifndef __BDCMOTOR_TIM_H__
#define __BDCMOTOR_TIM_H__

/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f4xx_hal.h"

#define BDCMOTOR_TIMx                         TIM1
#define BDCMOTOR_TIM_RCC_CLK_ENABLE()         __HAL_RCC_TIM1_CLK_ENABLE()
#define BDCMOTOR_TIM_RCC_CLK_DISABLE()        __HAL_RCC_TIM1_CLK_DISABLE()

// PWM引脚
#define BDCMOTOR_TIM_CH1_GPIO_CLK_ENABLE()    __HAL_RCC_GPIOA_CLK_ENABLE()     
#define BDCMOTOR_TIM_CH1_PORT                 GPIOA                            
#define BDCMOTOR_TIM_CH1_PIN                  GPIO_PIN_8   
  
// PWMN引脚                  
#define BDCMOTOR_TIM_CH1N_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOB_CLK_ENABLE()     
#define BDCMOTOR_TIM_CH1N_PORT                GPIOB                            
#define BDCMOTOR_TIM_CH1N_PIN                 GPIO_PIN_13                      

// PWM使能引脚
#define SHUTDOWN_GPIO_CLK_ENABLE()            __HAL_RCC_GPIOH_CLK_ENABLE()     
#define SHUTDOWN_PORT                         GPIOH                            
#define SHUTDOWN_PIN                          GPIO_PIN_6                       

#define ENABLE_MOTOR()                        HAL_GPIO_WritePin(SHUTDOWN_PORT,SHUTDOWN_PIN,GPIO_PIN_RESET)
#define SHUTDOWN_MOTOR()                      HAL_GPIO_WritePin(SHUTDOWN_PORT,SHUTDOWN_PIN,GPIO_PIN_SET)

#define BDCMOTOR_TIM_CC_IRQx                  TIM1_CC_IRQn
#define BDCMOTOR_TIM_CC_IRQxHandler           TIM1_CC_IRQHandler

// 定义定时器预分频,定时器实际时钟频率为:168MHz/(BDCMOTOR_TIMx_PRESCALER+1)
#define BDCMOTOR_TIM_PRESCALER               1    // 实际时钟频率为:84MHz

// 定义定时器周期,PWM频率为:168MHz/(BDCMOTOR_TIMx_PRESCALER+1)/(BDCMOTOR_TIM_PERIOD+1)
#define BDCMOTOR_TIM_PERIOD                  4199  // PWM频率为84MHz/(3999+1)=21KHz

#define BDCMOTOR_DUTY_ZERO                   (((BDCMOTOR_TIM_PERIOD+1)>>1)-1)       // 0%占空比
#define BDCMOTOR_DUTY_FULL                   (BDCMOTOR_TIM_PERIOD-100)              // 97.625%占空比
                            

// 定义高级定时器重复计数寄存器值
// 实际PWM频率为:168MHz/(BDCMOTOR_TIMx_PRESCALER+1)/(BDCMOTOR_TIM_PERIOD+1)/(BDCMOTOR_TIM_REPETITIONCOUNTER+1)
#define BDCMOTOR_TIM_REPETITIONCOUNTER       0

/* 扩展变量 ------------------------------------------------------------------*/
extern TIM_HandleTypeDef htimx_BDCMOTOR;
extern __IO int16_t PWM_Duty;

/* 函数声明 ------------------------------------------------------------------*/

void BDCMOTOR_TIMx_Init(void);

#endif	/* __BDCMOTOR_TIM_H__ */

timer.c

#include "DCMotor/bsp_BDCMotor.h" 

TIM_HandleTypeDef htimx_BDCMOTOR;
__IO int16_t PWM_Duty=BDCMOTOR_DUTY_ZERO;         // 占空比

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
  /* BDCMOTOR相关GPIO初始化配置 */
  if(htim == &htimx_BDCMOTOR)
  {
    GPIO_InitTypeDef GPIO_InitStruct; 
    /* 引脚端口时钟使能 */
    __HAL_RCC_GPIOE_CLK_ENABLE();
    BDCMOTOR_TIM_CH1_GPIO_CLK_ENABLE();
    BDCMOTOR_TIM_CH1N_GPIO_CLK_ENABLE();
    SHUTDOWN_GPIO_CLK_ENABLE();

    /* BDCMOTOR输出脉冲控制引脚IO初始化 */
    GPIO_InitStruct.Pin = BDCMOTOR_TIM_CH1_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
    HAL_GPIO_Init(BDCMOTOR_TIM_CH1_PORT, &GPIO_InitStruct);
    
    GPIO_InitStruct.Pin = BDCMOTOR_TIM_CH1N_PIN;
    HAL_GPIO_Init(BDCMOTOR_TIM_CH1N_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SHUTDOWN_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = 0;
    HAL_GPIO_Init(SHUTDOWN_PORT, &GPIO_InitStruct);
    
    /* 使能电机控制引脚 */
    ENABLE_MOTOR();
  }
}


void BDCMOTOR_TIMx_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig;             // 定时器时钟
  TIM_OC_InitTypeDef sConfigOC; 
  TIM_BreakDeadTimeConfigTypeDef  sBDTConfig;            // 定时器死区时间比较输出

  
  /* 基本定时器外设时钟使能 */
  BDCMOTOR_TIM_RCC_CLK_ENABLE();
  
  /* 定时器基本环境配置 */
  htimx_BDCMOTOR.Instance = BDCMOTOR_TIMx;                                 // 定时器编号
  htimx_BDCMOTOR.Init.Prescaler = BDCMOTOR_TIM_PRESCALER;                  // 定时器预分频器
  htimx_BDCMOTOR.Init.CounterMode = TIM_COUNTERMODE_UP;                  // 计数方向:向上计数
  htimx_BDCMOTOR.Init.Period = BDCMOTOR_TIM_PERIOD;                        // 定时器周期
  htimx_BDCMOTOR.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;              // 时钟分频
  htimx_BDCMOTOR.Init.RepetitionCounter = BDCMOTOR_TIM_REPETITIONCOUNTER;  // 重复计数器
  /* 初始化定时器比较输出环境 */
  HAL_TIM_PWM_Init(&htimx_BDCMOTOR);

  /* 定时器时钟源配置 */
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;       // 使用内部时钟源
  HAL_TIM_ConfigClockSource(&htimx_BDCMOTOR, &sClockSourceConfig);

  /* 死区刹车配置,实际上配置无效电平是高电平 */
  sBDTConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE ;
  sBDTConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW ;
  sBDTConfig.BreakState = TIM_BREAK_DISABLE ;
  sBDTConfig.DeadTime = 0 ;
  sBDTConfig.LockLevel = TIM_LOCKLEVEL_OFF ;
  sBDTConfig.OffStateIDLEMode= TIM_OSSI_DISABLE ;
  sBDTConfig.OffStateRunMode = TIM_OSSR_ENABLE ;
  HAL_TIMEx_ConfigBreakDeadTime(&htimx_BDCMOTOR,&sBDTConfig);

/* 定时器比较输出配置 */
  sConfigOC.OCMode = TIM_OCMODE_PWM1;                  // 比较输出模式:PWM1模式
  sConfigOC.Pulse =  PWM_Duty;                         // 占空比
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;          // 输出极性
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_LOW;        // 互补通道输出极性
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;           // 快速模式
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;       // 空闲电平
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;     // 互补通道空闲电平
  HAL_TIM_PWM_ConfigChannel(&htimx_BDCMOTOR, &sConfigOC, TIM_CHANNEL_1);

}



main.c

#include "stm32f4xx_hal.h"
#include "key/bsp_key.h"
#include "encoder/bsp_encoder.h"
#include "usart/bsp_usartx.h"
#include "adc/bsp_adc.h"
#include "DCMotor/bsp_BDCMotor.h" 

typedef struct 
{
  __IO int32_t  SetPoint;     //设定目标 Desired Value
  __IO long     SumError;     //误差累计
  __IO float    Proportion;   //比例常数 Proportional Const
  __IO float    Integral;     //积分常数 Integral Const
  __IO float    Derivative;   //微分常数 Derivative Const
  __IO int      LastError;    //上次误差
}PID_TypeDef;


#define ADC_Base      8      // 过采样处理
#define ADC_BUFFER    1024   // 采样数据缓存区 
  
/*************************************/
// 定义PID相关宏
// 这三个参数设定对电机运行影响非常大
// PID参数跟采样时间息息相关
/*************************************/
#define  P_DATA      0.1              // P参数
#define  I_DATA      0.2f             // I参数
#define  D_DATA      0                // D参数
#define DEFAULT_TARGET  50            // 默认的目标值 50mA
/* 私有变量 ------------------------------------------------------------------*/
__IO uint8_t  start_flag = 0;        // PID 开始标志

// 用于保存转换计算后的数值
__IO float ADC_VoltBus;
// AD转换结果值
__IO int16_t ADC_ConvValueHex[ADC_BUFFER];  // AD转换结果
__IO int32_t ADCSum = 0;                    // ADC结果累加值
__IO int32_t AverSum = 0;                   // 平均值的累加值
__IO int32_t AverCnt = 0;                   // 平均值的计数器
__IO uint32_t OffsetCnt_Flag = 0 ;          // 偏差值的计数器标志
uint32_t Motor_Dir = 0;                     // 电机方向
__IO  int32_t OffSetHex ;            // 偏差值
extern __IO uint32_t uwTick;


PID_TypeDef  sPID;

void (*ptr_Fun_)(void) ;//函数指针  ptr_Fun_ = Analyse_Data_Callback;

void SetChannel_Rank1(ADC_HandleTypeDef* hadc,uint32_t Channel);
void PID_ParamInit(void) ;
int32_t CurPIDCalc(int32_t NextPoint);

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
  __HAL_RCC_PWR_CLK_ENABLE();                                     // 使能PWR时钟

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);  // 设置调压器输出电压级别1

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;      // 外部晶振,8MHz
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;                        // 打开HSE 
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;                    // 打开PLL
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;            // PLL时钟源选择HSE
  RCC_OscInitStruct.PLL.PLLM = 8;                                 // 8分频MHz
  RCC_OscInitStruct.PLL.PLLN = 336;                               // 336倍频
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;                     // 2分频,得到168MHz主时钟
  RCC_OscInitStruct.PLL.PLLQ = 7;                                 // USB/SDIO/随机数产生器等的主PLL分频系数
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;       // 系统时钟:168MHz
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;              // AHB时钟: 168MHz
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;               // APB1时钟:42MHz
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;               // APB2时钟:84MHz
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);

  HAL_RCC_EnableCSS();                                            // 使能CSS功能,优先使用外部晶振,内部时钟源为备用
  
 	// HAL_RCC_GetHCLKFreq()/1000    1ms中断一次
	// HAL_RCC_GetHCLKFreq()/100000	 10us中断一次
	// HAL_RCC_GetHCLKFreq()/1000000 1us中断一次
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);                 // 配置并启动系统滴答定时器
  /* 系统滴答定时器时钟源 */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* 系统滴答定时器中断优先级配置 */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
          
/**
  * 函数功能: 主函数.
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 无
  */
int main(void)
{ 
  /* 复位所有外设,初始化Flash接口和系统滴答定时器 */
  HAL_Init();
  /* 配置系统时钟 */
  SystemClock_Config();
  /* 串口初始化 */
  MX_USARTx_Init();
  /* 按键初始化 */
  KEY_GPIO_Init();
  /* 高级控制定时器初始化并配置PWM输出功能 */
  BDCMOTOR_TIMx_Init();
  /* 启动定时器 */
  HAL_TIM_Base_Start(&htimx_BDCMOTOR);
  /* 启动定时器通道和互补通道PWM输出 */
  PWM_Duty = 0;
  __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,PWM_Duty);  // 0%

  /* ADC-DMA 初始化 */
  MX_ADCx_Init();
  MX_DMA_Init();
  /* 启动AD转换并使能DMA传输和中断 */
  HAL_ADC_Start_DMA(&hadcx,(uint32_t*)ADC_ConvValueHex,ADC_BUFFER);  
  __HAL_DMA_DISABLE_IT(&hdma_adcx,DMA_IT_HT);
  __HAL_DMA_DISABLE_IT(&hdma_adcx,DMA_IT_TE);
  __HAL_DMA_DISABLE_IT(&hdma_adcx,DMA_IT_FE);
  __HAL_DMA_DISABLE_IT(&hdma_adcx,DMA_IT_DME);
  
  /* PID 参数初始化 */
  PID_ParamInit();
  /* 无限循环 */
  while (1)
  {
    /* 停止按钮 */
    if(KEY1_StateRead()==KEY_DOWN)
    {
      HAL_TIM_PWM_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1);
      HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
      __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,0);  // 0%
      start_flag = 1;
    }
    if(KEY2_StateRead()==KEY_DOWN)
    {
      HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
      HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);         // 停止输出
    }
    if(KEY3_StateRead()==KEY_DOWN)//加速
    {
      sPID.SetPoint += 500;
      if(PWM_Duty >= 300)
        PWM_Duty = 300;
      
    }
    if(KEY4_StateRead()==KEY_DOWN)//减速
    {
      sPID.SetPoint -= 500;
      if(PWM_Duty <= 0)
        PWM_Duty = 0;
    }
    if(KEY5_StateRead()==KEY_DOWN)    //  换方向
    {
      if(Motor_Dir)
      {
        Motor_Dir = 0;
        HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
        HAL_TIMEx_PWMN_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1);
      }
      else 
      {
        Motor_Dir = 1;
        HAL_TIM_PWM_Start(&htimx_BDCMOTOR,TIM_CHANNEL_1);
        HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
      }
    }
  }
}

/**
  * 函数功能: 系统滴答定时器中断回调函数
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 每发生一次滴答定时器中断进入该回调函数一次
  */
void HAL_SYSTICK_Callback(void)
{
  __IO int32_t ADC_Resul= 0;
  __IO float Volt_Result = 0;
  __IO float ADC_CurrentValue;
  /* 数据反馈周期是50ms,由于电流采集周期大约是 2ms,所以数据反馈周期最好不要低于2ms */
  if((uwTick % 50) == 0)
  {
    if(AverCnt < 0)
      ADC_Resul  = 0;
    else
      ADC_Resul = AverSum/AverCnt ;
    /* 连续采样16次以后,以第17次作为偏差值 */
    OffsetCnt_Flag++;
    if(OffsetCnt_Flag >= 16)
    {
      if(OffsetCnt_Flag == 16)
      {
        OffSetHex = ADC_Resul;
      }
      OffsetCnt_Flag = 32;
      ADC_Resul -= OffSetHex;//减去偏差值
    }
    /* 计算电压值和电流值 */
    Volt_Result = ( (float)( (float)(ADC_Resul) * VOLT_RESOLUTION) );
    ADC_CurrentValue = (float)( (Volt_Result / GAIN) / SAMPLING_RES);

    if(Volt_Result<0)
      Volt_Result = 0;
    /* 清空计数 */
    AverCnt = 0;
    AverSum = 0;
    
    /* 计算电流PID结果 */
    if(start_flag == 1)
    {
      PWM_Duty = CurPIDCalc( (int32_t)ADC_CurrentValue);  // 位置PID
      
      if(PWM_Duty >= BDCMOTOR_DUTY_FULL)
        PWM_Duty = BDCMOTOR_DUTY_FULL;
      if(PWM_Duty <=0)
          PWM_Duty = 0;
      
      __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR, TIM_CHANNEL_1, PWM_Duty); // 设置占空比
    }
           
   printf("Volt: %.1f mV -- Curr: %d mA\n",Volt_Result,(int32_t)ADC_CurrentValue);

  }
}

/**
  * 函数功能: ADC转换完成回调函数
  * 输入参数: hadc:ADC外设设备句柄
  * 返 回 值: 无
  * 说    明: 中断一次的时间是1.479ms,利用过采样和求均值方法,提高分辨率
  */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)  
{
  uint16_t ConvCnt = 0;
  int32_t ADConv = 0 ; 
  /* ADC采集太快,需要先停止再处理数据 */
  HAL_ADC_Stop_DMA(hadc);

  
  /* 取平均 */
  for(ConvCnt = 0; ConvCnt < ADC_BUFFER; ConvCnt++)
  {
    ADConv += ((int32_t)ADC_ConvValueHex[ConvCnt]);
  }
  /* 计算平均值,采样数据设置为2的整数倍,获得14bitsADC值*/
  ADConv >>= ADC_Base;
  /* 累加采样结果并记录采样次数*/
  AverSum += ADConv;
  AverCnt++;
 
  
  HAL_ADC_Start_DMA(hadc,(uint32_t*)ADC_ConvValueHex,ADC_BUFFER);
}


/********************* PID参数初始化 ********************************/
/**
  * 函数功能: PID参数初始化
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 无
  */
void PID_ParamInit() 
{
    sPID.LastError=0;            // Error[-1]
    sPID.SumError = 0;           // 误差累计
    sPID.Proportion=P_DATA;      // 比例常数 Proportional Const
    sPID.Integral=I_DATA;        // 积分常数  Integral Const
    sPID.Derivative=D_DATA;      // 微分常数 Derivative Const
    sPID.SetPoint=DEFAULT_TARGET;// 设定目标Desired Value
}

/******************** 电流闭环 PID 控制设计 ***************************/

/** 
  * 函数名称:电流闭环PID控制设计
  * 输入参数:当前控制量
  * 返 回 值:目标控制量
  * 说    明:无
  */
int32_t CurPIDCalc(int32_t NextPoint)
{
  int32_t iError,dError;
  iError = sPID.SetPoint - NextPoint; //偏差
    /* 设定电流环死区 */
  if((iError >= -5) && (iError <= 5))
  {
    iError = 0;
  }
  sPID.SumError += iError; //积分
  dError = iError - sPID.LastError; //微分
  sPID.LastError = iError;
  return(sPID.Proportion * iError //比例项
  + sPID.Integral * sPID.SumError //积分项
  + sPID.Derivative * dError);    //微分项
}

3.总结

高级定时器1做互补的PWM,  ADC做电流采集。用到了位置式PID算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值