51单片机.之ADC数字模拟转换

2024-09-01 21:36

本文主要是介绍51单片机.之ADC数字模拟转换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

1、数字转模拟电路,输出波形,示波器采集来显示波形
单片机通过i2c给,模数转换器,写入数字信号,定时器1s扫描按键的切换
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
1、key.c
切换波形


#include <reg52.h>sbit KEY_IN_1  = P2^4;
sbit KEY_IN_2  = P2^5;
sbit KEY_IN_3  = P2^6;
sbit KEY_IN_4  = P2^7;
sbit KEY_OUT_1 = P2^3;
sbit KEY_OUT_2 = P2^2;
sbit KEY_OUT_3 = P2^1;
sbit KEY_OUT_4 = P2^0;unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表{ '1',  '2',  '3', 0x26 }, //数字键1、数字键2、数字键3、向上键{ '4',  '5',  '6', 0x25 }, //数字键4、数字键5、数字键6、向左键{ '7',  '8',  '9', 0x28 }, //数字键7、数字键8、数字键9、向下键{ '0', 0x1B, 0x0D, 0x27 }  //数字键0、ESC键、  回车键、 向右键
};
unsigned char pdata KeySta[4][4] = {  //全部矩阵按键的当前状态{1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1}
};extern void SwitchWave();
extern void KeyAction(unsigned char keycode);/* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用 */
void KeyDriver()
{unsigned char i, j;static unsigned char pdata backup[4][4] = {  //按键值备份,保存前一次的值{1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1}};for (i=0; i<4; i++)  //循环检测4*4的矩阵按键{for (j=0; j<4; j++){if (backup[i][j] != KeySta[i][j])    //检测按键动作{if (backup[i][j] != 0)           //按键按下时执行动作{KeyAction(KeyCodeMap[i][j]); //调用按键动作函数}backup[i][j] = KeySta[i][j];     //刷新前一次的备份值}}}
}
/* 按键扫描函数,需在定时中断中调用,推荐调用间隔1ms */
void KeyScan()
{unsigned char i;static unsigned char keyout = 0;   //矩阵按键扫描输出索引static unsigned char keybuf[4][4] = {  //矩阵按键扫描缓冲区{0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF},  {0xFF, 0xFF, 0xFF, 0xFF}};//将一行的4个按键值移入缓冲区keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;//消抖后更新按键状态for (i=0; i<4; i++)  //每行4个按键,所以循环4次{if ((keybuf[keyout][i] & 0x0F) == 0x00){   //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下KeySta[keyout][i] = 0;}else if ((keybuf[keyout][i] & 0x0F) == 0x0F){   //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起KeySta[keyout][i] = 1;}}//执行下一次的扫描输出keyout++;         //输出索引递增keyout &= 0x03;   //索引值加到4即归零switch (keyout)   //根据索引,释放当前输出引脚,拉低下次的输出引脚{case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;default: break;}
}

2、i2c.c

#include <reg52.h>
#include <intrins.h>#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;void I2CStart()  //产生总线起始信号
{I2C_SDA = 1; //首先确保SDA、SCL都是高电平I2C_SCL = 1;I2CDelay();I2C_SDA = 0; //先拉低SDAI2CDelay();I2C_SCL = 0; //再拉低SCL
}
void I2CStop()   //产生总线停止信号
{I2C_SCL = 0; //首先确保SDA、SCL都是低电平I2C_SDA = 0;I2CDelay();I2C_SCL = 1; //先拉高SCLI2CDelay();I2C_SDA = 1; //再拉高SDAI2CDelay();
}
bit I2CWrite(unsigned char dat) //I2C总线写操作,待写入字节dat,返回值为应答状态
{bit ack;  //用于暂存应答位的值unsigned char mask;  //用于探测字节内某一位值的掩码变量for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行{if ((mask&dat) == 0)  //该位的值输出到SDA上I2C_SDA = 0;elseI2C_SDA = 1;I2CDelay();I2C_SCL = 1;          //拉高SCLI2CDelay();I2C_SCL = 0;          //再拉低SCL,完成一个位周期}I2C_SDA = 1;   //8位数据发送完后,主机释放SDA,以检测从机应答I2CDelay();I2C_SCL = 1;   //拉高SCLack = I2C_SDA; //读取此时的SDA值,即为从机的应答值I2CDelay();I2C_SCL = 0;   //再拉低SCL完成应答位,并保持住总线return (~ack); //应答值取反以符合通常的逻辑:0=不存在或忙或写入失败,1=存在且空闲或写入成功
}
unsigned char I2CReadNAK() //I2C总线读操作,并发送非应答信号,返回值为读到的字节
{unsigned char mask;unsigned char dat;I2C_SDA = 1;  //首先确保主机释放SDAfor (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行{I2CDelay();I2C_SCL = 1;      //拉高SCLif(I2C_SDA == 0)  //读取SDA的值dat &= ~mask; //为0时,dat中对应位清零elsedat |= mask;  //为1时,dat中对应位置1I2CDelay();I2C_SCL = 0;      //再拉低SCL,以使从机发送出下一位}I2C_SDA = 1;   //8位数据发送完后,拉高SDA,发送非应答信号I2CDelay();I2C_SCL = 1;   //拉高SCLI2CDelay();I2C_SCL = 0;   //再拉低SCL完成非应答位,并保持住总线return dat;
}
unsigned char I2CReadACK() //I2C总线读操作,并发送应答信号,返回值为读到的字节
{unsigned char mask;unsigned char dat;I2C_SDA = 1;  //首先确保主机释放SDAfor (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行{I2CDelay();I2C_SCL = 1;      //拉高SCLif(I2C_SDA == 0)  //读取SDA的值dat &= ~mask; //为0时,dat中对应位清零elsedat |= mask;  //为1时,dat中对应位置1I2CDelay();I2C_SCL = 0;      //再拉低SCL,以使从机发送出下一位}I2C_SDA = 0;   //8位数据发送完后,拉低SDA,发送应答信号I2CDelay();I2C_SCL = 1;   //拉高SCLI2CDelay();I2C_SCL = 0;   //再拉低SCL完成应答位,并保持住总线return dat;
}

3、main.c

#include <reg52.h>unsigned char code SinWave[] = {  //正弦波波表127, 152, 176, 198, 217, 233, 245, 252,255, 252, 245, 233, 217, 198, 176, 152,127, 102,  78,  56,  37,  21,   9,   2,0,   2,   9,  21,  37,  56,  78, 102,
};
unsigned char code TriWave[] = {  //三角波波表0,  16,  32,  48,  64,  80,  96, 112,128, 144, 160, 176, 192, 208, 224, 240,255, 240, 224, 208, 192, 176, 160, 144,128, 112,  96,  80,  64,  48,  32,  16,
};
unsigned char code SawWave[] = {  //锯齿波表0,   8,  16,  24,  32,  40,  48,  56,64,  72,  80,  88,  96, 104, 112, 120,128, 136, 144, 152, 160, 168, 176, 184,192, 200, 208, 216, 224, 232, 240, 248,
};
unsigned char code *pWave;  //波表指针
unsigned char T0RH = 0;  //T0重载值的高字节
unsigned char T0RL = 0;  //T0重载值的低字节
unsigned char T1RH = 1;  //T1重载值的高字节
unsigned char T1RL = 1;  //T1重载值的低字节void ConfigTimer0(unsigned int ms);
void SetWaveFreq(unsigned char freq);
extern void KeyScan();
extern void KeyDriver();
extern void I2CStart();
extern void I2CStop();
extern bit I2CWrite(unsigned char dat);void main()
{    EA = 1;             //开总中断ConfigTimer0(1);  //配置T0定时1mspWave = SinWave;  //默认正弦波SetWaveFreq(10);  //默认频率10Hzwhile (1){KeyDriver();  //调用按键驱动}
}
void KeyAction(unsigned char keycode)
{	static unsigned char i = 0;if(keycode == 0x26){if(i==0){i = 1;pWave = TriWave;}else if(i==1){i = 2;pWave = SawWave;}else{i = 0;pWave = SinWave;}}
}/* 设置DAC输出值,val-设定值 */
void SetDACOut(unsigned char val)
{I2CStart();if (!I2CWrite(0x48<<1)) //寻址PCF8591,如未应答,则停止操作并返回{I2CStop();return;}I2CWrite(0x40);         //写入控制字节I2CWrite(val);          //写入DA值  I2CStop();
}
void SetWaveFreq(unsigned char freq)
{unsigned long tmp;tmp = (11059200/12)/(freq * 32);tmp = 65536 - tmp;tmp = tmp + 33;T1RH = (unsigned char)(tmp >> 8);T1RL = (unsigned char)tmp;TMOD &= 0x0F;TMOD |= 0X10;TH1 = T1RH;TL1 = T1RL;ET1 = 1;PT1 = 1;TR1 = 1;
}
/* 配置并启动T0,ms-T0定时时间 */
void ConfigTimer0(unsigned int ms)
{unsigned long tmp;  //临时变量tmp = 11059200 / 12;       //定时器计数频率tmp = (tmp * ms) / 1000;  //计算所需的计数值tmp = 65536 - tmp;         //计算定时器重载值tmp = tmp + 28;            //补偿中断响应延时造成的误差T0RH = (unsigned char)(tmp>>8);  //定时器重载值拆分为高低字节T0RL = (unsigned char)tmp;TMOD &= 0xF0;   //清零T0的控制位TMOD |= 0x01;   //配置T0为模式1TH0 = T0RH;     //加载T0重载值TL0 = T0RL;ET0 = 1;        //使能T0中断TR0 = 1;        //启动T0
}
/* T0中断服务函数,执行按键扫描 */
void InterruptTimer0() interrupt 1
{TH0 = T0RH;  //重新加载重载值TL0 = T0RL;KeyScan();   //按键扫描
}
/* T1中断服务函数,执行波形输出 */
void InterruptTimer1() interrupt 3
{static unsigned char i = 0;TH1 = T1RH;  //重新加载重载值TL1 = T1RL;//循环输出波表中的数据SetDACOut(pWave[i]);i++;if (i >= 32){i = 0;}
}

