本文主要是介绍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数字模拟转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!