功能实现要求:




每次建好工程文件夹,里边包含User(放工程文件,mian.c,可以在这里写如同我这个文章的文本文档)、Driver(存放底层文件如Led.c,Led.h等)
 新建的工程先搭建框架,可以先书写底层函数(此次书写了五个函数并包含相应的头文件共十个底层文件)
 底层函数内容:
 
 1.初始化底层驱动专用文件
 
 比如先用3个IO口控制74HC138译码器,控制Y4为低电平;当Y4为低电平时,或非门74HC02控制Y4C为高电平,使74HC573的OE端口有效,OE端口有效时,可使用P0口控制LED的亮灭。
 可以去多了解74HC138译码器,74HC02或非门,74HC573八路输出透明锁存器的相关内容会更好理解
 #include <Init.h>
//关闭外设
 void System_Init()
 {
     P0 = 0xff;
     P2 = P2 & 0x1f | 0x80;
     P2 &= 0x1f;
     P0 = 0x00;
     P2 = P2 & 0x1f | 0xa0;
     P2 &= 0x1f;
 }
 //头文件
 #include <STC15F2K60S2.H>
 void System_Init();
2.Led底层驱动专用文件
 与初始化底层驱动专用文件同理,需要了解对应的锁存器控制,可以在使用的芯片数据手册查看
 #include <Led.h>
void Led_Disp(unsigned char addr,enable)
 {
     static unsigned char temp = 0x00;
     static unsigned char temp_Old = 0xff;
     if(enable)
         temp |=0x01 << addr;
     else
         temp&= ~ (0x01 << addr);
     if(temp != temp_Old)
     {
         P0 = ~ temp;
         P2 = P2 & 0x1f | 0x80;
         P2 &= 0x1f;
         temp_Old = temp;
     }
 }
 void Beep(unsigned char flag)
 {
     static unsigned char temp = 0x00;
     static unsigned char temp_Old = 0xff;
     if(flag)
         temp |=0x40 ;
     else
         temp &= ~ 0x40 ;
     if(temp != temp_Old)
     {
         P0 = ~ temp;
         P2 = P2 & 0x1f | 0xa0;
         P2 &= 0x1f;
         temp_Old = temp;
     }
 }
 void Relay(unsigned char flag)
 {
     static unsigned char temp = 0x00;
     static unsigned char temp_Old = 0xff;
     if(flag)
         temp |= 0x10 ;
     else
         temp &= ~ 0x10 ;
     if(temp != temp_Old)
     {
         P0 = ~ temp;
         P2 = P2 & 0x1f | 0xa0;
         P2 &= 0x1f;
         temp_Old = temp;
     }
 }
//头文件
 #include <STC15F2K60S2.H>
 void Led_Disp(unsigned char addr,enable);
3.按键底层驱动专用文件
 (板子上的按键从按键4开始到按键19,可根据实际硬件修改)
 #include <Key.h>
unsigned char Key_Read()
 {
     unsigned char temp = 0;
     P44 = 0;P42 = 1; P35 = 1;P34 = 1;//这个仿真没有P4口,不适用,但是实际运行使用这个
     P37 = 0; P36 = 1; P35 = 1; P34 = 1;
     if(P33 == 0) temp = 4;
     if(P32 == 0) temp = 5;
     if(P31 == 0) temp = 6;
     if(P30 == 0) temp = 7;
     P37 = 1; P36 = 0; P35 = 1; P34 = 1;
     if(P33 == 0) temp = 8;
     if(P32 == 0) temp = 9;
     if(P31 == 0) temp = 10;
     if(P30 == 0) temp = 11;
     P37 = 1; P36 = 1; P35 = 0; P34 = 1;
     if(P33 == 0) temp = 12;
     if(P32 == 0) temp = 13;
     if(P31 == 0) temp = 14;
     if(P30 == 0) temp = 15;
     P37 = 1; P36 = 1; P35 = 1; P34 = 0;
     if(P33 == 0) temp = 16;
     if(P32 == 0) temp = 17;
     if(P31 == 0) temp = 18;
     if(P30 == 0) temp = 19;
     return temp;
     
 }
 //头文件
 #include <STC15F2K60S2.H>