在这里插入图片描述
在这里插入图片描述
2、模数转换,将电压显示到数码管
通过i2c读取转换结果,计算换成电压值,显示到数码管

/*
*******************************************************************************
*                     《手把手教你学51单片机(C语言版)》
*                    配套 KST-51 单片机开发板 示例源代码
*
*         (c) 版权所有 2014 金沙滩工作室/清华大学出版社 保留所有权利
*                 获取更多资料请访问:http://www.kingst.org
*
* 文件名:main.c
* 描  述:第17章 作业题2 将模拟输入通道0、1的电压值显示到数码管上
* 版本号:v1.0.0
* 备  注:通道0电压值显示在左侧,通道1电压值显示在右侧,中间隔两位不显示。
*******************************************************************************
*/#include <reg52.h>sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;bit flag300ms = 1;       //300ms定时标志
unsigned char T0RH = 0;  //T0重载值的高字节
unsigned char T0RL = 0;  //T0重载值的低字节
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, 0xFF
};void ConfigTimer0(unsigned int ms);
unsigned char GetADCValue(unsigned char chn);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadACK();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);void main()
{unsigned char val;EA = 1;           //开总中断ENLED = 0;        //使能U3,选择控制数码管ADDR3 = 1;        //因为需要动态改变ADDR0-2的值,所以0-2不需要初始化了ConfigTimer0(2);  //配置T0定时2mswhile (1){if (flag300ms){flag300ms = 0;//显示通道0的电压val = GetADCValue(0);   //获取ADC通道0的转换值val = (val*25) / 255;   //电压值=转换结果*2.5V/255,式中的25隐含了一位十进制小数LedBuff[5] = LedChar[val/10] & 0x7F;  //通道0的整数位显示到数码管第5位,并点亮第5位的小数点LedBuff[4] = LedChar[val%10];         //通道0的小数位显示到数码管第4位//显示通道1的电压val = GetADCValue(1);   //获取ADC通道1的转换值val = (val*25) / 255;   //电压值=转换结果*2.5V/255,式中的25隐含了一位十进制小数LedBuff[1] = LedChar[val/10] & 0x7F;  //通道1的整数位显示到数码管第1位,并点亮第1位的小数点LedBuff[0] = LedChar[val%10];         //通道1的小数位显示到数码管第0位}}
}
/* 读取当前的ADC转换值,chn-ADC通道号0~3 */
unsigned char GetADCValue(unsigned char chn)
{unsigned char val;I2CStart();if (!I2CWrite(0x48<<1))  //寻址PCF8591,如未应答,则停止操作并返回0{I2CStop();return 0;}I2CWrite(0x40|chn);        //写入控制字节,选择转换通道I2CStart();I2CWrite((0x48<<1)|0x01);  //寻址PCF8591,指定后续为读操作    I2CReadACK();              //先空读一个字节,提供采样转换时间val = I2CReadNAK();        //读取刚刚转换完的值I2CStop();return val;
}
/* 配置并启动T0,ms-T0定时时间 */
void ConfigTimer0(unsigned int ms)
{unsigned long tmp;  //临时变量tmp = 11059200 / 12;      //定时器计数频率tmp = (tmp * ms) / 1000;  //计算所需的计数值tmp = 65536 - tmp;        //计算定时器重载值tmp = tmp + 12;           //补偿中断响应延时造成的误差T0RH = (unsigned char)(tmp>>8);  //定时器重载值拆分为高低字节T0RL = (unsigned char)tmp;TMOD &= 0xF0;   //清零T0的控制位TMOD |= 0x01;   //配置T0为模式1TH0 = T0RH;     //加载T0重载值TL0 = T0RL;ET0 = 1;        //使能T0中断TR0 = 1;        //启动T0
}
/* T0中断服务函数,执行数码管动态显示、300ms定时 */
void InterruptTimer0() interrupt 1
{static unsigned char i = 0;  //动态扫描的索引static unsigned char tmr300ms = 0;  //300ms软件定时计数器TH0 = T0RH;  //重新加载重载值TL0 = T0RL;tmr300ms++;if (tmr300ms >= 150)  //定时300ms{tmr300ms = 0;flag300ms = 1;}//以下代码完成数码管动态扫描刷新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;}
}

