直流有刷电机的电流采集及过流过压保护

1.驱动板的原理图如下:

驱动板怎么驱动电机就简单说下采用的是单极模式正转时Q1,Q2接PWM信号并且是互补的PWM信号Q1接PWM,Q2接PWMN.而Q3截止,Q4导通。Q1与Q4导通有电流从左到右流。当Q1截止时。Q2与Q4导通由于自感电动势的原因也会有电流从左到右流,但是电流是衰减的。反转时Q3,Q4接PWM。Q1截止Q2导通。电流从右到左流的。(具体跟正转一样的分析)。

 

 图中的Current_amp点,是电流采集通道,DC-BUS是电压采集通道。分别接在对应MCU的PF10(ADC3的通道8),PF8(ADC3的通道6)。Currennt_amp点采集到的电压可以求出采样电流的值。

2.dome列子

1.电压,电流采集

ADC.h

#ifndef __ADC_H__
#define	__ADC_H__

#include "stm32f4xx_hal.h"

// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
/********************ADC输入通道(引脚)配置**************************/
#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_VOLT_CHANNEL                 ADC_CHANNEL_6
#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 ADC_VOLT_GPIO_ClK_ENABLE()       __HAL_RCC_GPIOF_CLK_ENABLE()
#define ADC_VOLT_GPIO                    GPIOF
#define ADC_VOLT_GPIO_PIN                GPIO_PIN_8


//  Vcurrent_amp=1.65v + 7.5K/(1K+68)VIsensor 根据公式求VIsensor电压从而求出采样电流
                                 
#define VOLT_REF                3.3f     // ADC参考电压
/* 根据驱动板设置放大倍数 和 采样电阻 */
#define GAIN                    6.8f      // 放大倍数 因为VIsensor太小ADC采集不到进行放大根据上面公式求出

#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 电压分辨率,单位:0.201mV
#define VOLTBUS_RESOLUTION  ((float)( 3.3f/(float)4096) * (42.4f+42.4f) / 3.9f)


/* 总线电压参数相关 */
#define VOLT_MAX                         60.0f // 最大电压值
#define VOLT_MIN                         12.0f

/* 总线分压电阻:3.9 kΩ,80.4 kΩ 把最大电压与最小电压转换为16进制*/
#define VOLT_LIMIT_MAX                   (int32_t)((((VOLT_MAX * 3.9f) / (3.9f+42.4f) ) /3.9f) *4096.0f) 
#define VOLT_LIMIT_MIN                   (int32_t)((((VOLT_MIN * 3.9f) / (3.9f+42.4f) ) /3.9f) *4096.0f)              //分压电阻:42.4kΩ.3.9kΩ


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

/* 函数声明 ------------------------------------------------------------------*/
void MX_ADCx_Init(void);
void MX_DMA_Init(void) ;
void SetChannelAsRank1(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结构体
__IO int32_t OffSetHex = 0;

void MX_ADCx_Init(void)
{ 
  ADC_ChannelConfTypeDef sConfig;
  ADC_AnalogWDGConfTypeDef AWDGConfig; // ADC看门狗结构体
 
  /* 外设时钟使能 */
  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_28CYCLES;
  HAL_ADC_ConfigChannel(&hadcx,&sConfig);

  /* 配置总线电压采集 */
  /* 模拟看门狗配置 */
  AWDGConfig.Channel = ADC_VOLT_CHANNEL;  // 哪个通道启动看门狗
  AWDGConfig.HighThreshold = VOLT_LIMIT_MAX; // 设置上限
  AWDGConfig.LowThreshold = VOLT_LIMIT_MIN;  // 设置下限 
  AWDGConfig.ITMode = ENABLE;                // 开启中断
  AWDGConfig.WatchdogMode = ADC_ANALOGWATCHDOG_SINGLE_REG; // 看门狗模式
  AWDGConfig.WatchdogNumber = 0; // Reserved for future use, can be set to 0
  HAL_ADC_AnalogWDGConfig(&hadcx,&AWDGConfig);
  
  sConfig.Channel = ADC_VOLT_CHANNEL;
  sConfig.Offset = 0;
  sConfig.Rank = 0x02;
  sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
  HAL_ADC_ConfigChannel(&hadcx,&sConfig);
  
  HAL_NVIC_SetPriority(ADC_OVP_IRQx, 0, 1);
  HAL_NVIC_EnableIRQ(ADC_OVP_IRQx); 
}

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);
  
  /* 外设中断优先级配置和使能中断 */
  HAL_NVIC_SetPriority(ADCx_DMA_IRQx, 1, 1);
  HAL_NVIC_EnableIRQ(ADCx_DMA_IRQx); 
}

