OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 177|回复: 2

STM32_LCR测量计算参考例程-水帖,易点击

[复制链接]

28

主题

146

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1261
金钱
1261
注册时间
2016-1-23
在线时间
491 小时
发表于 2025-8-21 18:08:49 | 显示全部楼层 |阅读模式
本帖最后由 STM129 于 2025-8-23 12:37 编辑


电子爱好常常要对  电阻R,电容C,电感L  进行测量,电阻R,电容C  万用表就行,对于电感L就有点力不从心
于是问AI,得到一个参考例程:

在实际应用中,校准程序是确保测量准确性的关键步骤。校准程序通常包括对硬件的零点校准和增益校准。对于ADC测量,可以通过测量已知的参考信号来校准ADC的增益和偏移。
以下是一个完整的程序,包括ADC初始化、同步采样、LCR计算、相位角计算以及校准程序。我们将使用两个已知的参考信号(电压和电流)来进行校准。

硬件连接
• 假设使用STM32F103系列单片机。
• 使用ADC1和ADC2进行同步采样,ADC1采样电压信号,ADC2采样电流信号。
• 电压信号和电流信号分别连接到PA1和PA2引脚。
• 基准电阻连接在电流信号路径上,用于测量电流。

配置步骤

• 使能时钟
• 使能GPIOA、ADC1和ADC2的时钟。
• 配置GPIO
• 将PA1和PA2配置为模拟输入模式。
• 配置ADC
• 配置ADC1和ADC2为同步规则模式。
• 设置采样时间、数据对齐方式等参数。
• 启动ADC
• 启动ADC校准。
• 启动ADC转换。
• 读取数据
• 读取ADC1和ADC2的转换结果。
• 计算LCR和相位角
• 根据采样数据计算电感(L)、电容(C)、电阻(R)和相位角(Phase Angle)。
• 校准程序
• 使用已知的参考信号进行零点和增益校准。

示例代码

1.配置文件`adc.h`

```c
#ifndef ADC_H
#define ADC_H

#include "stm32f10x.h"

void ADC_Init_Config(void);
uint16_t ADC_GetValue(ADC_TypeDef* ADCx);

#endif
```
2.配置文件`adc.c`

```c
#include "adc.h"

void ADC_Init_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;

    // 使能GPIOA、ADC1和ADC2时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);

    // 配置PA1和PA2为模拟输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置ADC1
    ADC_InitStructure.ADC_Mode = ADC_Mode_RegInjecSimult;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    // 配置ADC2
    ADC_InitStructure.ADC_Mode = ADC_Mode_RegInjecSimult;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC2, &ADC_InitStructure);

    // 配置ADC1规则组通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_1Cycles5);

    // 配置ADC2规则组通道
    ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 1, ADC_SampleTime_1Cycles5);

    // 启动ADC校准
    ADC_StartCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    while(ADC_GetCalibrationStatus(ADC1));

    ADC_StartCalibration(ADC2);
    while(ADC_GetResetCalibrationStatus(ADC2));
    while(ADC_GetCalibrationStatus(ADC2));

    // 启动ADC
    ADC_Cmd(ADC1, ENABLE);
    ADC_Cmd(ADC2, ENABLE);
}

uint16_t ADC_GetValue(ADC_TypeDef* ADCx)
{
    // 启动ADC转换
    ADC_SoftwareStartConvCmd(ADCx, ENABLE);
    while(ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC) == RESET); // 等待转换完成
    return ADC_GetConversionValue(ADCx); // 获取转换结果
}
```



3.主程序`main.c`

