记录每次学习的过程,总结学习的内容,希望能给到自己和别人帮助。
嵌入式学习-STC-ADC(模数转换器)
ADC
(Analog to Digital Converter 模数转换器)是一种将模拟信号转换为数字信号的电路。在电子系统中,模拟信号常常需要转换为数字信号进行处理和存储。模数转换的基本原理是将模拟信号进行采样,并将采样值量化为数字表示。
● 采样:是指在一定时间间隔内对模拟信号进行测量,并将测量值存储在数字形式的数据中
● 量化:是将这些连续的模拟信号值离散化为一系列数字值,通常使用二进制表示。
简单理解,ADC是把模拟信号转换为数字信号的工具,我们可以认为,一个信号有强弱之分,强弱的体现为电压的高低。在数字电路中,只有0和1之分,也就是高电平或低电平。那么体现不了这个强弱。ADC的作用就是体现强弱,精确化的拿到具体的值。
● 模拟信号:信号有强弱之分,强弱的体现为电压的高低
● 数字电路:只有0和1之分,也就是高电平或低电平
应用场景:
- 医疗设备:如心电图、血压计之类。
- 音频信号处理:在数字音频处理中,ADC将模拟音频信号转换为数字信号,然后可以进行数字信号处理和存储。
- 电力系统:测量电压。
总之,需要知道信号强弱的,需要将模拟信号转为数字信号的都会用到ADC。
主要功能:采集和量化
io设置模式:为了能准确采集,初始化为高阻输入
用4096表示基准电压
2.5 1.25
4096 2048
ADC为12位精度的,意思是最大值是2的12次方,值为4096.
ADC的这个最大值,表示的是最大测量范围:
- 数值最大为4096
- 测量的电压值不能超过基准电压
- 基准电压对应的值为4096
记住:我们用4096表示基准电压。
例如采集到4096的时候,证明是最大电压,这时候会是2.5伏。例如采集到2048的时候,证明当时的电压是1.25伏。
以上原理图中,基准电压由 VREF电压决定。这个电路中用到了一个芯片CJ431/CD431,这是一款电压基准芯片,会恒定的输出2.5V电压。
在我们的设计方案中,理论上可以不使用这个电压基准芯片的,直接连接3V3,但是LDO的输出稳定性不够,因此使用电压基准芯片会更为准确。
由以上我们可以得出:
- 基准电压为:2.5V
- 基准电压对应的数值是4096
- 测量的值为ADC引脚
- 电压值的计算:
反向得到电源输入电压
- 将ADC_Vref+引脚接到VCC管脚
- MCU_Vcc = 4096 * 1.19V / 12位ADC转换结果(CH15)
计算公式:
ADC功能引脚
STC8H芯片有15个通道的ADC功能引脚:
代码流程:
配置IO工作模式
配置ADC ADC_Config();
用 函数来读取采样值
IO初始化为高阻输入
void GPIO_config(void) {
GPIO_InitTypeDef GPIO_InitStructure; //结构定义
GPIO_InitStructure.Pin = GPIO_Pin_5; //指定要初始化的IO,
GPIO_InitStructure.Mode = GPIO_HighZ; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
}
ADC配置逻辑
一般内容不用修改
/******************* AD配置函数 *******************/
void ADC_config(void)
{
ADC_InitTypeDef ADC_InitStructure; //结构定义
ADC_InitStructure.ADC_SMPduty = 31; //ADC 模拟信号采样时间控制, 0~31(注意: SMPDUTY 一定不能设置小于 10)
ADC_InitStructure.ADC_CsSetup = 0; //ADC 通道选择时间控制 0(默认),1
ADC_InitStructure.ADC_CsHold = 1; //ADC 通道选择保持时间控制 0,1(默认),2,3
ADC_InitStructure.ADC_Speed = ADC_SPEED_2X1T; //设置 ADC 工作时钟频率 ADC_SPEED_2X1T~ADC_SPEED_2X16T
ADC_InitStructure.ADC_AdjResult = ADC_RIGHT_JUSTIFIED; //ADC结果调整, ADC_LEFT_JUSTIFIED,ADC_RIGHT_JUSTIFIED
ADC_Inilize(&ADC_InitStructure); //初始化
ADC_PowerControl(ENABLE); //ADC电源开关, ENABLE或DISABLE
NVIC_ADC_Init(DISABLE,Priority_0); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
}
数据读取与转换
result = Get_ADCResult(ADC_CH13);
v = result * 2.5 / 4096;
功能举例:
获取热敏电阻NTC数值
热敏电阻的特性:温度上升,电阻减少
NTC(Negative Temperature Coefficient)是指随温度上升电阻减小、具有负温度系数的热敏电阻现象和材料。
得到电阻,反推温度。
想得到电阻,要得到电压。
阻温特性表
表值过多,有兴趣的可以去浏览 相关所购买的热敏电阻的数据手册,我这里只截取部分作为例子
代码封装(转换成温度输出)
NTC.h
创建NTC.h,写入如下内容
#ifndef __NTC_H__
#define __NTC_H__
// 初始化NTC
void NTC_Initialize();
// 获取温度值
int NTC_get_temperature();
#endif
NTC.c
创建一个NTC.c文件,写入如下内容,请自行将temp_table拷贝进来(因为temp_table较长,我会放在最后面,有需要的话再复制进来这个.c文件中)
#include "NTC.h"
#include "GPIO.h"
#include "ADC.h"
#include "NVIC.h"
#include <stdio.h>
//1.初始化数组------------------------------------------------------------------
// static标志此函数,只能在本文件使用,避免冲突
// code 关键字作用 定义的数据要放在ROM(程序代码存储区)里面,写入后不能再更改
static u16 code temp_table[] = {
58354, // -55
55464, // -54
.....//剩余的不复制,需要复制就到数组定义中复制
}
//2.初始化配置------------------------------------------------------------------
//GPIO配置
static void GPIO_config(void) {
GPIO_InitTypeDef GPIO_InitStructure; //结构定义
// P04 高阻输入 为保证数据精确 热敏电阻用的是P04引脚
GPIO_InitStructure.Pin = GPIO_Pin_4; //指定要初始化的IO,
GPIO_InitStructure.Mode = GPIO_HighZ; //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
}
/******************* AD配置函数 *******************/
static void ADC_config(void)
{
ADC_InitTypeDef ADC_InitStructure; //结构定义
ADC_InitStructure.ADC_SMPduty = 31; //ADC 模拟信号采样时间控制, 0~31(注意: SMPDUTY 一定不能设置小于 10)
ADC_InitStructure.ADC_CsSetup = 0; //ADC 通道选择时间控制 0(默认),1
ADC_InitStructure.ADC_CsHold = 1; //ADC 通道选择保持时间控制 0,1(默认),2,3
ADC_InitStructure.ADC_Speed = ADC_SPEED_2X1T; //设置 ADC 工作时钟频率 ADC_SPEED_2X1T~ADC_SPEED_2X16T
ADC_InitStructure.ADC_AdjResult = ADC_RIGHT_JUSTIFIED; //ADC结果调整, ADC_LEFT_JUSTIFIED,ADC_RIGHT_JUSTIFIED
ADC_Inilize(&ADC_InitStructure); //初始化
ADC_PowerControl(ENABLE); //ADC电源开关, ENABLE或DISABLE
NVIC_ADC_Init(DISABLE,Priority_0); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
}
//内部方法------------------------------------------------------------------
//3.找下标,最接近的值
static int get_pos(float re) {
//定义入参和第一个电阻值的差值为diff
float diff = abs(re - temp_table[0]);
float temp;
//获取数组长度
int n = sizeof(temp_table)/sizeof(temp_table[0]);
int i, min_idx = 0;
//因为已经将第0个电阻值拿出来比较了,所以从1开始
for(i = 1; i < n; i++) {
temp = abs(re - temp_table[i]);
if (diff > temp) {
diff = temp;
//如果有更小的差值,在获取了值之后,还要将这个值的下标记住
min_idx = i;
}
}
return min_idx;
}
//提供给外部的方法------------------------------------------------------------------
// 初始化
void NTC_Initialize() {
GPIO_config();
ADC_config();
}
// 获取温度值
int NTC_get_temperature() {
u16 adc_value;
float vol,re;
int temperature;
// ADC_CH12 ===> P04 根据15个通道的ADC功能引脚查询 我们使用的是P04脚 所以用的是ADC12
adc_value = Get_ADCResult(ADC_CH12);
//拿到采样值之后,代入公式即可算出电压值
vol = adc_value * 2.5 / 4096;
//拿到电压之后,代入公式即可算出电阻值
re = vol * 10 / (3.3 - vol);
//拿到电阻之后,调用函数即可算出温度 因为温度的下标与实际温度之间相差55,所以需要减去55
temperature = get_pos(re*100)-55;
return temperature;
}
main函数使用
int temperature;
NTC_Initialize(); //调用自写的初始化方法
temperature=NTC_get_temperature(); //调用自写的温度函数
printf("温度temperature:%d \n", (int)temperature);
数组定义
采用表的方式来记录 电阻值和温度的关系。
其中,表中记录的是阻值,下标记录的是温度。可以通过阻值比对,查询出下标,下标就是对应的温度。
● 元素是阻值,注释是温度
● code 关键字作用
○ 定义的数据要放在ROM(程序代码存储区)里面,写入后不能再更改
● code关键字: 放到一个代码存储区 ,换一个地方存放的意思
这个就是 NTC.c里面的temp_table,需要用到时候将这一段复制到 NTC.c中去
u16 code temp_table[]= {
58354, // -55
55464, // -54
52698, // -53
50048, // -52
47515, // -51
45097, // -50
42789, // -49
40589, // -48
38492, // -47
36496, // -46
34597, // -45
32791, // -44
31075, // -43
29444, // -42
27896, // -41
26427, // -40
25034, // -39
23713, // -38
22460, // -37
21273, // -36
20148, // -35
19083, // -34
18075, // -33
17120, // -32
16216, // -31
15361, // -30
14551, // -29
13785, // -28
13061, // -27
12376, // -26
11728, // -25
11114, // -24
10535, // -23
9986, // -22
9468, // -21
8977, // -20
8513, // -19
8075, // -18
7660, // -17
7267, // -16
6896, // -15
6545, // -14
6212, // -13
5898, // -12
5601, // -11
5319, // -10
5053, // -9
4801, // -8
4562, // -7
4336, // -6
4122, // -5
3920, // -4
3728, // -3
3546, // -2
3374, // -1
3211, // 0
3057, // 1
2910, // 2
2771, // 3
2639, // 4
2515, // 5
2396, // 6
2284, // 7
2177, // 8
2076, // 9
1978, // 10
1889, // 11
1802, // 12
1720, // 13
1642, // 14
1568, // 15
1497, // 16
1430, // 17
1366, // 18
1306, // 19
1248, // 20
1193, // 21
1141, // 22
1092, // 23
1044, // 24
1000, // 25
957, // 26
916, // 27
877, // 28
840, // 29
805, // 30
771, // 31
739, // 32
709, // 33
679, // 34
652, // 35
625, // 36
600, // 37
576, // 38
552, // 39
530, // 40
509, // 41
489, // 42
470, // 43
452, // 44
434, // 45
417, // 46
401, // 47
386, // 48
371, // 49
358, // 50
344, // 51
331, // 52
318, // 53
306, // 54
295, // 55
284, // 56
274, // 57
264, // 58
254, // 59
245, // 60
236, // 61
228, // 62
220, // 63
212, // 64
205, // 65
198, // 66
191, // 67
184, // 68
178, // 69
172, // 70
166, // 71
160, // 72
155, // 73
150, // 74
145, // 75
140, // 76
135, // 77
131, // 78
126, // 79
122, // 80
118, // 81
115, // 82
111, // 83
107, // 84
104, // 85
101, // 86
97, // 87
94, // 88
91, // 89
89, // 90
86, // 91
83, // 92
81, // 93
78, // 94
76, // 95
74, // 96
71, // 97
69, // 98
67, // 99
65, // 100
63, // 101
61, // 102
60, // 103
58, // 104
56, // 105
55, // 106
53, // 107
52, // 108
50, // 109
49, // 110
47, // 111
46, // 112
45, // 113
43, // 114
42, // 115
41, // 116
40, // 117
39, // 118
38, // 119
37, // 120
36, // 121
35, // 122
34, // 123
33, // 124
32, // 125
};