(51單片機)LCD顯示紅外遙控相關數字(Delay延時函數)(LCD1602教程)(Int0和Timer0外部中斷教程)(IR紅外遙控模塊教程)

?前言:

本次Timer0模塊改裝了一下,注意!!!今天只是簡單的實現一下,明天用次功能顯示遙控密碼鎖

演示視頻:

在審核

源代碼:

如上圖將9個文放在Keli5 中即可,然后燒錄在單片機中就行了

燒錄軟件用的是STC-ISP,不知道怎么安裝的可以去看江科大的視頻:

【51單片機入門教程-2020版 程序全程純手打 從零開始入門】https://www.bilibili.com/video/BV1Mb411e7re?p=2&vd_source=ada7b122ae16cc583b4add52ad89fd5e

源代碼:

頭文件要記得宏定義和重定義,避免重復調用:

#ifndef _Timer0_h_//名字根據文件名定義即可
#define _Timer0_h_//聲明函數……#endif

main.c

#include <STC89C5xRC.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"unsigned char Mode=-1;//數字
unsigned char Key1,Key2,Key3,Key4,Key;//
unsigned char Num=0;//數字
unsigned char Command;//獲取紅外線命令void main(){LCD_Init();//初始化LCDLCD_ShowString(1,1,"Hello!");//初始化顯示LCDLCD_ShowNum(2,1,0,4);//初始密碼數字IR_Init();//初始化紅外線模塊while(1){if(IR_GetDataFlag()){//接收Command=IR_GetCommand();if(Command==IR_0){Num=0;}if(Command==IR_1){Num=1;}if(Command==IR_2){Num=2;}if(Command==IR_3){Num=3;}if(Command==IR_4){Num=4;}if(Command==IR_5){Num=5;}if(Command==IR_6){Num=6;}if(Command==IR_7){Num=7;}if(Command==IR_8){Num=8;}if(Command==IR_9){Num=9;}LCD_ShowNum(2,16,Num,1);//顯示數字//			if(Command==IR_POWER){
//				Mode=-Mode;
//				if(Mode==1){
//					
//					LCD_ShowString(1,1,"      ");
//					LCD_ShowString(1,1,"ON");//初始化顯示LCD
//					while(1){
//					
//						Key1=Num;
//						LCD_ShowNum(2,4,Key1,1);//顯示數字
//						if(Command==IR_RPT){Key1=0;}
//						LCD_ShowNum(2,4,Key1,1);//顯示數字
//						if(Key1){
//							Key2=Num;
//							LCD_ShowNum(2,3,Key2,1);//顯示數字
//							if(Command==IR_RPT){Key2=0;}
//							LCD_ShowNum(2,3,Key2,1);//顯示數字
//						}
//						if(Key2){
//							Key3=Num;
//							LCD_ShowNum(2,2,Key3,1);//顯示數字
//							if(Command==IR_RPT){Key3=0;}
//							LCD_ShowNum(2,2,Key3,1);//顯示數字
//						}
//						if(Key3){
//							Key4=Num;
//							LCD_ShowNum(2,1,Key4,1);//顯示數字
//							if(Command==IR_RPT){Key4=0;}
//							LCD_ShowNum(2,1,Key4,1);//顯示數字
//						}
//						Key=Key1+Key2*10+Key3*100+Key4*1000;
//						if(Command==IR_USD){
//							if(Key==0301){
//							LCD_ShowString(1,12,"Sure");//初始化顯示LCD
//							}else{
//							LCD_ShowString(1,12,"Eorr");//初始化顯示LCD
//							}
//							break;
//						}
//					}
//					}else{
//					LCD_ShowString(1,1,"      ");
//					LCD_ShowString(1,1,"OFF");//初始化顯示LCD
//					LCD_ShowNum(2,1,0,4);//初始密碼數字
//					}
//				}}}
}

?LCD1602.c

