基于STC串口通信和VC6.0MFC編程的電子琴設(shè)計(jì)
電子實(shí)訓(xùn)課程實(shí)驗(yàn)項(xiàng)目
????????????????????? --電子琴
【前言】
?????? 為進(jìn)一步激發(fā)學(xué)生對(duì)于硬件編程的興趣而開(kāi)展的課程“電子實(shí)訓(xùn)”課程到目前為止已經(jīng)要告一段落了。將近四周的時(shí)間,從電路板印刷、貼片參觀,到自己親手將原件焊接到電路板上,再到一步一步熟悉STC編程當(dāng)中的技巧,我們漸漸對(duì)硬件編程有了初步的認(rèn)識(shí),雖然并不一定能完成什么高級(jí)的設(shè)計(jì),但是對(duì)目前所涉及到的數(shù)碼管、LED、撥碼開(kāi)關(guān)、中斷、定時(shí)器、傳感器的應(yīng)用已經(jīng)有了初步認(rèn)識(shí),也可以寫(xiě)出一些簡(jiǎn)單的應(yīng)用。為驗(yàn)證十幾天的學(xué)習(xí)成果,每個(gè)人都會(huì)根據(jù)給出的53個(gè)例程當(dāng)中選擇自己比較熟悉或者擅長(zhǎng)的方面進(jìn)行一個(gè)創(chuàng)新設(shè)計(jì)或者擴(kuò)展設(shè)計(jì),也或者融合多個(gè)方面。
?? 我完成的項(xiàng)目是一個(gè)“電子琴”,基于工程“電子音樂(lè)”改編而來(lái),不過(guò)也做出了很多的改變,也增加了一些東西,使得整個(gè)項(xiàng)目看起來(lái)還算不錯(cuò)。
【實(shí)驗(yàn)?zāi)康呐c要求】
1、???熟練掌握定時(shí)器的應(yīng)用,以及如何通過(guò)定時(shí)器驅(qū)動(dòng)蜂鳴器發(fā)出一定頻率的聲音,掌握簡(jiǎn)譜與定時(shí)器重裝值之間的關(guān)系,能夠?qū)崿F(xiàn)蜂鳴器譜樂(lè);
2、???熟悉下位機(jī)編程,usb轉(zhuǎn)串口原理應(yīng)用,相關(guān)寄存器的使用,并且實(shí)現(xiàn)該功能,為上、下位機(jī)通信奠定基礎(chǔ);
3、???熟悉上位機(jī)編程方法,可以參考相關(guān)資料,設(shè)計(jì)并實(shí)現(xiàn)上位機(jī);
【設(shè)計(jì)概要】
本案例的設(shè)計(jì)主要可以分為兩個(gè)部分,上位機(jī)部分的設(shè)計(jì)和下位機(jī)部分的設(shè)計(jì)
上位機(jī):
?? 案例的上位機(jī)是用MFC以及MFC中的串口控件來(lái)完成的。上位機(jī)中共有8個(gè)按鍵,一個(gè)下拉框,三個(gè)復(fù)選框,七個(gè)按鍵完成對(duì)音符頻率的選擇,對(duì)應(yīng)簡(jiǎn)譜中的DO,RE,MI,FA,SO,LA,XI,每當(dāng)一個(gè)按鍵被按下的時(shí)候,上位機(jī)會(huì)通過(guò)串口將一個(gè)字節(jié)發(fā)送到單片機(jī),在發(fā)送的時(shí)候,上位機(jī)會(huì)檢測(cè)被選中的復(fù)選框,若選中的是“低八度”,則會(huì)發(fā)送低八度對(duì)應(yīng)的音節(jié);若選中的是“中八度”,則會(huì)發(fā)送中八度對(duì)應(yīng)的音節(jié),高八度也類似。下拉框控制的是串口號(hào)的選擇,列出了電腦主機(jī)所配置的串口號(hào),串口號(hào)下面有一個(gè)“打開(kāi)窗口”的按鈕,當(dāng)點(diǎn)擊的時(shí)候,上位機(jī)會(huì)檢測(cè)下拉框中選擇的串口號(hào),并將其對(duì)應(yīng)的串口號(hào)打開(kāi)。在點(diǎn)擊的同時(shí),上位機(jī)會(huì)設(shè)置串口的相關(guān)屬性值,m_ctrlComm.SetSettings("9600,n,8,1");設(shè)置串口波特率為9600,無(wú)校驗(yàn)位,8個(gè)數(shù)據(jù)位,1個(gè)停止位,并設(shè)置以二進(jìn)制方式撿取數(shù)據(jù),清空緩沖區(qū)。
下位機(jī):
? 下位機(jī)設(shè)計(jì)的重要部分在于串口和蜂鳴器兩個(gè)部分。串口部分使用的是串口1中斷,串口1中斷使用TH1和TL1作為串口中斷的計(jì)時(shí)器,使用TL1=(65536-(Machine_Focs/4/BAUD1));TH1=(65536-(Machine_Focs/4/BAUD1))>>8作為重裝值,當(dāng)串口發(fā)生中斷的時(shí)候,會(huì)觸發(fā)中斷處理程序。中斷處理程序中會(huì)將已經(jīng)轉(zhuǎn)換成1的RI端口和TI端口轉(zhuǎn)換成0,返回源程序,開(kāi)始下一次中斷的歷程,并且會(huì)觸發(fā)playmusic函數(shù),將display中的音節(jié)在蜂鳴器中播放。
測(cè)試方法:
(1)打開(kāi)“piano”工程文件,找到“hex”文件;
(2)打開(kāi)ISP下載器,選中該“hex文件”,選中對(duì)應(yīng)的端口,點(diǎn)擊下載;
(3)下載完成,打開(kāi)上位機(jī)“test.exe”,選中對(duì)應(yīng)的端口,點(diǎn)擊“打開(kāi)串口”;
(4)串口打開(kāi),選中對(duì)應(yīng)的音階,低八度,中八度,或者高八度,然后點(diǎn)擊按鈕,蜂鳴器發(fā)出聲音;
(5)測(cè)試完成;
?
【實(shí)驗(yàn)原理】
1、實(shí)驗(yàn)原理圖
(1)無(wú)源蜂鳴器電路原理圖
?????????????????????????????
(2)芯片相關(guān)引腳圖
?
(3)單片機(jī)下載電路
?
2、USB轉(zhuǎn)串口原理:?jiǎn)纹瑱C(jī)集成了USB轉(zhuǎn)串口模塊,對(duì)應(yīng)使用RXD線接收數(shù)據(jù),用TXD發(fā)送數(shù)據(jù)。每個(gè)串口由2個(gè)數(shù)據(jù)緩沖器(相互獨(dú)立1收1發(fā))、一個(gè)移位寄存器(一字節(jié)數(shù)據(jù)一位一位發(fā)送出去)、一個(gè)串行控制器和一個(gè)波特率發(fā)生器(這個(gè)比較重要,結(jié)合相關(guān)的定時(shí)器)組成。對(duì)應(yīng)發(fā)送、接收數(shù)據(jù)完成(RI、TI硬件置1)都會(huì)觸發(fā)串口中斷,但是無(wú)法確定是哪個(gè)觸發(fā)的,所以在串口中斷中我們要判斷是接收數(shù)據(jù)產(chǎn)生的中斷還是發(fā)送數(shù)據(jù)產(chǎn)生的中斷,對(duì)于發(fā)送數(shù)據(jù)產(chǎn)生的中斷,我們要軟件將TI清0,并將數(shù)據(jù)就緒標(biāo)志清0,允許下一字節(jié)數(shù)據(jù)發(fā)送,發(fā)送數(shù)據(jù)函數(shù)中通過(guò)while循環(huán),等待發(fā)送數(shù)據(jù)準(zhǔn)備就緒,完了將就緒的數(shù)據(jù)復(fù)制給SBUF;對(duì)于接收數(shù)據(jù)產(chǎn)生的中斷,我們要軟件將RI清0,并從SBUF中讀取數(shù)據(jù)。
3、蜂鳴器原理:
本實(shí)驗(yàn)板采用的是無(wú)源蜂鳴器,無(wú)源內(nèi)部不帶震蕩源,所以如果用直流信號(hào)無(wú)法令其鳴叫。必須用2K~5K的方波去驅(qū)動(dòng)它。相比與有源蜂鳴器,無(wú)源蜂鳴器的優(yōu)點(diǎn)在于價(jià)格便宜,可以通過(guò)控制其振動(dòng)頻率來(lái)改變發(fā)出的聲音,做出“多來(lái)米發(fā)索拉西”的效果。因此,無(wú)源蜂鳴器可以用于音樂(lè)的播放。而有源蜂鳴器的優(yōu)點(diǎn)在于使用簡(jiǎn)單,不需要編寫(xiě)“樂(lè)譜”。本實(shí)驗(yàn)板使用的無(wú)源蜂鳴器是電磁式蜂鳴器,電磁式蜂鳴器由振蕩器、電磁線圈、磁鐵、振動(dòng)膜片及外殼等組成。接通電源后,接收到的音頻信號(hào)電流通過(guò)電磁線圈,使電磁線圈產(chǎn)生磁場(chǎng)。振動(dòng)膜片在電磁線圈和磁鐵的相互作用下,周期性地振動(dòng)發(fā)聲。
蜂鳴器部分每個(gè)音符都有自己對(duì)應(yīng)的頻率,每個(gè)頻率也多有自己對(duì)應(yīng)的簡(jiǎn)譜碼,每一個(gè)簡(jiǎn)譜碼都對(duì)應(yīng)了一個(gè)計(jì)時(shí)器重裝值,比如說(shuō)低1DO頻率是262Hz,則振動(dòng)周期T=1000ms/262Hz=3.817ms,要求定時(shí)器在3.817ms會(huì)產(chǎn)生一個(gè)周期振動(dòng),由于一個(gè)周期振動(dòng)是兩次電平變化,而每一次定時(shí)器中斷只有一次電平變化,所以要將定時(shí)器設(shè)置成1.908ms震動(dòng)一次,也就是1908us,因此可以得到定時(shí)器重裝值為65536-1908=63628。
?
【源碼展示與說(shuō)明】
下位機(jī)部分:
/*文件名稱piano.c
通過(guò)串口用上位機(jī)控制單片機(jī)上面的蜂鳴器發(fā)出不同頻率的聲音
*/
#include"STC15F2K60S2.H"
//宏定義
#defineuchar unsigned char
#defineuint unsigned int
?
#defineMachine_Focs 11059200L? ?????????? //晶振頻率 11.0592MHz
#defineBAUD1 9600??????????????????????? //波特率,這里使用的是9600
?
sbitLED_SEL=P2^3;
sbitbeep=P3^4;????????? ? ??????????????? //蜂鳴器引腳
uchartimeh,timel;?????? ? ??????????????? //定時(shí)器的重裝值
?
/*收發(fā)數(shù)據(jù)相關(guān)*/
uchardisplay;??????????????????????????? //單片機(jī)上SBUF緩沖的數(shù)據(jù)
ucharflag;
intcount=0;????????????????????????????? //計(jì)數(shù)器,用來(lái)分頻
ucharduanxuan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,
0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
????????????????????????????????????????? //段選信號(hào),選擇0-f
/*蜂鳴器振動(dòng)頻率相關(guān)*/
ucharcode quzi[] ={ ? ??????????? ?????? //此數(shù)組作為各個(gè)音符在定時(shí)器中的重裝值
//低八度
0xf8,0x8c,0xf9,0x5b,0xfa,0x15,0xfa,0x67,0xfb,0x04,0xfb,0x90,0xfc,0x0c,
//中八度
0xfc,0x44,0xfc,0xac,0xfd,0x09,0xfd,0x34,0xfd,0x82,0xfd,0xc8,0xfe,0x06,
//高八度
0xfe,0x22,0xfe,0x56,0xfe,0x6e,0xfe,0x9a,0xfe,0xc1,0xfe,0xe4,0xff,0x03
};
/***************************************************************
曲調(diào)
****************************************************************/
ucharquyin(uchar tem)
{
??? uchar qudiao,jp,weizhi;???? ? //定義曲調(diào),音符,位置
??? qudiao=tem/16;????????????? ? //高四位為是曲調(diào)值
??? jp=tem%16;?????????????? ? ???//低四位是音符
??? if(qudiao==1)??????????? ? ???//當(dāng)曲調(diào)值為1的時(shí)候,低八度,低八度在quzi數(shù)組中的基址為0
??? qudiao=0;
??? else if(qudiao==2)????????? ? //當(dāng)曲調(diào)值為2的時(shí)候,中八度,中八度在quzi數(shù)組中的基址為14
?????? qudiao=14;
??? else if(qudiao==3)????????? ? //當(dāng)曲調(diào)值為3的時(shí)候,高八度,高八度在quzi數(shù)組中的基址為28
?
?????? qudiao=28;
??? weizhi=qudiao+(jp-1)*2;???? ? //基址加上偏移量得到音符對(duì)應(yīng)數(shù)組中的位置
??? return weizhi;????????????? ? //返回位置值
}
/**********************
函數(shù)名稱:void delay(unsigned int xms)
功能描述:延時(shí)
入口參數(shù):xms:輸入需要延時(shí)的毫秒值
出口參數(shù):無(wú)
***********************/
voiddelay(uint xms)
{
??? uint i;
??? for(; xms>0; xms--)???????????????????
?????? for(i=114; i>0; i--)
?????? {}
}???
/**********************
函數(shù)名稱:Timer0
功能描述:定時(shí)器0的中斷響應(yīng)函數(shù),用來(lái)控制蜂鳴器
***********************/
voidTimer0() interrupt 1?? ?
{
??? count++;
??? if (count==4)
??? {
?????? count=0;
??? }
??? TH0=timeh;
??? TL0=timel;
??? if(flag==1&&count==3)????????????? //四分頻
??? {
??? ?beep=~beep;
??? }
}
/**************************************************************
函數(shù)名稱:Uart1_Init
功能描述:初始化串口中斷
***************************************************************/
voidUart1_Init(void)
{
??? AUXR=0X80;??????????????????????????????? //輔助寄存器,使T0x12=1,此時(shí)不分頻
??? SCON|=0X50; ????????????????????????????? //SM0=0,SM1=1,串口以方式1工作,8位Uart,串行口1用定時(shí)器1作為其波特率發(fā)生器且定時(shí)器1工作于模式0;REN=1,開(kāi)啟串口接收
??? TL1=(65536-(Machine_Focs/4/BAUD1));
??? TH1=(65536-(Machine_Focs/4/BAUD1))>>8;
??? AUXR|=0X40;?????????????????????????????? //使T0x12=1,此時(shí)不分頻
??? RI=0;????? ?????????????????????????????? //接收終端標(biāo)志位
??? TI=0;????? ?????????????????????????????? //發(fā)送中斷標(biāo)志位
??? TR1=1;???? ?????????????????????????????? //啟動(dòng)的定時(shí)器1
??? ES=1;????? ?????????????????????????????? //串口中斷允許位
??? EA=1;????? ?????????????????????????????? //總中斷允許位
}
/**************************************************************
函數(shù)名稱:Init
功能描述:完成各部分功能模塊的初始化
***************************************************************/
voidInit()?????????????????????????????????? //初始化操作
{
??? P3M0=0x00;??????????????????????????????? //推挽模式
??? P3M1=0x00;
??? P2M0=0xff;
? ? P2M1=0x00;
? ? P0M0=0xff;
? ? P0M1=0x00;
??? TMOD=0x01;??????????????????????????????? //定時(shí)器0,方式1,要求每一次中斷之后手動(dòng)重裝
??? ET0=1;??????????????????????????????????? //開(kāi)啟定時(shí)器0中斷
??? EA=1;??????????????????????????????????? //開(kāi)啟總中斷
??? TH0=0x00;
??? TL0=0x00;
??? TR0=1;???? ??????????????????????????????? //啟動(dòng)定時(shí)器0
??? beep=0;?????????????????????????????????? //蜂鳴器初始化0
??? flag=0;
??? P0=0;
??? Uart1_Init();???????????????????????????? //串口中斷
??? display = 0x00;???????????????? ?????????? //初始化數(shù)據(jù)緩沖器
??? LED_SEL=0;??????????????????????????????? //設(shè)置數(shù)碼管顯示狀態(tài)
}
/**********************
函數(shù)名稱:void playmusic()
功能描述:播放音樂(lè)
***********************/
voidplaymusic(uchar p)?????????????????????? //p為音節(jié)
{
??? uchar tem;?????????????????????????????? ???
??? tem=quyin(p);???? ???????????????????????? //找到p音節(jié)在quzi數(shù)組中的位置
??? timeh=quzi[tem];? ?????? ????????????????? //音節(jié)重裝值的高八位
??? timel=quzi[tem+1];??????????????????????? //音節(jié)重裝值的低八位
??? TR0=1;???????????? ???????????????????????//開(kāi)啟定時(shí)器0中斷
??? delay(0x10*180);??????????? ?????????? ???//延時(shí)一個(gè)節(jié)拍?
??? TR0=0;??????????????????????????????????? //關(guān)閉定時(shí)器0中斷
}
/*************************************************************
串口1中斷相應(yīng)程序
**************************************************************/
voidUart1_fun() interrupt 4
{
??? if(RI)??????? ?????????????????????????? //接受完數(shù)據(jù)以后,RI自動(dòng)轉(zhuǎn)1
??? {
?????? flag=1;
?????? RI=0;
?????? display=SBUF;
?????? //TR0=1;
?????? playmusic(display);
??? }
??? //if(TI)????? ??? ??????????????????????? //接受完數(shù)據(jù)以后,RI自動(dòng)轉(zhuǎn)1
??? //{
?????? //TI=0;
?????? //Uart1_Sendbusy=0;
??? //}
}
/**************************************************************
主函數(shù)
***************************************************************/
voidmain()?????????????????????????????????????
{
??? Init();???
??? while(1){}??????????????????????????????? //執(zhí)行死循環(huán)
}
?
上位機(jī)部分:
int nIndex =m_chuankou_select.GetCurSel();
CString strCBText;
m_chuankou_select.GetLBText(nIndex,strCBText);
用來(lái)控制從下拉框m_chuankou_select中獲得已經(jīng)選擇的串口號(hào),并且將其存儲(chǔ)在字符串strCBText中;
if(strCBText=="")
?????? AfxMessageBox("請(qǐng)選擇串口");
??? else if(strCBText=="com1")
?????? m_ctrlComm.SetCommPort(1);
??? else if(strCBText=="com2")
?????? m_ctrlComm.SetCommPort(2);
。。。。。。
用來(lái)打開(kāi)strCBText對(duì)應(yīng)的串口號(hào)
m_ctrlComm.SetSettings("9600,n,8,1");//波特率9600,無(wú)校驗(yàn),8個(gè)數(shù)據(jù)位,1個(gè)停止位? ?m_ctrlComm.SetInputMode(1); //1:表示以二進(jìn)制方式檢取數(shù)據(jù)?
m_ctrlComm.SetRThreshold(1);//參數(shù)1表示每當(dāng)串口接收緩沖區(qū)中有多于或等于1個(gè)字符時(shí)將引發(fā)一個(gè)接收數(shù)據(jù)的OnComm事件?
m_ctrlComm.SetInputLen(0);//設(shè)置當(dāng)前接收區(qū)數(shù)據(jù)長(zhǎng)度為0?
m_ctrlComm.GetInput();//先預(yù)讀緩沖區(qū)以清除殘留數(shù)據(jù)?
設(shè)置串口屬性
UpdateData(TRUE); //讀取編輯框內(nèi)容
??? if(flag==3)
?????? m_ctrlComm.SetOutput(COleVariant((CString)0x31));//發(fā)送數(shù)據(jù)
??? else if(flag==2)
?????? m_ctrlComm.SetOutput(COleVariant((CString)0x21));//發(fā)送數(shù)據(jù)
??? else if(flag==1)
?????? m_ctrlComm.SetOutput(COleVariant((CString)0x11));//發(fā)送數(shù)據(jù)
??? else
?????? AfxMessageBox("請(qǐng)選擇一個(gè)音階");?
控制發(fā)送高中低八度的DO,其他音節(jié)類似
CStringstr[]={"com1","com2","com3","com4","com5","com6","com7","com8","com9"};
??? ((CComboBox*)GetDlgItem(IDC_chuankou_select))->ResetContent();
??? CString defaultstr=str[0];
??? SetDlgItemText(IDC_chuankou_select,defaultstr);
??? for(int i=1;i
??? {
?????? ((CComboBox*)GetDlgItem(IDC_chuankou_select))->AddString(str[i]);
??? }
填充下拉框
【實(shí)驗(yàn)總結(jié)】
1、???接收中斷自動(dòng)置1的問(wèn)題:在串口傳輸?shù)倪^(guò)程當(dāng)中,使用RXD接收上位機(jī)傳過(guò)來(lái)的數(shù)據(jù),使用TXD傳出數(shù)據(jù),每當(dāng)數(shù)據(jù)發(fā)送完成,TI會(huì)自動(dòng)置位為1,請(qǐng)求接收中斷處理;每當(dāng)數(shù)據(jù)接收完成,RI會(huì)自動(dòng)置位為1,請(qǐng)求發(fā)送中斷處理,由于TI和RI以“或”邏輯關(guān)系向主機(jī)請(qǐng)求中斷,所以主機(jī)響應(yīng)中斷時(shí)事先并不知道是TI還是RI請(qǐng)求的中斷,必須在中斷服務(wù)程序中查詢TI和RI進(jìn)行判別,然后分別處理,因此,兩個(gè)中斷請(qǐng)求標(biāo)志位均不能由硬件自動(dòng)置位,必須通過(guò)軟件置零,否則將出現(xiàn)一次請(qǐng)求多次向英的錯(cuò)誤。這里采用的是在中斷處理程序中進(jìn)行軟件置位,也就是每一次中斷結(jié)束時(shí)檢測(cè)中斷的類型,進(jìn)而對(duì)對(duì)應(yīng)的中斷標(biāo)志位進(jìn)行置位。
2、???音節(jié)的節(jié)拍的完成:即控制某一頻率的震動(dòng)在某一指定時(shí)間內(nèi)完成。這一功能是通過(guò)控制定時(shí)器0的開(kāi)關(guān)實(shí)現(xiàn)的,即當(dāng)單片機(jī)接收到串口數(shù)據(jù)時(shí),會(huì)執(zhí)行playmusic函數(shù),在默認(rèn)情況下,定時(shí)器0開(kāi)關(guān)TR0=0是關(guān)閉狀態(tài),執(zhí)行playmusic的同時(shí),打開(kāi)定時(shí)器開(kāi)關(guān)TR0=1,中間延時(shí)0x10*180ms一個(gè)節(jié)拍,延時(shí)結(jié)束后將定時(shí)器控制TR0置0,等待下一次輸入。由于在之前的“電子音樂(lè)”工程當(dāng)中是用數(shù)組存儲(chǔ)音節(jié)和節(jié)拍的,每?jī)蓚€(gè)字節(jié)決定一個(gè)音階和一個(gè)節(jié)拍,在串口中一次只能傳輸一個(gè)字節(jié),所以如果要實(shí)現(xiàn)節(jié)拍功能就會(huì)給用戶帶來(lái)很多不便,所以在這里舍去了節(jié)拍的選擇,轉(zhuǎn)而采用固定的一個(gè)節(jié)拍。
【實(shí)驗(yàn)心得】
1、???單片機(jī)使用c語(yǔ)言進(jìn)行編程,這樣給嵌入式開(kāi)發(fā)者帶來(lái)了很大的方便,畢竟我們最初接觸到的語(yǔ)言就是c語(yǔ)言,不過(guò)和之前的c語(yǔ)言編程相對(duì)比,現(xiàn)在編程語(yǔ)句結(jié)構(gòu)相對(duì)分散或者說(shuō)是獨(dú)立而不像之前的編程當(dāng)中體現(xiàn)的非常強(qiáng)的邏輯,但是由于新加入的中斷定時(shí)器等會(huì)再各個(gè)結(jié)構(gòu)之間產(chǎn)生影響,仍然需要非常注意,特別是對(duì)于中斷概念的理解,可以參考“深入理解計(jì)算機(jī)系統(tǒng)”這本書(shū);
2、???要多編程,了解各個(gè)引腳,寄存器的用法、功能。雖說(shuō)我們做硬件編程的時(shí)候可以隨時(shí)查找數(shù)據(jù)手冊(cè)尋找需要的知識(shí),但是如果能記住這些寄存器、引腳的分布、用法,無(wú)疑會(huì)給硬件編程帶來(lái)非常大的方便,這就要求我們多編程,才能比較好的掌握;
3、???stc硬件編程入門很容易,也就是說(shuō)很容易便可以掌握stc板的一些基本用法,像是數(shù)碼管、led、定時(shí)器等等,但是要編出比較強(qiáng)大好用的硬件程序,就必須多去了解各種中斷用法、編程技巧等,并且這些知識(shí)經(jīng)驗(yàn)只能靠自己在編程的過(guò)程中發(fā)現(xiàn),不是任何老師可以教授的,所以,學(xué)習(xí)單片機(jī),要親力親為。