這個本來應該周末寫的,可是一直想偷懶,只能是拖到周一了,今天把51結個尾,明天開始學32了。
學習內容LCD1602,直流電機,AD/DA,紅外遙控
LCD1602
內部的框架結構
屏幕小于數據顯示區,可以實現流動字幕
指令集和時序結構(RS1為數據,0為指令)
void LCD_WriteData(unsigned char Data)
{LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_E=1;LCD_Delay();LCD_E=0;LCD_Delay();
}
void LCD_WriteCommand(unsigned char Command)
{LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_E=1;LCD_Delay();LCD_E=0;LCD_Delay();
}
按照時序結構實現數據和指令的寫入
在明白數據和指令的寫入,我們就可以實現在LCD1602顯示各種文本
初始化設置輸入模式,顯示模式等(指令集中標紅)
void LCD_Init(void)
{LCD_WriteCommand(0x38);LCD_WriteCommand(0x0C);LCD_WriteCommand(0x06);LCD_WriteCommand(0x01);
}
設置輸入的地址(顯示位置)(0x80為DDRAM地址設置)
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else{LCD_WriteCommand(0x80|(Column-1)+0x40);}
}
剩下的顯示字符,字符串等就比較簡單了
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
{LCD_SetCursor(Line,Column);LCD_WriteData(Char);
}
實現流動字幕
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
void main()
{LCD_Init();LCD_ShowString(1,16,"Welcome to China!");while(1){LCD_WriteCommand(0x18);Delay(500);}}
直流電機
電機是小車的重要組成部分(重要性不必多說了吧)(端午會出小車代碼,已經在路上了)
LED呼吸燈
#include <REGX52.H>sbit LED=P2^0;void Delay(unsigned int t)
{while(t--);
}void main()
{unsigned char Time,i;while(1){for(Time=0;Time<100;Time++) //改變亮滅時間,由暗到亮{for(i=0;i<20;i++) //計次延時{LED=0; //LED亮Delay(Time); //延時TimeLED=1; //LED滅Delay(100-Time); //延時100-Time}}for(Time=100;Time>0;Time--) //改變亮滅時間,由亮到暗{for(i=0;i<20;i++) //計次延時{LED=0; //LED亮Delay(Time); //延時TimeLED=1; //LED滅Delay(100-Time); //延時100-Time}}}
}
直流電機調速
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Timer0.h"sbit Motor=P1^0;unsigned char Counter,Compare; //計數值和比較值,用于輸出PWM
unsigned char KeyNum,Speed;void main()
{Timer0_Init();while(1){KeyNum=Key();if(KeyNum==1){Speed++;Speed%=4;if(Speed==0){Compare=0;} //設置比較值,改變PWM占空比if(Speed==1){Compare=50;}if(Speed==2){Compare=75;}if(Speed==3){Compare=100;}}Nixie(1,Speed);}
}void Timer0_Routine() interrupt 1
{TL0 = 0x9C; //設置定時初值TH0 = 0xFF; //設置定時初值Counter++;Counter%=100; //計數值變化范圍限制在0~99if(Counter<Compare) //計數值小于比較值{Motor=1; //輸出1}else //計數值大于比較值{Motor=0; //輸出0}
}
AD/DA轉換
AD(Analog to Digital):模擬-數字轉換,將模擬信號轉換為計算機可操作的數字信號
DA(Digital to Analog):數字-模擬轉換,將計算機輸出的數字信號轉換為模擬信號
像是溫度傳感器就是AD,將外部溫度的變化(模擬信號)轉換為電壓變化(數字信號)
蜂鳴器就是DA,我們將譜子對應的編碼寫入,將內部電流的變化(數字信號)轉換為震動(模擬信號)
右下是一個負反饋的放大器
從右邊看,2R和2R并聯是R,R和R串聯是2R,循環往復。I就是VREF/R,I1=2*I0,遞推I=256*I0。流經運算放大器Rfb的電流是D7~D0的和,V0等于Rfb乘流經電流
所以很少用DA,大多都是通過PWM實現
IN傳遞進來電壓,然后通過比較器和VREF比較,調整VREF逐次逼近,最后輸出
AD模數轉換
XPT2064是A/D轉換器
#include <REGX52.H>
#include <INTRINS.H>//引腳定義
sbit XPT2046_DIN=P3^4;
sbit XPT2046_CS=P3^5;
sbit XPT2046_DCLK=P3^6;
sbit XPT2046_DOUT=P3^7;/*** @brief ZPT2046讀取AD值* @param Command 命令字,范圍:頭文件內定義的宏,結尾的數字表示轉換的位數* @retval AD轉換后的數字量,范圍:8位為0~255,12位為0~4095*/
unsigned int XPT2046_ReadAD(unsigned char Command)
{unsigned char i;unsigned int Data=0;XPT2046_DCLK=0;XPT2046_CS=0;for(i=0;i<8;i++){XPT2046_DIN=Command&(0x80>>i);XPT2046_DCLK=1;XPT2046_DCLK=0;}for(i=0;i<16;i++){XPT2046_DCLK=1;XPT2046_DCLK=0;if(XPT2046_DOUT){Data|=(0x8000>>i);}}XPT2046_CS=1;return Data>>8;
}
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "XPT2046.h"unsigned int ADValue;void main(void)
{LCD_Init();LCD_ShowString(1,1,"ADJ NTC GR");while(1){ADValue=XPT2046_ReadAD(XPT2046_XP); //讀取AIN0,可調電阻LCD_ShowNum(2,1,ADValue,3); //顯示AIN0ADValue=XPT2046_ReadAD(XPT2046_YP); //讀取AIN1,熱敏電阻LCD_ShowNum(2,6,ADValue,3); //顯示AIN1ADValue=XPT2046_ReadAD(XPT2046_VBAT); //讀取AIN2,光敏電阻LCD_ShowNum(2,11,ADValue,3); //顯示AIN2Delay(100);}
}
紅外遙控
#include <REGX52.H>
#include "Timer0.h"
#include "Int0.h"unsigned int IR_Time;
unsigned char IR_State;unsigned char IR_Data[4];
unsigned char IR_pData;unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;/*** @brief 紅外遙控初始化* @param 無* @retval 無*/
void IR_Init(void)
{Timer0_Init();Int0_Init();
}/*** @brief 紅外遙控獲取收到數據幀標志位* @param 無* @retval 是否收到數據幀,1為收到,0為未收到*/
unsigned char IR_GetDataFlag(void)
{if(IR_DataFlag){IR_DataFlag=0;return 1;}return 0;
}/*** @brief 紅外遙控獲取收到連發幀標志位* @param 無* @retval 是否收到連發幀,1為收到,0為未收到*/
unsigned char IR_GetRepeatFlag(void)
{if(IR_RepeatFlag){IR_RepeatFlag=0;return 1;}return 0;
}/*** @brief 紅外遙控獲取收到的地址數據* @param 無* @retval 收到的地址數據*/
unsigned char IR_GetAddress(void)
{return IR_Address;
}/*** @brief 紅外遙控獲取收到的命令數據* @param 無* @retval 收到的命令數據*/
unsigned char IR_GetCommand(void)
{return IR_Command;
}//外部中斷0中斷函數,下降沿觸發執行
void Int0_Routine(void) interrupt 0
{if(IR_State==0) //狀態0,空閑狀態{Timer0_SetCounter(0); //定時計數器清0Timer0_Run(1); //定時器啟動IR_State=1; //置狀態為1}else if(IR_State==1) //狀態1,等待Start信號或Repeat信號{IR_Time=Timer0_GetCounter(); //獲取上一次中斷到此次中斷的時間Timer0_SetCounter(0); //定時計數器清0//如果計時為13.5ms,則接收到了Start信號(判定值在12MHz晶振下為13500,在11.0592MHz晶振下為12442)if(IR_Time>13500-500 && IR_Time<13500+500){IR_State=2; //置狀態為2}//如果計時為11.25ms,則接收到了Repeat信號(判定值在12MHz晶振下為11250,在11.0592MHz晶振下為10368)else if(IR_Time>11250-500 && IR_Time<11250+500){IR_RepeatFlag=1; //置收到連發幀標志位為1Timer0_Run(0); //定時器停止IR_State=0; //置狀態為0}else //接收出錯{IR_State=1; //置狀態為1}}else if(IR_State==2) //狀態2,接收數據{IR_Time=Timer0_GetCounter(); //獲取上一次中斷到此次中斷的時間Timer0_SetCounter(0); //定時計數器清0//如果計時為1120us,則接收到了數據0(判定值在12MHz晶振下為1120,在11.0592MHz晶振下為1032)if(IR_Time>1120-500 && IR_Time<1120+500){IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //數據對應位清0IR_pData++; //數據位置指針自增}//如果計時為2250us,則接收到了數據1(判定值在12MHz晶振下為2250,在11.0592MHz晶振下為2074)else if(IR_Time>2250-500 && IR_Time<2250+500){IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8)); //數據對應位置1IR_pData++; //數據位置指針自增}else //接收出錯{IR_pData=0; //數據位置指針清0IR_State=1; //置狀態為1}if(IR_pData>=32) //如果接收到了32位數據{IR_pData=0; //數據位置指針清0if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3])) //數據驗證{IR_Address=IR_Data[0]; //轉存數據IR_Command=IR_Data[2];IR_DataFlag=1; //置收到連發幀標志位為1}Timer0_Run(0); //定時器停止IR_State=0; //置狀態為0}}
}
接收數據時32位,通過IR_pData控制哪個八位數據的哪一位
時間不會很精準,留下500誤差(注意不要讓兩種狀態交叉就行,比如start和repeat差值是2000,repeat+500和start-500就不會交叉)
由于對時間的要求比較高,我們需要通過外部中斷確保信號被接收
void Int0_Init(void)
{IT0=1;IE0=0;EX0=1;EA=1;PX0=1;
}
今日語錄:當開始的熱情褪去,剩下的一切都變得裸露,是懶惰,是氣餒,是焦慮。