#循循渐进学51单片机#1602液晶与串口通信实例#not.12

1、将通信时序的逻辑理解透彻,并且能够独立看懂其他器件的时序图。

   随着我们对通信技术的深入学习,大家要逐渐在头脑中建立起时序这种概念。所谓“时序”从字面意义上来理解,一是“时间问题”,二是“顺序问题”。

先说“顺序问题”,这个相对简单一些。我们在学UART串口通信的时候,先1位起始位,再8位数据位,最后1位停止位,这个先后顺序不能错。我们在学1602液晶的时候,比如写指令,RS=L,R/W=L,D0~D7=指令码,这三者的顺序是无所谓的,但是最终的E=高脉冲,必须是在这三条程序之后,这个顺序一旦错误,写的数据也可会出错。

      “时间问题”内容相对复杂。比如UART通信,每一位的时间宽度是1/baud。我们初中就学过一个概念,世界上没有绝对的准确。那么每一位的时间宽度1/baud要求精确到什么范围内呢?

      前边教程我提到过,单片机读取UART的RXD引脚数据的时候,一位数据,单片机平均分成了16份,取其中的7、8、9三次读到的结果,这三次中有2次是高电平那这一位就是1,有2次是低电平,那这一次就是0。如果我们的波特率稍微有些偏差,只要累计下来到最后一位停止位,这7、8、9还在范围内即可。

2、根据1602整屏移动程序,改写成整屏右移的程序。

#include <reg52.h>#define LCD1602_DB  P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E  = P1^5;bit flag500ms = 0;   //500ms¶¨Ê±±êÖ¾
unsigned char T0RH = 0;  //T0ÖØÔØÖµµÄ¸ß×Ö½Ú
unsigned char T0RL = 0;  //T0ÖØÔØÖµµÄµÍ×Ö½Úunsigned char code str1[] = "Kingst Studio";
//´ýÏÔʾµÄµÚ¶þÐÐ×Ö·û´®£¬Ðè±£³ÖÓëµÚÒ»ÐÐ×Ö·û´®µÈ³¤£¬½Ï¶ÌµÄÐпÉÓÿոñ²¹Æë
unsigned char code str2[] = "Let's move...";void ConfigTimer0(unsigned int ms);
void InitLcd1602();
void LcdShowStr(unsigned char x, unsigned char y,unsigned char *str, unsigned char len);
void main()
{unsigned char i;unsigned char index = 0;  //Òƶ¯Ë÷Òýunsigned char pdata bufMove1[16+sizeof(str1)+16]; //Òƶ¯ÏÔʾ»º³åÇø1unsigned char pdata bufMove2[16+sizeof(str2)+16]; //Òƶ¯ÏÔʾ»º³åÇø2EA = 1;ConfigTimer0(10);InitLcd1602();for(i = 0;i <= 16;i++){bufMove1[i] = ' ';bufMove2[i] = ' ';}for(i = 0;i <= 16;i++){bufMove1[16+i] = str1[i];bufMove2[16+i] = str2[i];}for(i = 16+sizeof(str1)-1;i < sizeof(bufMove1);i++){bufMove1[i] = ' ';bufMove2[i] = ' ';}while(1){if(flag500ms){flag500ms = 0;index--;if(index < 0){index = 15 + sizeof(str1)-1;}LcdShowStr(0,0,bufMove1+index,16);LcdShowStr(0,1,bufMove2+index,16); }
}
}
void ConfigTimer0(unsigned int ms)
{unsigned long tmp;tmp = 11059200 / 12;tmp = tmp*ms/1000;tmp = 65536 - tmp;tmp += 12;T0RH = (unsigned char)(tmp>>8);T0RL = (unsigned char)tmp;TMOD &= 0X0F;TMOD |= 0X01;TH0 = T0RH;TL0 = T0RL;ET0 = 1;TR0 = 1;
}
void LcdWaitReady()
{unsigned int sec;LCD1602_DB = 0xff;LCD1602_RS = 0;LCD1602_RW = 1;do{LCD1602_E = 1;sec = LCD1602_DB;LCD1602_E = 0;}while(sec & 0x80);
}
void LcdWriteCmd(unsigned char cmd)
{LcdWaitReady();LCD1602_RS = 0;LCD1602_RW = 0;LCD1602_DB = cmd;LCD1602_E = 1;LCD1602_E = 0;
}
void LcdWriteDat(unsigned char dat)
{LcdWaitReady();LCD1602_RS = 1;LCD1602_RW = 0;LCD1602_DB = dat;LCD1602_E = 1;LCD1602_E = 0;
}
void LcdSetCursor(unsigned char x, unsigned char y)
{unsigned char add;if(y == 0){add = 0x00 + x;}elseadd = 0x40 + x;LcdWriteCmd(add | 0x80);
}
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str, unsigned char len)
{LcdSetCursor(x, y);if(len--){LcdWriteDat(*str++);}}
void InitLcd1602()
{LcdWriteCmd(0x38);LcdWriteCmd(0x0C);LcdWriteCmd(0x06);LcdWriteCmd(0x01);	
}
void InterruptTimer0() interrupt 1
{static unsigned char tmr500ms = 0;TH0 = T0RH;TL0 = T0RL;tmr500ms++;if(tmr500ms >= 50){tmr500ms = 0;flag500ms = 1;}
}


