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

相关文章

day-51 合并零之间的节点

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

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

PDF 软件如何帮助您编辑、转换和保护文件。

如何找到最好的 PDF 编辑器。 无论您是在为您的企业寻找更高效的 PDF 解决方案,还是尝试组织和编辑主文档,PDF 编辑器都可以在一个地方提供您需要的所有工具。市面上有很多 PDF 编辑器 — 在决定哪个最适合您时,请考虑这些因素。 1. 确定您的 PDF 文档软件需求。 不同的 PDF 文档软件程序可以具有不同的功能,因此在决定哪个是最适合您的 PDF 软件之前,请花点时间评估您的

单片机毕业设计基于单片机的智能门禁系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍程序代码部分参考 设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订

C# double[] 和Matlab数组MWArray[]转换

C# double[] 转换成MWArray[], 直接赋值就行             MWNumericArray[] ma = new MWNumericArray[4];             double[] dT = new double[] { 0 };             double[] dT1 = new double[] { 0,2 };

STM32 ADC+DMA导致写FLASH失败

最近用STM32G070系列的ADC+DMA采样时,遇到了一些小坑记录一下; 一、ADC+DMA采样时进入死循环; 解决方法:ADC-dma死循环问题_stm32 adc dma死机-CSDN博客 将ADC的DMA中断调整为最高,且增大ADCHAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_Buffer_Size); 的ADC_Bu

代码训练营 Day26 | 47.排序II | 51. N-皇后 |

47.排序II 1.跟46题一样只不过加一个树层去重 class Solution(object):def backtracking(self,nums,path,result,used):# recursion stopif len(path) == len(nums):# collect our setresult.append(path[:])return for i in range(

数据流与Bitmap之间相互转换

把获得的数据流转换成一副图片(Bitmap) 其原理就是把获得倒的数据流序列化到内存中,然后经过加工,在把数据从内存中反序列化出来就行了。 难点就是在如何实现加工。因为Bitmap有一个专有的格式,我们常称这个格式为数据头。加工的过程就是要把这个数据头与我们之前获得的数据流合并起来。(也就是要把这个头加入到我们之前获得的数据流的前面)      那么这个头是

基于stm32的河流检测系统-单片机毕业设计

文章目录 前言资料获取设计介绍功能介绍具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机设计精品