```c
#include "adc.h"
#include "stdio.h"
#include "math.h"
#include "misc.h"

// 假设已配置好串口,用于打印结果
void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 使能GPIOA和USART1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    // 配置USART1 TX引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置USART1
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);

    // 使能USART1
    USART_Cmd(USART1, ENABLE);
}

// 打印函数
void USART_SendString(char* str)
{
    while(*str)
    {
        USART_SendData(USART1, *str++);
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
    }
}

// 校准函数
void ADC_Calibration(float* voltage_offset, float* current_offset, float* voltage_gain, float* current_gain)
{
    // 假设已知的参考电压和电流
    float known_voltage = 1.0; // 已知参考电压(V)
    float known_current = 0.01; // 已知参考电流(A)

    // 读取ADC值
    uint16_t adc1_value = ADC_GetValue(ADC1);
    uint16_t adc2_value = ADC_GetValue(ADC2);

    // 计算偏移和增益
    *voltage_offset = (float)adc1_value * 3.3 / 4095.0 - known_voltage;
    *current_offset = (float)adc2_value * 3.3 / 4095.0 - known_current;

    *voltage_gain = 3.3 / 4095.0;
    *current_gain = 3.3 / 4095.0;
}

// 计算LCR和相位角
void Calculate_LCR_Phase(float voltage, float current, float frequency, float reference_resistance, float* resistance, float* inductance, float* capacitance, float* phase_angle)
{
    *resistance = voltage / current; // 欧姆定律计算电阻

    // 计算相位角
    float voltage_phase = atan2f(voltage, current); // 电压相位
    float current_phase = 0; // 假设电流相位为0
    *phase_angle = voltage_phase - current_phase; // 相位差

    // 计算电感和电容
    if (frequency > 0)
    {
        *inductance = (*resistance) / (2 * 3.14159 * frequency);
        *capacitance = 1 / (2 * 3.14159 * frequency * (*resistance));
    }
    else
    {
        *inductance = 0;
        *capacitance = 0;
    }
}

int main(void)
{
    float voltage, current, resistance, inductance, capacitance, phase_angle;
    float voltage_offset, current_offset, voltage_gain, current_gain;
    uint16_t adc1_value, adc2_value;
    float frequencies[] = {100, 1000, 10000, 25000, 50000};
    float reference_resistances[] = {22, 100, 10000};
    int num_frequencies = sizeof(frequencies) / sizeof(frequencies[0]);
    int num_resistances = sizeof


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

3

主题

45

帖子

0

精华

高级会员

Rank: 4

积分
609
金钱
609
注册时间
2016-12-1
在线时间
124 小时
发表于 2025-8-22 10:12:10 | 显示全部楼层
回复 支持 反对

使用道具 举报

28

主题

146

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1261
金钱
1261
注册时间
2016-1-23
在线时间
491 小时
 楼主| 发表于 2025-8-22 22:43:50 | 显示全部楼层

使用ADC1和ADC2进行同步采样,ADC1采样电压信号,ADC2采样电流信号。


电压信号和电流信号分别连接到PA1和PA2引脚。

基准电阻连接在电流信号路径上,用于测量电流。

/*------------------------------------------------------*/
1.程序概述
这个程序的主要目标是通过STM32的ADC同步采样,测量电压和电流信号,并使用DFT计算电压和电流的相位角,进而计算LCR参数(电阻R、电感L、电容C)。


2.程序结构
程序分为以下几个部分:

• ADC初始化:配置ADC1和ADC2进行同步采样。

• 串口初始化:配置串口用于打印结果。

• DFT计算:使用DFT计算电压和电流的频域表示。

• LCR参数计算:根据DFT结果计算LCR参数。


3.详细解释


3.1 ADC初始化
在`adc.c`中,我们配置了两个ADC(ADC1和ADC2)进行同步采样。ADC1采样电压信号,ADC2采样电流信号。


```c
void ADC_Init_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;

    // 使能GPIOA、ADC1和ADC2时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);

    // 配置PA1和PA2为模拟输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置ADC1
    ADC_InitStructure.ADC_Mode = ADC_Mode_RegInjecSimult;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    // 配置ADC2
    ADC_InitStructure.ADC_Mode = ADC_Mode_RegInjecSimult;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC2, &ADC_InitStructure);

    // 配置ADC1规则组通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_1Cycles5);

    // 配置ADC2规则组通道
    ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 1, ADC_SampleTime_1Cycles5);

    // 启动ADC校准
    ADC_StartCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    while(ADC_GetCalibrationStatus(ADC1));

    ADC_StartCalibration(ADC2);
    while(ADC_GetResetCalibrationStatus(ADC2));
    while(ADC_GetCalibrationStatus(ADC2));

    // 启动ADC
    ADC_Cmd(ADC1, ENABLE);
    ADC_Cmd(ADC2, ENABLE);
}
```



3.2 串口初始化
在`main.c`中,我们配置了串口用于打印结果。


```c
void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 使能GPIOA和USART1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    // 配置USART1 TX引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置USART1
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);

    // 使能USART1
    USART_Cmd(USART1, ENABLE);
}
```



3.3 DFT计算
DFT(离散傅里叶变换)用于将时间域信号转换为频域信号。我们使用DFT计算电压和电流的实部和虚部。


```c
void DFT(float* signal, int length, float* real, float* imag)
{
    for (int k = 0; k < length; k++)
    {
        real[k] = 0;
        imag[k] = 0;
        for (int n = 0; n < length; n++)
        {
            real[k] += signal[n] * cosf(2 * M_PI * k * n / length);
            imag[k] += signal[n] * sinf(2 * M_PI * k * n / length);
        }
    }
}
我将详细解释`Calculate_LCR`函数的计算过程。这个函数的目的是根据电压和电流的实部和虚部,计算出电阻(R)、电感(L)和电容(C)。这些计算基于DFT(离散傅里叶变换)的结果,以及电路的基本原理。


1.输入参数

&#8226; `voltage_real`和`voltage_imag`:电压信号的实部和虚部。

&#8226; `current_real`和`current_imag`:电流信号的实部和虚部。

&#8226; `frequency`:信号的频率。

&#8226; `reference_resistance`:基准电阻的值,用于计算电流。

