/********************************************************************* MCU: ATmega16 外部晶振: 8MHz 程序功能: 4*4鍵盤識別,LED七段數碼管顯示,密碼功能模塊,直流電機正反轉控制 AD轉換模塊, 模擬比較器,外部中斷應用,12864液晶,C/T0 硬件設計: 參考PROTUES硬件仿真電路 調試: 所有程序主要功能都軟硬件仿真通過,實際使用時要根據需要加入可靠性。 編譯環境: ICC AVR Design by: wdw ********************************************************************/ #include<iom16v.h> #include<macros.h> #include<math.h> #define uchar unsigned char #define uint unsigned int #define SET_1(a,b) a|=bit(b) //將寄存器a的第b位置1 #define CLR_0(a,b) a&=~bit(b) //將寄存器a的第b位清0 #program date code: const date[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,//共陽數據; 0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E}; #program duan code: const duan[]={0x20,0x10,0x08,0x04,0x02,0x01}; //段選; char adchannel; int dispbuf[]={0,0,0,0,0,0,}; //顯示緩存; uchar securbuf[]={0,0,0,0,0,}; //5位密碼緩存; /******************************************************************** 名稱:ms延時子函數 功能:延時指定的ms *********************************************************************/ void delayms(int ms) { int i,j; for(i=ms;i>0;i--) { for(j=1722;j>0;j--); //8M時改為1141; } //12M時為1722; } /******************************************************************* 名稱:us延時子程序 功能:延時指定的us ********************************************************************/ void delayus(uchar us) { for(us;us<0;us--); { } //12M時單位T為1.45US } /******************************************************************** 單參數LED 顯示 硬件連接:數據PORTC-----D7~D0 段選PORTD-----D7~D4 *********************************************************************/ void LEDShow(long int shownum) { uchar i,tmp,curnum; long int tmpnum; tmp = 0xfb; tmpnum = shownum; for(i=0;i<6;i++) //顯示的位數為5 { curnum = tmpnum % 10; tmpnum = tmpnum / 10; PORTD = tmp; PORTC = date[curnum]; delayus(1); tmp = (tmp << 1) + 0x01; //補上移位造成的最右位為0; } } /******************************************************* 名稱:七段LED共陽數碼管,在指定數位上顯示指定的數,雙參數 功能:在指定的位置上顯示制定的數; 硬件連接:PC口複用控制段選和位選,有2片573進行數據鎖存; *******************************************************/ void show(uchar dat,uchar num) //六位七段數碼管顯示程序OK; { PORTC=date[dat]; PORTD|=BIT(0); PORTD&=~BIT(0); //送數據; delayus(2); PORTC=0X00; delayus(1); PORTC=duan[num]; PORTD|=BIT(1); delayus(2); PORTD&=~BIT(1); delayus(2); } //送選通信號; /************************************************************ 名稱:MCU端口初始化 功能:設置MCU端口初始方向為輸出初始狀態為低 *************************************************************/ void Gpioinit0() { DDRA=0XFF; PORTA=0X00; DDRB=0XFF; PORTB=0X00; DDRC=0XFF; PORTC=0X00; DDRD=0XFF; PORTD=0X00; } /*********************************************************** 名稱:MCU端口初始化 功能:設置MCU端口初始為輸出初始狀態為高 ************************************************************/ void Gpioinit1() { DDRA=0Xff; PORTA=0XFF; DDRB=0xff; PORTB=0XFF; DDRC=0xff; PORTC=0XFF; DDRD=0xff; PORTD=0XFF; } /************************************************** 名稱:單端口測試 輸入:測試PC的任意端口號0~7 功能:週期改變一個端口的電平 **************************************************/ void IOtest(uchar num) { DDRC|=BIT(num); // while(1) // { PORTC|=BIT(num); delayms(50); PORTC&=~BIT(num); delayms(50); // } } /************************************************* 名稱:按鍵動作檢測子程序。 功能:檢測4*4鍵盤是否有鍵按下; 硬件連接:PB0~PB3為行;PB4~pb7為列; *************************************************/ uchar key_press() { uchar key; DDRB=0XFF; PORTB=0X0F; DDRB=0XF0; delayms(2); //此處要加上適當延時否則讀不到端口信息; key=PINB; key&=0x0f; //按下返回1,否則為0; if(key==0x0f) { return 0; } else return 1; } /*************************************************** 名稱:4*4矩陣按鍵掃描子程序 功能:掃描一個4*4矩陣案件,比返回相應按鍵值; 硬件連接:PB0~PB3為行;PB4~pb7為列; ***************************************************/ uchar keyscan() //4*4鍵盤掃面程序,調試0K; { uchar keycode,keynum; do{ }while(key_press()==0); //按鍵是否按下? asm("nop"); asm("nop"); while(key_press()==1) { DDRB=0XFF; //PB0~PB3為列,PB4~PB7為行; PORTB=0X0F; //列輸出高電平,行輸出低電平; DDRB=0XF0; delayus(10); //延時2毫秒,讀列狀態; keycode=PINB; keycode&=0x0f; //讀列狀態,屏蔽行狀態; DDRB=0XFF; PORTB=0XF0; DDRB=0X0F; delayus(10); PINB&=0Xf0; //讀行狀態,屏蔽列狀態; keycode|=PINB; delayus(10); do{ }while(key_press()==1); //按鍵是否釋放; switch(keycode) { case 0xee: keynum=0; break; case 0xde: keynum=1; break; case 0xbe: keynum=2; break; case 0x7e: keynum=3; break; case 0xed: keynum=4; break; case 0xdd: keynum=5; break; case 0xbd: keynum=6; break; case 0x7d: keynum=7; break; case 0xeb: keynum=8; break; case 0xdb: keynum=9; break; case 0xbb: keynum=10; break; case 0x7b: keynum=11; break; case 0xe7: keynum=12; break; case 0xd7: keynum=13; break; case 0xb7: keynum=14; break; case 0x77: keynum=15; break; default : keynum=16; break; } } return keynum; } /************************************************************ 名稱:六位密碼輸入比對程序 功能:輸入六位數並與存儲密碼比對,0K返回1,按錯或者順序錯返回0; 原始密碼654321 **************************************************************/ uchar securtcom() //6位密碼輸入比對程序0K; { uchar keycount=0; while(key_press()==1); delayms(3); do{ while(key_press()==1); delayms(1); while(key_press()==0); show(8,keycount); securbuf[keycount++]=keyscan(); }while(keycount<6); if(securbuf[0]==6) //初始比對,密碼654321; { if(securbuf[1]==5) { if(securbuf[2]==4) { if(securbuf[3]==3) { if(securbuf[4]==2) { if(securbuf[5]==1); } } } } return 1; //密碼正確,返回1; } else return 0; //密碼錯誤,返回0; } uchar serset() //密碼設定子程序; { } uchar speci_keypress(uchar presstimes) //特殊按鍵F按下某一設定次數OK; { } uchar key_longpress(uchar prst) //按鍵長按子程序 { } /************************************************** 名稱: 直流蜂鳴器控制 功能: 控制蜂鳴器鳴響時間 硬件連接:PD5輸出控制信號 **************************************************/ void dcbeep(uchar beeptime) //直流蜂鳴器,調試OK; {uchar beep; DDRD|=BIT(5); PORTD&=~BIT(5); PORTD|=BIT(5); delayus(beeptime); PORTD&=~BIT(5); delayus(100-beeptime); } /********************************************************** 名稱:直流電機全橋控制 功能:fbw=1.正轉;fbw=0,反轉; 硬件連接:PD6與PD7口組合輸出控制信號; **********************************************************/ void moto(uchar fbw) //直流電機正反轉程序,驅動全橋; { DDRD|=BIT(6); DDRD|=BIT(7); PORTD&=~BIT(6); PORTD&=~BIT(7); delayms(5); if(fbw==1) { PORTD|=BIT(6); delayms(500); PORTD&=~BIT(6); delayms(20); } else { } if(fbw==0) { PORTD|=BIT(7); delayms(500); PORTD&=~BIT(7); delayms(20); } } /********************************************************* 名稱: AD單次轉換子程序; 功能: 採樣AD通路0的電壓,該函數輸出為AD 轉換後的數據, 查詢工作方式 *********************************************************/ void ad_init() { ADMUX=0XC0; //設置AD輸入通道為0通道,內部2.56V參考電壓; ADCSRA=0X80; //使能ADC,清中斷標誌ADIF,查詢方式; DDRA&=~BIT(0); //設置PA0口為輸入; PORTA&=~BIT(0); //PA0口清零; } int ad_conv() //單次轉換子程序,OK; { int addata; ADCSRA|=BIT(ADSC); //啟動一次轉換; while(!(ADCSRA&(BIT(ADIF)))); //查詢等待轉換結束標誌置位; ADCSRA&=~BIT(ADIF); addata=ADCL; addata=addata+ADCH*256; return addata; } int ad_aver() //轉換8次,做平均值濾波 { int adaver,count; for(count=7;count>0;count--) { adaver=ad_conv(); adaver+=adaver; delayms(1); } adaver=adaver/8; delayms(1); return adaver; } /********************************************************* 名稱:AD採樣 功能:採樣AD0的電壓,內部2.56V參考電壓,中斷工作方式 **********************************************************/ void ad0_init() { ADMUX=0xc0; adchannel=0; ADCSRA=0x98; DDRA&=~BIT(0); PORTA&=~BIT(0); SEI(); } #pragma interrupt_handler process:15 //interrupt process program void process() { int adcode=0; ADCSRA&=~BIT(ADIE); adcode=ADCL; adcode=(ADCH<<8)+adcode; //get adc value /************************ 中斷處理函數 *************************/ ADCSRA|=BIT(ADIE); } /******************************************************** 名稱:DAC0832 DA轉換 輸入:有效分度0~255 輸出:無 功能:對8為數字信號進行DA轉換,實際應用需調試 ********************************************************/ #define cs_low PORTD&=~BIT(0) #define cs_high PORTD|=BIT(0) #define wr1_low PORTD&=~BIT(1) #define wr1_high PORTD|=BIT(1) #define ile_high PORTD|=BIT(2) #define ile_low PORTD&=~BIT(2) #define wr2_low PORTD&=~BIT(3) #define wr2_high PORTD|=BIT(3) #define xfer_low PORTD&=~BIT(5) #define xfer_high PORTD|=BIT(5) void dac(int dadata) { Gpioinit1(); DDRD=0xff; DDRC=0xff; ile_high; //delayus(1); cs_low; wr1_low; //delayus(1); PORTC=dadata; //delayus(1); wr1_high; cs_high; // delayus(1); wr2_low; xfer_low; // delayus(1); xfer_high; } /***************************************************** 名稱: 數據拆分顯示函數 功能: 將四位數據分別顯示到對應的數碼管。 *****************************************************/ void addisplay(int addat) //轉換數據顯示,運算OK; { int a; a=addat; show(a/1000,3); delayms(2); show((a/100)%10,2); delayms(2); show(((a/10)%10)/10,1); delayms(2); show(a%10,0); delayms(2); } /********************************************************** 名稱:SPI通信 功能:應用mega16自身SPI通信模塊與外設進行通信.查詢方式; ***********************************************************/ void SPI_masterinit() { DDRB|=BIT(5)|BIT(7); //設置MOSI與SCK為輸出 DDRB&=~(BIT(4)&BIT(6)); //設置MISO與SS為輸入 SPCR|=BIT(4)|BIT(3)|BIT(1)|BIT(0); //數據高位先發送,主機模式,空閒時鐘極性 //為高,SCK起始沿採樣數據,128分頻 SPCR|=BIT(6); //使能SPI delayms(1); } void SPI_mastertransdata(uchar data) { SPDR=data; while(!(SPSR&BIT(7))); } void SPI_slaveinit() { } void SPI_slaverecive() { } /********************************************************* 名稱:比較匹配模式,,中斷方式,調試OK 功能:AVR比較匹配模式應用,當AIN大於AIN1時,上升沿產生中斷 **********************************************************/ void com_inti() { SEI(); //開總中斷; DDRB&=((~BIT(2))&(~BIT(3))); //設置PB2/3口為輸入; SFIOR&=~BIT(3); //設置AIN1為模擬比較器反向輸入端; ACSR&=~BIT(7); //使能模擬比較器,注意此處為低電平有效; ACSR&=~BIT(6); //設置AIN0為正極輸入端; ACSR|=BIT(3); //使能模擬比較器中斷; ACSR|=(BIT(1)|BIT(0)); //比較器上升沿觸發中斷; } #pragma interrupt_handler comp:17 //注意函數名後不加(); void comp() //模擬比較器中斷服務程序; { ACSR&=~BIT(3); /************* 此處加中斷處理函數 **************/ ACSR|=BIT(3); } /********************************************************** 名稱:外部中斷INT0 調試OK 功能:低電平觸發INT0端口引起中斷,並響應相關中斷服務程序 **********************************************************/ void int0_init() { DDRD&=~BIT(2); //PD2/INT0端口配置為輸入; MCUCR&=((~BIT(0))&(~BIT(0))); //INT0觸發方式為低電平; GICR|=BIT(6); //使能外部中斷0; SEI(); //開總中斷; } #pragma interrupt_handler int0:2 void int0() //INT0中斷服務函數; { GICR&=~BIT(6); //關閉外部INT0; /************************ 此處加INT0中斷處理函數 ************************/ GICR|=BIT(6); //開啟外部INT0; } /********************************************************* 名稱:外部中斷INT1, 功能:邊沿觸發INT0端口引起中斷,並響應相關中斷服務程序 **********************************************************/ void int1_init() { DDRD&=~BIT(3); //將PD3口配置為輸入; PORTD&=~BIT(3); //pd3口初始值為0; MCUCR|=(BIT(3)|BIT(2)); //INT1上升沿觸發中斷; GICR|=BIT(7); //使能INT1中斷; SEI(); //開總中斷; } #pragma interrupt_handler int1:3 void int1() { GICR&=~BIT(7); //關閉INT1中斷; /******************************* 此處加INT1中斷服務函數; *******************************/ GICR|=BIT(7); //開啟INT1中斷; } /********************************************************** /********************************************************** 名稱:12864(ks0108)顯示應用 功能:圖形液晶使用 **********************************************************/ #define EN_CLR PORTD&=~BIT(0) //使能12864 #define EN_SET PORTD|=BIT(0) //關閉12864 #define RW_READ PORTD|=BIT(1) //讀操作 #define RW_WRITE PORTD&=~BIT(1) //寫操作 #define RS_COMM PORTD&=~BIT(2) //命令操作 #define RS_DATA PORTD|=BIT(2) //數據操作 #define RESET PORTD&=~BIT(3) //12864液晶複位 #define RESET_OVER PORTD|=BIT(3) //12864液晶結束複位; #define CS1_EN PORTD&=~BIT(4) //使能CS1,選擇左半屏幕; #define CS1_DIS PORTD|=BIT(4) //禁止CS1 #define CS2_EN PORTD&=~BIT(5) //使能CS2,選擇右半屏幕; #define CS2_DIS PORTD|=BIT(5) //禁止CS2 #define basic_com 0x30 //一次送8BIT數據,基本指令集; #define explo_com 0xc4 //擴展指令集 #define show_stitude 0x0f //顯示狀態,整體顯示,游標開,游標位置開 #define cleanscreen 0x01 //顯示清零,AC歸零 #define cursor 0x07 //寫入/讀取時,游標右移,畫面左移 #define AC_0 0x03 //AC歸0,不改變DDRAM內容 const int char_0816[]={ 0xe0,0x18,0x04,0x04,0x04,0x18,0xe0,0x00, 0x07,0x18,0x20,0x20,0x20,0x18,0x07,0x00 }; /************************************************************ 名稱:MCU端口初始化 功能:設置MCU端口初始方向以及狀態 *************************************************************/ void GPIO_INIT() { DDRA=0XFF; PORTA=0X00; DDRB=0XFF; PORTA=0X00; DDRC=0XFF; PORTA=0X00; DDRD=0XFF; PORTA=0X00; } /**************************************************************** 名稱:輸入數據 功能:MCU將數據送到總線 *****************************************************************/ void SET_DATA(uchar data) { delayus(3); PORTC=data; delayus(3); } /**************************************************************** 名稱:開關顯示控制 功能:開啟與關閉顯示操作,1為開顯示,0為關閉顯示 *****************************************************************/ void LCD_Command_Displayonoff(uchar onoff) { RW_WRITE; RS_COMM; EN_CLR; SET_DATA(0x3e + onoff); //onoff為1開顯示,為0關顯示; EN_SET; delayus(2); EN_CLR; } /*************************************************************** 名稱: LCD顯示起始行設定 功能:設置LCD顯示起始行位置,startline範圍0~63 ***************************************************************/ void LCD_Command_setstartline(uchar startline) { RW_WRITE; RS_COMM; EN_CLR; SET_DATA(0Xc0 + startline); //12864行地址為0~63之間設定; EN_SET; delayus(2); EN_CLR; } /*************************************************************** 名稱:設置顯示行頁地址 功能:設置顯示行頁地址,12864為0~7 ****************************************************************/ void LCD_Command_set_x(uchar x) { RW_WRITE; RS_COMM; EN_CLR; SET_DATA(0xB8+x); //設置12864行頁地址 x範圍0~7; EN_SET; delayus(2); EN_CLR; } /**************************************************************** 名稱:設置顯示列地址 功能:設置顯示列地址y,範圍0~63; *****************************************************************/ void LCD_Command_set_y(uchar column) { RW_WRITE; RS_COMM; EN_CLR; SET_DATA(0x40+column); //設置顯示列地址y,範圍0~63; EN_SET; delayus(2); EN_CLR; } /******************************************************************** 名稱:LCD顯示數據寫入 功能:將顯示數據寫入12864液晶 *********************************************************************/ void LCD_Command_writebyte(uchar data) { RW_WRITE; RS_DATA; EN_CLR; SET_DATA(data); EN_SET; delayus(2); EN_CLR; } /********************************************************************* 名稱:LCD複位操作 功能:複位LCD **********************************************************************/ void LCD_RESET() { RESET; NOP(); NOP(); NOP(); RESET_OVER; } /************************************************************ 名稱:清除指定範圍函數 功能:清除LCD屏幕指定範圍起始行~結束行的顯示內容 ************************************************************/ void LCD_CLEAR_LINE(uchar startline,uchar endline) { uchar i,j; for(i-startline;i<=endline;i++) { CS1_EN; CS2_EN; LCD_Command_set_x(i); LCD_Command_set_y(0); //y地址每次操作後自動加1; for(j=0;j<64;j++) { LCD_Command_writebyte(0x00); } } } /************************************************************* 名稱:顯示初始化 功能:初始化LCD *************************************************************/ void LCD_init() { LCD_RESET(); LCD_CLEAR_LINE(0,7); NOP(); LCD_Command_Displayonoff(1); NOP(); LCD_Command_setstartline(0); } /************************************************************** 名稱:LCD_WRITE_DATA 輸入:x in [0,7] and y in [0,127] 輸出:無 功能:在LCD指定行列寫一個數據 **************************************************************/ void LCD_write_data(uchar x,uchar y,uchar data) { if(y<64) { CS1_EN; CS2_DIS; LCD_Command_set_x(x); LCD_Command_set_y(y); } else { CS1_DIS; CS2_EN; LCD_Command_set_x(x); LCD_Command_set_y(y-64); } LCD_Command_writebyte(data); delayus(1); } /********************************************************* 名稱:LCD_WRITE_DOT 輸入:x in [0,63] and y in [0,127] 輸出:無 功能:在指定像素位置描點,此函數為覆蓋式描點,原位置一個 字節數據被覆蓋 *********************************************************/ void LCD_write_dot(uchar x,uchar y) { uchar x_address,y_address; uchar data=0; if((x<64)&&(y<128)) { x_address=x>>3; y_address=y; LCD_write_data(x_address,y_address,data|(1<<(x%8))); } } /********************************************************** 名稱:LCD_WRITE_CHAR 輸入:x in [0,3],y in [0,15], disp-顯示字符自模數組首地址 輸出:無 功能:在LCD指定顯示單元顯示一個字符,每個顯示單元默認為8*16 點陣字體,對12864點陣LCD,x最多顯示4行,y最多顯示16個字 符。 ***********************************************************/ void LCD_write_char(uchar x,uchar y,const uchar *disp) { uchar i; uchar x_address; uchar y_address; if((x<4)&&(y<16)) { for(i=0;i<8;i++) { x_address=x<<1; y_address=(y<<3)+i; LCD_write_data(x_address,y_address,*disp++); } for(i=0;i<8;i++) { x_address=(x<<1)+1; y_address=(y<<3)+i; LCD_write_data(x_address,y_address,*disp++); } } } /*********************************************************** 名稱:LCD_write_word 輸入:x in [1,3],y in [1,14] disp-顯示字符字模組首地址 輸出:無 功能:在LCD指定顯示單元顯示一個中文字符,每個顯示單元默認為 16*16點陣字體,對128*64點陣LCD,x最多顯示4行,y最多顯 示16個字符,不允許顯示半個漢字 ***********************************************************/ void LCD_write_word(int x,int y,const uchar *disp) { int i; int x_address; int y_address; if((x<4)&&(y<15)) { for(i=0;i<16;i++) { x_address=x<<1; y_address=(y<<3)+i; LCD_write_data(x_address,y_address,*disp++); } } for(i=0;i<16;i++) { x_address=(x<<1)+1; y_address=(y<<3)+i; LCD_write_data(x_address,y_address,*disp++); } } /************************************************************ 名稱:LCD_write_picture 輸入:x in [1,7] and y in [0,127],x1_length-縱向像素(8的整數倍), y1_length-橫向像素(不一定為8的整數倍) 輸出:無 功能:在指定區域顯示指定像素大小的圖片 ************************************************************/ void LCD_write_picture(int x1,int y1,int x_length,int y_length,const int *pdata) { int x_address; int y_address; for(x_address=x1;x_address<=x1+(x_length>>3)-1;x_address++) { for(y_address=y1;y_address<y1+y_length;y_address++) { LCD_write_data(x_address,y_address,*pdata); } } } /********************************************************** 名稱: busy 輸入:無 輸出:忙狀態返回1,空閒狀態返回0; 功能:判別LCD控制器內部狀態,忙時返回1,閑時返回0 **********************************************************/ uchar busy(void) { RS_COMM; RW_READ; if(BIT(7)==0) { return 0; } else return 1; } /*************************************************************** 名稱:12864測試 功能:逐點掃描 ****************************************************************/ void LCD_test() { uchar i,j; for(i=0;i<63;i++) { for(j=0;j<127;j++) { delayms(20); LCD_write_dot(i,j); delayms(100); } } } /************************************************************** 名稱:axis() 輸入:無 輸出:無 功能:標定界面坐標 ***************************************************************/ void axis() { uchar a,b,y,x; for(a=0;a<63;a++) //y軸坐標及分位點; { LCD_write_data(a,29,0x01); } for(b=0;b<127;b+=5) //x軸坐標及分位點; { LCD_write_data(30,b,0x04); } } /********************************************************** /******************************************************** 名稱:定時器0初始化 輸入: 輸出: 描述:普通方式,中斷工作方式,注意中斷響應程序中要重裝初值 *********************************************************/ void timer0_init() { SFIOR|=BIT(0); //復位預定比例分頻器; TCCR0|=(BIT(2)|BIT(0)); //設定分頻器為1024分頻; //TCCR0|=BIT(0); //設定分頻器為無分頻; TCNT0=255; //裝訂計數初值 TIMSK|=BIT(0); //開啟T/C0中斷; SEI(); //開總中斷; DDRC|=BIT(0); PORTC|=BIT(0); } /************************************************************ 中斷處理函數 ************************************************************/ #pragma interrupt_handler time0_proc:10 void time0_proc() { TIMSK&=~BIT(0); //關閉中斷; TCNT0=255; //重新裝訂計數初值; PORTC^=BIT(0); TIMSK|=BIT(0); //開啟T0中斷 } /************************************************************* 名稱:6位數碼管按鍵位移顯示 輸入:無 輸出:無 描述:按鍵輸入時在LED上移位顯示按鍵值,當按下f時,全部清零。 **************************************************************/ void keymovedis() { uchar keyval1,keyval2,presstimes; while(1) { if(keyscan()==15) //F的鍵值為15; { for(presstimes=0;presstimes<5;presstimes++) { dispbuf[presstimes]=date[0]; show(0,presstimes); } } else { for(presstimes=0;presstimes<5;presstimes++) { dispbuf[5-presstimes]=dispbuf[4-presstimes]; dispbuf[0]=keyscan(); show(dispbuf[presstimes],presstimes); } } } } /********************************************************* 主函數main().c,地球人都知道,不必廢話 **********************************************************/ void main() { while(1) { } }