#include <STC89C5xRC.H>//引腳配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0//函數定義:
/*** @brief  LCD1602延時函數,12MHz調用可延時1ms* @param  無* @retval 無*/
void LCD_Delay()		//@11.0592MHz
{unsigned char i, j;i = 11;j = 190;do{while (--j);} while (--i);
}/*** @brief  LCD1602寫命令* @param  Command 要寫入的命令* @retval 無*/
void LCD_WriteCommand(unsigned char Command)
{LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief  LCD1602寫數據* @param  Data 要寫入的數據* @retval 無*/
void LCD_WriteData(unsigned char Data)
{LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief  LCD1602設置光標位置* @param  Line 行位置,范圍:1~2* @param  Column 列位置,范圍:1~16* @retval 無*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}
}/*** @brief  LCD1602初始化函數* @param  無* @retval 無*/
void LCD_Init()
{LCD_WriteCommand(0x38);//八位數據接口,兩行顯示,5*7點陣LCD_WriteCommand(0x0c);//顯示開,光標關,閃爍關LCD_WriteCommand(0x06);//數據讀寫操作后,光標自動加一,畫面不動LCD_WriteCommand(0x01);//光標復位,清屏
}/*** @brief  在LCD1602指定位置上顯示一個字符* @param  Line 行位置,范圍:1~2* @param  Column 列位置,范圍:1~16* @param  Char 要顯示的字符* @retval 無*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{LCD_SetCursor(Line,Column);LCD_WriteData(Char);
}/*** @brief  在LCD1602指定位置開始顯示所給字符串* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  String 要顯示的字符串* @retval 無*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);}
}/*** @brief  返回值=X的Y次方*/
int LCD_Pow(int X,int Y)
{unsigned char i;int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result;
}/*** @brief  在LCD1602指定位置開始顯示所給數字* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  Number 要顯示的數字,范圍:0~65535* @param  Length 要顯示數字的長度,范圍:1~5* @retval 無*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief  在LCD1602指定位置開始以有符號十進制顯示所給數字* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  Number 要顯示的數字,范圍:-32768~32767* @param  Length 要顯示數字的長度,范圍:1~5* @retval 無*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief  在LCD1602指定位置開始以十六進制顯示所給數字* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  Number 要顯示的數字,范圍:0~0xFFFF* @param  Length 要顯示數字的長度,范圍:1~4* @retval 無*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber<10){LCD_WriteData(SingleNumber+'0');}else{LCD_WriteData(SingleNumber-10+'A');}}
}/*** @brief  在LCD1602指定位置開始以二進制顯示所給數字* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  Number 要顯示的數字,范圍:0~1111 1111 1111 1111* @param  Length 要顯示數字的長度,范圍:1~16* @retval 無*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}
}

?LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__//用戶調用函數:
void LCD_Init();//初始化
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);//顯示單個字符
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);//顯示字符串
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//顯示數字
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);//顯示帶符號數字
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//顯示十進制數字
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//顯示二進制數字#endif

?Delay.c


void Delay(unsigned int xms)
{unsigned char i, j;while(xms--){i = 2;j = 239;do{while (--j);} while (--i);}
}

?Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__void Delay(unsigned int xms);#endif

Int0.c?

#include <STC89C5xRC.H>void Int0_Init(){IT0=1;IE0=0;EX0=1;EA=1;PX0=1;
}外部中斷函數模版
//void Int0_Routine() interrupt 0{
//	Num++;
//}

Int0.h

#ifndef __Int0_H__
#define __Int0_H__void Int0_Init();#endif

?Timer0.c

#include <REGX52.H>/*** @brief  定時器0初始化* @param  無* @retval 無*/
void Timer0_Init(void)
{TMOD &= 0xF0;		//設置定時器模式TMOD |= 0x01;		//設置定時器模式TL0 = 0;		//設置定時初值TH0 = 0;		//設置定時初值TF0 = 0;		//清除TF0標志TR0 = 0;		//定時器0不計時
}/*** @brief  定時器0設置計數器值* @param  Value,要設置的計數器值,范圍:0~65535* @retval 無*/
void Timer0_SetCounter(unsigned int Value)
{TH0=Value/256;TL0=Value%256;
}/*** @brief  定時器0獲取計數器值* @param  無* @retval 計數器值,范圍:0~65535*/
unsigned int Timer0_GetCounter(void)
{return (TH0<<8)|TL0;
}/*** @brief  定時器0啟動停止控制* @param  Flag 啟動停止標志,1為啟動,0為停止* @retval 無*/
void Timer0_Run(unsigned char Flag)
{TR0=Flag;
}

??Timer0.h

#ifndef _Timer0_h_
#define _Timer0_h_void Timer0_Init();
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter();
void Timer0_Run(unsigned char Flag);#endif

??IR.c

#include <STC89C5xRC.H>
#include "Timer0.h"
#include "Int0.h"unsigned int IR_Time;//外部中斷計時
unsigned char IR_State;//紅外遙控現階段狀態unsigned char IR_Data[4];//數據(共32位,分皮來儲存和表示)
unsigned char IR_pData;//分批數據0-31,用來IR_Data[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>12442-500 && IR_Time<12442+500){IR_State=2;			//置狀態為2}//如果計時為11.25ms,則接收到了Repeat信號(判定值在12MHz晶振下為11250,在11.0592MHz晶振下為10368)else if(IR_Time>10368-500 && IR_Time<10368+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>1032-500 && IR_Time<1032+500){IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));	//數據對應位清0IR_pData++;			//數據位置指針自增}//如果計時為2250us,則接收到了數據1(判定值在12MHz晶振下為2250,在11.0592MHz晶振下為2074)else if(IR_Time>2074-500 && IR_Time<2074+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}}
}

