外观
电流检测
电路分析
在简易万用表的电流测量电路上,同样是通过差分放大电路来进行检测的,只不过使用了专门的差分电流检测芯片TP181,这样做的好处是误差更小。具体的电路分析可前往第二章-电路原理分析进行查看。
电流测量电阻的重点是理清各引脚的作用,考虑到万用表红黑表笔是直接串联在被测电阻中的,没有方向,不同于常用的高低侧电流采样,这里使用的是双向电流采样,通过加入一个参考电压值来进行实现,没有电流时,电压的参考值是1.65V,若有电流流过,电压值应该大于或小于1.65V,这样会牺牲一般的采样率,但是在本项目中也够用了。
在电流测量电阻中,黑表笔是不需要接GND的,测量的是电流采样电阻两端的电压差从而放大得到电压值,最后换算出电流值。
功能实现
创建工程
- 点击左上方文件、选择新建,选择新建MounRiver工程;
- 对工程进行修改,修改完成后点击“完成”按键,系统会自动生成好工程;
- 创建自己的工程库文件夹,分别创建头文件夹与源文件夹,将之前的库文件包含进来,包括蜂鸣器驱动文件、屏幕驱动文件、长按开关机文件、黑表笔公共端导通控制文件、继电器控制文件等,同时新建1个文件,用于电流检测。
- 对工程库路径进行包含;
代码实现
- 电流检测代码与电阻、电压检测基本一致,且没有其余控制代码,仅有一个电流检测ADC引脚,这里按照之前初始化ADC一样的方式对这个ADC引脚进行初始化即可。
电流检测引脚初始化
c
#include "electricity.h"
#include "tftshow.h"
#include "comControl.h"
/*
* 函数内容:初始化电流检测ADC引脚
* 函数参数:无
* 返回值:无
*/
void Init_Electricity_ADC_GPIO(void)
{
ADC_InitTypeDef ADC_InitStructure = {0};
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //ADC时钟分频,最大14Mhz, 96/8 = 12M
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1工作在独立模式
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);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5 );
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_ResetCalibration(ADC1); //重置选择ADC1校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位完成
ADC_StartCalibration(ADC1); //开始ADC1校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准完成
}
/*
* 函数内容:得到任意通道的值
* 函数参数:uint8_t ch--通道值
* 返回值:无
*/
uint16_t Get_ADC_Val(uint8_t ch)
{
uint16_t val;
ADC_RegularChannelConfig( ADC1, ch, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
val = ADC_GetConversionValue(ADC1);
ADC_ClearFlag( ADC1, ADC_FLAG_EOC);
return val;
}
uint16_t Get_ADC_Average(uint8_t ch, uint16_t num)
{
uint16_t i = 0,val = 0;
uint32_t sum_val = 0;
uint16_t max_value = 0,min_value = 9999;
for(i =0;i<num;i++)
{
val = Get_ADC_Val(ch);
if(min_value > val){
min_value = val;
}
if(max_value < val){
max_value = val;
}
sum_val = sum_val +val;
}
sum_val = sum_val - max_value - min_value;
sum_val = sum_val / (num - 2);
return sum_val;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
- 在电流测量任务中,同样是循环读取电压值,然后记录数组取平均值最后换算为电流值,这里的换算需要注意采集的电压值是经过TP181芯片放大过的,实际I=U/R时需要还原回去,否则计算出来的电流值是不对的。
电流检测引脚初始化
c
static void bubble_sort(float arr[], uint16_t len)
{
uint16_t i = 0, j = 0;
float temp = 0;
for (i = 0; i < len - 1; i++)
{
for (j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
/*
* 函数内容:测电流任务
* 函数参数:无
* 返回值:无
*/
static uint8_t first_in = 0;
static float sum_volValue[100] = {0};
static uint16_t num = 0;
void Electricity_Task(void)
{
float TempVolValue = 0, Vol_Value = 0;
uint16_t Electricity_Value = 0,value_sum = 0,ref_vol_value = 0,i = 0;
if(first_in == 0)
{
first_in = 1;
DisCon_GND(); //黑表笔不导通到GND
LCD_ShowChinese(16, 0, "电流模式", BLACK, WHITE, 32, 0); //显示模式
}
ADC_TempSensorVrefintCmd(DISABLE); //取消内部测量
value_sum = Get_ADC_Average(ADC_Channel_1,200);
ADC_TempSensorVrefintCmd(ENABLE); //使能内部基准测量
ref_vol_value = Get_ADC_Average(ADC_Channel_Vrefint,100); //测量内部ADC值,取100次平均值
TempVolValue = ((value_sum*1.0f) / ref_vol_value)*1.2f; //换算为电压值
if(TempVolValue<0.01){ //如果是抖动电压
TempVolValue = 0;
}
sum_volValue[num] = TempVolValue; //记录每一个值
num++;
if(num >= 100) //计满100个
{
num = 0;
bubble_sort(sum_volValue,100); //冒泡排序
for(i=10;i<90;i++) //取中间80个
{
Vol_Value = Vol_Value + sum_volValue[i];
}
Vol_Value = Vol_Value / 80.0f; //取80个的平均值
//LCD_ShowIntNum(50,90,Vol_Value*1000,4,BLACK,WHITE,12);
if(Vol_Value > 1.667f) //此处需要做校准,标准应该是1.65V,但是实际输入值可能会偏大或偏小,这里要先获取实际电压值来进行校准
{
Vol_Value = Vol_Value - 1.667f;
}
else
{
Vol_Value = 1.667f - Vol_Value;
}
LCD_ShowString(0,56,"0~2A",BLACK, WHITE, 16, 0); //显示量程
Electricity_Value = (uint16_t)(Vol_Value / 50.0f / 0.01f * 1000);
LCD_ShowIntNum(40,48,Electricity_Value,4,BLACK,WHITE,32); //显示电流值
LCD_ShowString(126,48,"mA",BLACK, WHITE, 32, 0); //显示单位
for(i=0;i<100;i++) //清除计数
{
sum_volValue[i] = 0;
}
Vol_Value = 0;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
- 最后在主函数中调用该电阻测量任务,当开机后开始不断进行获取计算。
主函数
c
#include "Res_Vol_Control.h"
#include "debug.h"
#include "beep.h"
#include "comControl.h"
#include "ec11.h"
#include "electricity.h"
#include "powerswitch.h"
#include "tftinit.h"
#include "tftshow.h"
#include "main.h"
/* Global typedef */
/* Global define */
/* Global Variable */
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
struct DEVICE_PARAMETER device_parameter;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
device_parameter.device_page = ELECTRICITY_PAGE;
Init_BEEP_GPIO();
Init_Com_GPIO();
Init_EC11_GPIO();
Init_Power_Con_GPIO();
Init_RES_VOL_Con_GPIO();
Init_Electricity_ADC_GPIO();
LCD_Init();
LCD_Fill(0, 0, LCD_W, LCD_H, WHITE);
while(1)
{
key_task();
if(device_state == DEVICE_TURN_ON)
{
if(device_parameter.device_page == ELECTRICITY_PAGE){
Electricity_Task();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
TIP
烧录代码后,接上万用表表笔到电流测量端与COM端,进行测试,看一下实际电流值是否正常。
在实际测试过程中,电压值与真实值肯定会有偏差,此时需要通过增加显示空载的电压值,然后去校准代码中的参数,有可能不是1.65v或你的参考电压本身就不准,这些都是需要调整的。
源码&USB下载工具等软件,均在[gitee资料中](https://gitee.com/chen11232/simple-digital-multimeter)