3、掌握多.c源文件编写代码的方法以及调用其他文件中变量和函数的方法。

 我们上一节的这个液晶滚屏移动程序,大概有160行左右。随着我们硬件模块使用的增多,程序量的增大,我们往往要把程序写到多个文件里,方便代码的编写、维护和移植。

      比如这个液晶滚屏程序,我们就可以把1602底层的功能函数专门写到一个.c文件内,         如LcdWaitReady、LcdWriteCmd、LcdWriteDat、LcdShowStr、LcdSetCursor、InitLcd1602这些函数,都是属于液晶底层驱动的程序代码,我们要使用液晶功能的时候,只有两个函数对我们实际功能实现部分有用,一个是InitLcd1602这个函数,因为需要先初始化液晶,另外一个就是LcdShowStr这个函数,我们只需要把要显示的内容通过参数传递给这个函数,这个函数就可以实现我们想要的显示效果,所以我们把这几个底层的液晶驱动程序都放到另外一个文件Lcd1602.c文件中,而我们想实现的一些比如滚动实现、中断等上层功能程序全部都放到main.c中,但是main.c文件如何调用Lcd1602.c文件中的函数呢?

      C语言中,有一个extern关键字,它有两个基本作用。

1、当一个变量的声明不在文件的开头,在它声明之前的函数想要引用的话,则应该用extern进行“外部变量”声明。用一个简单的程序给大家介绍一下,知道这么回事,能看懂别人写的就行,自己写就别这么用了。

    #include <reg52.h>

    sbit LED = P0^0;

    void  main()

    {

        extern unsigned int i;

        while(1)

        {

             LED = 0;                 //点亮小灯

             for(i=0;i<30000;i++);  //延时

             LED = 1;                 //熄灭小灯

             for(i=0;i<30000;i++); //延时

        }

    }

    unsigned  int  i = 0;

    ... ...

     变量的作用域,是从声明这个变量开始往后所有的程序,如果我们调用在前,声明在后,那么就是这么用。但是实际开发过程中,我们一般都不会这样做,所以仅仅是表达一下extern的这个用法,但它并不实用。

    2、在一个工程中,我们为了方便管理和维护代码,用了多个.c源文件,如果其中一个main.c文件要调用Lcd1602.c文件里的变量或者函数的时候,我们就必须得在main.c里边进行一下外部声明,告诉编译器这个变量或者函数是在其它文件中定义的,可以直接在这个文件中进行调用。

     多.c文件的编程方式,大家不要想象的太复杂。首先新建一个工程,一个工程代表一个完整的单片机程序,只能生成一个hex,但是一个工程可以有很多个.c源文件组成共同参与编译。工程建立好之后,新建文件并且保存取名为main.c文件,再新建一个文件并且保存取名为Lcd1602.c文件,下面我们就可以在两个不同文件中分别编写代码了。当然,在编写程序的过程中,不是说我们要先把main.c的文件全部写完,再进行1602.c程序的编写,而往往是交互的。比如我们先写Lcd1602.c文件中部分Lcd1602液晶的底层函数LcdWaitReady、LcdWriteCmd、LcdWriteDat、InitLcd1602,然后编写main.c文件中的功能程序,在编写main.c文件中程序时,又有对Lcd1602.c底层程序的综合调用,这个时候需要Lcd1602.c文件提供一个被调用的函数比如LcdShowStr,我们就可以再到Lcd1602.c中把这个函数完成。当然了,这仅仅是一个说明例子而已,顺序完全是没有一个标准的,实际应用中如果对程序逻辑需求了解透彻,根据自己的理解去写程序即可。那我们把1602整屏移动的程序改造成为多文件的程序,大家先初步认识一下。