?IR.h

#ifndef __IR_H__
#define __IR_H__//紅外遙控對應命令
#define IR_POWER		0x45
#define IR_MODE			0x46
#define IR_MUTE			0x47
#define IR_START_STOP	0x44
#define IR_PREVIOUS		0x40
#define IR_NEXT			0x43
#define IR_EQ			0x07
#define IR_VOL_MINUS	0x15
#define IR_VOL_ADD		0x09
#define IR_0			0x16
#define IR_RPT			0x19
#define IR_USD			0x0D
#define IR_1			0x0C
#define IR_2			0x18
#define IR_3			0x5E
#define IR_4			0x08
#define IR_5			0x1C
#define IR_6			0x5A
#define IR_7			0x42
#define IR_8			0x52
#define IR_9			0x4Avoid IR_Init();
unsigned char IR_GetDataFlag();
unsigned char IR_GetRepeatFlag();
unsigned char IR_GetAddress();
unsigned char IR_GetCommand();#endif

?代碼解析與教程:

?Timer0模塊

  • 包含源代碼與頭文件,需要知道怎么實現,會用
  • 51單片機的定時器和計數器十分重要,要理解怎么用,要知道原理是什么,要結合原理圖來分析怎么做,先看代碼
#include <STC89C5xRC.H>//void Timer0_Init()
//{
//	TF0=0;TR0=1;//TCON,寄存器
//	//TMOD=0x01;//0000 0001,寄存器
//	TMOD=TMOD&0xF0;//把TMOD的低四位清零,高四位不變,方便使用兩個定時器
//	TMOD=TMOD&0x01;//把TMOD的最低位置1,高四位不變,方便使用兩個定時器
//	TH0=64535/256;//高電位,寄存器,1毫秒
//	TL0=64535%256;//低電位,寄存器,1毫秒
//	ET0=1;EA=1;PT0=0;//打開中斷開關
//	
//}
//定時器0初始化函數
void Timer0_Init()		//1毫秒@11.0592MHz
{
//	AUXR &= 0x7F;		//定時器時鐘12T模式TMOD &= 0xF0;		//設置定時器模式TMOD |= 0x01;		//設置定時器模式TL0 = 0x66;		//設置定時初值TH0 = 0xFC;		//設置定時初值TF0 = 0;		//清除TF0標志TR0 = 1;		//定時器0開始計時ET0=1;//允許中斷EA=1;//允許總中斷PT0=0;//低優先級
}中斷程序函數
//中斷函數模版
//void Timer0_Routine() interrupt 1
//{
//	static unsigned int T0Count;
//	TL0 = 0x66;		//設置定時初值
//	TH0 = 0xFC;		//設置定時初值
//	T0Count++;
//	if(T0Count>=1000){
//		T0Count=0;
//		//下面是代碼區
//	}
//}
  • 最上面注釋掉的代碼是要求理解的;中間的代碼是STC-ISP軟件生成的;最下面的代碼是中斷函數模版,拿到main.c中可直接使用,但是也要了解原理:
定時器/計數器、中斷教程(重點!!!!)
  1. 首先,要理解原理,會認原理圖:

(本篇均是我自己理解的,只是幫助大家理解,若像深學深究,請去自行找資源,如有不對,希望大家指出)

先看官方解釋:

  • 紅色部分是定時器,作用是自己設定一個最大值,讓計數器達到時,完成什么什么,比如執行中斷
  • 黃色部分是計數器,作用是自己設定,讓其變化,比如+1,毫秒,微妙,完成時繼續怎么怎
  • 藍色部分是中斷器,中斷當前執行,執行自己設定的東西,中斷這部分可以嵌套,像if函數一樣,低優先級讓高優先級

他們三者的關系非常微妙,僅僅相連,相輔相成:

  • 定時器就是設定一個時間,計數器開始計時,到點了中斷器開始執行;舉個例子:你訂一個10.00的鬧鐘(定時器)提醒你起床,時間一點一點的過去(計數器),10.00的時候鬧鐘提醒你(定時器),隨后你開始起床(中斷器);那么他們是怎么實現的呢
?定時器/計數器(模式一)
  • 先看原理圖:

????

  • 相關寄存器:

????

  • 官方解釋

????

????

  • 模式一官方解釋