unsigned char Key_Read();
4.数码管底层驱动专用文件
 #include <Seg.h>
unsigned char Seg_Dula[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf};//数码管段码储存数组
 unsigned char Seg_Wela[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//数码管位码储存数组
void Seg_Disp(unsigned char wela,dula,point)
 {
     P0 = 0xff; //
     P2 = P2 & 0x1f |0xe0;
     P2 &= 0x1f;
     P0 = Seg_Wela[wela];
     P2 = P2 & 0x1f |0xc0;
     P2 &= 0x1f;
     P0 = Seg_Dula[dula];
     if(point)
         P0 &= 0x7f;
     P2 = P2 & 0x1f |0xe0;
     P2 &= 0x1f;
 }
 //头文件
 #include <STC15F2K60S2.H>
void Seg_Disp(unsigned char wela,dula,point);
5.IIC底层驱动文件
 /*    #   I2C代码片段说明
     1.     本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
     2.     参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
         中对单片机时钟频率的要求,进行代码调试和修改。
 */
 #include "iic.h"
#include "reg52.h"
 #include <intrins.h>
sbit sda = P2^1;
 sbit scl = P2^0;
#define DELAY_TIME 5
//
 static void I2C_Delay(unsigned char n)
 {
     do
     {
         _nop_();_nop_();_nop_();_nop_();_nop_();
         _nop_();_nop_();_nop_();_nop_();_nop_();
         _nop_();_nop_();_nop_();_nop_();_nop_();        
     }
     while(n--);          
 }
//
 void I2CStart(void)
 {
     sda = 1;
     scl = 1;
     I2C_Delay(DELAY_TIME);
     sda = 0;
     I2C_Delay(DELAY_TIME);
     scl = 0;    
 }
//
 void I2CStop(void)
 {
     sda = 0;
     scl = 1;
     I2C_Delay(DELAY_TIME);
     sda = 1;
     I2C_Delay(DELAY_TIME);
 }
//
 void I2CSendByte(unsigned char byt)
 {
     unsigned char i;
     
     for(i=0; i<8; i++){
         scl = 0;
         I2C_Delay(DELAY_TIME);
         if(byt & 0x80){
             sda = 1;
         }
         else{
             sda = 0;
         }
         I2C_Delay(DELAY_TIME);
         scl = 1;
         byt <<= 1;
         I2C_Delay(DELAY_TIME);
     }
     
     scl = 0;  
 }
//
 unsigned char I2CReceiveByte(void)
 {
     unsigned char da;
     unsigned char i;
     for(i=0;i<8;i++){   
         scl = 1;
         I2C_Delay(DELAY_TIME);
         da <<= 1;
         if(sda) 
             da |= 0x01;
         scl = 0;
         I2C_Delay(DELAY_TIME);
     }
     return da;    
 }
//
 unsigned char I2CWaitAck(void)
 {
     unsigned char ackbit;
     
     scl = 1;
     I2C_Delay(DELAY_TIME);
     ackbit = sda; 
     scl = 0;
     I2C_Delay(DELAY_TIME);
     
     return ackbit;
 }
//
 void I2CSendAck(unsigned char ackbit)
 {
     scl = 0;
     sda = ackbit; 
     I2C_Delay(DELAY_TIME);
     scl = 1;
     I2C_Delay(DELAY_TIME);
     scl = 0; 
     sda = 1;
     I2C_Delay(DELAY_TIME);
 }
unsigned char Ad_Read(unsigned char addr)//AD读取,要有一个入口参数
 {
     unsigned char temp;//接收返回值变量
     I2CStart();//启动单总线
     I2CSendByte(0x90);//发送一个0x90,告诉单片机要写数据了
     I2CWaitAck();//等待应答
     I2CSendByte(addr);//发送一个地址(获取的数据)
     I2CWaitAck();//等待应答
     I2CStart();//启动单总线
     I2CSendByte(0x91);//写一个0x91
     I2CWaitAck();//等待应答
     temp = I2CReceiveByte();//读取数据
     I2CSendAck(1);//发送一个非应答信号
     I2CStop();//停止
     return temp;
 }
void Da_Write(unsigned char dat)
 {
     I2CStart();//启动单总线
     I2CSendByte(0x90);//发送一个0x90,告诉单片机要写数据了
     I2CWaitAck();//等待应答
     I2CSendByte(0x41);//使能DAC转换
     I2CWaitAck();//等待应答
     I2CSendByte(dat);
     I2CWaitAck();//等待应答
     I2CStop();//停止
 }
 //头文件    
 #ifndef _IIC_H
 #define _IIC_H
unsigned char Ad_Read(unsigned char addr);//AD读取,要有一个入口参数
 void Da_Write(unsigned char dat);
void IIC_Start(void);
 void IIC_Stop(void);
 bit IIC_WaitAck(void);
 void IIC_SendAck(bit ackbit);
 void SendByte(unsigned char byt);
 unsigned char IIC_RecByte(void);
#endif
工程主函数内容:
1.头文件声明(把需要用到的头文件添加进来)
 /*头文件声明区*/
 #include <STC15F2K60S2.H>//单片机寄存器专用头文件
 #include <Init.h>//初始化底层驱动专用头文件
 #include <Led.h>//LED底层驱动专用头文件
 #include <Key.h>//按键底层驱动专用头文件
 #include <Seg.h>//数码管底层驱动专用头文件
 #include "iic.h"//数模转换底层驱动头文件
2.变量声明(把需要用到的所有变量现在这里进行声明)
/*变量声明区*/
 unsigned char Key_Val,Key_Old,Key_Down,Key_Up;//按键扫描专用变量
 unsigned char Key_Slow_Down;//按键减速专用变量 10ms
 unsigned char Seg_Buf[8] = {10,10,10,10,10,10,10,10};//数码管显示数据存放数组
 unsigned char Seg_Point[8] = {0,0,0,0,0,0,0,0};//数码管小数点数据存放数组
 unsigned char Seg_Pos;//数码管扫描专用变量
 unsigned char Seg_Slow_Down;//数码管减速专用变量
 unsigned char ucLed[8] = {0,0,0,0,0,0,0,0};//LED显示数据存放数组
 //unsigned char dat,dat2;
 bit Seg_Disp_Mode;//数码管显示模式变量 0-电压显示界面 1-电压输出界面
 float voltage;//实时电压变量
 float voltage_Output;//实时电压输出变量
 bit Output_Mode;//输出模式专用变量 0-2V 1-随AD输出
 bit Seg_Flag = 1;//数码管功能标志位
3.按键处理函数(在这里编写按键控制的函数)
/*键盘处理函数*/
 void Key_Proc()
 {
     if(Key_Slow_Down)return;
     Key_Slow_Down = 1;//按键减速程序
     
     Key_Val = Key_Read();//读取按下的键码值
     Key_Down = Key_Val & (Key_Val ^ Key_Old);//捕捉下降沿
     Key_Up = ~ Key_Val & (Key_Val ^ Key_Old);//捕捉上升沿
     Key_Old = Key_Val;//辅助扫描
     
     switch(Key_Down)
     {
         case 19://显示界面切换按键
             Seg_Disp_Mode ^= 1;//取反
         break;
         case 18://输出模式切换按键
             Output_Mode ^= 1;
         break;
         case 16://数码管功能按键
             Seg_Flag ^= 1;
         break;
     }
         
 }
4.信息处理函数(需要使用到到的函数进行简单的预处理)
 /*信息处理函数*/
 void Seg_Proc()
 {
     if(Seg_Slow_Down)return;
     Seg_Slow_Down = 1;//数码管减速程序
     
     voltage = Ad_Read(0x43) / 51.0;//实时读取RB2电压数据
     if(Output_Mode == 0)//固定输出2V
         voltage_Output = 2;
     else
         voltage_Output = voltage;//随AD输出
     //voltage_Output = Output_Mode?voltage:2;//这个同样可以判断输出电压,使用实现两种电压值输出
     if(Seg_Disp_Mode == 0)
     {
         Seg_Buf[0] = 11;//显示U
         Seg_Buf[5] = (unsigned char)voltage;//
         Seg_Buf[6] = (unsigned int)(voltage * 100) / 10 % 10;//
         Seg_Buf[7] = (unsigned int)(voltage * 100)  % 10;//
         Seg_Point[5] = 1;//点亮小数点
         
     }
     else//处于电压输出界面
     {
         
         Seg_Buf[0] = 12;//显示U
         Seg_Buf[5] = (unsigned char)voltage_Output;//
         Seg_Buf[6] = (unsigned int)(voltage_Output * 100) / 10 % 10;//
         Seg_Buf[7] = (unsigned int)(voltage_Output * 100)  % 10;//
         Seg_Point[5] = 1;//点亮小数点
     }
 //    //读取的值是上一次转换的结果,读取两个数据时,人为调换一下
 //    dat2 = Ad_Read(0x41);//读取AD0x41数据量
 //    dat = Ad_Read(0x43);
 //    Da_Write(255);
 //    
 //    Seg_Buf[0] = dat / 100 % 10;
 //    Seg_Buf[1] = dat / 10 % 10;
 //    Seg_Buf[2] = dat % 10;
 //    
 //    Seg_Buf[4] = dat2 / 100 % 10;
 //    Seg_Buf[5] = dat2 / 10 % 10;
 //    Seg_Buf[6] = dat2 % 10;
 }
5.其他函数(其他编写的函数,在这里书写会比较方便理解)
 /*其他函数*/
 void Led_Proc()
 {
     unsigned char i;
     Relay(1);//关闭继电器
     Beep(1);//关闭蜂鸣器
     Da_Write(voltage_Output);//电压输出
     for(i =0;i<2;i++)//互斥点亮
     ucLed[i] = (i == Seg_Disp_Mode);
     if(voltage < 1.5 || (voltage >= 2.5 && voltage < 3.5))
         ucLed[2] = 0;
     else
         ucLed[2] = 1;
         ucLed[3] = Output_Mode;
 }
6.定时器0中断初始化函数
(这个可以使用STC的定时器计算那里生成c代码,后面要自己添加ET0,EA打开中断)
 /*定时器0初始化函数*/
 void Timer0Init(void)        //1毫秒@12.000MHz
 {
     AUXR &= 0x7F;        //定时器时钟12T模式
     TMOD &= 0xF0;        //设置定时器模式
     TL0 = 0x18;        //设置定时初值
     TH0 = 0xFC;        //设置定时初值
     TF0 = 0;        //清除TF0标志
     TR0 = 1;        //定时器0开始计时
     ET0 = 1;
     EA = 1;
 }
7.定时器0中断服务函数
 (为了定时执行特定的任务,如此处设置了定时的时间触发了数码管和LED产生特定反应)
/*定时器0中断服务函数*/
 void Timer0Serve() interrupt 1
 {
     if(++Key_Slow_Down == 10)Key_Slow_Down = 0;
     if(++Seg_Slow_Down == 500)Seg_Slow_Down = 0;
     if(++Seg_Pos == 8)Seg_Pos = 0;
     if(Seg_Flag == 1)
         Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
     else
         Seg_Disp(Seg_Pos,10);//熄灭数码管
         Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
 }
8.主函数Main(调用书写的函数实现所需的相应功能)
/*Main*/
 void main()
 {
     Sys_Init();
     Timer0Init();
     while(1)
     {
         Key_Proc();
         Seg_Proc();
         Led_Proc();
     }
 }
其他的详细资料在另一篇PCF8591的笔记,有详细讲解AD数模转换的内容和IIC的使用等等。



















