// 中斷函數(shù)注意養(yǎng)成指定寄存器組的習慣//不同優(yōu)先級的中斷程序絕對不能使用同一組寄存器/*****編程時防止中斷把寄存器中的數(shù)據(jù)改變的解決方法是給中斷指定寄存器,同優(yōu)先級的使用同一組沒事。1、寫中斷程序一定要用using語句指定寄存器組。第1、2、3組都可以,不能是0.2、51單片機的中斷有兩個優(yōu)先級。一個中斷不會打斷另一個相同優(yōu)先級的中斷。 這樣相同級別中斷可以使用同一個組。比如:低優(yōu)先級的中斷函數(shù)都用 using 1,高優(yōu)先級的中斷都用 using 2 。這樣不會沖突。 下面是一個正常的例子: C程序: void int0() interrupt 0 using 1默認5個中斷時同級的,不會沖突,但是最好養(yǎng)成好習慣不指定中斷要使用的寄存器,每次都要入棧保護數(shù)據(jù),中斷完還要出棧,代碼會增加32字節(jié)完整代碼下載:http://www.51hei.com/f/hwxx52.rar********************************************************************/#include stc12c2052ad.h>#include intrins.h>#define uchar unsigned char#define uint unsigned int//少占魚制作 河北正定歡迎您 長沙航空職業(yè)技術(shù)學院 2010 年QQ:41165643////定義Flash 操作等待時間及允許IAP/ISP/EEPROM 操作的常數(shù)//#define ENABLE_ISP 0x80 //系統(tǒng)工作時鐘30MHz 時,對ISP_CONTR 寄存器設(shè)置此值 //#define ENABLE_ISP 0x81 //系統(tǒng)工作時鐘24MHz 時,對ISP_CONTR 寄存器設(shè)置此值 #define ENABLE_ISP 0x82 //系統(tǒng)工作時鐘20MHz 時,對ISP_CONTR 寄存器設(shè)置此值 //#define ENABLE_ISP 0x83 //系統(tǒng)工作時鐘12MHz 時,對ISP_CONTR 寄存器設(shè)置此值 //#define ENABLE_ISP 0x84 //系統(tǒng)工作時鐘6MHz 時,對ISP_CONTR 寄存器設(shè)置此值 //#define ENABLE_ISP 0x85 //系統(tǒng)工作時鐘3MHz 時,對ISP_CONTR 寄存器設(shè)置此值 //#define ENABLE_ISP 0x86 //系統(tǒng)工作時鐘2MHz 時,對ISP_CONTR 寄存器設(shè)置此值 //#define ENABLE_ISP 0x87 //系統(tǒng)工作時鐘1MHz 時,對ISP_CONTR 寄存器設(shè)置此值 union union_temp16{uint un_temp16;uchar un_temp8[2];}my_unTemp16;uchar Byte_Read(uint add); //讀一字節(jié),調(diào)用前需打開IAP 功能 void Byte_Program(uint add, uchar ch); //字節(jié)編程,調(diào)用前需打開IAP 功能 void Sector_Erase(uint add); //擦除扇區(qū) void IAP_Disable(); //關(guān)閉IAP 功能 sbit JIESHOU=P1^0; //接收指示燈sbit FASHE=P1^1; //發(fā)射指示燈sbit KEY=P3^5;sbit cin=P3^2; //接收端sbit contrl=P3^0;//發(fā)射控制端sbit khz=P3^1;//38KHZ產(chǎn)生 ,由T1設(shè)置/****************************************************************/void delayms(uint); void ADC();void InitADC();void init1();void init2();void fashe();void jieshou();void delayus(uchar i);void led(uchar x);/******************************************************/uint voltage;bit receive=0;//接收標志bit flag=0;//低電平記錄完成標志 bit end=0;bit finish=1;uchar a[43]={121,1,3,4,44,55,24,156,35};
//間接尋址的高128RAM,內(nèi)部256RAM高128只能間接尋址uchar j=0;uint zu=0,addr=0; //扇區(qū)地址uchar k;//按鍵代號uchar m=0;//寫EEPROM時用來移動數(shù)組的 uchar b[6];//用來存儲每一組數(shù)據(jù)的總字節(jié)數(shù)/**********************************************************************//****************************************************************/void main(){ delayus(5);delayms(1000);InitADC();//這里對其他用到P1口的地方有影響,盡量放前面contrl=0;//關(guān)閉38K輸出KEY=1;Sector_Erase(0x0000);//擦除扇區(qū) 1Sector_Erase(0x200);Sector_Erase(0x400);Sector_Erase(0x600);Sector_Erase(0x800);Sector_Erase(0xa00);Sector_Erase(0xc00);//擦除扇區(qū) 7addr=0xc00;FASHE=0;JIESHOU=0;FASHE=1;delayms(4900);for(j=6;j>0;j--)//j是數(shù)據(jù)總長度,如此判斷,不會存儲多余的空位{ Byte_Program(addr,a??);//從本組數(shù)據(jù)對應(yīng)扇區(qū)首地址開始寫EEPROMm++; //數(shù)組下移addr++; //地址下移delayms(1);} //for endm=Byte_Read(0xc00);if(m==121){JIESHOU=1;delayms(6000);}m=0;//下面還要用,所以清0 addr=0;zu=0;FASHE=0;KEY=1;JIESHOU=0;delayms(1000);Sector_Erase(0x0000);//擦除扇區(qū) 1Sector_Erase(0x200);Sector_Erase(0x400);Sector_Erase(0x600);Sector_Erase(0x800);Sector_Erase(0xa00);Sector_Erase(0xc00);//擦除扇區(qū) 7EX1=1; //開外部中斷1IT1=1;//外部中斷1邊沿觸發(fā),不然按住的時候一直中斷EA=1;delayms(1);//等待按鍵時兩個燈滅while(1){if(receive)//外部按鍵中斷1,正式進入接收函數(shù){FASHE=0;//發(fā)射指示燈JIESHOU=1; //接收指示燈delayms(3000);JIESHOU=0;delayms(2);FASHE=1;delayms(3000);FASHE=0;jieshou(); //接收函數(shù)是T0定時開始后計數(shù)滿溢出跳出的 }if(!KEY) //KEY為0時進入發(fā)射模式{JIESHOU=0;delayms(200);FASHE=1;delayms(200);KEY=1;fashe();}} }/********************************************************************///接收函數(shù)初始化void init1()//接收初始化{finish=1;EA=0; //因為下面要寫EEPROM,必須關(guān)閉EATMOD=0x01;//T0方式1TH0=0x00;TL0=0x00;TR0=0;EX0=1;EX1=0;//關(guān)閉外部中斷1按鍵 ,一旦進入接收函數(shù),就關(guān)閉按鍵防止干擾 ET0=1; //開T0中斷IT0=1; //外部中斷邊沿觸發(fā)EA=0;}///********************************************************************///發(fā)射函數(shù)初始化void init2()//發(fā)射初始化{contrl=0;//關(guān)閉發(fā)射端,由于它與38K輸出端并聯(lián),所以拉低不輸出TMOD=0x21;//T0方式1,外部INTO喚醒 ,T1方式2TH1=-(13/256);//定時13us翻轉(zhuǎn)一次,即38KHZ (26us)TL1=-(13%256);ET1=1; //T1中斷TR1=1;EA=1;}/************************************************************************/// 紅外接收子程序void jieshou(){init1(); //接收初始化delayms(3000);flag=0;finish=1;JIESHOU=1;//接收燈亮才可以開始按遙控EA=1;//開中斷 EX0=1;//接收燈亮等待接收while(finish) //退出接收循環(huán)檢測{while(flag)//T0已啟動標志,用完記得清0,由外部中斷0啟動,初次啟動檢測{//第一次低電平測寬已經(jīng)開始while(!cin);//等待高電平到來,T0中斷不會在這里發(fā)生,因為低電平寬度不會有65MS這么長{TR0=0; a[j]=TH0; //低電平寬度 先存高8位數(shù)據(jù)j++;a[j]=TL0;//存儲的是低電平寬度j++;//數(shù)組下移 TH0=0;//重裝T0TL0=9;//補償前面消耗的時間TR0=1;//重新啟動T0,計時高電平 }//高電平測寬開始while(cinflag);
//等待cin低電平到來。T0中斷就是在這里等待的時候發(fā)生的,
因為最后一個電平必然是高電平(無信號就是高)//flag=1表示T0還沒中斷,還是接收有效if(flag)//flag為1才表示計時有效,flag=0表示最后高電平很長結(jié)束了{ //加個flag才能退出這個等待TR0=0;a[j]=TH0; //先存高8位數(shù)據(jù)j++;a[j]=TL0;//存儲的是低電平段j++;//數(shù)組下移 TH0=0; //重裝T0TL0=0;TR0=1;//重新啟動T0,計時低電平}}//判斷是否退出接收if(end){receive=0;//用完接收啟動標志要清0flag=0;FASHE=1;delayms(122);JIESHOU=1;//亮兩個燈表示接收成功 j=0;finish=0;end=0;}}//接收完亮兩個燈finish=1;EX1=1;//開外部按鍵中斷1EA=1;}
/***********************************************/// 紅外發(fā)射子程序void fashe() //發(fā)射程序里沒有安排推出操作,所以只有重啟才能重新進入選擇模式{while(1){ADC();switch(k){case 1:for(j=0jb[0];j++){a[j]=Byte_Read(j);}led(b[0]); break;case 2:for(j=0jb[1];j++){a[j]=Byte_Read(j+0x200);}led(b[1]);break;case 3:for(j=0jb[2];j++){a[j]=Byte_Read(j+0x400);}led(b[2]);break;case 4:for(j=0jb[3];j++){a[j]=Byte_Read(j+0x600);}led(b[3]);break;case 5:for(j=0jb[4];j++){a[j]=Byte_Read(j+0x800);}led(b[4]);break;case 6:for(j=0jb[5];j++){a[j]=Byte_Read(j+0xa00);}led(b[5]);break;default:k=0;break;}init2();//必須先讀EEPROM再開定時器中斷,不然會無法讀EEPROM}}///**********************************************************************/void led(uchar x){j=0;x=x/2;//2個數(shù)組是一段電平,而且肯定是偶數(shù)個數(shù)組 2*N 是偶數(shù)嘛while(x){TH0=a[j];j++;TL0=a[j];j++;TR0=1;//while(!TF0);//等待T0溢出,因為沒有采用T0中斷 contrl=!contrl;x--;}}///***************************************************************//*編程時防止中斷把寄存器中的數(shù)據(jù)改變的解決方法是給中斷指定寄存器,
同優(yōu)先級的使用同一組沒事。1、寫中斷程序一定要用using語句指定寄存器組。第1、2、3組都可以,不能是0.2、51單片機的中斷有兩個優(yōu)先級。一個中斷不會打斷另一個相同優(yōu)先級的中斷。 這樣相同級別中斷可以使用同一個組。比如:低優(yōu)先級的中斷函數(shù)都用 using 1,
高優(yōu)先級的中斷都用 using 2 。這樣不會沖突。 下面是一個正常的例子: C程序: void int0() interrupt 0 using 1默認5個中斷時同級的,不會沖突,但是最好養(yǎng)成好習慣不指定中斷要使用的寄存器,每次都要入棧保護數(shù)據(jù),中斷完還要出棧,代碼會增加32字節(jié)********************************************************************///中斷函數(shù)要指定使用那組寄存器,使用同一組時可能會破壞了上次寄存器中的數(shù)據(jù) //同一優(yōu)先級的中斷可以使用同一組寄存器void time0() interrupt 1 using 1//定時器0中斷{EA=0;EX0=0;EX1=0;ET0=0;FASHE=0;delayms(200);JIESHOU=0; //接收指示燈delayms(1000);//有65MS以上了,表示接收完畢b[zu/0x200]=j; //j是從0開始的,最后一次電平存完j自加1了,總長度正好是當前值 addr=zu;//因為下面zu值還要用,所以這里轉(zhuǎn)移其數(shù)據(jù) //zu是每個存儲空間的起始地址m=0;for(;j>0;j--)//j是數(shù)據(jù)總長度,如此判斷,不會存儲多余的空位{ Byte_Program(addr,a??);//從本組數(shù)據(jù)對應(yīng)扇區(qū)首地址開始寫EEPROMm++; //數(shù)組下移addr++; //地址下移delayms(1);} //for endi(zu0xa00)//第一組代碼完畢后,轉(zhuǎn)到第二組,每組都是200個空間zu+=0x200; //測完一組 ,扇區(qū)地址指向下一個扇區(qū)else{ zu=0x000;} //超過6組代碼,內(nèi)存重新指向第1組flag=0;receive=0;//用完接收啟動標志要清0end=1;//退出接收函數(shù)最外層循環(huán)}/******************************************************/// 發(fā)射頻率38khz由T1產(chǎn)生void time1() interrupt 3 using 1 //定時器1中斷 ,因為默認是同優(yōu)先級,
所以可以使用同一組寄存器{khz=!khz;}/******************************************************/// 外部中斷 存儲高電平長度void interint0() interrupt 0 using 1 //外部中斷0 {if (flag==0)//flag=0表示是首次接收到脈沖{TH0=0;TL0=10;//前面延時函數(shù)消耗的時間補上TR0=1;EX0=0;//關(guān)閉外部中斷0,以后的計數(shù)都在接收函數(shù)里flag=1;//表示啟動T0}}///************************************************************//******************************************************/// 外部按鍵中斷 1void interint1() interrupt 2 using 1 //外部中斷1 {receive=1;delayms(122);//等過抖動時間EA=0;}/******************************************************///AD轉(zhuǎn)換初始化 ----打開ADC電源void InitADC(){P1=0xff;//這里對其他用到P1口的地方有影響ADC_CONTR|=0x80;delayms(30);//這兩個寄存器用來設(shè)置 P1口四種狀態(tài),每一位對應(yīng)一個P1引腳 ,按狀態(tài)組合操作P1M0=0x08;//這兩個寄存器用來設(shè)置 P1口四種狀態(tài),每一位對應(yīng)一個P1引腳 ,按狀態(tài)組合操作P1M1=0x08;//設(shè)置 P1.3做AD }/******************************************************************/// AD轉(zhuǎn)換程序void ADC(){ ADC_DATA = 0; //清除結(jié)果ADC_CONTR = 0x60; //轉(zhuǎn)換速度設(shè)置 0x60 最快速度ADC_CONTR = 0xE0; //1110,0000 清 ADC_FLAG,
ADC_START 位和低 3 位ADC_CONTR |= 0x03; //選擇 A/D 當前通道 P1.3delayus(100); //使輸入電壓達到穩(wěn)定ADC_CONTR |= 0x08; //0000,1000 令 ADCS = 1, 啟動A/D轉(zhuǎn)換, while(!(ADC_CONTR 0x10)); //!的優(yōu)先級比高太多了 /***************這里while 不能改成while(ADC_CONTR 0x10==0) ;就錯誤了,因為優(yōu)先級 ==比高 ,
所以要加括號while( (ADC_CONTR 0x10) ==0) 或者非一下 while(!(ADC_CONTR 0x10));
//!的優(yōu)先級比高太多了 ******************************/ADC_CONTR = 0xE7; //1111,0111 清 ADC_FLAG 位, 關(guān)閉A/D轉(zhuǎn)換, voltage=ADC_DATA;if( volage40){k=1; //對應(yīng)0X000扇區(qū)內(nèi)容}if(voltage>=40volage80){k=2; //對應(yīng)0X200扇區(qū)內(nèi)容}if(voltage>=80volage110){k=3;}if(voltage>=110volage130){k=4;}if(voltage>=130volage148){k=5;}if(voltage>=148volage160)//注意:默認是165 電壓AD值{k=6;}}/******************************************//*--- STC International Limited ----------------一個完整的EEPROM 測試程序,用宏晶的下載板可以直接測試STC12C52xxAD 系列單片機 EEPROM/IAP 功能測試程序演示 STC11xx 系列單片機 EEPROM/IAP 功能測試程序演示STC10xx 系列單片機 EEPROM/IAP 功能測試程序演示--- STC International Limited --------------------- 宏晶科技 設(shè)計 2009/1/12 V1.0 --------------***********************************************///讀一字節(jié),調(diào)用前需打開IAP 功能,入口:DPTR = 字節(jié)地址,返回:A = 讀出字節(jié) uchar Byte_Read(uint add){ISP_DATA = 0x00;ISP_CONTR = ENABLE_ISP; //打開IAP 功能, 設(shè)置Flash 操作等待時間 ISP_CMD = 0x01; //IAP/ISP/EEPROM 字節(jié)讀命令 my_unTemp16.un_temp16 = add; //聯(lián)合體變量賦值 ,
這里是倆字節(jié),因為公用內(nèi)存,所以下面數(shù)組也是此內(nèi)容ISP_ADDRH = my_unTemp16.un_temp8[0]; //設(shè)置目標單元地址的高8 位地址 ISP_ADDRL = my_unTemp16.un_temp8[1]; //設(shè)置目標單元地址的低8 位地址 EA = 0;ISP_TRIG = 0x46; //先送 5Ah,再送A5h 到ISP/IAP 觸發(fā)寄存器,每次都需如此 ISP_TRIG = 0xB9; //送完A5h 后,ISP/IAP 命令立即被觸發(fā)起動_nop_();//EA = 1;IAP_Disable(); //關(guān)閉IAP 功能, 清相關(guān)的特殊功能寄存器,使CPU 處于安全狀態(tài),//一次連續(xù)的IAP 操作完成之后建議關(guān)閉IAP 功能,不需要每次都關(guān) return (ISP_DATA); //數(shù)據(jù)在ISP_DATA寄存器中}//字節(jié)編程,調(diào)用前需打開IAP 功能,入口:DPTR = 字節(jié)地址, A= 須編程字節(jié)的數(shù)據(jù) void Byte_Program(uint add, uchar ch){ISP_CONTR = ENABLE_ISP; //打開 IAP 功能, 設(shè)置Flash 操作等待時間 ISP_CMD = 0x02; //IAP/ISP/EEPROM 字節(jié)編程命令 my_unTemp16.un_temp16 = add;
//聯(lián)合體變量賦值 ,這里是倆字節(jié),因為公用內(nèi)存,所以下面數(shù)組也是此內(nèi)容ISP_ADDRH = my_unTemp16.un_temp8[0]; //設(shè)置目標單元地址的高8 位地址 ISP_ADDRL = my_unTemp16.un_temp8[1]; //設(shè)置目標單元地址的低8 位地址 ISP_DATA = ch; //要編程的數(shù)據(jù)先送進ISP_DATA 寄存器 EA = 0;//必須關(guān)中斷,不然沒法寫ISP_TRIG = 0x46; //先送 46h,再送B9h 到ISP/IAP 觸發(fā)寄存器,每次都需如此 ISP_TRIG = 0xb9; //送完B9h 后,ISP/IAP 命令立即被觸發(fā)起動 _nop_();//EA = 1;IAP_Disable(); //關(guān)閉IAP 功能, 清相關(guān)的特殊功能寄存器,使CPU 處于安全狀態(tài),//一次連續(xù)的IAP 操作完成之后建議關(guān)閉IAP 功能,不需要每次都關(guān) }//擦除扇區(qū), 入口:DPTR = 扇區(qū)地址void Sector_Erase(uint add){ISP_CONTR = ENABLE_ISP; //打開IAP 功能, 設(shè)置Flash 操作等待時間 ISP_CMD = 0x03; //IAP/ISP/EEPROM 扇區(qū)擦除命令 my_unTemp16.un_temp16 = add;ISP_ADDRH = my_unTemp16.un_temp8[0]; //設(shè)置目標單元地址的高8 位地址 ISP_ADDRL = my_unTemp16.un_temp8[1]; //設(shè)置目標單元地址的低8 位地址 EA = 0;//必須關(guān)中斷ISP_TRIG = 0x46; //先送 46h,再送B9h 到ISP/IAP 觸發(fā)寄存器,每次都需如此 ISP_TRIG = 0xB9; //送完B9h 后,ISP/IAP 命令立即被觸發(fā)起動 _nop_();//EA = 1;IAP_Disable(); //關(guān)閉IAP 功能, 清相關(guān)的特殊功能寄存器,使CPU 處于安全狀態(tài),//一次連續(xù)的IAP 操作完成之后建議關(guān)閉IAP 功能,不需要每次都關(guān) }void IAP_Disable(){//關(guān)閉IAP 功能, 清相關(guān)的特殊功能寄存器,使CPU 處于安全狀態(tài),//一次連續(xù)的IAP 操作完成之后建議關(guān)閉IAP 功能,不需要每次都關(guān) ISP_CONTR = 0; //關(guān)閉IAP 功能ISP_CMD = 0; //清命令寄存器,使命令寄存器無命令,此句可不用 ISP_TRIG = 0; //清命令觸發(fā)寄存器,使命令觸發(fā)寄存器無觸發(fā),此句可不用 ISP_ADDRH = 0;ISP_ADDRL = 0;}//void delayus(uchar i ){while(i--);}//延時函數(shù)void delayms(uint k){uint data i,j;for(i0;ik;i++){for(j0;j200;j++){;}}}
評論