蓝桥杯第十届省赛 NE555测方波

蓝桥杯第十届省赛 NE555测方波

代码部分

main.c

#include<STC15F2K60S2.H>
#include<iic.h>
#define keypress P3
#define u8 unsigned char
#define u16 unsigned int
u16 volt;
u16 voltled;
sbit buzzer = P0^6;	
bit flag500ms, flag1s, key_flag;
u8   smg_count, key_count, smg_i, table1[8], table2[8];
bit  smg_translator=0;//按下S4数码管界面转换的诶,我们假装第一个界面是频率显示界面
bit DAC_flag=1, LED_flag=1, smg_dispaly=1;//按下S5DAC变化
u16 pulse,count_500;
u8 code d_display[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x71 ,0x3e};
u8 code w_display[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
void led_light();
void volt_smg();
void dac();
void display()
{
			  P2 = 0xE0; 
	       if(smg_translator)
				 {
	         P0 =  ~table1[smg_i];
				 }
				 else
				 {
					 P0 =  ~table2[smg_i];//dianya
				 }
          P2=0x00;//~table1[smg_i];
			  P2 = 0xC0; P0 = w_display[smg_i];P2=0x00;
			  smg_i++;
        if(smg_i == 8) smg_i = 0;			
}
void dispear()
{
			  P2 = 0xE0; P0 =  ~0x00; P2=0x00;//~table1[smg_i];
			  P2 = 0xC0; P0 = w_display[smg_i];P2=0x00;
			  smg_i++;
        if(smg_i == 8) smg_i = 0;		
}

void allint()
{P0=0XFF;P2=0X80;P2=0X1F;
 P0=0X00;P2=0XA0;P2=0X1F;}


	
typedef unsigned char BYTE;
typedef unsigned int WORD;

//-----------------------------------------------


WORD count;                         //1000 times counter


	
void tm1_isr() interrupt 3 using 1
{

    if (count-- == 0)               //1ms * 1000 -> 1s
    {
        count = 500; 
         flag1s = 1;			//reset counter
    }
		if (count_500-- == 0)               //1ms * 1000 -> 1s
    {
      flag500ms = 1;
			count_500 = 250;               //reset counter
			
    }

		if (key_count-- == 0)               //1ms * 1000 -> 1s
    {
        key_count = 4;               //reset counter
			  key_flag = 1;

    }
   if(smg_dispaly)
	 {
		display();
	 }
	 else
	 {
      dispear();  //按键s7
	 }
	 
		
}

//-----------------------------------------------

void Timer0Init(void)		//@11.0592MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0x04;		//设置定时器模式
	TL0 = 0x00;		//设置定时初值
	TH0 = 0x00;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}

void Timer1Init(void)		//2毫秒@11.0592MHz
{
	AUXR |= 0x40;		//定时器时钟1T模式
	TMOD = 0x04;		//设置定时器模式
	TL1 = 0x9A;		//设置定时初值
	TH1 = 0xA9;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	ET1=1;
}

u8 trg,cont;
void keyread()
{
	u8  readdat = keypress^0xff;
	    trg = readdat&(readdat^cont);
	    cont = readdat;
}

