项目二十三:电阻测量(需要简单的外围检测电路,将电阻转换为电压)测量100,1k,4.7k,10k,20k的电阻阻值,由数码管显示。要求测试误差 <10%

news2025/2/27 17:13:17

资料查找:

01 方案选择

使用单片机测量电阻有多种方法,以下是一些常见的方法及其原理:

  1. 串联分压法(ADC)
    • 原理:根据串联电路的分压原理,通过测量已知电阻和待测电阻上的电压,计算出待测电阻的阻值。
    • 优点:电路简单,易于实现。
    • 缺点:测量精度受电源电压稳定性和电压测量精度的影响。

对于89C5x单片机一般需要外接PCF8591(A/D转换芯片)

  1. 直流电桥法
    • 原理:利用直流电桥的平衡原理,通过调节电位器使电桥平衡,从而计算出待测电阻的阻值。
    • 优点:测量精度较高。
    • 缺点:电路相对复杂,调节较为麻烦。
  2. 恒流源法
    • 原理:将恒流源施加到待测电阻上,通过测量电阻两端的电压来计算电阻值。
    • 优点:测量精度较高,不受电源电压波动的影响。
    • 缺点:需要稳定的恒流源电路,实现相对复杂。
  3. 频率法
    • 原理:利用RC振荡电路和555定时器电路,将电阻转换为频率信号,通过测量频率来计算电阻值。
    • 优点:测量速度快,适用于动态测量。
    • 缺点:测量精度受振荡电路稳定性和频率测量精度的影响。
  4. IO口测量法
    • 原理:使用单片机的IO口通过电容充电时间来测量电阻。电容充电时间与电阻成正比,通过测量充电时间来计算电阻值。
    • 优点:不需要额外的AD转换电路,利用单片机IO口即可实现。
    • 缺点:测量精度受单片机定时器精度和电容稳定性的影响。

以为大部分单片机通用,试了一下,发现不行,STC89C52,只能上拉输入

2 使用单片机的IO口通过电容充电时间来测量电阻

测量原理
  1. 使用两个单片机IO口,连接两个电阻,向同一个电容充电。设置一个IO口为输出端口,另一个为输入端口。
  2. 输出端口通过连接的电阻向电容充电。电容上的电压上升,当超过一定阈值,输入端口逻辑电平就会变成1。

关于阈值可以通过输入三角波得到,但是我们并不需要知道(比值可消)

  1. 这个充电时间与终止电压、阈值电压以及RC对应的时间常数有关系。具体数值由这个公式决定: 

  1. 对应的时间与R2成正比。因此,两次时间的比值,就等于电阻的比值。如果已知其中一个电阻阻值,另外一个电阻便可以根据时间比值计算出来。12
实验步骤
  1. 需要一个电容和两个电阻。电容容值为313.8nF,电阻1的阻值为19.545kΩ;电阻2的阻值为4.718kΩ。
  2. 电容一端接地,另外一端与两个电阻相连。两个电阻分别与单片机的PF0,PF1端口相连。
  3. 设置PF0为输出端口,PF1为输入端口。周期改变PF0高低电平。分别测量PF0,以及电容上的电压信号。
  4. 测量软件先将PF0,PF1输出0电平,对于电容进行放电。然后将其中一个设置为输入端口,另外一个置为高电平,对电容充电。同时启动定时器1进行计时。
  5. 在此过程中,监视输入端口逻辑电平是否为1。当输入端口变为1时,停止定时器,并读取时间。然后再进行放电,更换另外一个端口为输入端口。测试充电时间。1
实验结果
  1. 测试298个数据进行统计。数据的平均值为4.119,标准方差为0.043。测量平均值比实际电阻比值4.143小了0.6%。1

3 硬件部分

4 软件部分

介绍:

STC89C52的端口P2是一个双向I/O口,每个引脚(如P2.0、P2.1等)

P0=1时为上拉输出,P0=0时为低电平输出

因此在已有上拉输入的条件下,也同样理论可行

最终按照如图思路把代码改出来了,但发现因为单片机太低级了,影响了开发效率

那就准备直接ADC写了算了

main.c

#include <reg52.h>//0.000 001 085069444444444=1
sbit  IO1 = P2^2;
sbit  IO2 = P2^3;
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