&#8226; `resistance`、`inductance`和`capacitance`:输出参数,分别存储计算得到的电阻、电感和电容。


2.计算步骤


2.1 计算电压和电流的幅度
电压和电流的幅度可以通过欧几里得距离(即模)来计算:

```c
float voltage_magnitude = sqrtf(voltage_real * voltage_real + voltage_imag * voltage_imag);
float current_magnitude = sqrtf(current_real * current_real + current_imag * current_imag);
```


&#8226; `voltage_magnitude`是电压信号的幅度。

&#8226; `current_magnitude`是电流信号的幅度。


2.2 计算相位角
相位角是电压和电流之间的相位差,可以通过`atan2f`函数计算:

```c
float phase_angle = atan2f(voltage_imag, voltage_real) - atan2f(current_imag, current_real);
```


&#8226; `atan2f(voltage_imag, voltage_real)`计算电压信号的相位角。

&#8226; `atan2f(current_imag, current_real)`计算电流信号的相位角。

&#8226; `phase_angle`是电压和电流之间的相位差。


2.3 计算电阻
电阻可以通过电压和电流的幅度以及相位角来计算:

```c
*resistance = voltage_magnitude / current_magnitude * cosf(phase_angle);
```


&#8226; `voltage_magnitude / current_magnitude`是电压和电流的幅度比,即阻抗的模。

&#8226; `cosf(phase_angle)`是相位角的余弦值,表示阻抗的实部(即电阻)的比例。


2.4 计算电感
电感可以通过电阻和频率来计算:

```c
*inductance = *resistance / (2 * M_PI * frequency);
```


&#8226; `2 * M_PI * frequency`是角频率\(\omega\)。

&#8226; 电感\(L\)和电阻\(R\)之间的关系为\(L=\frac{R}{\omega}\)。


2.5 计算电容
电容也可以通过电阻和频率来计算:

```c
*capacitance = 1 / (2 * M_PI * frequency * *resistance);
```


&#8226; 电容\(C\)和电阻\(R\)之间的关系为\(C=\frac{1}{\omega R}\)。


3.公式解释


3.1 电压和电流的幅度
电压和电流的幅度可以通过欧几里得距离来计算:
\[\text{voltage\_magnitude}=\sqrt{(\text{voltage\_real})^2+(\text{voltage\_imag})^2}\]
\[\text{current\_magnitude}=\sqrt{(\text{current\_real})^2+(\text{current\_imag})^2}\]


3.2 相位角
相位角是电压和电流之间的相位差,可以通过`atan2f`函数计算:
\[\text{phase\_angle}=\text{atan2f}(\text{voltage\_imag},\text{voltage\_real})-\text{atan2f}(\text{current\_imag},\text{current\_real})\]


3.3 电阻
电阻可以通过电压和电流的幅度以及相位角来计算:
\[R=\frac{\text{voltage\_magnitude}}{\text{current\_magnitude}}\cdot\cos(\text{phase\_angle})\]


3.4 电感
电感可以通过电阻和频率来计算:
\[L=\frac{R}{\omega}=\frac{R}{2\pi f}\]


3.5 电容
电容可以通过电阻和频率来计算:
\[C=\frac{1}{\omega R}=\frac{1}{2\pi f R}\]


4.注意事项

&#8226; 相位角的单位:`atan2f`函数返回的相位角单位是弧度。

&#8226; 频率单位:频率\(f\)的单位是赫兹(Hz)。

&#8226; 基准电阻:基准电阻用于将电流信号转换为电压信号,以便通过ADC测量。


5.示例
假设:

&#8226; 电压信号的实部和虚部分别为 1.0 和 0.5。

&#8226; 电流信号的实部和虚部分别为 0.1 和 0.05。

&#8226; 信号频率为 1000 Hz。

&#8226; 基准电阻为 100 欧姆。

计算过程如下:

&#8226; 电压和电流的幅度:
\[\text{voltage\_magnitude}=\sqrt{1.0^2+0.5^2}=\sqrt{1.25}\approx 1.118\]
\[\text{current\_magnitude}=\sqrt{0.1^2+0.05^2}=\sqrt{0.0125}\approx 0.1118\]


&#8226; 相位角:
\[\text{phase\_angle}=\text{atan2f}(0.5,1.0)-\text{atan2f}(0.05,0.1)\approx 0.4636-0.0499\approx 0.4137\]


&#8226; 电阻:
\[R=\frac{1.118}{0.1118}\cdot\cos(0.4137)\approx 10.0\cdot 0.9135\approx 9.135\]


&#8226; 电感:
\[L=\frac{9.135}{2\pi\cdot 1000}\approx 1.45\times 10^{-3}\text{H}\]


&#8226; 电容:
\[C=\frac{1}{2\pi\cdot 1000\cdot 9.135}\approx 1.75\times 10^{-5}\text{F}\]

通过这些步骤,我们可以准确地计算出电阻、电感和电容。


回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-9-2 22:04

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表