9.1 ADC简介
AD转换器(Analog-to-Digital Converter,模数转换器)是一种将连续的模拟信号(如电压、电流)转换为离散的数字信号(二进制代码)的电子器件。它是连接现实模拟世界与数字系统的桥梁,广泛应用于数据采集、传感器信号处理、通信系统等领域。
LPC1100系列中有10位逐次逼近式ADC,在8个引脚中实现输入多路复用。逐次逼近式在算法上就叫做二分,通过十次二分,求得引脚上的电压值。通过读到的十位数字,线性映射到电压上即可。具体如下图
因为每次二分都会比较出来一位,具体如下
-
第一次二分:将输入电压与中间值(Vref/2Vref/2)比较,确定输入电压是在上半部分还是下半部分。
-
第二次二分:根据第一次的结果,将范围缩小到 Vref/4Vref/4 或 3Vref/43Vref/4,再次比较。
-
重复:每次比较都将范围缩小一半,直到完成 10 次二分搜索,最终确定输入电压的精确值。
得到十位结果之后,除以2^10 - 1 再乘以 3.3v就是最终得到的电压
实际使用时,以上内部二分原理用不到(≈白学了,实则不然,更清晰了),我们只需要启动ADC,等待转换完成,从数据寄存器读取结果然后套个公式就行了。
一句话,ADC就是模拟转数字求引脚电压的。
9.2 被采样器件 KNTC0603/10KF3950 简介
9.2.1 基本特性
KNTC0603/10KF3950 是一种 负温度系数热敏电阻(NTC Thermistor) 的型号。
NTC(Negative Temperature Coefficient)热敏电阻,即电阻值随温度升高而减小
-
封装:0603(表示尺寸为 0.06 英寸 × 0.03 英寸,即 1.6mm × 0.8mm)。
-
标称电阻值:10kΩ(在 25°C 时的电阻值)。
-
B 值:3950K(B 值是描述热敏电阻温度特性的参数,B 值越大,温度变化对电阻值的影响越明显)。
9.2.2 主要参数
9.2.3 工作原理
先看电路图
R / (R + 10k)*V = ADCvalue / 2^10 * V
R = 10k * ADC / (2^10 - ADC)
原理就是ADC转换得到PIO1_11处的电压值,然后根据分压得到该热敏电阻的阻值,阻值与温度之间有一种神秘的关系,然而数据手册不语,打了几张RT表。。。
KNTC0603/10KF3950 -PDF数据手册-参考资料-立创商城
9.3 寄存器
9.3.1 控制寄存器 AD0CR
位 | 符号 | 描述 |
7:0 | SEL | 选择采样和转换的输入脚 第16位的BURST控制了两种模式 BURST = 0 软件控制模式下,只能有一个位被置1,也就是只能有一个通道开 BURST = 1 硬件控制模式下,可选用任意数目的通道 另外,把全部为0当作为0x01,即通道0开 |
15:8 | CLKDIV | APB时钟(PCLK)进行分频(/(CLKDIV + 1)),得到AD转换时钟,考虑到各方面原因,该时钟必须小于等于 4.5 MHz ,而且通常情况要求CLKDIV尽量小(ADCLK 越高,AD 转换的速度越快,可快速采样,提高效率和性能),这时就应该选择合适的值使得AD时钟接近甚至等于4.5MHz |
16 | BURST | 0 软件控制模式,转换由软件控制,11个时钟完成一次 1 硬件控制模式,以[19:17]位CLKS选择的速率 从低位到高位扫描,对[7:0]位SEL中置一的进行转换 BURST置1则[26:24]START必须为0 |
19:17 | CLKS | 硬件扫描模式下每次转换占用的时钟数以及AD0DRn中转换结果的有效位数,最高十次二分对吧 000 11个时钟/10位 001 10个时钟/9位 010 9个时钟/8位 011 8个时钟/7位 100 7个时钟/6位 101 6个时钟/5位 110 5个时钟/4位 111 4个时钟/3位 |
23:20 | 保留 | |
26:24 | START | BURST = 0软件控制模式下,第27位EDGE指定的边沿下,选中的位何时启动 000 不启动 001 立即启动转换 010 边沿出现在PIO0_2/SSEL/CT16B0_CAP0时启动 011 边沿出现在PIO1_5/DIR/CT32B0_CAP0时启动 100 边沿出现在CT32B0_MAT0时启动 101 边沿出现在CT32B0_MAT1时启动 110 边沿出现在CT16B0_MAT0时启动 111 边沿出现在CT16B0_MAT1时启动 |
27 | EDGE | 只有在[26:24]START 在 010~111时有效 0 所选信号的上升沿 1 所选信号的下降沿 |
31:28 | 保留 |
9.3.2 全局数据寄存器 AD0GDR
包含最近一次A-D转换的结果
位 | 符号 | 描述 |
5:0 | 未使用(10次二分,最多十位) | |
15:6 | V/VREF | 二分得到的值也就是真实电压与最大电压的比 |
23:16 | 多的连续的A-D值从[15:6]溢出到这里,防止影响高位 | |
26:24 | CHN | LS位转换通道 |
29:27 | 未使用,未来CHN扩展 | |
30 | OVERRUN | 提示系统可能存在数据处理不及时或数据丢失的问题 BURST模式下,LS位结果的转换有一个或多个转换结果丢失或被覆盖,该位会置一 |
31 | DONE | A-D转换结束该位会置一 读该寄存器和写ADCR都会清零该位 转换过程中读ADCR会置位该位并启动新转换 |
9.3.3 状态寄存器 AD0STAT
位 | 符号 | 描述 |
7:0 | DONE[7:0] | 反映每个A-D通道的结果寄存器中DONE的状态 |
15:8 | OVERRUN[7:0] | 反映每个A-D通道的结果寄存器中OVERRUN的状态, 允许同时检查所有A-D状态 |
16 | ADINT | A-D中断标志。任何一条A-D通道的DONE置一且使能A-D产生中断时,该位置一 |
31:17 | 保留 |
9.3.4 中断使能寄存器 AD0INTEN
位 | 符号 | 描述 |
7:0 | AD0INTEN[7:0] | 哪一位置一,哪一位转换结束就会产生中断 |
8 | ADGINTEN | 0 相当于无效 1 [7:0]位当无效处理,任意通道A-D转换结束都产生中断,即全部使能 |
31:9 | 未使用,全0 |
9.3.5 数据寄存器 AD0DR0~AD0DR7
保存各个通道的转换结果及一些标志
位 | 描述 | |
5:0 | 未用,二分次数不够 | |
15:6 | V/VREF | 采样到的数据 |
29:16 | 未用,低位溢出的到这 | |
30 | OVERRUN | BURST模式下数据处理不及时或数据丢失置一 |
31 | DONE | A-D转换完成时该位置一,在读寄存器时清零 |
9.4 读取温度
此处使用了32位定时器以2Hz发送信号,每次在中断里手动转换
有个小问题就是,不知道边沿转换怎么实现的
main.c
#include <LPC11xx.h>
#include "LED.h"
#include "Button.h"
#include "TIMER.h"
#include "UART.h"
#include "string.h"
#include "ADC.h"
int main(void)
{
LED_Init();
LED_ON();
UART_Init();
char message[] = "Hello, ADC!";// 定义要发送的数据
int length = strlen(message); // 计算数据长度,不包括结束符 '\0'
UART_Send(message, length); // 调用UART_Send函数发送数据
ADC_init();
TMR32B0_Init();
while (1)
{
}
}
ADC.c
#include "ADC.h"
#include "UART.h"
uint32_t ADC7Value = 0; // 采样值
void ADC_init(void)
{
LPC_SYSCON -> SYSAHBCLKCTRL |= (1UL << 6) | (1UL << 16); //使能GPIO和IO配置时钟
LPC_IOCON -> PIO1_11 &= ~0x9f; // 把P1.11引脚选择模拟输入方式
LPC_IOCON -> PIO1_11 |= 0x01; // 将PIO1_11 配置为AD7
LPC_SYSCON -> PDRUNCFG &= ~(1UL << 4); // 打开电源
LPC_SYSCON -> SYSAHBCLKCTRL |= (1UL << 13);// 使能ADC时钟
LPC_ADC -> CR = (1UL << 7) | //选择AD7引脚
(23UL << 8)| //ADC时钟24MHz/24
(0UL << 16)| //BURST = 0,软件控制模式
(0UL << 17)| //11个时钟/10位
(0UL << 24)| // 不启动
(0UL << 27); //MAT信号上升沿启动转换
LPC_ADC->INTEN = (1UL << 7); //AD7使能中断
NVIC_EnableIRQ(ADC_IRQn); //使能ADC中断
}
#include <math.h>
#include <stdio.h>
#include <string.h>
// 网上找的求温度的代码
float resistance2Temperature(float R1)
{
float B = 3950.0f;
float R2 = 10000.0f;
float T2 = 25.0f;
return (1.0 / ((1.0 / B) * log(R1 / R2) + (1.0 / (T2 + 273.15))) - 273.15);
}
void ADC_IRQHandler()
{
uint32_t regVal;
regVal = LPC_ADC -> STAT; // 读状态寄存器
if((regVal & 0xFF) & (0x01 << 7)) // 如果通道7 A-D转换完成
{
ADC7Value = (LPC_ADC -> DR[7] >> 6) & 0x3FF; //读取AD7的值,读完自动清除中断
float R = ADC7Value / (1024.0f - ADC7Value) * 10000.0f;
float T = resistance2Temperature(R);
char buffer[20];
int integerPart = (int)T; // 提取整数部分
int decimalPart = (int)((T - integerPart) * 1000); // 提取小数部分(保留3位)
sprintf(buffer, "%d.%03d", integerPart, decimalPart); // 格式化为字符串
UART_Send(buffer, strlen(buffer));
}
}
ADC.h
#ifndef _ADC_H_
#define _ADC_H_
#include <LPC11XX.h>
void ADC_init(void);
#endif
TIMER.c
#include "TIMER.h"
#include "UART.h"
void TMR32B0_Init(void)//32位定时器0初始化 设置中断时间 MR0/SystemCoreClock *(PR + 1) = 0.01s
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 9);//使能32位定时器0的时钟
LPC_TMR32B0->IR = 0x1F;//清除所有中断标志位
LPC_TMR32B0->PR = 0;//设置分频系数
LPC_TMR32B0->MCR = 3;//设置MR0匹配后复位TC并产生中断
LPC_TMR32B0->MR0 = SystemCoreClock / 2 ; // 计数值 频率2Hz
LPC_TMR32B0->TCR = 0x01;//启动定时器
NVIC_EnableIRQ(TIMER_32_0_IRQn);//开中断
}
void TIMER32_0_IRQHandler(void)//32位定时器0中断子程序
{
if(LPC_TMR32B0->IR & 0x01)//判断是否MR0中断
{
LPC_TMR32B0->IR = 0x01; // 清除第一中断标志位
// 单次启动ADC
LPC_ADC -> CR |= 1UL << 24;
}
}
void TMR16B1_Init(void)
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1UL << 8) | (1UL << 16); // 16位定时器1时钟使能 | IO配置块时钟使能
LPC_IOCON->PIO1_9 |= 0x01; // MAT0匹配IO1_9
}
void TMR16B1_PWM_Mode(void)// PWM呼吸灯模式 1s 占空比0 -> 1 or 1 -> 0
{
LPC_TMR16B1->TCR = 0x02;//定时器复位
LPC_TMR16B1->PR = 99; // 分频系数
LPC_TMR16B1->PWMC = 0x01;//设置MAT0为PWM输出
LPC_TMR16B1->MCR = 0x02 << 9; //设置MR3匹配后复位TC;
LPC_TMR16B1->MR3 = SystemCoreClock / 10000; // PWM周期设置为0.01s,设置中断时间
LPC_TMR16B1->MR0 = LPC_TMR16B1->MR3 / 100;//MAT0初始化输出亮度1%
LPC_TMR16B1->TCR = 0x01; // 启动定时器
}
// 匹配输出翻转
void TMR16B1_Blinky_Mode(void) // 闪烁灯模式 1s翻转一次
{
LPC_TMR16B1->TCR = 0x02;//定时器复位
LPC_TMR16B1->PR = 999; // 分频系数;
LPC_TMR16B1->MCR = 2; // 设置MR0匹配后复位TC不产生中断;
LPC_TMR16B1->MR0 = SystemCoreClock / 1; // 定时1s
LPC_TMR16B1->PWMC = 0x00;//设置MAT0不为PWM输出
LPC_TMR16B1->EMR |= (3UL << 4);// MAT0外部匹配翻转
LPC_TMR16B1->TCR = 0x01; //定时器启动
}
TIMER.h
#ifndef _TIMER_H_
#define _TIMER_H_
#include <LPC11xx.h>
void TMR32B0_Init(void);
void TMR16B1_Init(void);
void TMR16B1_PWM_Mode(void);
void TMR16B1_Blinky_Mode(void);
#endif