????

  • 下面開始來解釋我的理解(再看原理圖):
    ????
  1. 序號1是非門:反向輸出,例如:GATE是1,輸出0,是0,輸入1。
  2. 序號2是或門:符號為梯形(或弧形缺口),邏輯上滿足 “有 1 出 1,全 0 出 0”。
  3. 序號3是與門:符號為矩形缺口,邏輯上滿足 “全 1 出 1,有 0 出 0”。
    ????
    1,2,3序號均是TMOD里的,請看原理圖:????
    • 代碼TMOD=0x01,是16進制,轉化為二進制為0000 0001,前(高)四位對應著定時器1;后(低)四位對應著定時器0;本代碼使用的是定時器0, 0001分別對應GATE,C/T,M1,M0,由上面的官方解釋可得,M1=0,M0=1時,就是使用并啟動本寄存器。
      但是這樣定義有弊端,也就是定時器1和定時器0不能一起使用,因次,使用下面的代碼:TMOD &=0xF0,也就是TMOD = TMOD & 0xF0(1111 0000)。看不懂沒關系,舉個例子:1010 0101 &? 1111 0000 = 1010 0000,也就是有0出0;
      1010 0101 |? 1111 0000 = 1111?0101,也就是有1出1;因此使用代碼:
       
      1. TMOD &= 0xF0; //設置定時器模式

      2. TMOD |= 0x01; //設置定時器模式

      就可以定義定時器0;
    • 因此,當GATE=0時,通過序號1,輸出1;然后通過序號2,輸出1;因此,只需要將TR0設定成1,輸出就是1,就可以啟動寄存器了。
  4. 序號4是高電位和低電位寄存器:用來設置定時初值,如圖:????
    • 先解釋上面的代碼,可幫助理解,下面的代碼可以不理解(后續可生成):TH0和TL0,最大位就是65535,為了分開儲存,用TH0和TL0分別儲存,例如:現在有一個數123,但是一個盒子只能裝進2位數,因此分成123/100=1和123%100=23儲存,同理身為16進制,就要用256來做除數;代碼中設定為64535的目的是因為1000毫秒就是1秒,65535-64535=1000,用來表示1秒,計時器每次加1秒。
  5. 序號7.8(圖中忘記標了(TR0))是TCON(定時器控制)寄存器:TF0=0時,可以理解成初始化;TR0=1時,表示允許計時;反之兩個就是反義理解
  6. 序號5.6是TMOD(定時器模式)寄存器:用來控制定時器和計數器的模式,本篇只講用的多的模式一
?中斷器
  • 先看原理圖:
    ????
    這里我們定義好定時器0后,TF0=1,ET0=1,打開中斷,隨后PT0=0,接入低級優先:

    ????

    理解上面的東西后,再看中斷函數:

    ????

    運用靜態局部變量T0Count,來表示定時區間,達到1000毫秒(1秒)后重新執行該函數
    P20行代碼是代碼區,也就是放你想每1秒就重復執行的代碼。

?Dealy模塊

  • 包含源代碼與頭文件,不需要知道怎么實現的會用即可,后續使用,直接將頭文件和源代碼拿過來用即可;

?

xms是定義的毫秒,1000毫秒就是1秒;模版生成的是1毫秒的,因此xms等于1000

Int0和Timer0外部中斷模塊?

  • 先來了解一下圖:

    之前講過定時器中斷就是第二個,今天來講一下第一個0,外部中斷,對應引腳是P32;

    先初始化:

    上邊這條INT0全通就可以初始化成功:
    #include <STC89C5xRC.H>void Int0_Init(){IT0=1;IE0=0;EX0=1;EA=1;PX0=1;
    }外部中斷函數模版
    //void Int0_Routine() interrupt 0{
    //	Num++;
    //}

    下面配合Timer0來實現外部中斷:
    ?

    void Timer0_Init(void)
    {TMOD &= 0xF0;		//設置定時器模式TMOD |= 0x01;		//設置定時器模式TL0 = 0;		//設置定時初值TH0 = 0;		//設置定時初值TF0 = 0;		//清除TF0標志TR0 = 0;		//定時器0不計時
    }

    將初值TL0,TH0設定為0,TR0定時器不計時設置為0;
    設置定時器0計數器值:
    ?

    /*** @brief  定時器0設置計數器值* @param  Value,要設置的計數器值,范圍:0~65535* @retval 無*/
    void Timer0_SetCounter(unsigned int Value)
    {TH0=Value/256;TL0=Value%256;
    }

    獲取定時器0計數器值:
    ?

    /*** @brief  定時器0獲取計數器值* @param  無* @retval 計數器值,范圍:0~65535*/
    unsigned int Timer0_GetCounter(void)
    {return (TH0<<8)|TL0;
    }

    控制定時器0的啟動和關閉:
    ?

    /*** @brief  定時器0啟動停止控制* @param  Flag 啟動停止標志,1為啟動,0為停止* @retval 無*/
    void Timer0_Run(unsigned char Flag)
    {TR0=Flag;
    }

    現在就完成了外部中斷的配置,在外部函數中書寫代碼即可;

?LCD1602模塊