/***************************Lcd1602.c文件程序源代码*****************************/

#include <reg52.h>

#define LCD1602_DB  P0

sbit LCD1602_RS = P1^0;

sbit LCD1602_RW = P1^1;

sbit LCD1602_E  = P1^5;

/* 等待液晶准备好 */

void LcdWaitReady()

{

    unsigned char sta;

    

    LCD1602_DB = 0xFF;

    LCD1602_RS = 0;

    LCD1602_RW = 1;

    do {

        LCD1602_E = 1;

        sta = LCD1602_DB;  //读取状态字

        LCD1602_E = 0;

    } while (sta & 0x80); //bit7等于1表示液晶正忙,重复检测直到其等于0为止

}

/* 向LCD1602液晶写入一字节命令,cmd-待写入命令值 */

void LcdWriteCmd(unsigned char cmd)

{

    LcdWaitReady();

    LCD1602_RS = 0;

    LCD1602_RW = 0;

    LCD1602_DB = cmd;

    LCD1602_E  = 1;

    LCD1602_E  = 0;

}

/* 向LCD1602液晶写入一字节数据,dat-待写入数据值 */

void LcdWriteDat(unsigned char dat)

{

    LcdWaitReady();

    LCD1602_RS = 1;

    LCD1602_RW = 0;

    LCD1602_DB = dat;

    LCD1602_E  = 1;

    LCD1602_E  = 0;

}

/* 设置显示RAM起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */

void LcdSetCursor(unsigned char x, unsigned char y)

{

    unsigned char addr;

    

    if (y == 0)  //由输入的屏幕坐标计算显示RAM的地址

        addr = 0x00 + x;  //第一行字符地址从0x00起始

    else

        addr = 0x40 + x;  //第二行字符地址从0x40起始

    LcdWriteCmd(addr | 0x80);  //设置RAM地址

}

/* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,

   str-字符串指针,len-需显示的字符长度 */

void LcdShowStr(unsigned char x, unsigned char y, 

                unsigned char *str, unsigned char len)

{

    LcdSetCursor(x, y);   //设置起始地址

    while (len--)          //连续写入len个字符数据

    {

        LcdWriteDat(*str++);

    }

}

/* 初始化1602液晶 */

void InitLcd1602()

{

    LcdWriteCmd(0x38);  //16*2显示,5*7点阵,8位数据接口

    LcdWriteCmd(0x0C);  //显示器开,光标关闭

    LcdWriteCmd(0x06);  //文字不动,地址自动+1

    LcdWriteCmd(0x01);  //清屏

}

/*****************************main.c文件程序源代码******************************/

#include <reg52.h>

bit flag500ms = 0;   //500ms定时标志

unsigned char T0RH = 0;  //T0重载值的高字节

unsigned char T0RL = 0;  //T0重载值的低字节

//待显示的第一行字符串

unsigned char code str1[] = "Kingst Studio";

//待显示的第二行字符串,需保持与第一行字符串等长,较短的行可用空格补齐

unsigned char code str2[] = "Let's move...";

void ConfigTimer0(unsigned int ms);

extern void InitLcd1602();

extern void LcdShowStr(unsigned char x, unsigned char y,

                       unsigned char *str, unsigned char len);

void main()

{

    unsigned char i;

    unsigned char index = 0;  //移动索引

    unsigned char pdata bufMove1[16+sizeof(str1)+16]; //移动显示缓冲区1

    unsigned char pdata bufMove2[16+sizeof(str2)+16]; //移动显示缓冲区2

    EA = 1;              //开总中断

    ConfigTimer0(10);  //配置T0定时10ms

    InitLcd1602();     //初始化液晶

    //缓冲区开头一段填充为空格

    for (i=0; i<16; i++)

    {

        bufMove1[i] = ' ';

        bufMove2[i] = ' ';

    }

    //待显示字符串拷贝到缓冲区中间位置

    for (i=0; i<(sizeof(str1)-1); i++)

    {

        bufMove1[16+i] = str1[i];

        bufMove2[16+i] = str2[i];

    }

    //缓冲区结尾一段也填充为空格

    for (i=(16+sizeof(str1)-1); i<sizeof(bufMove1); i++)

    {

        bufMove1[i] = ' ';

        bufMove2[i] = ' ';

    }

    

    while (1)

    {

        if (flag500ms)  //每500ms移动一次屏幕

        {

            flag500ms = 0;

            //从缓冲区抽出需显示的一段字符显示到液晶上

            LcdShowStr(0, 0, bufMove1+index, 16);

            LcdShowStr(0, 1, bufMove2+index, 16);

            //移动索引递增,实现左移

            index++;

            if (index >= (16+sizeof(str1)-1))

            {   //起始位置达到字符串尾部后即返回从头开始

                index = 0;

            }

        }

    }

}

