代码部分
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