360优化大师官方网站电商网站 外包
题目要求:
1、 基本要求
2、 竞赛板配置要求
3、 硬件框图
系统硬件框图

4、 功能描述
4.1基本功能
4.2显示功能
和之前一样建好工程文件夹,里边包含User(放工程文件,mian.c)、Driver(存放底层文件如Led.c,Led.h等)
 新建的工程先搭建框架,可以先书写底层函数(此次书写了五个函数并包含相应的头文件共十个底层文件)
4.3按键功能
 
4.4LED 指示灯功能
 4.5初始状态说明
底层函数内容:
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;
     }
 }
#include <STC15F2K60S2.H>
 void Led_Disp(unsigned char addr,enable);
 3.按键底层驱动专用文件
 
 (板子上的按键从按键4开始到按键19,可根据实际硬件修改)
 #include <Key.h>
unsigned char Key_Read()
 {
     unsigned char temp = 0;
     if(P33 == 0) temp = 4;
     if(P32 == 0) temp = 5;
     if(P31 == 0) temp = 6;
     if(P30 == 0) temp = 7;
     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};//数码管段码储存数组
 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总线驱动程序
   软件环境: Keil uVision 4.10 
   硬件环境: CT107单片机综合实训平台 8051,12MHz
   日    期: 2011-8-9
 */
#include "iic.h"
 #include "intrins.h"
#define DELAY_TIME 5
#define Photo_Res_Channel 0x41
 #define Adj_Res_Channel 0x43
 //总线引脚定义
 
 sbit SDA = P2^1;  /* 数据线 */
 sbit SCL = P2^0;  /* 时钟线 */
void IIC_Delay(unsigned char i)
 {
     do{_nop_();}
     while(i--);        
 }
//总线启动条件
 void IIC_Start(void)
 {
     SDA = 1;
     SCL = 1;
     IIC_Delay(DELAY_TIME);
     SDA = 0;
     IIC_Delay(DELAY_TIME);
     SCL = 0;    
 }
//总线停止条件
 void IIC_Stop(void)
 {
     SDA = 0;
     SCL = 1;
     IIC_Delay(DELAY_TIME);
     SDA = 1;
     IIC_Delay(DELAY_TIME);
 }
//发送应答
 void IIC_SendAck(bit ackbit)
 {
     SCL = 0;
     SDA = ackbit;                      // 0:应答,1:非应答
     IIC_Delay(DELAY_TIME);
     SCL = 1;
     IIC_Delay(DELAY_TIME);
     SCL = 0; 
     SDA = 1;
     IIC_Delay(DELAY_TIME);
 }
//等待应答
 bit IIC_WaitAck(void)
 {
     bit ackbit;
     
     SCL  = 1;
     IIC_Delay(DELAY_TIME);
     ackbit = SDA;
     SCL = 0;
     IIC_Delay(DELAY_TIME);
     return ackbit;
 }
//通过I2C总线发送数据
 void IIC_SendByte(unsigned char byt)
 {
     unsigned char i;
    for(i=0; i<8; i++)
     {
         SCL  = 0;
         IIC_Delay(DELAY_TIME);
         if(byt & 0x80) SDA  = 1;
         else SDA  = 0;
         IIC_Delay(DELAY_TIME);
         SCL = 1;
         byt <<= 1;
         IIC_Delay(DELAY_TIME);
     }
     SCL  = 0;  
 }
//从I2C总线上接收数据
 unsigned char IIC_RecByte(void)
 {
     unsigned char i, da;
     for(i=0; i<8; i++)
     {   
         SCL = 1;
     IIC_Delay(DELAY_TIME);
     da <<= 1;
     if(SDA) da |= 1;
     SCL = 0;
     IIC_Delay(DELAY_TIME);
     }
     return da;    
 }
//函数名:ADC转换函数,这个要自己写
 //入口参数:要进行转换的通道控制位
 //返回值:ADC转换的数值
 //函数功能:对指定的通道进行ADC转换,函数返回转换的数值
 