/**
  * 函数功能: ADC外设初始化配置
  * 输入参数: hadc:AD外设句柄类型指针
  * 返 回 值: 无
  * 说    明: 该函数被HAL库内部调用
  */
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);
    
    GPIO_InitStruct.Pin = ADC_VOLT_GPIO_PIN;
    HAL_GPIO_Init(ADC_VOLT_GPIO, &GPIO_InitStruct);
  }
}


/*
  * 函数功能: 设置AD转换通道的转换顺序为1
  * 输入参数: hadc ADC句柄 , Channel可以是ADC_VOLT_CHANNEL,ADC_CURRENT_CHANNEL
  * 返 回 值: 无
  * 说    明: 无
  */
void SetChannelAsRank1(ADC_HandleTypeDef* hadc,uint32_t Channel)
{
  ADC_ChannelConfTypeDef sConfig;
  if(Channel == ADC_VOLT_CHANNEL)
  {
    /* 配置电压通道 */
    sConfig.Channel = ADC_VOLT_CHANNEL;
    sConfig.Offset = 0;
    sConfig.Rank = 0x01;
    sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
    HAL_ADC_ConfigChannel(&hadcx,&sConfig);
    /* 配置电流通道 */
    sConfig.Channel = ADC_CURRENT_CHANNEL;
    sConfig.Rank = 0x02;
    HAL_ADC_ConfigChannel(&hadcx,&sConfig);
  }
  else
  {
    /* 配置电流通道 */
    sConfig.Channel = ADC_CURRENT_CHANNEL;
    sConfig.Offset = 0;
    sConfig.Rank = 0x01;
    sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES;
    HAL_ADC_ConfigChannel(&hadcx,&sConfig);
    /* 配置电压通道 */
    sConfig.Channel = ADC_VOLT_CHANNEL;
    sConfig.Rank = 0x02;
    HAL_ADC_ConfigChannel(&hadcx,&sConfig);
  }
}


time.h  做互补PWM

#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()

#define BDCMOTOR_TIM_CH1_GPIO_CLK_ENABLE()    __HAL_RCC_GPIOA_CLK_ENABLE()     // 输出PWM脉冲给电机控制器的的IN引脚
#define BDCMOTOR_TIM_CH1_PORT                 GPIOA                            // CH1和CH1N两个引脚配套使用
#define BDCMOTOR_TIM_CH1_PIN                  GPIO_PIN_8                       // 如果电机接在驱动器的OUT1和OUT2端子上
#define BDCMOTOR_TIM_CH1N_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOB_CLK_ENABLE()     // CH1和CH1N对应接在IN1和IN2
#define BDCMOTOR_TIM_CH1N_PORT                GPIOB                            // 如果电机接在驱动器的OUT3和OUT4端子上
#define BDCMOTOR_TIM_CH1N_PIN                 GPIO_PIN_13                      // CH1和CH1N对应接在IN3和IN4

#define SHUTDOWN_GPIO_CLK_ENABLE()            __HAL_RCC_GPIOH_CLK_ENABLE()     // CH1和CH1N对应接在IN1和IN2
#define SHUTDOWN_PORT                         GPIOH                            // 如果电机接在驱动器的OUT3和OUT4端子上
#define SHUTDOWN_PIN                          GPIO_PIN_6                       // CH1和CH1N对应接在IN3和IN4

#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/(4199+1)=20KHz

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

// 定义高级定时器重复计数寄存器值
// 实际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;// 占空比:PWM_Duty/BDCMOTOR_TIM_PERIOD*100%

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);

    __HAL_RCC_GPIOE_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_11;
    HAL_GPIO_Init(GPIOE, &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();
  }
}

/**
  * 函数功能: BDCMOTOR定时器初始化
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 无
  */
void BDCMOTOR_TIMx_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig;             // 定时器时钟
  TIM_OC_InitTypeDef sConfigOC; 
  TIM_BreakDeadTimeConfigTypeDef  sBDTConfig;            // 定时器死区时间比较输出
//  TIM_MasterConfigTypeDef sMasterConfig;
  
  /* 基本定时器外设时钟使能 */
  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 "DCMotor/bsp_BDCMotor.h" 