/* 配置并启动T0,ms-T0定时时间 */

void ConfigTimer0(unsigned int ms)

{

    unsigned long tmp;  //临时变量

    

    tmp = 11059200 / 12;       //定时器计数频率

    tmp = (tmp * ms) / 1000;  //计算所需的计数值

    tmp = 65536 - tmp;       

    tmp = tmp + 12;            //补偿中断响应延时造成的误差

    T0RH = (unsigned char)(tmp>>8);  //定时器重载值拆分为高低字节

    T0RL = (unsigned char)tmp;

    TMOD &= 0xF0;   //清零T0的控制位

    TMOD |= 0x01;   //配置T0为模式1

    TH0 = T0RH;     //加载T0重载值

    TL0 = T0RL;

    ET0 = 1;        //使能T0中断

    TR0 = 1;        //启动T0

}

/* T0中断服务函数,定时500ms */

void InterruptTimer0() interrupt 1

{

    static unsigned char tmr500ms = 0;

    

    TH0 = T0RH;  //重新加载重载值

    TL0 = T0RL;

    tmr500ms++;

    if (tmr500ms >= 50)

    {

        tmr500ms = 0;

        flag500ms = 1;

    }

}

     我们在main.c中要调用Lcd1602.c文件中的InitLcd1602()和LcdShowStr这两个函数,只需要在main.c中进行extern声明即可。大家用Keil软件编程试试,真正的感觉一下多.c源文件的好处。

4、底理解实用串口通信机制程序,能够完全就解析明白实用串口通信例程,为今后自己独立编写类似程序打下基础。

我们前边学串口通信的时候,比较注重的是串口底层时序上的操作过程,所以例程都是简单的收发字符或者字符串。在实际应用中,往往串口还要和电脑上的上位机软件进行交互,实现电脑软件发送不同的指令,单片机对应执行不同操作的功能,这就要求我们组织一个比较合理的通信机制和逻辑关系,用来实现我们想要的结果。

      本节所提供程序的功能是,通过电脑串口调试助手下发三个不同的命令,第一条指令:buzz on可以让蜂鸣器响;第二条指令:buzz off可以让蜂鸣器不响;第三条指令:showstr ,这个命令空格后边,可以添加任何字符串,让后边的字符串在1602液晶上显示出来,同时不管发送什么命令,单片机收到后把命令原封不动的再通过串口发送给电脑,以表示“我收到了……你可以检查下对不对”。这样的感觉是不是更像是一个小项目了呢?

      对于串口通信部分来说,单片机给电脑发字符串好说,有多大的数组,我们就发送多少个字节即可,但是单片机接收数据,接收多少个才应该是一帧完整的数据呢?数据接收起始头在哪里,结束在哪里?这些我们在接收到数据前都是无从得知的。那怎么办呢?

      我们的编程思路基于这样一种通常的事实:当需要发送一帧(多个字节)数据时,这些数据都是连续不断的发送的,即发送完一个字节后会紧接着发送下一个字节,期间没有间隔或间隔很短,而当这一帧数据都发送完毕后,就会间隔很长一段时间(相对于连续发送时的间隔来讲)不再发送数据,也就是通信总线上会空闲一段较长的时间。于是我们就建立这样一种程序机制:设置一个软件的总线空闲定时器,这个定时器在有数据传输时(从单片机接收角度来说就是接收到数据时)清零,而在总线空闲时(也就是没有接收到数据时)时累加,当它累加到一定时间(例程里是30ms)后,我们就可以认定一帧完整的数据已经传输完毕了,于是告诉其它程序可以来处理数据了,本次的数据处理完后就恢复到初始状态,再准备下一次的接收。那么这个用于判定一帧结束的空闲时间取多少合适呢?它取决于多个条件,并没有一个固定值,我们这里介绍几个需要考虑的原则:第一,这个时间必须大于波特率周期,很明显我们的单片机接收中断产生是在一个字节接收完毕后,也就是一个时刻点,而其接收过程我们的程序是无从知晓的,因此在至少一个波特率周期内你绝不能认为空闲已经时间达到了。第二,要考虑发送方的系统延时,因为不是所有的发送方都能让数据严格无间隔的发送,因为软件响应、关中断、系统临界区等等操作都会引起延时,所以还得再附加几个到十几个ms的时间。我们选取的30ms是一个折中的经验值,它能适应大部分的波特率(大于1200)和大部分的系统延时(PC机或其它单片机系统)情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/139329.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】图的基本概念,图的存储结构(邻接矩阵;邻接表;十字链表;邻接多重表)