void Delay_ms(int ms)	//@11.0592MHz
{
	unsigned char data i, j;
	do
		{
			i = 15;
			j = 90;
			do
			{
				while (--j);
			} while (--i);
    }while (--ms);
}


unsigned char code LedChar[] = {  //数码管显示字符转换表
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = {  //数码管显示缓冲区
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};


bit StopwatchRunning = 0;  //秒表运行标志
bit StopwatchRefresh = 1;  //秒表计数刷新标志
unsigned char DecimalPart = 0;  //秒表的小数部分
unsigned int  IntegerPart = 0;  //秒表的整数部分
unsigned char T0RH = 0;  //T0重载值的高字节
unsigned char T0RL = 0;  //T0重载值的低字节
unsigned long time_R1, time_R2; // 用于存储充电时间
unsigned long Rx = 0; 
float ratio =0;
unsigned char flag1s = 0;
void ConfigTimer0(unsigned int ms);
unsigned long timer_read();
void U_Measure();
void LedScan();
void LCD_paly();
void main()
{
    EA = 1;      //开总中断
    ENLED = 0;   //使能选择数码管
    ADDR3 = 1;
    P2 = 0xFE;   //P2.0置0,选择第4行按键作为独立按键
		ConfigTimer0(6);  //配置T0定时2ms
    
    while (1)
    {
			U_Measure();
			while (1)
			{
				LedScan();
				LCD_paly();
			}
    }
}


void U_Measure()
{
	 // 第一步:放电
        IO1 = 1;
        IO2 = 1;
        TR0 = 0;
        Delay_ms(20); // 确保电容放电干净

        // 第二步:测量参考电阻回路上的充电时间
        IO1 = 0;
        IO2 = 1;
        TR0 = 1;
        while(IO2<=0); // 等待充电结束
        time_R1 = timer_read(); // 读取计时器值

        // 第三步:放电
        IO1 = 1;
        IO2 = 1;
        TR0 = 0;
        Delay_ms(20); // 确保电容放电干净

        // 第四步:测温度电阻回路上的充电时间
        IO1 = 1;
        IO2 = 0;
        TR0 = 1;
        while(IO1<=0); // 等待充电结束
        time_R2 = timer_read(); // 读取计时器值

        // 第五步:计算电阻比率
        ratio = ((time_R1*10) / (time_R2*10))/10;
				Rx=ratio;
				//flag1s = 1;
				
}
/* 配置并启动T0,ms-T0定时时间 */
void ConfigTimer0(unsigned int ms)//最大71ms
{
    unsigned long tmp;  //临时变量

    tmp = 11059200 / 12;      //定时器计数频率
    tmp = (tmp * ms) / 1000;  //计算所需的计数值
    tmp = 65536 - tmp;        //计算定时器重载值
    tmp = tmp + 18;           //补偿中断响应延时造成的误差(经验值)
//    T0RH = (unsigned char)(tmp>>8);  //定时器重载值拆分为高低字节
//    T0RL = (unsigned char)tmp;
	  T0RH = 0;  //定时器重载值拆分为高低字节
    T0RL = 0;
    TMOD &= 0xF0;   //清零T0的控制位
    TMOD |= 0x01;   //配置T0为模式1=16位
    TH0 = T0RH;     //加载T0重载值
    TL0 = T0RL;
    ET0 = 1;        //使能T0中断

}
/* 秒表计数显示函数 */
void LedScan()
{
	
	// flag1s = 0;   //1秒定时标志清零
	
            //以下代码将Rx按十进制位从低到高依次提取并转为数码管显示字符
            LedBuff[0] = LedChar[Rx%10];
            LedBuff[1] = LedChar[Rx/10%10];
            LedBuff[2] = LedChar[Rx/100%10];
            LedBuff[3] = LedChar[Rx/1000%10];
            LedBuff[4] = LedChar[Rx/10000%10];
            LedBuff[5] = LedChar[Rx/100000%10];

}
void LCD_paly()
{
	static signed char i;
	 P0 = 0xFF;   //显示消隐
    switch (i)
    {
        case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;
        case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;
        case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;
        case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;
        case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;
        case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break;
        default: break;
    }
	}


/* 定时停止函数 */
unsigned long timer_read()
{
	unsigned long tmp1 =0;
	TR0 = 0;      
	
	tmp1 =  (unsigned char)((TH0-T0RH)<<8)|(unsigned char)(TL0-T0RL);  //定时器重载值拆分为高低字节

	TH0 = T0RH;  //重新加载重载值
  TL0 = T0RL;
	return tmp1;
}





/* T0中断服务函数,完成数码管、按键扫描与秒表计数 */
void InterruptTimer0() interrupt 1
{

    TH0 = T0RH;  //重新加载重载值
    TL0 = T0RL;	 
//  LedScan();   //数码管扫描显示

}

02 使用方法——串联分压法(ADC)测电阻

 完整工程代码部分,待课设结束再公开

 如果需要技术支持可以加我的QQ交流:

27969203789

02 实验现象

03 硬件部分

金沙滩51单片机+需要测量相近的电阻

04 软件部分

核心部分:

val = (GetADCValue(0)*2.5);   //获取ADC通道0的转换值  电压值=转换结果*2.48V/255,式中的25隐含了一位十进制小数
Rx=(val/(6375-val))*R1;

XX 拓展资料(收集资料仅供参考):

虽然STC89C52无法配置上下拉操作,但是一般现代翻新的芯片都可以配置,至于配置方法可能类似如下:

 

例如:

有些单片机的端口P2的输入和输出模式通过配置端口的P2M0P2M1寄存器来控制。P2M0和P2M1分别是P2端口的输入/输出模式控制寄存器。

关于P2M1 和 P2M0 寄存器介绍:

P2M1 和 P2M0 寄存器是常见的微控制器(例如 51 系列单片机)中的特定控制寄存器,用于设置端口(特别是端口 2)的功能模式。

通常在 51 系列单片机中,P2M1 和 P2M0 寄存器用于控制端口 2 的工作模式(例如是否作为 I/O 端口,或者是否用于特殊功能)。

1. P2M1 寄存器:

P2M1 寄存器的位数通常是 8 位,每一位对应于端口 2 的各个引脚的模式。具体来说,P2M1 中的每一位控制端口 2 的每一个引脚的工作方式。

  • 位 7 (P2.7):

    • 0: 作为 I/O 端口;
    • 1: 用于特殊功能。
  • 位 6 (P2.6)

    • 0: 作为 I/O 端口;
    • 1: 用于特殊功能。
  • 依此类推,P2M1 的每一位控制对应的端口引脚的模式。

2. P2M0 寄存器:

P2M0 寄存器也是 8 位,与 P2M1 配合使用,进一步控制端口 2 的引脚模式。它的每一位也用于设置端口 2 上每个引脚的功能模式。例如:

  • 位 7 (P2.7):可以通过设置此位来选择端口引脚的功能(是 I/O,还是其他功能)。
  • 位 6 (P2.6):控制端口引脚的工作模式。

P2M1 和 P2M0 的配合:

P2M1 和 P2M0 配合使用,通常具有以下几个常见的工作模式(以位 P2.x 为例):

  • 00:作为普通的数字 I/O 端口;
  • 01:用于某些特定的功能,如外部中断等;
  • 10:用于其他一些特殊功能,例如定时器、串口等;
  • 11:也可以用于其他特定功能模式。

总结:

  • P2M1 和 P2M0 寄存器通常是 8 位,每一位对应端口 2 上的一个引脚(P2.0 到 P2.7)的功能控制。
  • 每一位设置为 0 或 1,控制对应引脚是作为 I/O 端口,还是用于特定的功能模式

P2 引脚P2M1P2M0描述
P2.x00普通输入(无上下拉电阻)
P2.x01带下拉电阻输入模式(低电平有效)
P2.x10推挽输出模式
P2.x11开漏输出模式

一般高级的51单片机的寄存器操作,可以通过P2M1和P2M0的配置来控制P2口的工作模式

配置P2.0为下拉输入模式。我们清除P2M1的对应位(即 P2M1 &= 0xFE),然后设置P2M0的对应位(即 P2M0 |= 0x01)来启用下拉电阻。

配置P2.0为推挽输出模式。我们设置P2M1的对应位(即 P2M1 |= 0x01)并清除P2M0的对应位(即 P2M0 &= 0xFE


int IN1(void)
{
// 设置P2.0为下拉输入模式
    P2M1 &= 0xFE;   // P2M1位为0
    P2M0 |= 0x01;   // 设置P2M0位为1,启用下拉输入模式

}

int IN2(void)
{
// 设置P2.0为下拉输入模式
    P2M1 &= 0xFD;   // P2M1位为0
    P2M0 |= 0x02;   // 设置P2M0位为1,启用下拉输入模式

}

void ON1(void)
{
	// 设置P2.0为推挽输出模式
	 P2M1 |= 0x01;   // 设置P2M1位为1
   P2M0 &= 0xFE;   // 设置P2M0位为0,设置为推挽输出模式
}

void ON2(void)
{
	// 设置P2.0为推挽输出模式
	 P2M1 |= 0x02;   // 设置P2M1位为1
   P2M0 &= 0xFD;   // 设置P2M0位为0,设置为推挽输出模式
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux中 vim 常用命令大全详细讲解

文章目录 前言一、Vim 基本操作 &#x1f579;️1.1 打开或创建1.2 退出编辑1.3 模式切换 二、Vim 光标移动命令 ↕️2.1 基本移动2.2 行内移动2.3. 单词移动2.4. 页面移动2.5. 行跳转 三、Vim 文本编辑命令 &#x1f4cb;3.1 插入和删除3.2 复制、剪切与粘贴3.3 替换与修改 四…

ARM架构服务器国产麒麟V10安装nginx

目前ARM架构服务器越来越多的出现在我们的工作中&#xff0c;尤其大数据时代的需要&#xff0c;服务器操作系统linux国产化进程的推进。本人已经编写了很多ARM架构下安装java环境&#xff0c;安装mysql&#xff0c;安装redis等等的文档。现在我们演示一下国产麒麟V10安装nginx-…

【SQL】语句练习

1. 更新 1.1单表更新 例1: 所有薪水低于30000的员工薪水增加10% SQL命令&#xff1a; update employee set salarysalary*1.1 where salary < 30000; 1.2多表更新 例1: 将下图两表张三的语文成绩从95修改为80 SQL命令&#xff1a; update exam set score80 where subjec…

【开源】使用环信UIKit for uniapp 做一个IM即时聊天应用

环信单群聊 UIKit 是基于环信即时通讯云 IM SDK 开发的一款即时通讯 UI 组件库&#xff0c;提供各种组件实现会话列表、聊天界面、联系人列表及后续界面等功能&#xff0c;帮助开发者根据实际业务需求快速搭建包含 UI 界面的即时通讯应用。 本文教大家使用环信 uniapp UIKit 快…

用 Python Turtle 绘制经典杰瑞鼠:捕捉卡通世界中的小聪明

用 Python Turtle 绘制经典杰瑞鼠&#xff1a;捕捉卡通世界中的小聪明 &#x1f438; 前言 &#x1f438;&#x1f41e;往期绘画>>点击进所有绘画&#x1f41e;&#x1f40b; 效果图 &#x1f40b;&#x1f409; 代码 &#x1f409; &#x1f438; 前言 &#x1f438; 杰…

Excel拆分脚本

Excel拆分 工作表按行拆分为工作薄 工作表按行拆分为工作薄 打开要拆分的Excel文件&#xff0c;使用快捷键&#xff08;AltF11&#xff09;打开脚本界面&#xff0c;选择要拆分的sheet&#xff0c;打开Module&#xff0c;在Module中输入脚本代码&#xff0c;然后运行脚本 Su…

ModStartCMS v9.1.0 数据Grid样式优化,富文本格式刷支持,精简代码

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议&#xff0c;免费且不限制商业使用。 功能特性 丰富的模块市…

2024年12月16日Github流行趋势

项目名称&#xff1a;PDFMathTranslate 项目维护者&#xff1a;Byaidu reycn hellofinch Wybxc YadominJinta项目介绍&#xff1a;基于 AI 完整保留排版的 PDF 文档全文双语翻译&#xff0c;支持 Google/DeepL/Ollama/OpenAI 等服务&#xff0c;提供 CLI/GUI/Docker。项目star数…

3-机器人视觉-机器人抓取与操作

文章目录 3机器人视觉目录 1. 传感器和标定摄像头模型Intrinsic MatrixExtrinsic Matrix 标定内参标定手眼标定和外参标定 力传感器&其它传感器其它传感器 2. 神经网络和图像处理2D特征处理常见架构 训练流程推理流程部署流程2D 图像任务3D Point Cloud FeaturePointNet Ap…

Java String详解(二)

上一篇博客&#xff1a;Java String详解&#xff08;一&#xff09; 写在前面&#xff1a;大家好&#xff01;我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正&#xff0c;感谢大家的不吝赐教。我的唯一博客更新地址是&#xff1a;https://ac-fun.blo…

Qt之点击鼠标右键创建菜单栏使用(六)

Qt开发 系列文章 - menu&#xff08;六&#xff09; 目录 前言 一、示例演示 二、菜单栏 1.MenuBar 2.Menu 总结 前言 QMainWindow是一个为用户提供主窗口程序的类&#xff0c;包含一个菜单栏&#xff08;menubar&#xff09;、多个工具栏(toolbars)、一个状态栏(status…

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(一)

《拉依达的嵌入式\驱动面试宝典》—C/CPP基础篇(一) 你好&#xff0c;我是拉依达。 感谢所有阅读关注我的同学支持&#xff0c;目前博客累计阅读 27w&#xff0c;关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析&#xff08;持续更新&#xff09;-CSDN博客》已经是 Lin…

IntelliJ IDEA(2024版) 的安装、配置与使用教程:常用配置、创建工程等操作(很详细,你想要的都在这里)

IDEA的安装、配置与使用&#xff1a; Ⅰ、IDEA 的安装&#xff1a;1、IDEA 的下载地址(官网)&#xff1a;2、IDEA 分为两个版本&#xff1a;旗舰版 (Ultimate) 和 社区版 (Community)其一、两个不同版本的安装文件&#xff1a;其二、两个不同版本的详细对比&#xff1a; 3、IDE…

MybatisPlus-配置加密

配置加密 目前配置文件中的很多参数都是明文&#xff0c;如果开发人员发生流动&#xff0c;很容易导致敏感信息的泄露。所以MybatisPlus支持配置文件的加密和解密功能。 我们以数据库的用户名和密码为例。 生成秘钥 首先&#xff0c;我们利用AES工具生成一个随机秘钥&#…

深度学习基础--将yolov5的backbone模块用于目标识别会出现怎么效果呢??

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 yolov5网络结构比较复杂&#xff0c;上次我们简要介绍了yolov5网络模块&#xff0c;并且复现了C3模块&#xff0c;深度学习基础–yolov5网络结构简介&a…

数据结构---图(Graph)

图&#xff08;Graph&#xff09;是一种非常灵活且强大的数据结构&#xff0c;用于表示实体之间的复杂关系。在图结构中&#xff0c;数据由一组节点&#xff08;或称为顶点&#xff09;和连接这些节点的边组成。图可以用于表示社交网络、交通网络、网络路由等场景。 1. 基本概…

人工智能技术的深度解析与推广【人工智能的应用场景】

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c; 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把…

软件开发中 Bug 为什么不能彻底消除

在软件开发中&#xff0c;Bug无法彻底消除的原因主要包括&#xff1a;软件复杂度高、人员认知与沟通受限、需求和环境不断变化、工具与测试覆盖不足、经济与时间成本制约。其中“需求和环境不断变化”尤为关键&#xff0c;因为在实际开发中&#xff0c;业务逻辑随着市场与用户反…

【嵌入式软件】跑开发板的前置服务配置

在嵌入式开发中,通常需要在 开发板和主机之间共享、传输和挂载文件。 这篇文章是关于如何在 Ubuntu 中配置 Samba、TFTP 和 NFS 协议的详细步骤。这些协议分别用于远程文件共享、文件传输和内核挂载文件系统。 如何安装协议: 参考:ubuntu18配置:详细的内容我手写了一份文档。…

CTF 攻防世界 Web: FlatScience write-up

题目名称-FlatScience 网址 index 目录中没有发现提示信息&#xff0c;链接会跳转到论文。 目前没有发现有用信息&#xff0c;尝试目录扫描。 目录扫描 注意到存在 robots.txt 和 login.php。 访问 robots.txt 这里表明还存在 admin.php admin.php 分析 在这里尝试一些 sql…