当前位置: 首页 > news >正文

(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 Num;//数字
unsigned char Address,Command;//地址,命令void main(){LCD_Init();//初始化LCDLCD_ShowString(1,1,"ADDR  CMD  NUM");//初始化显示LCDLCD_ShowString(2,1,"00    00   000");//对应地址,命令,数字IR_Init();//初始化红外线模块while(1){if(IR_GetDataFlag() || IR_GetRepeatFlag()){//接收或者连发接收Address=IR_GetAddress();//获取当前地址Command=IR_GetCommand();//获取当前命令LCD_ShowHexNum(2,1,Address,2);//显示16进制的地址LCD_ShowHexNum(2,7,Command,2);//显示16进制的命令if(Command==IR_VOL_MINUS){Num--;//如果按音量-,Num-}if(Command==IR_VOL_ADD){Num++;//如果按音量+,Num+}LCD_ShowNum(2,12,Num,3);//显示数字}}
}

 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;

 main模块

  • 注释写的很清楚,这里不做解释了
#include <STC89C5xRC.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"unsigned char Num;//数字
unsigned char Address,Command;//地址,命令void main(){LCD_Init();//初始化LCDLCD_ShowString(1,1,"ADDR  CMD  NUM");//初始化显示LCDLCD_ShowString(2,1,"00    00   000");//对应地址,命令,数字IR_Init();//初始化红外线模块while(1){if(IR_GetDataFlag() || IR_GetRepeatFlag()){//接收或者连发接收Address=IR_GetAddress();//获取当前地址Command=IR_GetCommand();//获取当前命令LCD_ShowHexNum(2,1,Address,2);//显示16进制的地址LCD_ShowHexNum(2,7,Command,2);//显示16进制的命令if(Command==IR_VOL_MINUS){Num--;//如果按音量-,Num-}if(Command==IR_VOL_ADD){Num++;//如果按音量+,Num+}LCD_ShowNum(2,12,Num,3);//显示数字}}
}

 注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!

 

相关文章:

(51单片机)LCD显示红外遥控相关数据(Delay延时函数)(LCD1602教程)(Int0和Timer0外部中断教程)(IR红外遥控模块教程)

前言&#xff1a; 本次Timer0模块改装了一下&#xff0c;注意&#xff01;&#xff01;&#xff01; 演示视频&#xff1a; 红外遥控 源代码&#xff1a; 如上图将9个文放在Keli5 中即可&#xff0c;然后烧录在单片机中就行了 烧录软件用的是STC-ISP&#xff0c;不知道怎么安…...