#include "key/bsp_key.h"
#include "usart/bsp_usartx.h"
#include "adc/bsp_adc.h"
#include "stdlib.h"

#define ADC_Base      8                     // 取2的整数倍作为缓存区大小,得到14bits的ADC值

#define ADC_BUFFER    1024                  // 采样数据缓存区 


// 用于保存转换计算后的数值
__IO float ADC_VoltValue;
__IO float ADC_VoltBus; /* PF8电压的采集值 */

// 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 ;                   // 电机方向 
extern __IO  int32_t OffSetHex ;            // 偏差值
extern __IO uint32_t uwTick;


__IO int32_t CaptureNumber = 0;      // 输入捕获数

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 初始化 */
  HAL_Delay(100);
  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);  // 失能一些DMA中断标志
  __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);
  
  /* 无限循环 */
  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%
    }
    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)//加速
    {
      PWM_Duty += 200;
      if(PWM_Duty >=BDCMOTOR_DUTY_FULL)
        PWM_Duty = BDCMOTOR_DUTY_FULL;
      __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,PWM_Duty);
    }
    if(KEY4_StateRead()==KEY_DOWN)//减速
    {
      PWM_Duty -= 200;
      if(PWM_Duty <=0)
        PWM_Duty = 0;
      __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,PWM_Duty);
    }
    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)
  {
    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);


    /* 清空计数 */
    AverCnt = 0;
    AverSum = 0;      
      printf("Volt: %.1f mV -- Curr: %d mA\n",Volt_Result,(int32_t) 
     (ADC_CurrentValue+17)); // 电机未启动有驱动板有17mA

  }
}

/**
  * 函数功能: 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);
  /* 采集总线电压 */
  SetChannelAsRank1(hadc,ADC_VOLT_CHANNEL);
  HAL_ADC_Start(hadc);
  // ADC设置先电流采集,并且ADC通道数设置为1。电压采集与电流采集要轮流切换采集
  /* 取平均值 */
  for(ConvCnt = 0; ConvCnt < (ADC_BUFFER ); ConvCnt++)
  {
    ADConv += ((int32_t)ADC_ConvValueHex[ConvCnt]);
  }
  /* 计算平均值,采样数据设置为2的整数倍,获得14bitsADC值*/
  ADConv >>= ADC_Base;
  /* 累加采样结果并记录采样次数*/
  AverSum += ADConv;
  AverCnt++;
 
  HAL_ADC_Stop(hadc);
  
  SetChannelAsRank1(hadc,ADC_CURRENT_CHANNEL);
  
  HAL_ADC_Start_DMA(hadc,(uint32_t*)ADC_ConvValueHex,ADC_BUFFER);
}

/**
  * 函数功能: ADC看门狗中断回调函数
  * 输入参数: ADC句柄
  * 返 回 值: 无
  * 说    明: ADC窗口看门狗,检测到电压过低或者过高的时候就调用这个函数,停止输出.
  */
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc)
{
    /* 使能电机控制引脚 */
  static uint8_t i = 0;
  i++;
  if(ADC_VoltBus > VOLT_LIMIT_MIN  && ADC_VoltBus < VOLT_LIMIT_MAX)
    i = 0 ;
  else if(i>=6)
  {
    SHUTDOWN_MOTOR();
    HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
    HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
    PWM_Duty = 0;
//    ADC_VoltBus = (float)ADC_VoltBus * VOLTBUS_RESOLUTION;// ADC_VoltBus是在中断响应函数中读取的adc值
     
    printf("Bus Voltage is out of range!!\n");
    printf("Please Reset the Target!\n");
    while(1);  // PF8 的电压不在范围内就会触发看门狗 然后就一直死循环
  }
}




void ADC_OVP_IRQHandler(void)
{

   /* 读取总线电压值 */
  ADC_VoltBus = HAL_ADC_GetValue(&hadcx);  // PF8的电压采集
  HAL_ADC_IRQHandler(&hadcx);

}

其实PF8(电压采集在超出范围启动看门狗,然后在看门狗中断里采集电压。不超过不会采集。

2.过流保护

 

Current_amp点的电压是求采样电流的大小。先设置一个最大电流,如果采样电流超过这个最大电流就停止PWM输出从而电机也停止起到保护作用。采样电流是否在安全电流取决Current_amp点的电压,当Current_amp电压大于Verf电压比较器输出(接在SD引脚))低电平。