unsigned char Ad_Read(unsigned char addr)//AD读取,要有一个入口参数
 {
     unsigned char temp;//接收返回值变量
     IIC_Start();//启动单总线
     IIC_SendByte(0x90);//发送一个0x90,告诉单片机要写数据了
     IIC_WaitAck();//等待应答
     IIC_SendByte(addr);//发送一个地址(获取的数据)
     IIC_WaitAck();//等待应答
     
     IIC_Start();//启动单总线
     IIC_SendByte(0x91);//写一个0x91
     IIC_WaitAck();//等待应答
     temp = IIC_RecByte();//读取数据
     IIC_SendAck(1);//发送一个非应答信号
     IIC_Stop();//停止
     return temp;
 }
//函数名:DAC转换函数,这个要自己写
 //入口参数:要进行转换的数值
 //返回值:无
 //函数功能:对入口参数要转换的DA数据进行转换
 
void Da_Write(unsigned char dat)
 {
     IIC_Start();//启动单总线
     IIC_SendByte(0x90);//发送一个0x90,告诉单片机要写数据了
     IIC_WaitAck();//等待应答
     IIC_SendByte(0x41);//使能DAC转换
     IIC_WaitAck();//等待应答
     IIC_SendByte(dat);//发送一个地址(获取的数据)
     IIC_WaitAck();//等待应答
     IIC_Stop();
 }
 //头文件   头文件都需要自己编写,目前最新的一版赛点资源包没有头文件。
#include <STC15F2K60S2.H>
 void IIC_Start(void); 
 void IIC_Stop(void);  
 bit IIC_WaitAck(void);  
 void IIC_SendAck(bit ackbit); 
 void IIC_SendByte(unsigned char byt); 
 unsigned char IIC_RecByte(void); 
unsigned char Ad_Read(unsigned char addr);
 void Da_Write(unsigned char dat);
  
工程主函数内容:
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 Seg_Pos;//数码管扫描专用变量
 unsigned char Key_Slow_Down;//按键减速专用变量
 unsigned char Seg_Slow_Down;//数码管减速专用变量
 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 ucLed[8] = {0,0,0,0,0,0,0,0};//Led显示数据存放数组
 unsigned int Timer_1000ms;//1000毫秒计时变量
 unsigned int Freq;//实时频率值
 bit Seg_Disp_Mode;//数码管显示模式变量 0-频率显示界面 1-电压显示界面
 float Voltage;//实时电压值
 bit Output_Mode;//DAC输出模式标志位 0-固定2V 1-随AD变化
 float Vlotage_Output;//实时输出电压
 bit Seg_Flag = 1;//数码管使能标志位 默认开启
 bit Led_Flag = 1;//Led使能标志位 默认开启
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 4://显示界面切换
             Seg_Disp_Mode ^= 1;
         break;
         case 5://输出模式切换
             Output_Mode ^= 1;
         break;
         case 6://LED 指示灯功能控制
             Led_Flag ^= 1;
         break;
         case 7://数码管显示功能控制
             Seg_Flag ^= 1;
         break;
     }
}
4.信息处理函数(需要使用到到的函数进行简单的预处理)
void Seg_Proc()
 {
     unsigned char i = 3;//高位熄灭专用变量
     if(Seg_Slow_Down)return;
     Seg_Slow_Down = 1;//数码管减速程序
     
     /*信息读取区*/
     Voltage = Ad_Read(0x43) / 51.0;//实时获取电压值
     
     if(Output_Mode == 0)//处于固定输出模式
         Vlotage_Output = 2;
     else//处于随AD输出模式
         Vlotage_Output = Voltage;
     
         /*数据显示区*/
     Seg_Point[5] = Seg_Disp_Mode;//小数点使能
     if(Seg_Disp_Mode == 0)//频率显示
     {
         
         Seg_Buf[0] = 11;//显示F
         Seg_Buf[3] = Freq / 10000 % 10;
         Seg_Buf[4] = Freq / 1000 % 10;
         Seg_Buf[5] = Freq / 100 % 10;
         Seg_Buf[6] = Freq / 10 % 10;
         Seg_Buf[7] = Freq % 10;
         while(Seg_Buf[i] == 0)//数码管高位熄灭
         {
             Seg_Buf[i] = 10;
             if(++i == 7)break;//保证最低位不熄灭 避免程序卡死
         }
     }
     else//处于电压显示界面
     {
         Seg_Buf[0] = 12;//显示U
         Seg_Buf[3] = 10;
         Seg_Buf[4] = 10;
         Seg_Buf[5] = (unsigned char)Voltage;
         Seg_Buf[6] = (unsigned int)(Voltage * 100) / 10 % 10;
         Seg_Buf[7] = (unsigned int)(Voltage * 100) % 10;
     }
 }
