分析
在电机驱动电路中串入一个 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);
}