电机学习笔记 ADC dma 电流电压采集

分析

在这里插入图片描述
在电机驱动电路中串入一个 0.02Ω、2W 的采样电阻,将电流信号
转换成电压信号,再经过隔离运放放大 8 倍后差分输出,使用普通运放将差分输出转换成单端输
出给 STM32 的 ADC 采样通道。

从上图中我们可以知道是一个负反馈电路

反馈:将输出信号的一·部分或全部通过反馈网络引回到输入端的过程

反馈网络:一般是阻容元件

看是否存在反馈:结构,输入端与输出端有没有一个反馈网络就是有没有阻容元件连接

反馈的极性:采用瞬时极性法

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

负反馈对放大倍数起到了限制作用,也增大了放大电路的稳定性,

在这里插入图片描述
相同端子,极性相反所以是负反馈

那么根据虚短和虚断可以知道 Up=Un,p 点和 n 点
没有电流到运放的 5 脚和 6 脚,可以得:

在这里插入图片描述
R61 与后面的电容组成 RC 滤波电路,R61 上流过的电流很小,压降也小,可以忽略不计,Vo
等于 Vcurrent_adc。

RC 滤波电路:低通滤波器

将(1)式和(2)式整理可得:

在这里插入图片描述
在这里插入图片描述
电压采样电路
在这里插入图片描述
电源电压采样电路,在电源电压上并联 R18 和 R19、R59 的串联电阻,R19
两端的电压作为隔离运放的输入,再经过隔离器件后差分输出,使用普通运放将差分输
出转换成单端输出,连接到 STM32 的 ADC 采样通道。隔离运放的输入电压为 Vi,则
有:Vi/R19=POWER/(R18+R59+R19),带入电阻值可得:Vi=POWER/37,通过上一节中电流
采样电流的计算方法可以计算得到 POWER_ADC=POWER/37+1.24,不同的是,电压检测部
分的隔离器件是没有进行放大的

在这里插入图片描述

在这里插入图片描述
vi=POWER/37

ADC

ADC 的输入电压范围为: 0~3.3V

在这里插入图片描述

在这里插入图片描述最大的时钟的和典形值,最大36Mhz,fpclk2=APB2

在这里插入图片描述

我们这里4分频,84/4 =21Mhz,这个是ADC的采样时钟

在这里插入图片描述
DMA Requests 一定要开启因为要使用DMA

这里选择左对齐,为了放在16位(半字)数据的高12位,满量程就是 FFF,数据左对齐后就是 FFF0

在这里插入图片描述
要选择软件触发

每个Rank

sampling Time(采样时间)
假如选择的是56周期

(56 + 12) * 21/1 = 3.23us 一次采样时间

这里是3个周期 3+12 =15

这里就是(3 + 12) * 21/1 =一次采样时间,采集5000的话就 * 一次采样时间

后开启DMA

在这里插入图片描述
流固定的,选择半字

ADC 为什么使用半字,因为ADC只用了16位

然后模式选择循环模式

代码生成


__IO uint16_t ADC_ConvertedValue;
static uint16_t adc_buff[ADC_NUM_MAX];    // 电压采集缓冲区
static uint16_t vbus_adc_mean = 0;        // 电源电压 ACD 采样结果平均值
static uint32_t adc_mean_sum = 0;        // 平均值累加
static uint32_t adc_mean_count = 0;      // 累加计数

void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ENABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_LEFT;
  hadc1.Init.NbrOfConversion = 2;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_9;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_8;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  }
  

要开ADC中断
DMA

void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(adcHandle->Instance==ADC1)
  {
  /* USER CODE BEGIN ADC1_MspInit 0 */

  /* USER CODE END ADC1_MspInit 0 */
    /* ADC1 clock enable */
    __HAL_RCC_ADC1_CLK_ENABLE();
  
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**ADC1 GPIO Configuration    
    PB0     ------> ADC1_IN8
    PB1     ------> ADC1_IN9 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* ADC1 DMA Init */
    /* ADC1 Init */
    hdma_adc1.Instance = DMA2_Stream0;
    hdma_adc1.Init.Channel = DMA_CHANNEL_0;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);

    /* ADC1 interrupt Init */
    HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(ADC_IRQn);
  /* USER CODE BEGIN ADC1_MspInit 1 */

  /* USER CODE END ADC1_MspInit 1 */
  }
}
init后不要忘了要开启
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adc_buff, ADC_NUM_MAX);

#define ADC_NUM_MAX 2048 // ADC 转换结果缓冲区最大值
这里ADC_NUM_MAX 1024 + 1024 存放电流和电压

读电流和电压

后在ADC中断回调函数中写

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
  uint32_t adc_mean = 0;

  HAL_ADC_Stop_DMA(hadc);       // 停止 ADC 采样,处理完一次数据在继续采样
  
  /* 计算电流通道采样的平均值 */
  for(uint32_t count = 0; count < ADC_NUM_MAX; count+=2)
  {
    adc_mean += (uint32_t)adc_buff[count];
  }
  
  adc_mean_sum += adc_mean / (ADC_NUM_MAX / 2);    // 累加电压
  adc_mean_count++;
  
#if 1
  
  adc_mean = 0;
  
  /* 计算电压通道采样的平均值 */
  for(uint32_t count = 1; count < ADC_NUM_MAX; count+=2)
  {
    adc_mean += (uint32_t)adc_buff[count];
  }
  
  vbus_adc_mean = adc_mean / (ADC_NUM_MAX / 2);    // 保存平均值
  
#else
  vbus_adc_mean = adc_buff[1];
#endif
  
  HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adc_buff, ADC_NUM_MAX);    // 开始 ADC 采样
}

获取电流值

int32_t get_curr_val(void)
{
  static uint8_t flag = 0;
  static uint32_t adc_offset = 0;    // 偏置电压
  int16_t curr_adc_mean = 0;         // 电流 ACD 采样结果平均值
  
  curr_adc_mean = adc_mean_sum / adc_mean_count;    // 保存平均值
  

    adc_mean_count = 0;
    adc_mean_sum = 0;
    
    if (flag < 17)
    {
      adc_offset = curr_adc_mean;    // 多次记录偏置电压,待系统稳定偏置电压才为有效值
      flag += 1;
    }
    if(curr_adc_mean>=adc_offset)
	{
		curr_adc_mean -= adc_offset;                     // 减去偏置电压
	}else
	{
		curr_adc_mean=0;
	}


  float vdc = GET_ADC_VDC_VAL(curr_adc_mean);      // 获取电压值
  
  return GET_ADC_CURR_VAL(vdc);
}

#define VREF 3.3f // 参考电压,理论上是3.3,可通过实际测量得3.258
#define GET_ADC_VDC_VAL(val) ((float)val/(float)65536.0*VREF) // 得到电压值

#define GET_ADC_CURR_VAL(val) (((float)val)/(float)8.0/(float)0.02*(float)1000.0)
// 得到电流值,电压放大8倍,0.02是采样电阻,单位mA。

#define GET_VBUS_VAL(val) (((float)val-(float)1.24) * (float)37.0) // 电压最大值(测量电压是电源电压的1/37)

读取电压

/**
  * @brief  获取电源电压值
  * @param  无
  * @retval 转换得到的电流值
  */
float get_vbus_val(void)
{
  float vdc = GET_ADC_VDC_VAL(vbus_adc_mean);      // 获取电压值
  
  return GET_VBUS_VAL(vdc);
}

在这里插入图片描述

  • 11
    点赞
  • 98
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值