标签:时钟 i2c总线 构造 做了 会同 drive 离散 变化 转换时间
17.2 A/D的主要指标
17.3 PCF8591的硬件接口
17.4 PCF8591的软件编程
/***********************lcd1602.c文件程序源代码*************************/ #include <reg52.h> #define LCD1602_DB P0 sbit LCD1602_RS = P1^0; sbit LCD1602_RW = P1^1; sbit LCD1602_E = P1^5; void LcdWaitReady() //等待液晶准备好 { unsigned char sta; LCD1602_DB = 0xFF; LCD1602_RS = 0; LCD1602_RW = 1; do { LCD1602_E = 1; sta = LCD1602_DB; //读取状态字 LCD1602_E = 0; } while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止 } void LcdWriteCmd(unsigned char cmd) //写入命令函数 { LcdWaitReady(); LCD1602_RS = 0; LCD1602_RW = 0; LCD1602_DB = cmd; LCD1602_E = 1; LCD1602_E = 0; } void LcdWriteDat(unsigned char dat) //写入数据函数 { LcdWaitReady(); LCD1602_RS = 1; LCD1602_RW = 0; LCD1602_DB = dat; LCD1602_E = 1; LCD1602_E = 0; } void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str) //显示字符串,屏幕起始坐标(x,y),字符串指针str { unsigned char addr; //由输入的显示坐标计算显示RAM的地址 if (y == 0) addr = 0x00 + x; //第一行字符地址从0x00起始 else addr = 0x40 + x; //第二行字符地址从0x40起始 //由起始显示RAM地址连续写入字符串 LcdWriteCmd(addr | 0x80); //写入起始地址 while (*str != ‘\0‘) //连续写入字符串数据,直到检测到结束符 { LcdWriteDat(*str); str++; } } void LcdInit() //液晶初始化函数 { LcdWriteCmd(0x38); //16*2显示,5*7点阵,8位数据接口 LcdWriteCmd(0x0C); //显示器开,光标关闭 LcdWriteCmd(0x06); //文字不动,地址自动+1 LcdWriteCmd(0x01); //清屏 } /***********************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; //先拉低SDA I2CDelay(); I2C_SCL = 0; //再拉低SCL } void I2CStop() //产生总线停止信号 { I2C_SCL = 0; //首先确保SDA、SCL都是低电平 I2C_SDA = 0; I2CDelay(); I2C_SCL = 1; //先拉高SCL I2CDelay(); I2C_SDA = 1; //再拉高SDA I2CDelay(); } 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; else I2C_SDA = 1; I2CDelay(); I2C_SCL = 1; //拉高SCL I2CDelay(); I2C_SCL = 0; //再拉低SCL,完成一个位周期 } I2C_SDA = 1; //8位数据发送完后,主机释放SDA,以检测从机应答 I2CDelay(); I2C_SCL = 1; //拉高SCL ack = 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; //首先确保主机释放SDA for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行 { I2CDelay(); I2C_SCL = 1; //拉高SCL if(I2C_SDA == 0) //读取SDA的值 dat &= ~mask; //为0时,dat中对应位清零 else dat |= mask; //为1时,dat中对应位置1 I2CDelay(); I2C_SCL = 0; //再拉低SCL,以使从机发送出下一位 } I2C_SDA = 1; //8位数据发送完后,拉高SDA,发送非应答信号 I2CDelay(); I2C_SCL = 1; //拉高SCL I2CDelay(); I2C_SCL = 0; //再拉低SCL完成非应答位,并保持住总线 return dat; } unsigned char I2CReadACK() //I2C总线读操作,并发送应答信号,返回值为读到的字节 { unsigned char mask; unsigned char dat; I2C_SDA = 1; //首先确保主机释放SDA for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行 { I2CDelay(); I2C_SCL = 1; //拉高SCL if(I2C_SDA == 0) //读取SDA的值 dat &= ~mask; //为0时,dat中对应位清零 else dat |= mask; //为1时,dat中对应位置1 I2CDelay(); I2C_SCL = 0; //再拉低SCL,以使从机发送出下一位 } I2C_SDA = 0; //8位数据发送完后,拉低SDA,发送应答信号 I2CDelay(); I2C_SCL = 1; //拉高SCL I2CDelay(); I2C_SCL = 0; //再拉低SCL完成应答位,并保持住总线 return dat; } /***********************main.c文件程序源代码*************************/ #include <reg52.h> bit flag300ms = 1; //300ms定时标志 unsigned char T0RH = 0; //T0重载值的高字节 unsigned char T0RL = 0; //T0重载值的低字节 unsigned char GetADCValue(unsigned char chn); void ValueToString(unsigned char *str, unsigned char val); void ConfigTimer0(unsigned int ms); extern void LcdInit(); extern void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str); extern void I2CStart(); extern void I2CStop(); extern unsigned char I2CReadACK(); extern unsigned char I2CReadNAK(); extern bit I2CWrite(unsigned char dat); void main () { unsigned char val; unsigned char str[10]; EA = 1; //开总中断 ConfigTimer0(10); //配置T0定时10ms LcdInit(); //初始化液晶 LcdShowStr(0, 0, "AIN0 AIN1 AIN3"); //显示通道指示 while(1) { if (flag300ms) { flag300ms = 0; //显示通道0的电压 val = GetADCValue(0); //获取ADC通道0的转换值 ValueToString(str, val); //转为字符串格式的电压值 LcdShowStr(0, 1, str); //显示到液晶上 //显示通道1的电压 val = GetADCValue(1); ValueToString(str, val); LcdShowStr(6, 1, str); //显示通道3的电压 val = GetADCValue(3); ValueToString(str, val); LcdShowStr(12, 1, str); } } } unsigned char GetADCValue(unsigned char chn) //读取当前的ADC转换值,chn为ADC通道号0-3 { 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; } void ValueToString(unsigned char *str, unsigned char val) //ADC转换值转为实际电压值的字符串形式 { val = (val*25) / 255; //电压值=转换结果*2.5V/255,式中的25隐含了一位十进制小数 str[0] = (val/10) + ‘0‘; //整数位字符 str[1] = ‘.‘; //小数点 str[2] = (val%10) + ‘0‘; //小数位字符 str[3] = ‘V‘; //电压单位 str[4] = ‘\0‘; //结束符 } void ConfigTimer0(unsigned int ms) //T0配置函数 { 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为模式1 TH0 = T0RH; //加载T0重载值 TL0 = T0RL; ET0 = 1; //使能T0中断 TR0 = 1; //启动T0 } void InterruptTimer0() interrupt 1 //T0中断服务函数 { static unsigned char tmr300ms = 0; TH0 = T0RH; //定时器重新加载重载值 TL0 = T0RL; tmr300ms++; if (tmr300ms >= 30) //定时300ms { tmr300ms = 0; flag300ms = 1; } }
17.5 A/D差分输入信号
17.6 D/A输出
/***********************I2C.c文件程序源代码*************************/ 略 /***********************keyboard.c文件程序源代码*************************/ #include <reg52.h> sbit KEY_IN_1 = P2^4; //矩阵按键的扫描输入引脚1 sbit KEY_IN_2 = P2^5; //矩阵按键的扫描输入引脚2 sbit KEY_IN_3 = P2^6; //矩阵按键的扫描输入引脚3 sbit KEY_IN_4 = P2^7; //矩阵按键的扫描输入引脚4 sbit KEY_OUT_1 = P2^3; //矩阵按键的扫描输出引脚1 sbit KEY_OUT_2 = P2^2; //矩阵按键的扫描输出引脚2 sbit KEY_OUT_3 = P2^1; //矩阵按键的扫描输出引脚3 sbit KEY_OUT_4 = P2^0; //矩阵按键的扫描输出引脚4 const unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到PC标准键盘键码的映射表 { ‘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 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]; } } } } 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,即16ms(4*4ms)内都只检测到按下状态时,可认为按键已按下 KeySta[keyout][ i] = 0; } else if ((keybuf[keyout][ i] & 0x0F) == 0x0F) { //连续4次扫描值为1,即16ms(4*4ms)内都只检测到弹起状态时,可认为按键已弹起 KeySta[keyout][ i] = 1; } } //执行下一次的扫描输出 keyout++; keyout &= 0x03; 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; } } /***********************main.c文件程序源代码*************************/ #include <reg52.h> unsigned char T0RH = 0; //T0重载值的高字节 unsigned char T0RL = 0; //T0重载值的低字节 void ConfigTimer0(unsigned int ms); 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定时1ms while(1) { KeyDriver(); } } void SetDACOut(unsigned char val) //设置DAC输出值 { I2CStart(); if (!I2CWrite(0x48<<1)) //寻址PCF8591,如未应答,则停止操作并返回 { I2CStop(); return; } I2CWrite(0x40); //写入控制字节 I2CWrite(val); //写如DA值 I2CStop(); } void KeyAction(unsigned char keycode) //按键动作函数,根据键码执行相应动作 { static unsigned char volt = 0; //输出电压值,隐含了一位十进制小数位 if (keycode == 0x26) //向上键,增加0.1V电压值 { if (volt < 25) { volt++; SetDACOut(volt*255/25); //转换为AD输出值 } } else if (keycode == 0x28) //向下键,减小0.1V电压值 { if (volt > 0) { volt--; SetDACOut(volt*255/25); //转换为AD输出值 } } } void ConfigTimer0(unsigned int ms) //T0配置函数 { unsigned long tmp; tmp = 11059200 / 12; //定时器计数频率 tmp = (tmp * ms) / 1000; //计算所需的计数值 tmp = 65536 - tmp; //计算定时器重载值 tmp = tmp + 34; //修正中断响应延时造成的误差 T0RH = (unsigned char)(tmp >> 8); //定时器重载值拆分为高低字节 T0RL = (unsigned char)tmp; TMOD &= 0xF0; //清零T0的控制位 TMOD |= 0x01; //配置T0为模式1 TH0 = T0RH; //加载T0重载值 TL0 = T0RL; ET0 = 1; //使能T0中断 TR0 = 1; //启动T0 } void InterruptTimer0() interrupt 1 //T0中断服务函数 { TH0 = T0RH; //定时器重新加载重载值 TL0 = T0RL; KeyScan(); }
17.7 PCF8591信号发生器
/***********************I2C.c文件程序源代码*************************/ 略 /***********************keyboard.c文件程序源代码********************/ 略 /***********************main.c文件程序源代码************************/ #include <reg52.h> unsigned char T0RH = 0; //T0重载值的高字节 unsigned char T0RL = 0; //T0重载值的低字节 unsigned char T1RH = 1; //T1重载值的高字节 unsigned char T1RL = 1; //T1重载值的低字节 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; //波表指针 void SetWaveFreq(unsigned char freq); void ConfigTimer0(unsigned int ms); 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定时1ms pWave = SinWave; //默认正弦波 SetWaveFreq(10); //默认频率10Hz while(1) { KeyDriver(); } } void KeyAction(unsigned char keycode) //按键动作函数,根据键码执行相应动作 { static unsigned char wave = 0; if (keycode == 0x26) //向上键,切换波形 { if (wave == 0) { wave = 1; pWave = TriWave; } else if (wave == 1) { wave = 2; pWave = SawWave; } else { wave = 0; pWave = SinWave; } } } void SetDACOut(unsigned char val) //设置DAC输出值 { 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); //定时器计数频率,是波形频率的32倍 tmp = 65536 - tmp; //计算定时器重载值 tmp = tmp + 36; //修正中断响应延时造成的误差 T1RH = (unsigned char)(tmp >> 8); //定时器重载值拆分为高低字节 T1RL = (unsigned char)tmp; TMOD &= 0x0F; //清零T1的控制位 TMOD |= 0x10; //配置T1为模式1 TH1 = T1RH; //加载T1重载值 TL1 = T1RL; ET1 = 1; //使能T1中断 PT1 = 1; //设置为高优先级 TR1 = 1; //启动T1 } void ConfigTimer0(unsigned int ms) //T0配置函数 { unsigned long tmp; tmp = 11059200 / 12; //定时器计数频率 tmp = (tmp * ms) / 1000; //计算所需的计数值 tmp = 65536 - tmp; //计算定时器重载值 tmp = tmp + 34; //修正中断响应延时造成的误差 T0RH = (unsigned char)(tmp >> 8); //定时器重载值拆分为高低字节 T0RL = (unsigned char)tmp; TMOD &= 0xF0; //清零T0的控制位 TMOD |= 0x01; //配置T0为模式1 TH0 = T0RH; //加载T0重载值 TL0 = T0RL; ET0 = 1; //使能T0中断 TR0 = 1; //启动T0 } void InterruptTimer0() interrupt 1 //T0中断服务函数 { TH0 = T0RH; //定时器重新加载重载值 TL0 = T0RL; KeyScan(); } void InterruptTimer1() interrupt 3 //T1中断服务函数 { static unsigned char i = 0; TH1 = T1RH; //定时器重新加载重载值 TL1 = T1RL; //循环输出波表中的数据 SetDACOut(pWave[ i]); i++; if (i >= 32) { i = 0; } }
17.8 作业
[size=12.0000pt]3、将信号发生器的程序改装,可以通过按键实现频率的调整。
标签:时钟 i2c总线 构造 做了 会同 drive 离散 变化 转换时间
原文地址:https://www.cnblogs.com/isAndyWu/p/9646398.html