#include<reg52.h>
#include <intrins.h> //因为用到_nop_();
#include<stdio.h>
typedef unsigned char uchar;
sbit SCL = P2^0; //注意P1、P2、P3口有内部上拉电阻,P0需外接上拉电阻,
sbit SDA = P2^1;
sbit key = P1^3; //按键检测脚
sbit qiang1 = P3^4; // <<<<控制输入接焊枪 A
sbit qiang2 = P3^6; // >>>>焊枪电平变脉冲>>>>去外部中断0P3.2脚>>>>>>>>>>>
sbit ming = P3^5; // 接蜂鸣器
sbit kong = P3^7; // 控制输出脚
uchar moshi0,m1,moshi4,moshi5,moshi6,moshi7,j,x,y,jin,dan,liant,lianh; //j用于计数50ms的个数的全局变量 x暂存键值y暂存判断数值
uchar x0,x1,x2,x3,x4,x5,x6,k,y0,y1,y2,y3,y4,y5,y6; // jin焊枪开关按下的标记 dan单次焊接是否开始标记
// lian 连续焊接是否开始标记 lianh连续焊接工作与间歇的标记
uchar code bihua[11] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x80};
//共阴0-9的LED笔划{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x80}
//共阴0-9的LED笔划0 1 2 3 4 5 6 7 8 9 点dep
//共阳0-9的LED笔划{0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x7f};
uchar code xuan[8] = {0x08,0x09,0x0A,0x0B,0x0C,0x0d,0x0e,0x0f};
uchar ReceivedData[8];//用于接收8个字节数据(1页)的数组
uchar jieshou[8]; //用于存储8个字节数据(1页)的数组
uchar moshi1[8];
//延时5us子程序
void delay5us(void)
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
//10ms定时器0中断函数
void timer0() interrupt 1 //j是个全局变量,不是返回值,所以这里还是void。
{
TH0 = (65536-10000)/256; //12MHz时每10ms一次定时器中断
TL0 = (65536-10000)%256;
j++;
qiang2 =1 ;
delay5us();// 给外部中断0的信号
qiang2 =0; //判断j,数据放在中断里处理
}
//约2ms的延时
void delay(uchar t)
{
uchar x,y;
for(x=0;x<t;x++)
for(y=0;y<250;y++)
;
}
//I2C初始化
void InitI2C(void)
{
SDA = 1; //总线空闲时,因各设备都是集电极或漏极开路,上拉电阻使SDA和SCL线都保持高电平。
SCL = 1;
delay5us();
}
//产生I2C开始信号
void StartI2C(void)
{
SDA = 1; //SDA在SCL为高期间由高变低表示开始,所以先要高
SCL = 1;
delay5us(); //时序图要求tSU.STA(Start Set-up Time)大于4.7us
SDA = 0; //注意SDA拉低前后都要维持5us以上!
delay5us(); //tHD.STA(Start Hold Time)大于4us
SCL = 0; //拉低SCL,准备发送或接收数据(这两句也可在写或读字节的程序中先将SCL置0,延时)
delay5us();
}
//产生I2C结束信号
void StopI2C(void)
{
SDA = 0; //SDA在SCL为高期间由低变高,说明结束
SCL = 1;
delay5us();
SDA = 1;
delay5us();
}
//发送方在发完一个字节后检测接收方有没有应答。返回应答成功否。
bit ChkAck(void)
{
bit SDAtemp;
SDA=1; //释放SDA(置1),然后等待接收方应答将它拉低。
SCL=1; //WriteI2CByte中写完一字节后又将SCL拉低,这里拉高产生第9个时钟上升沿,然后在SCL为高期间对SDA进行检测
delay5us();
SDAtemp = SDA; //如果不用暂存变量,直接return SDA,就不会执行后面的SCL = 0,检测期间的第9个时钟就不完整了
SCL=0;
delay5us();
return SDAtemp;
}
//51作为主机时,如果接收数据,模拟产生应答时序。形参Ack为0,则应答0,为1不应答。
void AckAsMaster(bit Ack)
{
if(!Ack)
SDA = 0;
else
SDA = 1;
delay5us();
SCL = 1; //主机控制SCL时序。关键是保证在SCL脉冲上升沿之前SDA数据已稳定即可。
delay5us();
SCL = 0;
delay5us();
}
void WriteI2CByte(uchar);
uchar ReadI2CByte();
//页写。输入两参数,一个为首字地址,另一个是指向待写入数据数组的指针(括号内第二个参数也可写作uchar ToSDAdataBuffer[],即数组名代表首地址)。
bit PageWrite(uchar WordAddress,uchar *ToSDAdataBuffer)
{
//下面的程序我用的if嵌套,网上有些程序是顺序结构,但因为遇到return就返回主程序不再往下执行,所以效果是一样的。
uchar i;
StartI2C();
WriteI2CByte(0xa0);//最后一位不属于地址。E2PROM一般前四位为1010,这里A2~A0接地,为0,最后一位0表示写
if(!ChkAck())
{ //检查应答函数返回0说明从机应答0成功。
WriteI2CByte(WordAddress); //写8-bit data word address,(24C02有2kbits,所以数据字有2048/8=256个,故地址线有8位)
if(!ChkAck())
{
for(i = 0; i < 8; i++)
{
WriteI2CByte(ToSDAdataBuffer[i]);
if(ChkAck())
{
//这里可添加错误处理代码。如用几个LED的亮灭组合表示此I2C器件有问题,类似主板错误提示。
return 1;//一般返回1表示异常,且遇到return就退出整个子程序。
}
}
StopI2C(); //写完发送结束信号。
return 0; //一般返回0表示程序正常
}
else{
return 1; //之前可添加错误处理代码。
}
}
else{
return 1;
}
}
//不能用Current Address Read,因为那是24C02数据字地址计数器上次操作后加1的值;而SEQUENTIAL_READ如果不给一个要读取的开始地址,会从头输出,
//所以需要Random Read的开始部分,但不要停止信号。
bit SequentialRead(uchar WordAddress)
{
uchar i;
StartI2C();
WriteI2CByte(0xa0);
if (!ChkAck())
{
WriteI2CByte(WordAddress);
if (!ChkAck())
{
StartI2C(); //the microcontroller must generate another start condition
WriteI2CByte(0xa1); //Device Address后的1说明是读,24C02内部就是根据最后这位来判断是从SDA上读数,还是往SDA上送数
//之所以设为1是读,是因为根据WriteI2CByte子程序,最后给SDA赋1,P3^4就维持1,这样24C02内部Dout为高就将SDA拉低;如果最后一位是0,24C02没能力拉高!
if (!ChkAck())
{
for(i = 0;i < 8;i++)
{
ReceivedData[i] = ReadI2CByte();
AckAsMaster(0); //51此时接收数据,调用应答的函数(置SDA为0)
}
AckAsMaster(1); //NO ACK.The microcontroller does not respond with a zero but does generate a following stop condition.
StopI2C();
return 0;
}
else{
return 1; //之前可添加错误处理代码。
}
}
else{
return 1;
}
}
else{
return 1;
}
}
//往I2C总线写一个字节的数据(即将一个字节的数据发送到SDA上)
void WriteI2CByte(uchar ByteData)
{
uchar i,temp;
temp = ByteData;
// (StartI2C()最后已经先将SCL变0了):
for(i=0;i<8;i++){
temp <<= 1; //左移一位,I2C要求由MSB最高位开始,移出的CY即要发送到SDA上的数据。下面考虑时序:
SDA = CY; //此时SCL已为低,每次移一位送出去(下次进循环后SDA还保持着上次发出去的数据)
delay5us(); //SDA IN数据变化中点SCL上升沿中点的一段时间是tSU.DAT,即数据建立时间Data In Set-up Time,需大于200ns,多延无所谓
SCL = 1;
delay5us(); //tHIGH即Clock Pulse Width High,最小4us
SCL = 0;
delay5us(); //tLOW即Clock Pulse Width Low,最小4.7us
}
}
//读取I2C总线一个字节的数据
uchar ReadI2CByte() //串行总线,51一位位接收从机发送到SDA上的数据,这里只考虑数据已在SDA上时如何存下来这几位,组成一个字节
{
uchar i,ByteData;
SDA = 1; //SCL在ChkAck中已经置0了。注意SCL时序仍然由主机控制!24C02只能将SDA由高拉低,象橡皮筋松手又恢复高,而下面只是读SDA,没赋值
//其实程序中多处给SDA置1都可省,因为检查应答时为0就正常,无所谓,写字节时也无所谓,就是在读之前要保证SDA为1!
//因之前有WriteI2CByte(0xa1); 其实这句也可省略。
delay5us(); //24C02作为发送方在第9个时钟的negative edge clocks data out of each device,所以现在SDA上为新数据
for(i=0;i<8;i++)
{
SCL = 1; //置时钟线为高使数据线上数据有效
delay5us();
ByteData = (ByteData<<1)|SDA; //SDA上已是新数据了,读之。data不管以前多少,左移后最右边为0,和SDA"按位或"后MLB就是SDA
SCL = 0;
delay5us();
}
return ByteData;
}
void time0_Init(void) // 定时器0,的初始化
{
TMOD = 0x01; //方式1的16位计数器
TH0 = (65536-10000)/256;
TL0 = (65536-10000)%256;
TR0 = 1; //启动定时器0工作
ET0 = 1; //开T0的中断
IT0 = 1; //负边沿触发 IT0 = 0; //低电平触发
EX0 = 1; //开外部中断0
EA = 1; //开总中断
}
void Outside_Init(void) // 外部中断0,1 的初始化
{
EX0 = 1; //开外部中断0
IT0 = 1; //负边沿触发 IT0 = 0; //低电平触发
EA = 1; //开总中断
}
void Outside_Int1(void) interrupt 0 using 1 // 外部中断0 的中断处理
{
if( moshi1[4]==0xf0&jieshou[0]>0&qiang1==0) //焊枪开关已按下且单焊
{if (dan==0 )
{j=0;
dan=1;
kong=1; //开始焊接
ming =1;
TMOD = 0x01; //方式1的16位计数器
TH0 = (65536-10000)/256; // 定时器0,的初始化
TL0 = (65536-10000)%256;
TR0 = 1; //启动定时器0工作
ET0 = 1; //开T0的中断
}
else if(jieshou[0]==j)
{ ET0 = 0; //关T0的中断
TR0 = 0; //关定时器0
kong=0; //停止焊接
ming =0;
}
;
}
else if (moshi1[5]==0xf0&jieshou[0]>0&jieshou[1]>0 &qiang1==0) //焊枪开关已按下且连焊
{
if (lianh==0& liant==1&qiang1==0 )
{lianh=1;
liant=0;
j=0;
kong=1; //开始焊接
ming =1;
TMOD = 0x01; //方式1的16位计数器
TH0 = (65536-10000)/256; // 定时器0,的初始化
TL0 = (65536-10000)%256;
TR0 = 1; //启动定时器0工作
ET0 = 1; //开T0的中断 // 定时器0,的初始化
}
else if(lianh==1&liant==0&qiang1==0)
{
if(jieshou[0]==j)
{
j=0;
kong=0; //停止焊接
ming =0;
liant=1;
}
;
}
else if(lianh==1&liant==1&qiang1==0)
{
if(jieshou[1]==j)
{
j=0;
kong=1; //重新开始焊接
ming =1;
lianh=1;
liant=0;
}
;
}
;
}
else if (moshi1[6]==0xf0&qiang1==0 ) //焊枪开关已按下且氩弧焊
{j=0;
ET0 = 0; //关T0的中断
TR0 = 0; //关定时器0
kong=1; //开始焊接
ming =1;
}
else if (j==50 )
{ y0=0;
y1=0;
y2=0;
y3=0; //按键标记
y4=0;
y5=0;
y6=0;
}
}
void anjianchushi(void) //按键标记初始化
{y0=0; //按键标记
y1=0;
y2=0;
y3=0; //按键标记
y4=0;
y5=0;
y6=0; //按键标记
}
main(void)
{
uchar i;
moshi0=25;
m1=25;
moshi4 =0xf0;
moshi5 =0x00;
moshi6 =0x00;
moshi7 =0x00;
jin=0;//焊枪开关控制焊枪的标记
begin:
ming =0;// 发光管
kong =0;// 控制输出脚去继电器
qiang1 =1;//控制输入接焊枪开关
qiang2 =1; //焊枪电平变脉冲>>>>去外部中断0P3.2脚
P0 = 0x00;
InitI2C();
//注意在24C02中用到的页写和顺序读的地址是同一个,且必须是8的整数倍,即每页的首地址才行,如0x08,0x20等。因为24C02页写时后三位地址自动加1,
//When the word address,internally generated, reaches the page boundary, the following byte is placed at the beginning of the same page.
//而顺序读时只有在达到整个存储区边界时才会roll over。所以,如读写都用0x32这个地址,由于不是8的整数倍,只有前6个数显示是正确的,最后两个数
//虽然又从头写在了该页的前面,但SequentialRead确读到了该页之外的两个存储单元,造成错误。
//if (PageWrite(0x08,canshu) == 0) // 写原始焊接参数 canshu[8] = {0x19,25,25,25,25,25,25,25};
// {delay(100); // 行页写操作,设从地址00开始,没问题就延迟一下
if(SequentialRead(0x08) == 0) //读取24C02焊接参数
{ delay(100);
for(i = 0; i < 8; i++)
{
jieshou[i] = ReceivedData[i];
delay5us();
}
x0=jieshou[0]/10;
moshi1[0]=bihua[x0];
x1=jieshou[0]%10;
moshi1[1]=bihua[x1];
x2=jieshou[1]/10;
moshi1[2]=bihua[x2];
x3 =jieshou[1]%10;
moshi1[3]=bihua[x3];
moshi1[4]=jieshou[4] ;
moshi1[5]=jieshou[5] ;
moshi1[6]=jieshou[6] ;
moshi1[7]=jieshou[7] ;
// }
while(1)
{
xian:
for(y = 0; y < 8; y++)
{
P0 = moshi1[y]; //显示24C02数据
P1 = xuan[y];
delay(4); //延时4个2ms
if(key==0) //按键判断
{ delay(200);
if(key==0)
{
if(y==0&y0==0)
{ x = ReceivedData[0];
x=x-1; //焊接时间减少
while(x >99)
{
x=0 ;
}
ReceivedData[0]=x;
if (PageWrite(0x08, ReceivedData) == 0)
delay(200);
anjianchushi(); //按键标记初始化
y0=1;
time0_Init(); // 定时器0,的初始化
j=0;
delay5us();
goto begin ;
}
else if (y==1&y1==0)
{x = ReceivedData[0];
x=x+1; //焊接时间减少
while(x >99)
{
x=99 ;
}
ReceivedData[0]=x;
if (PageWrite(0x08, ReceivedData) == 0)
delay(200);
anjianchushi(); //按键标记初始化
y1=1;
time0_Init(); // 定时器0,的初始化
j=0;
delay5us();
goto begin ;
}
else if(y==2&y2==0)
{ x = ReceivedData[1];
x=x-1; //焊接时间减少
while(x >99)
{
x=0 ;
}
ReceivedData[1]=x;
if (PageWrite(0x08, ReceivedData) == 0)
delay(200);
anjianchushi(); //按键标记初始化
y2=1;
time0_Init(); // 定时器0,的初始化
j=0;
delay5us();
goto begin ;
}
else if(y==3&y3==0)
{ x = ReceivedData[1];
x=x+1; //焊接时间减少
while(x >99)
{
x=99 ;
}
ReceivedData[1]=x;
if (PageWrite(0x08, ReceivedData) == 0)
delay(200);
anjianchushi(); //按键标记初始化
y3=1;
time0_Init(); // 定时器0,的初始化
j=0;
delay5us();
goto begin ;
}
else if(y==4&y4==0)
{ ReceivedData[4] =0xf0; //单焊标记
ReceivedData[5] =0x00;
ReceivedData[6] =0x00;
if (PageWrite(0x08, ReceivedData) == 0)
delay(200);
anjianchushi();
y4=1;
time0_Init(); // 定时器0,的初始化
j=0;
delay5us();
goto begin ;
}
else if(y==5&y5==0)
{ ReceivedData[4] =0x00;
ReceivedData[5] =0xf0; //连焊标记
ReceivedData[6] =0x00;
if (PageWrite(0x08, ReceivedData) == 0)
delay(200);
anjianchushi(); //按键标记初始化
y5=1;
time0_Init(); // 定时器0,的初始化
j=0;
delay5us();
goto begin ;
}
else if(y==6&y6==0)
{ReceivedData[4] =0x00;
ReceivedData[5] =0x00;
ReceivedData[6] =0xf0; //氩弧焊标记
if (PageWrite(0x08, ReceivedData) == 0)
delay(200);
anjianchushi(); //按键标记初始化
y6=1;
time0_Init(); // 定时器0,的初始化
j=0;
delay5us();
goto begin ;
}
}
}
else if(qiang1==1&jin==1) //焊枪开关已经松开
{
EA = 0;//关总中断
kong=0; //停止焊接
ming =0;
jin=0;
dan=0;
lianh=0;
liant=1;
}
else if (qiang1==0) //焊枪开关按下
{
if(jin==0) //又按下时
{ jin=1 ;
Outside_Init(); // 外部中断0,1 的初始化
delay5us();
qiang2 =1 ;
delay5us();// 给外部中断0的信号
qiang2 =0;
goto xian;
}
else //一直未松开
{ ; // goto xian;
}
}
}
}
}
}
|