农产品园区展示系统——仙盟创梦IDE开发

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>农业大数据平台</title><style>* {margi…...

Copilot:您的AI伴侣-微软50周年系列更新

回顾微软五十年来持续创新带来的深远影响&#xff0c;比尔盖茨当年"让每张办公桌、每个家庭都拥有电脑"的宏伟愿景至今仍激励着我们。微软AI团队正秉承同样的精神&#xff0c;打造属于每个人的AI伙伴——Copilot。 这意味着什么&#xff1f;它是什么模样&#xff1f…...

【人工智能】深入探索Python中的自然语言理解:实现实体识别系统

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 自然语言理解(NLU)是人工智能(AI)领域中的重要研究方向之一,其目标是让计算机理解和处理人类语言。在NLU的众多应用中,实体识别(Nam…...

​​Steam安装下载及新手注册

&#x1f4e2;博客主页&#xff1a;肩匣与橘 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由肩匣与橘编写&#xff0c;首发于CSDN&#x1f649; &#x1f4e2;生活依旧是美好而又温柔的&#xff0c;你也…...

Gradio全解20——Streaming:流式传输的多媒体应用(1)——流式传输音频:魔力8号球

Gradio全解20——Streaming&#xff1a;Streaming&#xff1a;流式传输的多媒体应用&#xff08;1&#xff09;——流式传输音频&#xff1a;魔力8号球 前言本篇摘要20. Streaming&#xff1a;流式传输的多媒体应用20.1 流式传输音频&#xff1a;魔力8号球20.1.1 工作原理20.1.…...

Netflix系统架构解析

Netflix系统架构解析 Netflix架构旨在高效可靠地同时为数百万用户提供内容。以下是其特性和组件的详细分析。 是否曾好奇Netflix如何让您目不转睛地享受无中断的流畅播放体验&#xff1f;幕后功臣正是Netflix架构&#xff0c;它负责提供吸引全球观众的无缝流媒体体验。Netflix的…...

宝塔面板运行docker的jenkins

1.在宝塔面板装docker&#xff0c;以及jenkins 2.ip:端口访问jenkins 3.获取密钥&#xff08;点击日志&#xff09; 4.配置容器内的jdk和maven环境&#xff08;直接把jdk和maven文件夹放到jenkins容器映射的data文件下&#xff09; 点击容器-->管理-->数据存储卷--.把相…...

【计算机视觉】目标检测:深度解析Detectron2:Meta开源目标检测与图像分割框架实战指南

深度解析Detectron2&#xff1a;Meta开源目标检测与图像分割框架实战指南 技术架构与设计哲学核心设计理念关键技术组件 环境配置与安装硬件建议配置详细安装步骤 实战流程详解1. 自定义数据集准备2. 模型配置与训练3. 模型评估与推理 核心功能扩展1. 自定义模型架构2. 混合精度…...

Notepad编辑器实现换行符替换

在不同的Note编辑器中&#xff0c;批量把换行替换为空的方法有所不同&#xff0c;以下是常见编辑器的操作方法&#xff1a; Notepad 打开文件后&#xff0c;按CtrlH打开“查找和替换”对话框&#xff0c;在“查找”字段中输入\r\n&#xff0c;在“替换为”字段中输入一个空格…...

【数据通信完全指南】从物理层到协议栈的深度解析

目录 1. 通信技术演进与核心挑战1.1 从电报到5G的技术变迁1.2 现代通信系统的三大瓶颈 2. 通信系统架构深度解构2.1 OSI七层模型运作原理2.2 TCP/IP协议栈实战解析 3. 物理层关键技术实现3.1 信号调制技术演进路线3.2 信道复用方案对比 4. 数据传输可靠性保障4.1 CRC校验算法数…...

SpringBoot多工程项目微服务install时如何不安装到本地仓库

在 Spring Boot 微服务项目中&#xff0c;比如各业务微服务模块由于不存在相互依赖度的问题&#xff0c;因此执行maven install时无需安装到本地仓库&#xff0c;但仍然需要参与构建&#xff08;如 mvn compile 或 mvn package&#xff09;。公共模块&#xff08;如​​辅助工具…...

强化学习_Paper_2017_Curiosity-driven Exploration by Self-supervised Prediction

paper Link: ICM: Curiosity-driven Exploration by Self-supervised Prediction GITHUB Link: 官方: noreward-rl 1- 主要贡献 对好奇心进行定义与建模 好奇心定义&#xff1a;next state的prediction error作为该state novelty 如果智能体真的“懂”一个state&#xff0c;那…...

iview内存泄漏

iview在升级到view-design之前&#xff0c;是存在严重的内存泄漏问题的&#xff0c;而如果你在项目中大量使用了iview组件&#xff0c;就可能面临大量的升级工作要做&#xff0c;因为样式很多是不兼容的。 我们今天就看一下iview的源码&#xff0c;看看到底问题在哪里&#xff…...

【Hive入门】Hive高级特性:事务表与ACID特性详解

目录 1 Hive事务概述 2 ACID特性详解 3 Hive事务表的配置与启用 3.1 启用Hive事务支持 3.2 创建事务表 4 Hive事务操作流程 5 并发控制与隔离级别 5.1 Hive的锁机制 5.2 隔离级别 6 Hive事务的限制与优化 6.1 主要限制 6.2 性能优化建议 7 事务表操作示例 7.1 基本…...

Modbus转PROFIBUS网关:电动机保护新突破!

Modbus转PROFIBUS网关&#xff1a;电动机保护新突破&#xff01; 在现代工业自动化领域&#xff0c;Modbus RTU和PROFIBUS DP是两种常见且重要的通讯协议。它们各自具有独特的优势和应用场景&#xff0c;但在实际工程中&#xff0c;我们常常需要将这两种不同协议的设备进行互联…...

大数据应用开发和项目实战-Seaborn

设计目标 seaborn 建立在 matplotlib 之上&#xff0c;专注于统计数据可视化&#xff0c;简化绘图过程&#xff0c;提供高级接口和美观的默认主题 Seaborn的安装&#xff1a; 1.pip install seaborn -i 2.conda install seaborn &#xff08;清华源&#xff1a;https://pypi.t…...

弹窗探索鸿蒙之旅:揭秘弹窗的本质与奥秘

嘿&#xff0c;小伙伴们&#xff01;&#x1f44b; 今天我们要一起探索那些在日常应用中无处不在的小精灵——弹窗&#xff01;&#x1f4ac; &#x1f914; ‌弹窗到底是什么&#xff1f;‌ 简单来说&#xff0c;弹窗就是应用程序中突然冒出来的交互元素&#xff0c;它们像…...

“技术创新+全球视野”良性驱动,首航新能的2025新征程正式起航

撰稿 | 行星 来源 | 贝多财经 近日&#xff0c;备受瞩目的“2025年光伏第一股”深圳市首航新能源股份有限公司&#xff08;301658.SZ&#xff0c;下称“首航新能”&#xff09;对外发布了上市后的首份年报&#xff0c;交出了一份量质齐升的业绩答卷&#xff0c;构筑更加强大的…...

黑群晖Moments视频无缩略图,安装第三方ffmpeg解决

黑群晖Moments视频无缩略图&#xff0c;安装第三方ffmpeg解决 1. 设置套件来源 黑群晖Moments视频无缩略图&#xff0c;安装第三方ffmpeg解决 基于这个文章&#xff0c;补充一下&#xff1a; 1. 设置套件来源 设置套件来源时(http://packages.synocommunity.com)&#xff0c…...

工业控制「混合架构」PK大战 —— 神经网络 + MPC vs 模糊 PID+MPC 的场景选型与实战指南

1. 引言 在工业控制领域&#xff0c;传统的 PID 控制器因其结构简单、稳定性好而被广泛应用&#xff0c;但面对复杂非线性系统时往往力不从心。模型预测控制&#xff08;MPC&#xff09;作为一种基于模型的先进控制策略&#xff0c;能够有效处理多变量、多约束问题&#xff0c…...

树莓派智能摄像头实战指南:基于TensorFlow Lite的端到端AI部署

引言&#xff1a;嵌入式AI的革新力量 在物联网与人工智能深度融合的今天&#xff0c;树莓派这一信用卡大小的计算机正在成为边缘计算的核心载体。本文将手把手教你打造一款基于TensorFlow Lite的低功耗智能监控设备&#xff0c;通过MobileNetV2模型实现实时物体检测&#xff0…...

OpenCV 图形API(73)图像与通道拼接函数-----执行 查找表操作图像处理函数LUT()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 对矩阵执行查找表变换。 函数 LUT 使用来自查找表中的值填充输出矩阵。输入矩阵中的值作为查找表的索引。也就是说&#xff0c;函数对 src 中的…...

【MySQL】增删改查(CRUD)

目录 一. CRUD是什么 二. Create&#xff08;新增数据&#xff09; 2.1 单行数据全列插入 2.2 单行数据指定列插入 2.3 多行数据指定列插入 三. Retrieve &#xff08;检索/查询&#xff09; 3.1 全列查询 3.2 指定列查询 3.3 查询字段为表达式 3.4 为查询结果指定别名 3…...

iview 如何设置sider宽度

iview layout组件中&#xff0c;sider设置了默认宽度和最大宽度&#xff0c;在css样式文件中修改无效&#xff0c;原因是iview默认样式设置在了element.style中&#xff0c;只能通过行内样式修改 样式如下&#xff1a; image.png image.png 修改方式&#xff1a; 1.官方文档中写…...

Unity URP RenderTexture优化(二):深度图优化

目录 前言&#xff1a; 一、定位深度信息 1.1&#xff1a;k_DepthStencilFormat 1.2&#xff1a;k_DepthBufferBits 1.3&#xff1a;_CameraDepthTexture精度与大小 1.4&#xff1a;_CameraDepthAttachment数量 二、全代码 前言&#xff1a; 在上一篇文章&#xff1a;Un…...

iview表单提交验证时,出现空值参数被过滤掉不提交的问题解决

如图所示 有时候在表单提交的时候 个别参数是空值&#xff0c;但是看提交接口的反馈 发现空值的参数根本没传 这是因为表单验证给过滤掉了空值&#xff0c;有时候如果空值传不传都不无所谓&#xff0c;那可以不用管&#xff0c;但如果就算是空值也得传的吗&#xff0c;那就需要…...

GEO vs SEO:从搜索引擎到生成引擎的优化新思路

随着人工智能技术的快速发展&#xff0c;生成引擎优化&#xff08;GEO&#xff09;作为一种新兴的优化策略&#xff0c;逐渐成为企业和内容创作者关注的焦点。与传统的搜索引擎优化&#xff08;SEO&#xff09;相比&#xff0c;GEO不仅关注如何提升内容在搜索结果中的排名&…...

Python-pandas-操作csv文件(读取数据/写入数据)及csv语法详细分享

Python-pandas-操作csv文件(读取数据/写入数据) 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是pandas的使用语法。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系列文章】&#xff1a;每个…...

如何在Windows上实现MacOS中的open命令

在MacOS的终端中&#xff0c;想要快速便捷的在Finder中打开当前目录&#xff0c;直接使用oepn即可。 open . 但是Windows中没有直接提供类似open这样的命令&#xff0c;既然没有直接提供&#xff0c;我们就间接手搓一个实现它。 步骤1&#xff1a;创建open.bat echo OFF expl…...

读论文笔记-LLaVA:Visual Instruction Tuning

读论文笔记-LLaVA&#xff1a;Visual Instruction Tuning 《Visual Instruction Tuning》 研究机构&#xff1a;Microsoft Research 发表于2023的NeurIPS Problems 填补指令微调方法&#xff08;包括数据、模型、基准等&#xff09;在多模态领域的空白。 Motivations 人工…...

Vue3源码学习3-结合vitetest来实现mini-vue

文章目录 前言✅ 当前已实现模块汇总&#xff08;mini-vue&#xff09;✅ 每个模块简要源码摘要1. reactive.ts2. effect.ts3. computed.ts4. ref.ts5. toRef.ts6. toRefs.ts ✅ 下一阶段推荐目标所有核心模块对应的 __tests__ 测试文件&#xff0c;**带完整注释**✅ reactive.…...

K8S - 从零构建 Docker 镜像与容器

一、基础概念 1.1 镜像&#xff08;Image&#xff09; “软件的标准化安装包” &#xff0c;包含代码、环境和配置的只读模板。 技术解析 镜像由多个层组成&#xff0c;每层对应一个Dockerfile指令&#xff1a; 应用代码 → 运行时环境 → 系统工具链 → 启动配置核心特性…...

贪心算法求解边界最大数

贪心算法求解边界最大数&#xff08;拼多多2504、排列问题&#xff09; 多多有两个仅由正整数构成的数列 s1 和 s2&#xff0c;多多可以对 s1 进行任意次操作&#xff0c;每次操作可以置换 s1 中任意两个数字的位置。多多想让数列 s1 构成的数字尽可能大&#xff0c;但是不能比…...

C++类和对象(中)

类的默认成员函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数。一个类&#xff0c;我们不写的情况下编译器会默认生成6个默认成员函数&#xff0c;C11以后还会增加两个默认成员函数&#xff0c;移动构造和移动赋值。默认成员函数 很重要&#x…...

(Go Gin)Gin学习笔记(五)会话控制与参数验证:Cookie使用、Sessions使用、结构体验证参数、自定义验证参数

1. Cookie介绍 HTTP是无状态协议&#xff0c;服务器不能记录浏览器的访问状态&#xff0c;也就是说服务器不能区分两次请求是否由同一个客户端发出Cookie就是解决HTTP协议无状态的方案之一&#xff0c;中文是小甜饼的意思Cookie实际上就是服务器保存在浏览器上的一段信息。浏览…...

Windows 10 环境二进制方式安装 MySQL 8.0.41

文章目录 初始化数据库配置文件注册成服务启停服务链接服务器登录之后重置密码卸载 初始化数据库 D:\MySQL\MySQL8.0.41\mysql-8.0.41-winx64\mysql-8.0.41-winx64\bin\mysqld -I --console --basedirD:\MySQL\MySQL8.0.41\mysql-8.0.41-winx64\mysql-8.0.41-winx64 --datadi…...

Day.js一个2k轻量级的时间日期处理库

dayjs介绍 dayjs是一个极简快速2kB的JavaScript库&#xff0c;可以为浏览器处理解析、验证、操作和显示日期和时间&#xff0c;它的设计目标是提供一个简单、快速且功能强大的日期处理工具&#xff0c;同时保持极小的体积&#xff08;仅 2KB 左右&#xff09;。 Day.js 的 API…...

SQL实战:05之间隔连续数问题求解

概述 最近刷题时遇到一些比较有意思的题目&#xff0c;之前多次遇到一些求解连续数的问题&#xff0c;这次遇到了他们的变种&#xff0c;连续数可以间隔指定的数也视为是一个完整的“连续”。针对连续数的这类问题我们之前讲的可以利用等差数列的思想来解决&#xff0c;然而现…...

Windows下Dify安装及使用

Dify安装及使用 Dify 是开源的 LLM 应用开发平台。提供从 Agent 构建到 AI workflow 编排、RAG 检索、模型管理等能力&#xff0c;轻松构建和运营生成式 AI 原生应用。比 LangChain 更易用。 前置条件 windows下安装了docker环境-Windows11安装Docker-CSDN博客 下载 Git下载…...

回归分析丨基于R语言复杂数据回归与混合效应模型【多水平/分层/嵌套】技术与代码

回归分析是科学研究特别是生态学领域科学研究和数据分析十分重要的统计工具&#xff0c;可以回答众多科学问题&#xff0c;如环境因素对物种、种群、群落及生态系统或气候变化的影响&#xff1b;物种属性和系统发育对物种分布&#xff08;多度&#xff09;的影响等。纵观涉及数…...

EasyRTC嵌入式音视频实时通话SDK技术,打造低延迟、高安全的远程技术支持

一、背景 在当今数字化时代&#xff0c;远程技术支持已成为解决各类技术问题的关键手段。随着企业业务的拓展和技术的日益复杂&#xff0c;快速、高效地解决远程设备与系统的技术难题变得至关重要。EasyRTC作为一款高性能的实时通信解决方案&#xff0c;为远程技术支持提供了创…...

webrtc ICE 打洞总结

要搞清webrtc ICE连接是否能成功 &#xff0c; 主要是搞懂NAT NAT 类型 简单来说 一 是本地的ip和端口 决定外部的 ip和端口(和目的Ip和端口无关) &#xff0c; &#xff08;这种情况又分为 &#xff0c; 无限制&#xff0c;仅限制 ip &#xff0c; 限制ip和port , 也就是…...

AI开发者的Docker实践:汉化(中文),更换镜像源,Dockerfile,部署Python项目

AI开发者的Docker实践&#xff1a;汉化&#xff08;中文&#xff09;&#xff0c;更换镜像源&#xff0c;Dockerfile&#xff0c;部署Python项目 Dcoker官网1、核心概念镜像 (Image)容器 (Container)仓库 (Repository)DockerfileDocker Compose 2、Docker 的核心组件Docker 引擎…...

4.30阅读

一. 原文阅读 Passage 7&#xff08;推荐阅读时间&#xff1a;6 - 7分钟&#xff09; In department stores and closets all over the world, they are waiting. Their outward appearance seems rather appealing because they come in a variety of styles, textures, and …...

区块链:跨链协的技术突破与产业重构

引言&#xff1a;区块链的“孤岛困境”与跨链的使命 区块链技术自诞生以来&#xff0c;凭借去中心化、透明性和安全性重塑了金融、供应链、身份认证等领域。然而&#xff0c;不同区块链平台间的​​互操作性缺失​​&#xff0c;如同“数据与价值的孤岛”&#xff0c;严重限制…...

Github 热点项目 Qwen3 通义千问全面发布 新一代智能语言模型系统

阿里云Qwen3模型真是黑科技&#xff01;两大模式超贴心——深度思考能解高数题&#xff0c;快速应答秒回日常梗。支持百种语言互译&#xff0c;跨国客服用它沟通零障碍&#xff01;打工人福音是内置API工具&#xff0c;查天气做报表张口就来。字&#xff09; 1Qwen3 今日星标 …...

有状态服务与无状态服务:差异、特点及应用场景全解

有状态服务和无状态服务是在分布式系统和网络编程中常提到的概念&#xff0c;下面为你详细介绍&#xff1a; 一、无状态服务 无状态服务指的是该服务的单次请求处理不依赖之前的请求信息&#xff0c;每个请求都是独立的。服务端不会存储客户端的上下文信息&#xff0c;每次请…...

【网络入侵检测】基于源码分析Suricata的引擎日志配置解析

【作者主页】只道当时是寻常 【专栏介绍】Suricata入侵检测。专注网络、主机安全&#xff0c;欢迎关注与评论。 1. 概要 &#x1f44b; Suricata 的引擎日志记录系统主要记录该引擎在启动、运行以及关闭期间应用程序的相关信息&#xff0c;如错误信息和其他诊断信息&#xff0c…...

Attention层的FLOPs计算

前置知识 设矩阵 A 的维度为 mn&#xff0c;矩阵 B 的维度为 np&#xff0c;则它们相乘后得到矩阵 C 的维度为 mp。其中&#xff0c;C 中每个元素的计算需要进行 n 次乘法和 n−1 次加法。也就是说&#xff0c;总的浮点运算次数&#xff08;FLOPs&#xff09;约为 m p (2n) …...