void main()
{
	  Timer1Init();
    count = 0;                     
    Timer0Init();
	  EA = 0;
	  PCF_ADC_w(0x03);
//	  volt = PCF_adc(0x03)*100/51;//保留两位小数害,要一直读取才行,所以放在while循环中
	
	  EA = 1;   
    allint();	
    while (1)
		{
			if(key_flag)
			{  
				key_flag = 0;
				keyread();
				switch(trg)
				{
					case 0x08: smg_translator = ~smg_translator; break;
					case 0x04: DAC_flag = ~DAC_flag; break;
					case 0x02: LED_flag = ~LED_flag;   break;
					case 0x01: smg_dispaly = ~smg_dispaly;  break;
				}
				dac();//V display
			}
			
			led_light();//按键s6
			 
			if(flag500ms)
			{
				flag500ms = 0;
				TR0 = 0;                //计数器关闭
				pulse = TH0*256 + TL0; //脉冲数
			  pulse = pulse*2; //1s内的脉冲数
				TH0 = 0; TL0 = 0;
				TR0 = 1;
			}
			if(flag1s)
			{
			flag1s = 0;
			table1[0] = d_display[10];
      table1[1]	= 0x00;
      if( pulse>99999 ) 	table1[2] = d_display[pulse/100000];
      else 	table1[2] = 0x00;		
			if( pulse>9999 ) 	table1[3] = d_display[pulse/10000%10];
      else 	table1[3] = 0x00;
			table1[4] = d_display[pulse/1000%10];
			table1[5] = d_display[pulse/100%10];
			table1[6] = d_display[pulse/10%10];
			table1[7] = d_display[pulse%10];
			pulse = 0;
				volt_smg();
			}
		}
}
void led_light()
{
	if(LED_flag)
			{
				P2 = 0x80;
				P0 = ~0x00;
				P2 = 0x00;
			}
			else
			{
				P2 = 0x80;
				if(smg_translator)
				{
					P0 = ~0x02;
				}
				else
				{
					P0 = ~0x01;
				}
				if(voltled > 350 ||  (voltled>=150 && voltled<=250))
				{
					P0 &= ~0x04; 
				}
				if(pulse>10000 || (pulse >=1000 && pulse <=5000))
				{
					P0 &= ~0x08;
				}
				if(DAC_flag)   P0 &= ~0x20;
				else  P0 &= 0xcf;
				
				P2=0X00;
					
				
			}
}
15单片机工作方式0x00自动装载,也就是中断里面无论是否设置初值,都是一样的。
这里比较疑惑的一点是我设置的初值溢出后是1ms,所以按照计算500ms内的脉冲数却是 
初值溢出值为2ms脉冲数的两倍。
void volt_smg()
{
	table2[0] = d_display[11];
	table2[1] = 0x00;
	table2[2] = 0x00;
	table2[3] = 0x00;
	table2[4] = 0x00;
	table2[5] = d_display[volt/100%10]|0x80;
	table2[6] = d_display[volt/10%10];
	table2[7] = d_display[volt%10];
}
void dac()
{
//	P2 = 0x80;
	if(DAC_flag)
			 {
				 volt = 200;
	//			P0 &= ~0x20;    //da kai L5
			EA = 0;
			voltled = PCF_adc(0x03);//保留两位小数
			voltled = voltled/255.0f*500;
	    EA = 1; 
			 }
	else
	{
			EA = 0;
			volt = PCF_adc(0x03);//保留两位小数
			volt = volt/255.0f*500;
	    EA = 1; 
  //关闭l5,其他位置0不变,L5=0
		voltled = volt;
	}
//	P2 = 0x00;
}

iic.c