??

  • LCD1602相關重要知識:
    • LCD1602有兩上下兩行顯示屏,每行各有16個小顯示屏,如上圖中的LCD_ShowString(1,3,"Hello"),第一個參數是第一行還是第二行,第2個參數是對應第幾行的第幾個小顯示屏,最后一個是輸出的東西,同理,到LCD_ShowNum(1,9,123,3)里,前三個和前面一樣,最后一個參數是顯示的位數,不夠就在前面補0,例如輸入1,參數為4,顯示就是0001,輸入23,參數為3,顯示就是023
  • 先看原理圖

?

?

?

VO是調節顯示亮度的,讓你的顯示屏顯示更清楚。

RS,WR,EN(E)是引腳定義,看單片機核心,對應著P25,P26,P27:(下述所有代碼WR都寫成了RW,不過不影響,引腳對就行)

再看D0-D7,也就是數據,看單片機核心可知,對應著P00-P07,也就是P0,因此宏定義LCD_DataPort=P0;

//引腳配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

?

LCD1602,屏幕是16*2的,每一個小格格就是CGRAM+CGROM(字模庫)組成,這個東西就是顯示數據的,這些數據是DDPAN(數據顯示區)來存儲的,然后映射到屏幕上,一一對應;但是DDRAM有40*2個小格格,因此,LCD1602,其實可以滾動顯示;然后DDRAM是由控制器來決定的,AC就是小格格的地址;

  • DDRAM部分

?

看第8個DDRAM部分:前兩個是00

?

上圖就是DDRAM的地址,也就是小格格的,如果是第一行顯示,A6前面那個等于0就行了,A6前面等于1就是第二行顯示,剩下的A6-A0就是小格格的地址,比如第一個,0000 0000,因此簡寫成00H,第二行第一個,0100?0000,0x40,簡寫40H,以此類推;

  • CGRAM+CGROM部分

?

這部分可以理解成二維數組,比如你想顯示A,在圖中找到A,A的列是0100,行是0001,把列放到行的前面組成二進制就是他的地址,0100 0001,就是0x41;

?

來看編碼表,A確實是0x41;

  • 掌握上述知識,就可以寫顯示數據了,這里只講寫數據/指令:

?

  • 寫指令/數據部分

可以看到,寫入一個數據要將RS變化(默認為1),R/W變化(默認為1),EN(E)也要變化,因此有:

