电子电路设计实验报告电子线路专题实验Ⅱ一、实验要求:1. 认真阅读学习系统线路及相关资料2. 将键盘阵列定义为0. 1. 2------ E. F,编程实现将键盘输入内容显示在LCD显示器上。
3. 编程实现将日历、时钟显示在LED显示屏上(注意仔细阅读PCF8563资料),日历、时钟轮回显示。
4. 利用D/A转换通道(下行通道)实现锯齿波发生器;输出(1~5V)固定电压转换成(4~20mA)电流。
5. 利用A/D转换通道(上行通道)实现数据采集,将采集信号显示在LED屏上。
程序要求分别具有平均值滤波、中值滤波和滑动滤波功能。
6. 将按键阵列定义成与16个语音段对应,编写程序,实现按键播放不同的语音段。
二、实验设计思路:本次实验用c语言实现,主要包括LCD,LED,AD,DA,日历芯片,测温传感芯片。
受到嵌入式系统实验的启发,将LCD,LED,I2C总线协议,键盘扫描模块接口写成一个文件库(放在library文件夹下),尽量做到调用时与底层硬件无关。
通过调用库文件中的函数,实现代码的重用性。
键盘,LCD的代码由于与嵌入式实验具有相通之处,因此可将高层的函数(与底层硬件无关的函数)方便地移植过来。
三、实验设计:1.矩阵键盘扫描模块4×4的矩阵键盘,通过扫描可得到按下键的行列值,将行列值转换为相应的对应数字0~F。
函数GetKey()实现获得按键的键值。
对于键盘模块对于对按键的键值识别主要是通过两次扫描而取得。
对于第一次扫描,给四行键全部赋予1,然后读回键盘值,对于第二次扫描,逐行为键盘送1,每次送1后再读回键盘值,若非零,说明此行有键按下,最终确定键值。
通过调用GetKey函数构造GetChar()函数,实现获取键盘字符(’0’~’F’)的功能。
通过调用GetChar()函数构造GetDec()函数,实现获取键盘输入整数的功能,整数范围在0~99999。
有按’C’键回退一格,按’E’清空当前未完输入,按’F’键结束输入的功能。
程序代码://键盘初始化,将标志位置1;void Key_Init(void){bKeyUp_Flag=1;//标志(全局变量)位置1}//键盘扫描函数,得到键的行列位置;unsigned char GetScanKey(void){unsigned char key, i, temp;unsigned char xdata * ptr;key=0xff;for (i=1; iptr=0x8fff;* ptr =i;temp = * ptr; //取键盘IO口的值temp &= 0x0f; //屏蔽高四位if (temp!=0x00) //是否有有效键值{key = ibreak;}}return key; //返回行位(高四)和列位(低四)}//取键值,长按无效unsigned char GetKey(void){unsigned char key, temp;if (!bKeyUp_Flag) //判断标志,是0执行/***按键程序执行一次后会将bKeyUp_Flag标志位清零,执行此段程序,长按键无效返回无效值,直至按键无效返回无效按键值,置"1"标志位。
按键输入恢复有效。
屏蔽这部分则长按键有效***/ {key=GetScanKey();if (key==0xff) //没有按键,置标志位bKeyUp_Flag=1;else //保持按键return 0xff; //因为0xff大于15,故为无效键值,实现长按键无效 }key=GetScanKey();if (key==0xff) //没有按键return key;else //有按键有效temp=key; //取键值Delay_ms(20); //延时20ms 消抖key=GetScanKey(); //键盘扫描if(key!=temp) //判断两次键值是否相同,排除干扰信号影响确认有效信号{key=0xff;return key;}else //取键值{/*这部分主要作用是软件抗干扰*/temp=Key_Value_Table[key>>4]; //见说明/*行值有效位(键盘的4个行SEL返回的值含有的有效位"1")有且只有一位键值才有效否则返回无效键值*/if (temp==0xff){key=0xff;return key;}temp=Key_Value_Table[key&0x0f];/*列值有效位(键盘的4个列RL返回的值含有的有效位"1")有且只有一位键值才有效否则返回无效键值*/if (temp==0xff){key=0xff;return key;}key=Key_Value_Table[key>>4]*4+Key_Value_Table[key&0x0f];//行对应的中间值的四倍与列对应的中间值之和即为按键编号0~15/*行列组合后的值大于15无效*/if (key>15){key=0xff;return key;}bKeyUp_Flag=0;return key;}}//获得键盘输入字符int GetChar(void){unsigned char key=0xff;while(key==0xff)key=GetKey();/* wait input fix */return ((int)key_arrenge[key]);/* change to ASCII code and return */}//获得键盘输入整数long GetDec(void){long out_dec = 0; /* result(decimal number) 数值范围0~99999 */unsigned char i,j; /* variable for character count */ int temp = 0; int key;for(i = 0;i{key =GetChar(); /* 获取键值’0’-’9’ */if((key= '0')){temp = key - '0'; /* 获取数值0-9 */out_dec = out_dec * 10 + (long)temp;if(out_dec == 0){continue; /* 首位数据输入为0时,显示不变 */ }i++;LCD__putchar(key); /* 显示当前输入的数据 */ continue;}if('E'== key) /* 当前输入清零 */{out_dec = 0;back_cursol(i); /* 清显示区 */for(j=0;jLCD__putchar(' ');back_cursol(i); /* 清显示区 */continue;}if('F' == key) /* ENTER键,数值确认 */{return out_dec;}if('C' == key) /* 撤销最近一个输入数字*/{out_dec = out_dec / 10;back_cursol(1);LCD__putchar(' ');back_cursol(1);continue;}}}2. LED模块通过送字形码和字位码可以点亮对应的一个8段LED。
8个LED的同时显示通过循环扫描显示实现,即每次在一个LED上显示设定的数字,延时显示一段时间(延时越长,亮度越好),然后切换显示下一个,依次轮换。
程序代码:unsigned char Led_table[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};void Delay_us1(int t){while(t-->=0);}//函数功能描述:给显示数组赋初始值;void Led_Init(unsigned char *show){unsigned char i;for (i=0;i{//在此处给显示数组赋值*show= 0x00;show++;}}//函数功能描述:在LED上显示;void display(unsigned char *show) {unsigned char i=1,j;unsigned char xdata *ptr;for (j=0; j{ptr=0x8fff;//段选地址*ptr=i;ptr=0x9fff;//位选地址//在此给*ptr赋值送字形码*ptr=Led_table[show[j]];Delay_us1(30);*ptr=0x00;i}}//数字转led字形码unsigned char asc2led(unsigned char a){return *(Led_table+a);}3. LCD显示模块设计思路:LCD的显示通过给LCD写命令和数据实现。
写数据与写指令时各控制引脚的电平不同,对应了不同的地址。
先构造写数据和写命令函数(与硬件相关操作),已经检测LCD是否忙的函数(当处于忙状态时不能写数据,否则硬件会工作不正常),在此接口的基础上构造库(与硬件无关操作,通过调用写数据和写命令函数实现),实现各种不同的功能,包括初始化设定工作方式,显示字符,显示字符串,显示数字(0~99999),清屏,设定光标的位置等。
这里的显示字符,显示整数和键盘检测模块的读入字符,读入整数对应起来,可以合作使用。
使用时先调用初始化函数初始化LCD,设定工作方式,然后可以调用不同的函数实现不同的显示。
程序代码:/*===== define variable =====*/unsigned char cursol_x; /* cursor position (horizontal) */ unsigned char cursol_y; /* cursor positon (vertical) */unsigned char xdata *ptr;//指向XDATA的指针(访问片外地址)unsigned char code ASC2_Value_Table[16]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44,0x45,0x46};//===========向1602指令寄存器写指令=============void WriteW(unsigned char a){ptr=0xAFF0; //RS=0,R/W=0*ptr=a;}//==============向1602指令寄存器写指数据============ void WriteD(unsigned char key_asc2){CheckBF();ptr=0xAF02;*ptr=key_asc2;}//===========检查忙标志位BF=============void CheckBF(void){unsigned int i;while(1){ptr=0xAFF1;//RS=0,R/W=1i=*ptr;i&= 0x80;if(i==0)break;}}//==========LCD初始化==========void LCD_Init(void){CheckBF();WriteW(0x38);CheckBF();WriteW(0x01);//Clear display 清显示CheckBF();WriteW(0x06);//Entry mode set:I/D=1,S=0 [I:Increase,D:Decline,S:Shift] CheckBF();WriteW(0x0F);//Dislpay on/off controlD=1,C=1,B=1[D:Display,C:Cursor,B:Blink] CheckBF();WriteW(0x80);//Dislpay on/off controlD=0,C=0,B=0[D:Display,C:Cursor,B:Blink] }//======设定光标所在位置==========void LCD__setcursol(unsigned char x, unsigned char y){cursol_x = x; /* cursor position (horizontal) */ cursol_y = y; /* cursor positon (vertical) */ CheckBF(); /* wait LCD process */ WriteW(0x80 | y*0x40 + x); /* address of the second line 0x40-0x4f */}//======后移光标=======void move_cursol(void){if(++cursol_x > 0x0f){ /* move cursor to right, if get to end of the line */cursol_x = 0; /* move to begin of the line */ if(++cursol_y >= 2){ /* return, if over the second line */ cursol_y = 0; /* return to begin of the line */ }LCD__setcursol(cursol_x, cursol_y); /* set cursor */}}//======前移光标=======void back_cursol(int i){cursol_x -= i;LCD__setcursol(cursol_x, cursol_y); /* set cursor */}//======清屏=======void LCD__clear(void){CheckBF(); /* wait LCD process */ WriteW(0x01); /* clear display */ }//=======写字符==========void LCD__putchar(int c){if((c == '\n')||(c == '\r')) /* line feed code */{cursol_x = 0;cursol_y ^= 1;LCD__setcursol(cursol_x, cursol_y); /* set cursor */}CheckBF(); /* wait LCD process */WriteD(c); /* write Data-Register */CheckBF(); /* wait LCD process */move_cursol(); /* move cursor position */ }//=========写字符串========void LCD__puts(const unsigned char *s){for(; '\0' != *s; s++){ /* loop before null code */ LCD__putchar(*s); /* display character on LCD */}}//=========写整数=======void LCD__putdec(long dec) /* 可打印的数据小于99999 */{long num ; /* number to be displayed */ int i; /* loop counter */ unsigned char str_buf[5]; /* buffer for data display */num = dec; /* save number to be displayed */for(i = 0 ;num != 0; i++){ /* confirm digit */ str_buf[i] = num % 10; /* get every digit number and save in buffer*/num = num / 10;}if(i == 0){ /* if number is 0 */ LCD__putchar('0'); /* display '0' in LCD */ }else{ /* if number is not 0 */for(--i; i >= 0; i--){ /* loop times of digits */LCD__putchar(str_buf[i] + '0'); /* after transfered into ASCII code */} /* display in LCD */ }}//十六进制码转换为ascii码unsigned char Key_ASC2(unsigned char key){unsigned char key_asc2;key_asc2=ASC2_Value_Table[key];return key_asc2;}4.I2C总线协议I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。