ADC代码,timer(产生互补PWM)跟上个列子一样直接看main.c


#include "stm32f4xx_hal.h"
#include "DCMotor/bsp_BDCMotor.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"
#include "stdlib.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
#define ADC_Base      8                      // 取2的整数倍作为缓存区大小,得到14bits的ADC
#define ADC_BUFFER    1024                      // 采样数据缓存区

#define CURRENT_MAX   400.0f         // 最大电流值 400 mA

/* 私有变量 ------------------------------------------------------------------*/

// 用于保存转换计算后的数值
__IO float ADC_VoltValue;
__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 ;                   // 电机方向
extern __IO  int32_t OffSetHex ;            // 偏差值
extern __IO uint32_t uwTick;
static __IO uint32_t OverCurCount;          // 过流次数记录

/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
  * 函数功能: 系统时钟配置
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 无
  */
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 = 100;
  __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,PWM_Duty);  // 0%
  /* ADC-DMA 初始化 */
  HAL_Delay(100);
  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);

  /* 无限循环 */
  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%
    }
    if(KEY2_StateRead()==KEY_DOWN)
    {
      HAL_TIM_PWM_Stop(&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%
    }
    if(KEY3_StateRead()==KEY_DOWN)//加速
    {
      PWM_Duty += 200;
      if(PWM_Duty >=BDCMOTOR_DUTY_FULL)
        PWM_Duty = BDCMOTOR_DUTY_FULL;
      __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,PWM_Duty);
    }
    if(KEY4_StateRead()==KEY_DOWN)//减速
    {
      PWM_Duty -= 200;
      if(PWM_Duty <=0)
        PWM_Duty = 0;
      __HAL_TIM_SET_COMPARE(&htimx_BDCMOTOR,TIM_CHANNEL_1,PWM_Duty);
    }
    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)
  {
    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);
    
    printf("Volt: %.2f -- Cur: %.2f mA\n",Volt_Result,ADC_CurrentValue);
    /* 清空计数 */
    AverCnt = 0;
    AverSum = 0;
        /* 过流保护 */
    if(OffsetCnt_Flag >= 32 )
    {
      if(ADC_CurrentValue >= CURRENT_MAX )  // 检测到五次如果超过最大电流就关掉PWM输出
      {
        OverCurCount++;
        if(OverCurCount >= 5)
        {
          printf("Over Current %.2f \n",ADC_CurrentValue);
          printf("Please reset the target!!\n");
          SHUTDOWN_MOTOR();
          HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
          HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
          OverCurCount = 0;
          while(1);
        }
      }
      else
        OverCurCount = 0;
    }
  }
}

/**
  * 函数功能: 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);
  /* 采集总线电压 */
  SetChannelAsRank1(hadc,ADC_VOLT_CHANNEL);
  HAL_ADC_Start(hadc);

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

  HAL_ADC_Stop(hadc);
  SetChannelAsRank1(hadc,ADC_CURRENT_CHANNEL);

  HAL_ADC_Start_DMA(hadc,(uint32_t*)ADC_ConvValueHex,ADC_BUFFER);

}

/**
  * 函数功能: ADC看门狗中断回调函数
  * 输入参数: ADC句柄
  * 返 回 值: 无
  * 说    明: ADC窗口看门狗,检测到电压过低或者过高的时候就调用这个函数,停止输出.
  */
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc)
{
  /* 使能电机控制引脚 */
  static uint8_t i = 0;
  i++;
  if(ADC_VoltBus > VOLT_LIMIT_MIN  && ADC_VoltBus < VOLT_LIMIT_MAX)
    i = 0 ;
  else if(i>=6)
  {
    SHUTDOWN_MOTOR();
    HAL_TIM_PWM_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
    HAL_TIMEx_PWMN_Stop(&htimx_BDCMOTOR,TIM_CHANNEL_1);
    PWM_Duty = 0;
//    ADC_VoltBus = (float)ADC_VoltBus * VOLTBUS_RESOLUTION;// ADC_VoltBus是在中断响应函数中读取的adc值
     
    printf("Bus Voltage is out of range!!\n");
    printf("Please Reset the Target!\n");
    while(1);
  }
}


void ADC_OVP_IRQHandler(void)
{
  /* 读取总线电压值 */
  ADC_VoltBus = HAL_ADC_GetValue(&hadcx);
  HAL_ADC_IRQHandler(&hadcx);

}

跟上个列子差不多只是加了限流保护。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值