这篇关于51单片机.之ADC数字模拟转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1128110

相关文章

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

在java中如何将inputStream对象转换为File对象(不生成本地文件)

《在java中如何将inputStream对象转换为File对象(不生成本地文件)》:本文主要介绍在java中如何将inputStream对象转换为File对象(不生成本地文件),具有很好的参考价... 目录需求说明问题解决总结需求说明在后端中通过POI生成Excel文件流,将输出流(outputStre

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

Java实现XML与JSON的互相转换详解

《Java实现XML与JSON的互相转换详解》这篇文章主要为大家详细介绍了如何使用Java实现XML与JSON的互相转换,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. XML转jsON1.1 代码目的1.2 代码实现2. JSON转XML3. JSON转XML并输出成指定的

Java实现将Markdown转换为纯文本

《Java实现将Markdown转换为纯文本》这篇文章主要为大家详细介绍了两种在Java中实现Markdown转纯文本的主流方法,文中的示例代码讲解详细,大家可以根据需求选择适合的方案... 目录方法一:使用正则表达式(轻量级方案)方法二:使用 Flexmark-Java 库(专业方案)1. 添加依赖(Ma

Java实现将byte[]转换为File对象

《Java实现将byte[]转换为File对象》这篇文章将通过一个简单的例子为大家演示Java如何实现byte[]转换为File对象,并将其上传到外部服务器,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言1. 问题背景2. 环境准备3. 实现步骤3.1 从 URL 获取图片字节数据3.2 将字节数组

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.

Python使用PIL库将PNG图片转换为ICO图标的示例代码

《Python使用PIL库将PNG图片转换为ICO图标的示例代码》在软件开发和网站设计中,ICO图标是一种常用的图像格式,特别适用于应用程序图标、网页收藏夹图标等场景,本文将介绍如何使用Python的... 目录引言准备工作代码解析实践操作结果展示结语引言在软件开发和网站设计中,ICO图标是一种常用的图像