欢~迎~光~临~^_^ 目录 1、图的基本概念 2、图的存储结构 2.1邻接矩阵 2.2邻接表 2.3十字链表 2.4邻接多重表 2.5图的四种存储结构的对比 1、图的基本概念 图是由一组节点&#xff08;通常称为顶点&#xff09;和一组连接这些节点的边&#xff08;通常称为边&#xff0…

Linux中sudo命令的添加和操作

使用 sudo分配权限 &#xff08;1&#xff09;修改/etc/sudoers 文件分配文件 # chmod 740 /etc/sudoers # vi /etc/sudoers 找到这行&#xff1a;root ALL(ALL) ALL, 在这行下面添加 xxx ALL(ALL) ALL (这里的xxx就是你的普通用户&#xff0c;而ruice就是我的普通用户 ) 编…

外汇天眼:外汇交易市场与股票交易市场优势对比!

在纽约证券交易所上市的股票大约有2800多只。纳斯达克证券交易所还列出了另外3,300多家股票。您将交易哪一个&#xff1f;有时间留在这么多公司的头上吗&#xff1f;在外汇交易中&#xff0c;有数十种货币交易&#xff0c;但是大多数市场参与者交易了七种主要货币对。难道七个主…

微信开放平台第三方开发,实现代小程序备案申请

大家好&#xff0c;我是小悟 微信小程序备案整体流程总共分为五个环节&#xff1a;备案信息填写、平台初审、工信部短信核验、通管局审核和备案成功。 服务商可以代小程序发起备案申请。在申请小程序备案之前&#xff0c;需要确保小程序基本信息已填写完成、小程序至少存在一个…

如何利用播放器节省20%点播成本

点播成本节省的点其实涉及诸多部分&#xff0c;例如&#xff1a;CDN、转码、存储等&#xff0c;而利用播放器降本却是很多客户比较陌生的部分。火山引擎基于内部支撑抖音集团相关业务的实践&#xff0c;播放器恰恰是成本优化中最重要和最为依赖的部分。 火山引擎的视频团队做了…

华为云云耀云服务器L实例评测|Docker版的Minio安装 Springboot项目中的使用 结合vue进行图片的存取

前言 最近华为云云耀云服务器L实例上新&#xff0c;也搞了一台来玩&#xff0c;期间遇到过MySQL数据库被攻击的情况&#xff0c;Redis被攻击的情况&#xff0c;教训是密码不能太简单。在使用服务器时&#xff0c;学习到很多运维相关的知识。 本篇博客介绍如何在Linux中安装mi…

IP协议的相关特性

文章目录 一.IP协议二. IP地址不够用了?1. 动态分配IP(DHCP)2. NAT机制(网络地址转换)(理解网络结构的关键要点)3. IPv64. 为什么IPv6不如NAT受用? 二. IP组成三. 路由转发(了解) 一.IP协议 概念 IP地址&#xff08;Internet Protocol Address&#xff09;是指互联网协议地…

FL Studio21水果编曲软件怎么下载中文版?

FL Studio21这款软件在国内被广泛使用&#xff0c;因此又被称为"水果"。它提供音符编辑器&#xff0c;可以针对作曲者的要求编辑出不同音律的节奏&#xff0c;例如鼓、镲、锣、钢琴、笛、大提琴、筝、扬琴等等任何乐器的节奏律动。此外&#xff0c;它还提供了方便快捷…

代码随想录算法训练营第57天| 647. 回文子串,516.最长回文子序列,动态规划总结

