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数字转换工具类NumberUtil的使用

《Java数字转换工具类NumberUtil的使用》NumberUtil是一个功能强大的Java工具类,用于处理数字的各种操作,包括数值运算、格式化、随机数生成和数值判断,下面就来介绍一下Number... 目录一、NumberUtil类概述二、主要功能介绍1. 数值运算2. 格式化3. 数值判断4. 随机

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

Python实现视频转换为音频的方法详解

《Python实现视频转换为音频的方法详解》这篇文章主要为大家详细Python如何将视频转换为音频并将音频文件保存到特定文件夹下,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5. 注意事项

使用Python实现图片和base64转换工具

《使用Python实现图片和base64转换工具》这篇文章主要为大家详细介绍了如何使用Python中的base64模块编写一个工具,可以实现图片和Base64编码之间的转换,感兴趣的小伙伴可以了解下... 简介使用python的base64模块来实现图片和Base64编码之间的转换。可以将图片转换为Bas

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

基于C#实现将图片转换为PDF文档

《基于C#实现将图片转换为PDF文档》将图片(JPG、PNG)转换为PDF文件可以帮助我们更好地保存和分享图片,所以本文将介绍如何使用C#将JPG/PNG图片转换为PDF文档,需要的可以参考下... 目录介绍C# 将单张图片转换为PDF文档C# 将多张图片转换到一个PDF文档介绍将图片(JPG、PNG)转

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*