/*
  程序说明: IIC总线驱动程序
  软件环境: Keil uVision 4.10 
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/

#include "reg52.h"
#include "intrins.h"
#define u8 unsigned char

#define DELAY_TIME 5

#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1

//总线引脚定义
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)   //我知道了,憨憨,这得先写再读,PCF85芯片才知道你干了啥!
{
    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;    
}

void PCF_ADC_w(u8 add)
{
		
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(add);
	IIC_WaitAck();
	IIC_Stop();
}

unsigned int PCF_adc(u8 add)
{
	u8 temp;
	IIC_Start();
	IIC_SendByte(0x90);
	IIC_WaitAck();
	IIC_SendByte(add);
	IIC_WaitAck();
	
	IIC_Start();
	IIC_SendByte(0x91);
	IIC_WaitAck();
	temp = IIC_RecByte();
//	IIC_WaitAck();
//	IIC_SendAck(0);
	IIC_Stop();
	
	return temp;
}

void writePCF( u8 add)
{
	
	IIC_Start();
	IIC_SendByte(0x90);//write
	while(!IIC_WaitAck());
	IIC_SendByte(add);
	while(!IIC_WaitAck());
	IIC_Stop();
	
}

u8 readPCF( u8 add)   //通道1光敏电阻,通道3电压信号
{
	u8 temp;
	IIC_Start();
	IIC_SendByte(0x90);//write
		while(!IIC_WaitAck());
	IIC_SendByte(add);
		while(!IIC_WaitAck());
	
	IIC_Start();
	IIC_SendByte(0x91);//read
	while(!IIC_WaitAck());
  temp=IIC_RecByte();
	while(!IIC_WaitAck());
	IIC_Stop();
	
	return temp;
}

iic.h

#ifndef _IIC_H
#define _IIC_H
#define u8 unsigned char

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 int PCF_adc(u8 add);
void PCF_ADC_w(u8 add);
u8 readPCF( u8 add) ;
void writePCF( u8 add);
#endif

注意事项

1.测方波时ms不要写成us(惨痛经历)
2.定时器0工作方式外部计数后四位C/T=1 即TMOD=0X04;此时定时计数器1和 定时计数器2 都是自动重装载模式
3.有关iic时序的问题,最简单的写法即另一篇博客中的模块化部分(底层驱动的时序部分是不需要改的)
4.如果测试电压(rb2)出现数码管闪烁导致看不清数字时——>
①分析闪烁原因
将数码管显示的数字设置为1s更新一次,可以发现闪烁的数字有两种或者以上。
② 从另一种不正确的数字出发,本次我遇见的bug是另一个数字是0,说明我读取的rb2电压有时没有成功
③分析:读取时间太快导致电压不能输出…,于是我放在10ms扫描一次按键的过程中,编译后发现没有发生数码管闪烁现象√问题解决

5.LED模块关闭时仍有微弱(较强灯光),说明程序发生了碰撞,肉眼可以看见余晖现象。这里我的方案是将控制LED的程序写在一个函数 void led()里,方便控制,从最开始的要么开,要么关出现分支,慢慢写出细节√
6.一定要注意数据类型,大于255的数要谨慎谨慎再谨慎
这里电压我用的unsigned int类型

volt = PCF_adc(0x03);//保留两位小数
		volt = volt/255.0f*500;
		//也可以写成volt = volt/255.0*500;(双精度)
		//volt = PCF_adc(0x03)/255.0f*500;经测试这样也行

这里255.0f表示单精度浮点数
7.单精度bai浮点型(float )专指占用du32位存储空间的单精zhi度(single-precision )值。超出位数部分会四舍五入,单精度浮点数最多有7位十进制有效数字,双精度浮点数可以表示十进制的15或16位有效数字。
8.再来数据类型转换的栗子
(int)(11.0/3+0.5)
11.0为实数,3为整数,因此首先要统一数据类型,将整型数据3转换为3.0,转换后数据类型统一为实型数据,11.0 /3=3.666666 , 3.666666+0.5=4.166666,最后将4.166666强制类型转换为整型,即将其小数部分全部舍掉,结果为4

数据类型

【测试题】设整型变量 a=2,则执行下列语句后,浮点型变量b的值不为0.5的是( B )
A.b=1.0/a B.b=(float)(1/a)
C.b=1/(float)a D.b=1/(a*1.0)

①若参与运算量的类型不同,则先转换成同一类型,然后进行运算

    ②转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算

        a、若两种类型的字节数不同,转换成字节数高的类型

        b、若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型

    ③所有的浮点运算都是以双精度进行的,即使是两个float单精度量运算的表达式,也要先转换成double型,再作运算.

    ④char型和short型参与运算时,必须先转换成int型

    ⑤在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。

         如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入,

更正:此处在博友反馈后,代码VS和Linux下实测丢失部分是直接舍去,而不是四舍五入;

2.强制类型转换

    强制类型转换一般格式如下:

        (类型名)(表达式)

    这种强制类型转换操作并不改变操作数本身

测试程序

1.写代码是一定要一个模块一个模块写,并且要边写边测试,出现问题一定要及时解决。
2.这样你的重心就放在一个较小的区域中,就更容易找出bug啦
3.找bug时要根据出现的现象进行有效的联想,这样也会速度快一点呀,不要漫无目的的不加思考的乱改。
4.平时练习呢更改程序时养成记录对应代码对应现象(相信我,你不记录…一会就忘记了,也许会重来…waste time

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值