/*** @brief  LCD1602延時函數,12MHz調用可延時1ms* @param  無* @retval 無*/
void LCD_Delay()		//@11.0592MHz
{unsigned char i, j;i = 11;j = 190;do{while (--j);} while (--i);
}/*** @brief  LCD1602寫命令* @param  Command 要寫入的命令* @retval 無*/
void LCD_WriteCommand(unsigned char Command)
{LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief  LCD1602寫數據* @param  Data 要寫入的數據* @retval 無*/
void LCD_WriteData(unsigned char Data)
{LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}

注意:EN(E)操作的時候需要時間,數據讀取需要時間,因此延時1ms;RS在寫數據和指令的時候不一樣,前者1,后者0;

  • 屏幕顯示部分

?

由上控制指令,完成操作,就可以初始化屏幕:

/*** @brief  LCD1602初始化函數* @param  無* @retval 無*/
void LCD_Init()
{LCD_WriteCommand(0x38);//八位數據接口,兩行顯示,5*7點陣LCD_WriteCommand(0x0c);//顯示開,光標關,閃爍關LCD_WriteCommand(0x06);//數據讀寫操作后,光標自動加一,畫面不動LCD_WriteCommand(0x01);//光標復位,清屏
}

然后是顯示字符的操作,先用指令確定AC地址:

/*** @brief  LCD1602設置光標位置* @param  Line 行位置,范圍:1~2* @param  Column 列位置,范圍:1~16* @retval 無*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}
}

然后發送數據:

?

字符:

/*** @brief  在LCD1602指定位置上顯示一個字符* @param  Line 行位置,范圍:1~2* @param  Column 列位置,范圍:1~16* @param  Char 要顯示的字符* @retval 無*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{LCD_SetCursor(Line,Column);LCD_WriteData(Char);
}

確定AC地址,然后用函數直接寫數據;

字符串:

/*** @brief  在LCD1602指定位置開始顯示所給字符串* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  String 要顯示的字符串* @retval 無*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);}
}

這部分就是C語言程序部分,看看代碼很好理解

數字:

/*** @brief  返回值=X的Y次方*/
int LCD_Pow(int X,int Y)
{unsigned char i;int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result;
}/*** @brief  在LCD1602指定位置開始顯示所給數字* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  Number 要顯示的數字,范圍:0~65535* @param  Length 要顯示數字的長度,范圍:1~5* @retval 無*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}
}

這部分也是C語言代碼部分,將數字各位分開,例如:

?

這樣操作。

其他部分:

/*** @brief  在LCD1602指定位置開始以有符號十進制顯示所給數字* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  Number 要顯示的數字,范圍:-32768~32767* @param  Length 要顯示數字的長度,范圍:1~5* @retval 無*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief  在LCD1602指定位置開始以十六進制顯示所給數字* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  Number 要顯示的數字,范圍:0~0xFFFF* @param  Length 要顯示數字的長度,范圍:1~4* @retval 無*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber<10){LCD_WriteData(SingleNumber+'0');}else{LCD_WriteData(SingleNumber-10+'A');}}
}/*** @brief  在LCD1602指定位置開始以二進制顯示所給數字* @param  Line 起始行位置,范圍:1~2* @param  Column 起始列位置,范圍:1~16* @param  Number 要顯示的數字,范圍:0~1111 1111 1111 1111* @param  Length 要顯示數字的長度,范圍:1~16* @retval 無*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}
}

和數字部分差不多,也是C語言程序,認真看代碼即可;

?IR紅外遙控模塊

  • 先來認識一下

初始化和定義變量:

使用前先初始化外部中斷模塊,和定義變量:

/*** @brief  紅外遙控初始化* @param  無* @retval 無*/
void IR_Init(void)
{Timer0_Init();Int0_Init();
}
unsigned int IR_Time;//外部中斷計時
unsigned char IR_State;//紅外遙控現階段狀態unsigned char IR_Data[4];//數據(共32位,分皮來儲存和表示)
unsigned char IR_pData;//分批數據0-31,用來IR_Data[IR_pData]表示數據unsigned char IR_DataFlag;//數據幀信號
unsigned char IR_RepeatFlag;//連發幀信號
unsigned char IR_Address;//地址
unsigned char IR_Command;//命令
檢查紅外線標志位(是否接收成功):
/*** @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>12442-500 && IR_Time<12442+500){IR_State=2;			//置狀態為2}//如果計時為11.25ms,則接收到了Repeat信號(判定值在12MHz晶振下為11250,在11.0592MHz晶振下為10368)else if(IR_Time>10368-500 && IR_Time<10368+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>1032-500 && IR_Time<1032+500){IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));	//數據對應位清0IR_pData++;			//數據位置指針自增}//如果計時為2250us,則接收到了數據1(判定值在12MHz晶振下為2250,在11.0592MHz晶振下為2074)else if(IR_Time>2074-500 && IR_Time<2074+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}}
}

紅外線遙控有三個狀態:

其中State=0是空閑狀態;State=1是等待Start信號或Repeat信號;State=2是接收數據;

空閑狀態的時候,要把定時器0清空并打開(IR_State=0):

if(IR_State==0)				//狀態0,空閑狀態{Timer0_SetCounter(0);	//定時計數器清0Timer0_Run(1);			//定時器啟動IR_State=1;				//置狀態為1}

State=1是等待Start信號或Repeat信號,要判斷當前定時器0計數器的值在哪個區間來判斷接收Data數據區間,如圖:

如果定時器0計數器在9+4.5=13.5ms,13500us的時候,就是Start(接收數據)狀態,于是準備接收Data,然后將State=2,(置為狀態2);在9+2.25=11.25ms,11250us的時候,就是Repeat(連續接收數據)狀態,這時Data已經被接收了,只需要重新從State=0開始即可重復接收;如果接收失敗,將State=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>12442-500 && IR_Time<12442+500){IR_State=2;			//置狀態為2}//如果計時為11.25ms,則接收到了Repeat信號(判定值在12MHz晶振下為11250,在11.0592MHz晶振下為10368)else if(IR_Time>10368-500 && IR_Time<10368+500){IR_RepeatFlag=1;	//置收到連發幀標志位為1Timer0_Run(0);		//定時器停止IR_State=0;			//置狀態為0}else					//接收出錯{IR_State=1;			//置狀態為1}}

?State=2是接收數據;如果狀態1通過,進入狀態2(接收數據);先獲取上次的中斷定時器0計數器值,判斷接收的是高電平還是低電平;

如果定時器0計數器在560=560us=1120us的時候,就是接收低電平,然后將數據(IR_Data[])對應位置為0,然后數組數據(IR_pData)增加;在560+1690us=2250us,就是接收高電平,然后將數據(IR_Data[])對應位置為0,然后數組數據(IR_pData)增加;如果接收失敗,將數組指針(IR_pData)和State=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>1032-500 && IR_Time<1032+500){IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));	//數據對應位清0IR_pData++;			//數據位置指針自增}//如果計時為2250us,則接收到了數據1(判定值在12MHz晶振下為2250,在11.0592MHz晶振下為2074)else if(IR_Time>2074-500 && IR_Time<2074+500){IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));	//數據對應位置1IR_pData++;			//數據位置指針自增}else					//接收出錯{IR_pData=0;			//數據位置指針清0IR_State=1;			//置狀態為1}

然后判斷數據是否達到32位:

如果達到就進行數據驗證,驗證方法是看數據第一位和第二位的補碼是否相等,看數據第三位和第四位的補碼是否相等;驗證通過的話,轉存數據:

Address對應IR_Data[0];Command對應IR_Data[2],存儲之后,IR_DataFlag=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}

然后停止定時器0,置紅外線狀態為空閑狀態0;

?注:該代碼是本人自己所寫,可能不夠好,不夠簡便,歡迎大家指出我的不足之處。如果遇見看不懂的地方,可以在評論區打出來,進行討論,或者聯系我。上述內容全是我自己理解的,如果你有別的想法,或者認為我的理解不對,歡迎指出!!!如果可以,可以點一個免費的贊支持一下嗎?謝謝各位彥祖亦菲!!!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/80932.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/80932.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/80932.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

網絡實驗-防火墻雙機熱備份

實驗目的 了解防火墻雙機熱備份配置&#xff0c;提供部署防火墻可靠性。 網絡拓撲 左側為trust域&#xff0c;右側為untrust域。防火墻之間配置雙機熱備份。 配置內容 master VRRP 由于防火墻是基于會話表匹配回程流量&#xff0c;流量去向和回程必須通過同一個防火墻。…

【2025最新】VSCode Cline插件配置教程:免費使用Claude 3.7提升編程效率

 ?2025年最新VSCode Cline插件安裝配置教程&#xff0c;詳解多種免費使用Claude 3.7的方法&#xff0c;集成DeepSeek-R1與5大實用功能&#xff0c;專業編程效率提升指南。 Cline是VSCode中功能最強大的AI編程助手插件之一&#xff0c;它能與Claude、OpenAI等多種大模型無縫集…

考研英一真題學習筆記 2018年

2018 年全國碩士研究生招生考試 英語 &#xff08;科目代碼&#xff1a;201&#xff09; Section Ⅰ Use of English Directions: Read the following text. Choose the best word(s) for each numbered blank and mark A, B, C or D on the ANSWER SHEET. (10 points) Trust i…

華碩服務器-品類介紹

目錄 一、核心產品線解析 1. 機架式服務器 2. 塔式服務器 3. 高密度計算服務器 二、關鍵技術與模組配置 1. 主板與管理模塊 2. 電源與散熱 3. 存儲與網絡 三、應用場景與行業解決方案 1. 人工智能與高性能計算 2. 云計算與虛擬化 3. 邊緣計算與工業物聯網 一、核心…

硅基計劃2.0 學習總結 貳

一、程序邏輯控制&#xff08;順序、選擇&循環&#xff09; 順序結構就不多介紹了&#xff0c;就是各個語句按照先后順序進行執行 &#xff08;1&#xff09;選擇結構 三大選擇類型&#xff1a;if、if-else、if-else if-else以及懸浮else的問題 基本已經在之前在C語言文章…

RabbitMQ最新入門教程

文章目錄 RabbitMQ最新入門教程1.什么是消息隊列2.為什么使用消息隊列3.消息隊列協議4.安裝Erlang5.安裝RabbitMQ6.RabbitMQ核心模塊7.RabbitMQ六大模式7.1 簡單模式7.2 工作模式7.3 發布訂閱模式7.4 路由模式7.5 主題模式7.6 RPC模式 8.RabbitMQ四種交換機8.1 直連交換機8.2 主…

工具學習_VirusTotal使用

VirusTotal Intelligence 允許用戶在其龐大的數據集中進行搜索&#xff0c;以查找符合特定條件的文件&#xff0c;例如哈希值、殺毒引擎檢測結果、元數據信息、提交時的文件名、文件結構特征、文件大小等。可以說&#xff0c;它幾乎是惡意軟件領域的“谷歌搜索引擎”。 網頁使…

計算機系統----軟考中級軟件設計師(自用學習筆記)

目錄 1、計算機的基本硬件系統 2、CPU的功能 3、運算器的組成 4、控制器 5、計算機的基本單位 6、進制轉換問題 7、原碼、反碼、補碼、移碼 8、浮點數 9、尋址方式 10、奇偶校驗碼 11、海明碼 12、循環冗余校驗碼 13、RISC和CISC 14、指令的處理方式 15、存儲器…

揚州卓韻酒店用品:優質洗浴用品,提升酒店滿意度與品牌形象

在酒店提供的服務里&#xff0c;沐浴用品占據了非常重要的地位&#xff0c;其質量與種類直接關系到客人洗澡時的感受。好的沐浴用品能讓客人洗澡時感到舒心和快樂&#xff0c;反之&#xff0c;質量不好的用品可能會影響客人整個住宿期間的愉悅心情。挑選恰當的洗浴用品不僅能夠…

學習筆記:黑馬程序員JavaWeb開發教程(2025.4.5)

12.4 登錄認證-登錄校驗-會話跟蹤方案一 設置cookie&#xff0c;服務器給瀏覽器響應數據&#xff0c;通過control方法形參當中獲取response&#xff0c;調用response當中的addCookie方法實現 獲取cookie&#xff0c;調用getCookie方法 用戶可以通過瀏覽器設置禁用cookie 跨域…

進程替換講解

1. 基本概念 1.1 進程替換 vs. 進程創建 進程創建&#xff1a;使用fork()或clone()等系統調用創建一個新的子進程&#xff0c;子進程是父進程的副本&#xff0c;擁有相同的代碼和數據。進程替換&#xff1a;使用exec系列函數在當前進程中加載并執行一個新的程序&#xff0c;替…

【微服務】SpringBoot + Docker 實現微服務容器多節點負載均衡詳解

目錄 一、前言 二、前置準備 2.1 基本環境 2.2 準備一個springboot工程 2.2.1 準備幾個測試接口 2.3 準備Dockerfile文件 2.4 打包上傳到服務器 三、制作微服務鏡像與運行服務鏡像 3.1 拷貝Dockerfile文件到服務器 3.2 制作服務鏡像 3.3 啟動鏡像服務 3.4 訪問一下服…

1.2.2.1.4 數據安全發展技術發展歷程:高級公鑰加密方案——同態加密

引言 在密碼學領域&#xff0c;有一種技術被圖靈獎得主、著名密碼學家Oded Goldreich譽為"密碼學圣杯"&#xff0c;那就是全同態加密&#xff08;Fully Homomorphic Encryption&#xff09;。今天我們就來聊聊這個神秘而強大的加密方案是如何從1978年的概念提出&…

vllm量化03—INT4 W4A16

本系列基于Qwen2.5-7B&#xff0c;學習如何使用vllm量化&#xff0c;并使用benchmark_serving.py、lm_eval 測試模型性能和評估模型準確度。 測試環境為&#xff1a; OS: centos 7 GPU: nvidia l40 driver: 550.54.15 CUDA: 12.3本文是該系列第3篇——INT4 W4A16 一、量化 f…

第二十五天打卡

常見報錯類型 try-except-else-finally 語句 首先執行try語句&#xff0c;若正確直接執行else語句 若try語句發生錯誤&#xff0c;則判斷錯誤類型&#xff0c;執行錯誤類型對應的except語句&#xff0c;不執行else語句 finally語句無條件執行&#xff0c;多用于資源保存&…

城市掃街人文街頭紀實膠片電影感Lr調色預設,DNG/手機適配濾鏡!

調色詳情 城市掃街人文街頭紀實膠片電影感 Lr 調色是通過 Lightroom&#xff08;Lr&#xff09;軟件&#xff0c;對城市街頭抓拍的人文紀實照片進行后期調色處理。旨在賦予照片如同膠片拍攝的質感以及電影般濃厚的敘事氛圍&#xff0c;不放過每一個日常又珍貴的瞬間&#xff0c…

【hadoop】Kafka 安裝部署

一、Kafka安裝與配置 步驟&#xff1a; 1、使用XFTP將Kafka安裝包kafka_2.12-2.8.1.tgz發送到master機器的主目錄。 2、解壓安裝包&#xff1a; tar -zxvf ~/kafka_2.12-2.8.1.tgz 3、修改文件夾的名字&#xff0c;將其改為kafka&#xff0c;或者創建軟連接也可&#xff1…

UDP 多點通信

一、setsockopt/getsockopt 函數詳解 1. 函數原型 c #include <sys/socket.h> int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);…

說一說Node.js高性能開發中的I/O操作

眾所周知&#xff0c;在軟件開發的領域中&#xff0c;輸入輸出&#xff08;I/O&#xff09;操作是程序與外部世界交互的重要環節&#xff0c;比如從文件讀取數據、向網絡發送請求等。這段時間&#xff0c;也指導項目中一些項目的開發工作&#xff0c;發現在Node.js運用中&#…

Charles抓包并破解ProtoBuf請求

安裝Charles并抓包 如果是外網的需要root安裝一系列證書等&#xff0c;詳細見參考文章&#xff1a; 在雷電模擬器安卓7.0上使用Charles抓包詳細教程 遇到如下問題&#xff1a; 1.粘貼到目錄/system/etc/security/cacerts內&#xff0c;粘貼不了。需要打開這個 2.模擬器wifi打…