链接: 647. 回文子串 链接: 516.最长回文子序列 链接: 动态规划总结 647. 回文子串 理解dp数组的含义很重 class Solution {public int countSubstrings(String s) {char[] chars s.toCharArray();boolean[][] dp new boolean[s.length()][s.length()];int res 0;// 遍…

目标检测:Edge Based Oriented Object Detection

论文作者&#xff1a;Jianghu Shen,Xiaojun Wu 作者单位&#xff1a;Harbin Institute of Technology Shenzhen 论文链接&#xff1a;http://arxiv.org/abs/2309.08265v1 内容简介&#xff1a; 1&#xff09;方向&#xff1a;遥感领域中的目标检测技术 2&#xff09;应用&…

购物H5商城架构运维之路

一、引言 公司属于旅游行业&#xff0c;需要将旅游&#xff0c;酒店&#xff0c;购物&#xff0c;聚合到线上商城。通过对会员数据进行聚合&#xff0c;形成大会员系统&#xff0c;从而提供统一的对客窗口。 二、业务场景 围绕更加有效地获取用户&#xff0c;提升用户的LTV&a…

Python线程和进程

1、深度解析Python线程和进程 一篇文章带你深度解析Python线程和进程 - 知乎使用Python中的线程模块&#xff0c;能够同时运行程序的不同部分&#xff0c;并简化设计。如果你已经入门Python&#xff0c;并且想用线程来提升程序运行速度的话&#xff0c;希望这篇教程会对你有所帮…

AI写作宝-为什么要使用写作宝

写作一直是一项需要创造力和思考的任务&#xff0c;人工智能&#xff08;AI&#xff09;正逐渐成为我们写作过程中的一位新伙伴。AI写作宝等在线AI写作工具正日益普及&#xff0c;为我们提供了更多的写作选择和可能性。 AI写作宝&#xff1a;什么是它们&#xff0c;以及它们能做…

【计算机网络】——传输层

//图片取自王道&#xff0c;仅做交流学习 一、传输层提供的服务 物理层、数据链路层、网络层是通信子网。 传输层&#xff1a;它属于面向通信部分的最高层&#xff0c;同时也是用户功能的最低层 为应用层提供通信服务使用网络层的服务 网络层提供主机之间的逻辑通信。 1、传输…

数据结构——八叉树

八叉树&#xff08;Octree&#xff09;是一种用于表示和管理三维空间的树状数据结构。它将三维空间递归地分割成八个八分体&#xff08;octant&#xff09;&#xff0c;每个八分体可以继续分割&#xff0c;以实现对三维空间的更精细的划分。八叉树通常用于解决空间搜索和查询问…

GitHub Copilot Chat

9月21日&#xff0c;GitHub在官网宣布&#xff0c;所有个人开发者可以使用GitHub Copilot Chat。用户通过文本问答方式就能生成、检查、分析各种代码。 据悉&#xff0c;GitHub Copilot Chat是基于OpenAI的GPT-4模型打造而成&#xff0c;整体使用方法与ChatGPT类似。例如&…

菜单栏图标管理软件Bartender mac 5.0.10中文版介绍

Bartender mac是一款菜单栏图标管理软件&#xff0c;功能强大&#xff0c;可以快速管理菜单栏的图标、显示内容和时间&#xff0c;只需在菜单栏中滑动或滚动、单击菜单栏&#xff0c;或者如果您愿意&#xff0c;只需将鼠标悬停即可立即访问隐藏的菜单栏项目。 Bartender软件介绍…

SAP 选择屏幕动态通过Radio Button 显示与隐藏以及控制是否必输

如何在选择屏幕上进行动态展示屏幕字段&#xff0c;并且进行必输项检查控制 1. 选择屏幕定义 SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.SELECTION-SCREEN BEGIN OF LINE.PARAMETERS: p_r1 TYPE c RADIOBUTTON GROUP grp USER-COMMAND uc DEFAULT X. &q…

基于Java+SpringBoot+Vue物流管理小程序系统的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

FPGA 图像缩放 千兆网 UDP 网络视频传输,基于B50610 PHY实现,提供工程和QT上位机源码加技术支持

目录 1、前言版本更新说明免责声明 2、相关方案推荐UDP视频传输--无缩放FPGA图像缩放方案我这里已有的以太网方案 3、设计思路框架视频源选择IT6802解码芯片配置及采集动态彩条跨时钟FIFO图像缩放模块详解设计框图代码框图2种插值算法的整合与选择 UDP协议栈UDP视频数据组包UDP…