5.其他函数(其他编写的函数,在这里书写会比较方便理解)
void Led_Proc()
 {
     unsigned char i;//For循环专用变量
     /* DAC相关 */
     Da_Write(Vlotage_Output * 51);//实时输出电压值
     /* Led相关 */
     for(i=0;i<2;i++) //互斥点亮
     ucLed[i] = (i == Seg_Disp_Mode);
     ucLed[2] = ((Voltage >= 1.5 && Voltage <= 2.5) || (Voltage >= 3.5));
     ucLed[3] = ((Freq >= 1000 && Freq < 5000) || (Freq >= 10000));
     ucLed[4] = Output_Mode;
 }
6.定时器中断初始化函数
 (这个可以使用STC的定时器计算那里生成c代码,后面要自己添加ET0,EA打开中断)这里使用定时器0计数,定时器1计时
/* 定时器1中断初始化函数 */
 void Timer1Init(void)        //1毫秒@12.000MHz
 {
     AUXR &= 0xBF;        //定时器时钟12T模式
     TMOD &= 0x0F;        //设置定时器模式
     TL1 = 0x18;        //设置定时初值
     TH1 = 0xFC;        //设置定时初值
     TF1 = 0;        //清除TF1标志
     TR1 = 1;        //定时器1开始计时
     ET1 = 1;
     EA = 1;
 }
//频率测量定时器配置/* 定时器0中断初始化函数 */
void Timer0Init(void)        //0毫秒@12.000MHz
 {
     TMOD &= 0xF0;        //设置定时器模式
     TMOD |= 0x05;        //GATE = 0 计数模式 16位不自动重装//设置计数模式
     TL0 = 0x00;        //设置定时初值
     TH0 = 0x00;        //设置定时初值
     TF0 = 0;        //清除TF0标志
     TR0 = 1;        //定时器0开始计时
 }
7.定时器1中断服务函数
 (为了定时执行特定的任务,如此处设置了定时的时间触发了数码管和LED产生特定反应)//中断在测试时可以先注释掉,但是这里按键状态有延时,测试按键时可以解除注释void Timer1server()interrupt 3
 {
     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,0);//熄灭所有数码管
     if(Led_Flag == 1)//Led使能
         Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
     else
         Led_Disp(Seg_Pos,0);//熄灭所有Led
     if(++Timer_1000ms == 1000)//实时读取频率值
     {
         Timer_1000ms = 0;
         Freq = TH0 << 8 | TL0;
         TH0 = TL0 = 0;
         
     }
 }
8.主函数Main(调用书写的函数实现所需的相应功能)
void main()
 {
     Sys_Init();
     Timer0Init();
     Timer1Init();
     while(1)
     {
        Key_proc();
         Seg_Proc();
         Led_Proc();
     }
 }
NE555相关资料:

通过Rb3调节,电压测量可以在这个图的19,20脚测量。

GATE = 0,定时器计数;C/T = 1,连接P34。

根据这两个信息可知,定时器0设置成0101的模式,使用计数器。
定时器1计时

