超声波测距系统

文章目录

  • 前言
  • 一、功能描述
    • 一、界面一
    • 二、界面二
    • 三、界面三
    • 四、界面四
    • 五、初始界面
  • 二、编程实现


前言

  具有测距、温度补充、实时时钟、记忆、阈值警报、串口数据发送等等功能,通过LCD1602显示,按键进行相关操作。

一、功能描述

  LCD1602显示共有五个界面,按键一用于切换显示界面。

一、界面一

  此界面测距为连续测距模式,LCD1602不间断刷新测量距离和温度,一旦测量距离小于设置的阈值,单片机将会发出警报。此界面按下按键4可进入与上位机连接状态,当上位机发送1至单片机后,mcu立刻将测量数据发送至上位机。

二、界面二

  此界面为阈值设置界面,按键二用于改变设置位(上限还是下限),设置位会闪烁显示,以此提示。按键三四用于加减。

三、界面三

  此界面测距为单次测距模式,按键二按下测量一次,按键三按下则将当前测量距离和测量时间(如23:59:45时测量距离为45cm)保存到EEPROM里面,最多可以记录十次,第十一次将会覆盖第一次数据。

四、界面四

  此界面测距为回放模式,按下按键二三切换回放序号,LCD1602会显示在界面三时记录的距离和测量时间。

五、初始界面

  上电后,LCD1602会显示DS1302的初始时间设置,此时需要通过按键二三四进行时间设置。设置完成后,按下按键一,系统进入界面一,开始工作。

二、编程实现

  核心代码main.c如下,项目工程见超声波测距系统
  各模块原理和代码见我的CSDN博客专栏单片机

#include "main.h"
typedef unsigned char u8;
//P2用于选择P0输出通道,P0输出数据
#define outputp0(y,x) P0=x,P2&=0x1f,P2=y,P2&=0x1f;
//按键值,按键值缓存值
u8 kbdnum=0,kbdtemp=0;
//显示页面值,距离阈值设置选择位,保存次数值,时间设置选择位,测量数据保存起始地址;
u8 show_page=0,distance_set_flag=0,save_times=1,set_time_flag=0,save_distance_addr=0x04;
//测量阈值上限,测量阈值下限,LCD闪烁位
int distance_h=350,distance_l=6,LCD_showflag=0;
//测量阈值上下限数组,十六进制,便于进行保存到EEPROM或从EEPROM读取的操作
u8 distance_limit[4]={0};
//测量温度
float temper=0;
//时间保存中间量
u8 time_s_temp=0;
//主函数延时函数,t ms
void main_Delay1ms(int t)	//@11.0592MHz
{unsigned char data i, j;while(t--){_nop_();_nop_();_nop_();i = 11;j = 190;do{while (--j);} while (--i);}
}/**  @brief     按键值读取工作函数*  @param    *  @reval      *  @note:    
*/void work_kbd()
{if(kbdnum)   //如果有按键按下{if(kbdnum==13)   //key1按下{show_page++;show_page%=4;   //切换显示界面if(show_page==0)    //如果为界面0,连续测距模式{TR0=0;temper=rd_temper();TR0=1;work_ultra();  //界面0LCD_ShowString(1,1,"Distance:   . CM");  //显示相应界面LCD_ShowString(2,1,"Temper:    .  C ");}else if(show_page==1)   //如果为界面1,测量阈值设置模式{outputp0(0xa0,0x00);    //关闭蜂鸣器,防止在界面0警报状态下进入界面1导致蜂鸣器一直响distance_set_flag=0;LCD_showflag=0;    //复位距离阈值设置位和LCD闪烁位LCD_ShowString(1,1,"High:   CM      ");  //显示相应界面LCD_ShowString(2,1,"Low:   CM       ");}else if(show_page==2)  //如果为界面2,逐次测量模式{			save_distance_addr=4;save_times=1;  //复位数据保存起始地址和保存次数LCD_ShowString(1,1,"Distance:   . CM");   //显示相应界面LCD_ShowString(2,1,"Time:  -  -     ");}else if(show_page==3)   //如果为界面3,回放模式{save_distance_addr=4;save_times=1;  //复位数据保存起始地址和保存次数LCD_ShowString(1,1,"Save_Dis:   . CM");  //显示相应界面LCD_ShowString(2,1,"Time:  -  -     ");TR0=0;at24c02_read_multi(read_time,save_distance_addr,3);distance=at24c02_read(save_distance_addr+3)*256+at24c02_read(save_distance_addr+4);TR0=1;   //将第一次保存数据读取出来}}else if(kbdnum==14)  //key2按下{if(show_page==1)   //如果是界面1,测量阈值设置模式distance_set_flag=!distance_set_flag;  //改变距离阈值设置位,用于切换选择设置上限或下限else if(show_page==2)  //如果是界面2,逐次测量模式{				TR0=0;temper=rd_temper();TR0=1;work_ultra();    //执行一次距离测量}	else if(show_page==3)  //如果是界面3,回放模式{if(save_distance_addr<=48)  //用于选取下一个数据保存地址{save_distance_addr+=5;save_times++;}else if(save_distance_addr>48)  //超出数据保存最大地址,则返回最小地址{save_distance_addr=4;save_times=1;}TR0=0;     //将保存数据读取出来at24c02_read_multi(read_time,save_distance_addr,3);distance=at24c02_read(save_distance_addr+3)*256+at24c02_read(save_distance_addr+4);	TR0=1;				}	}else if(kbdnum==15) //key3按下{if(show_page==1)  //如果是界面1,测量阈值设置模式{if(distance_set_flag==0)  //如果是设置距离上限{distance_h++;    //加一if(distance_h>350)   //防超过最大值distance_h--;}else if(distance_set_flag==1)  //如果是设置距离下限{distance_l++;     //加一if(distance_l>=distance_h)  //防超过距离上限distance_l--;}	}else if(show_page==2)  //如果是界面2,逐次测量模式,将测量数据进行保存{TR0=0;     time_s_temp=read_time[0];read_time[0]=read_time[2]; //用于DS1302读取时间顺序为秒分时,而人习惯为时分秒,因此将秒与时交换位置read_time[2]=time_s_temp;at24c02_write_multi_page(read_time,save_distance_addr,3); //保存测量时间save_distance_addr+=3;at24c02_write(save_distance_addr++,distance/256);  //保存测量距离at24c02_delay5ms();at24c02_write(save_distance_addr++,distance%256);	 //保存测量距离TR0=1;				if(save_distance_addr>=54)  //数据读取地址达到最大,返回最小地址save_distance_addr=4;save_times++;     //读取的数据对应的保存次序if(save_times>10)  //如果次序超过最大次数10,则返回第一次save_times=1;}	else if(show_page==3)  //如果是界面3,回放模式{if(save_distance_addr>=9)   {save_distance_addr-=5;  //读取上一次保存数据save_times--;}else if(save_distance_addr<9)  //如果读取数据地址达到最小地址,则返回最大地址{save_distance_addr=49;save_times=10;}TR0=0;    //将数据读取出来at24c02_read_multi(read_time,save_distance_addr,3);distance=at24c02_read(save_distance_addr+3)*256+at24c02_read(save_distance_addr+4);	TR0=1;				}}else if(kbdnum==16)  //key4按下{if(show_page==0)    //如果是界面0,连续测距模式{LCD_ShowString(1,1,"Please connect  ");   //等待与上位机连接LCD_ShowString(2,1,"  to computer!  ");while(receivebit==0);										  //一旦连接for(save_distance_addr=4;save_distance_addr<=53;save_distance_addr++){sendbit(at24c02_read(save_distance_addr));  //将保存数据全部发送至上位机}LCD_ShowString(1,1,"Datas sent  OK! ");   //提示发送完成LCD_ShowString(2,1,"                ");	main_Delay1ms(3000);LCD_ShowString(1,1,"Distance:   . CM");   //回到连续测距显示LCD_ShowString(2,1,"Temper:    .  C ");	receivebit=0;	   //复位接收值}else if(show_page==1)    //如果是界面1,测量阈值设置模式{if(distance_set_flag==0)  //如果是设置距离上限{distance_h--;   //减一if(distance_h<=distance_l)   //防小于距离下限distance_h++;}else if(distance_set_flag==1)  //如果是设置距离下限{distance_l--;			//减一if(distance_l<6)   //防止小于最小值distance_l++;}				}else if(show_page==2)  //如果是界面2,逐次测距模式{show_page=0;  //切换回界面0,连续测距模式LCD_ShowString(1,1,"Distance:   . CM");   //显示相应界面LCD_ShowString(2,1,"Temper:    .  C ");				}		}if((kbdnum==15||kbdnum==16)&&show_page==1)  //如果在测量阈值设置模式改变了距离上限或下限的值,则将新值进行保存{distance_limit[0]=distance_h/256;  //将距离上下限转换为16进制distance_limit[1]=distance_h%256;distance_limit[2]=distance_l/256;distance_limit[3]=distance_l%256;TR0=0;at24c02_write_multi(distance_limit,0x00,4);  //一次写入多个数据TR0=1;}kbdnum=0;  //清零按键值}
}/**  @brief     界面0,连续测距模式*  @param    *  @reval      *  @note:    
*/void page_0()
{	TR0=0;temper=rd_temper();  //测量温度TR0=1;if(tultra>=60)  //每60ms测量一次{work_ultra();  //测量距离}if(distance>=(distance_l*10)&&distance<=(distance_h*10))   //如果在测量范围之内{LCD_ShowString(1,1,"distance:");  //正常显示测量距离LCD_ShowNum(1,10,distance/10,3);LCD_ShowNum(1,14,distance%10,1);LCD_ShowChar(1,13,'.');LCD_ShowString(1,15,"CM");		outputp0(0xa0,0x00);  //蜂鸣器关}else if(distance<(distance_l*10)||distance>(distance_h*10))  //如果超出测量范围{LCD_ShowString(1,1,"Error!          ");  //测量距离显示“ERROR!”outputp0(0xa0,0x40);  //蜂鸣器开}LCD_ShowSignedNum(2,8,temper,3);LCD_ShowNum(2,13,(int)(temper*100)%100,2);	//显示温度
}/**  @brief     界面1,测量阈值设置模式*  @param    *  @reval      *  @note:    
*/void page_1()
{if(LCD_showflag==0)   //如果LCD闪烁位为0,正常显示{LCD_ShowNum(1,6,distance_h,3);LCD_ShowNum(2,5,distance_l,3);}else if(LCD_showflag==1)  //如果LCD闪烁位为1,则清空相应数据显示位置{if(distance_set_flag==0)LCD_ShowString(1,6,"   ");else if(distance_set_flag==1)LCD_ShowString(2,5,"   ");}
}/**  @brief     界面2,逐次测距模式*  @param    *  @reval      *  @note:    
*/void page_2()
{TR0=0;DS1302_read(DS1302_write_addr);  //读取时间TR0=1;LCD_ShowNum(1,10,distance/10,3);   //显示相应数据LCD_ShowNum(1,14,distance%10,1);LCD_ShowNum(2,6,read_time[2]/16*10+read_time[2]%16,2);LCD_ShowNum(2,9,read_time[1]/16*10+read_time[1]%16,2);LCD_ShowNum(2,12,read_time[0]/16*10+read_time[0]%16,2);LCD_ShowNum(2,15,save_times,2);
}/**  @brief     界面3,回放模式*  @param    *  @reval      *  @note:    
*/void page_3()
{LCD_ShowNum(1,10,distance/10,3);  //显示相应数据LCD_ShowNum(1,14,distance%10,1);LCD_ShowNum(2,6,read_time[0]/16*10+read_time[0]%16,2);  //由于数据以16进制保存,因此需要转换为10进制LCD_ShowNum(2,9,read_time[1]/16*10+read_time[1]%16,2);LCD_ShowNum(2,12,read_time[2]/16*10+read_time[2]%16,2);LCD_ShowNum(2,15,save_times,2);	
}/**  @brief     界面显示*  @param    *  @reval      *  @note:    
*/void page_show()
{switch(show_page){case 0:page_0();break;case 1:page_1();break;case 2:page_2();break;case 3:page_3();break;}
}/**  @brief     上电时间初始化*  @param    *  @reval      *  @note:    
*/void set_time()
{outputp0(0x80,0xff);  //关闭8位LEDoutputp0(0xa0,0x00);		//关闭蜂鸣器继电器等LCD_Init();	     //LCD1602初始化rd_temper();   //由于DS18B20上电默认+85°,为了防止温度有极小时间错误显示,因此上电后即让DS18B20测量一次温度main_Delay1ms(1000);  //系统上电后延时一秒,确保各单位准备好工作Timer0_Init();  //定时器0初始化LCD_ShowString(1,1,"Please set time!");  //设置时间提示LCD_ShowString(2,1,"Time:  -  -     ");while(kbdnum!=13)   //如果key1按下,则退出时间设置{	if(kbdnum==14)   //如果key2按下{set_time_flag++;set_time_flag%=3; //时间设置选择位kbdnum=0;		  //清零按键值}else if(kbdnum==15) //如果key3按下{DS1302_write_time[set_time_flag]++;  //相应数据加一if(DS1302_write_time[set_time_flag]==24&&set_time_flag==2) //防止秒分时超出各自上限DS1302_write_time[set_time_flag]=0;else if(DS1302_write_time[set_time_flag]==60)DS1302_write_time[set_time_flag]=0;kbdnum=0;		  //清零按键值}else if(kbdnum==16) //如果key4按下{if(DS1302_write_time[set_time_flag]>=1)DS1302_write_time[set_time_flag]--;    //相应数据减一elseDS1302_write_time[set_time_flag]=59*(set_time_flag<2)+23*(set_time_flag==2); //防止秒分时小于各自下限kbdnum=0;		  //清零按键值}	if(LCD_showflag==0)   //如果LCD闪烁位为0,正常显示{LCD_ShowNum(2,6,DS1302_write_time[2],2);LCD_ShowNum(2,9,DS1302_write_time[1],2);LCD_ShowNum(2,12,DS1302_write_time[0],2);}else if(LCD_showflag==1)    //如果LCD闪烁位为1,则清空相应数据显示位置{switch(set_time_flag){case 0:LCD_ShowString(2,12,"  ");break;case 1:LCD_ShowString(2,9,"  ");break;case 2:LCD_ShowString(2,6,"  ");break;	}	}}if(kbdnum==13)  //key1按下{TR0=0;	for(set_time_flag=0;set_time_flag<3;set_time_flag++) //将设置时间从10进制转换为16进制DS1302_write_time[set_time_flag]=DS1302_write_time[set_time_flag]/10*16+DS1302_write_time[set_time_flag]%10;DS1302_set(DS1302_write_addr,DS1302_write_time);  //设置DS1302时间TR0=1;kbdnum=0;	  //清零按键值}	
}/**  @brief     系统初始化*  @param    *  @reval      *  @note:    
*/void init()
{Timer1_Init();  //定时器0初始化Uart1Init();    //串口1初始化LCD_ShowString(1,1,"distance:   CM  "); //上电默认界面0,显示相应界面LCD_ShowString(2,1,"Temper:    .  C ");//将保存的距离上下限读取并设置为距离阈值at24c02_read_multi(distance_limit,0x00,4);distance_h=distance_limit[0]*256+distance_limit[1];distance_l=distance_limit[2]*256+distance_limit[3];distance=distance_l*10+1;  //防止未开始测量即报警
}
void main()
{set_time();  //设置时间init();   //系统初始化receivebit=0;  //清零接收位while(1){work_kbd();  //按键读取page_show();   //界面显示}}void Timer0_Isr(void) interrupt 1
{static u8 kbd_longflag=0;      //矩阵键盘长按标志位,1长按0短按static int kbd_short_t=0,kbd_t=0,kbd_long_t=0,tlcd=0;TL0 = 0x66;				//设置定时初始值TH0 = 0xFC;				//设置定时初始值kbd_short_t++;          //矩阵键盘按键短按时间计数if(kbd_short_t>=(500*kbd_longflag+25*(!kbd_longflag)))   //短按每25ms检测一次按键{if(!(colu1&colu2&colu3&colu4))     //长按检测,每25ms检测矩阵键盘按键是否仍在按下,如果是{kbd_t++;						//矩阵键盘按键按下时间计数(以25ms为单位)if(kbd_t>=80)       //如果持续按下2s{kbd_t=80;         //防止溢出kbd_longflag=1;           //切换为长按模式}}else if(colu1&colu2&colu3&colu4)		//长按检测,每25ms检测按键是否仍在按下,如果不是{kbd_t=0;							//清零按键按下时间计数kbd_longflag=0;								//切换为短按模式}kbd_short_t=0;             //清零短按时间计数if(kbd_longflag==0)         //如果是短按模式{kbdtemp=kbd_send(0);if(kbdtemp)   //按键以短按模式检测kbdnum=kbdtemp;}}kbd_long_t++;           //按键长按时间计数if(kbd_long_t>=500)     //长按时每500ms检测一次按键{kbd_long_t=0;						  //清零按键长按时间计数if(kbd_longflag==1)         //如果是长按模式{if(kbd_send(1))			  //按键以长按模式检测kbdnum=kbd_send(1);}}tultra++; //超声波工作时间间隔计数tlcd++;   //LCD闪烁位时间计数if(tlcd>=500){tlcd=0;LCD_showflag=!LCD_showflag;  //实现LCD1602闪烁显示}
}

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

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

相关文章

第1章 Kali Linux入门

本章将带领读者初步了解渗透测试专用的独立Linux 操作系统——Kali Linux。本章涵盖下述主题&#xff1a; ● Kali 的发展简史&#xff1b; ● Kali 的一般用途&#xff1b; ● Kali 的下载与安装&#xff1b; ● Kali 的配置与更新。 在本章的结尾部分&#xff0c;我们还…

【进阶】【JS逆向爬虫】【2.JavaScript 基础语法】JS代码导入方式

JS逆向爬虫 JS代码导入方式1.行内式写法2.内嵌式&#xff08;建议写在</body>之前&#xff09;3.外部式&#xff08;建议写在</head>之前&#xff09; JS代码导入方式 1.行内式写法 可以将单行或少量 JS 代码写在HTML标签的事件属性中&#xff08;以 on 开头的属…

centos安装Docker和DockerCompose

Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10&#xff0c; CentOS 7 满足最低内核的要求&#xff0c;所以我们在CentOS 7安装Docker。 安装Docker 卸载旧版docker 旧版本的 Docker 采用docker或docker-engine。在尝试安装新版本之前卸载任何此…

GitHub提交项目到仓库fatal: No configured push destination.

原因&#xff1a;没指定提交到哪个远程仓库 解决方法&#xff1a; 在你git add .和commit之后 git add . git commit -m "信息" git push前输入以命令 git branch -M main git remote add origin gitgithub.com:xxx(你GitHub的名称)/xxx.git(你仓库的名称) git …

【计算机网络】第五,六章摘要重点

1.运输层协议概述 运输层提供的是进程之间的通信 2. 3.套接字指的是什么 ip地址端口号 4.每一条TCP语句唯一地被通信两端连接的两个端点 5.TCP传输如何实现 以字节为单位的滑动窗口 超时重传 选择确认 6.TCP流量控制和拥塞控制的区别 流量控制&#xff1a;点对点通信…

Cisco模拟器-OSPF路由协议

设计要求用两台双口路由器连接不同IP网段的计算机&#xff0c;并使用OSFP协议发现路由表使不同IP网段的计算机可以相互通信。 通过设计&#xff0c;可以连通IP地址网段不同的局域网&#xff0c;可应用在园区网的互连和互通的实现上。 主要配置步骤 路由器0&#xff1a; Router…

【PostgreSQL】从零开始:(三十九)约束-主键

主键 主键&#xff08;Primary Key&#xff09;是数据库表中用于唯一标识每一行记录的字段。主键具有以下特点&#xff1a; 唯一性&#xff1a;每个主键值在表中是唯一的&#xff0c;不允许出现重复值。非空性&#xff1a;主键字段的值不能为空&#xff0c;即主键字段不能为n…

四十八----react实战

一、项目中css模块化管理 1、css-loader 以下可以使用styles.xxx方式使用class是因为使用css-loader配置了module。 import styles from ./index.less export const App(){return <div className={styles.xxx}>hello word</div> }//webpack配置 {test:/\.css$/,u…

猫捉老鼠(C语言)

读题&#xff1a; 一只老鼠躲进了10个环形分布的洞的一个中。并按1,2,3,4,5,6,7,8,9,10进行编号&#xff0c;猫在第一个洞中没有找到老鼠&#xff0c;就隔一个洞查找&#xff0c;及到第三个洞去找&#xff1b;但没有找到&#xff0c;然后就隔两个洞继续寻找&#xff0c;及到第…

编程笔记 html5cssjs 017 HTML样式

编程笔记 html5&css&js 017 HTML样式 一、HTML样式二、CSS3小结 HTML样式是用来控制网页元素外观的一组属性和值。 一、HTML样式 可以通过以下几种方式来为HTML元素添加样式&#xff1a; 内联样式&#xff1a;直接在HTML元素的style属性中添加样式。例如&#xff1a;…

Spring java和go并发的实现策略

Spring Java框架和Go框架在处理并发请求时采用了不同的策略。 1. Spring Java框架&#xff1a; Spring框架基于Java语言&#xff0c;通常使用线程池来处理并发请求。具体来说&#xff0c;Spring框架中的Servlet容器&#xff08;如Tomcat、Jetty等&#xff09;会使用线程池来管…

24、Web攻防——通用漏洞SQL注入MYSQL跨库ACCESS偏移

文章目录 一、SQL注入原理   脚本代码在与数据库进行数据通讯时&#xff08;从数据库取出相关数据进行页面显示&#xff09;&#xff0c;使用预定义的SQL查询语句进行数据查询。能通过参数传递自定义值来实现SQL语句的控制&#xff0c;执行恶意的查询操作&#xff0c;例如查询…

C# 使用ZXing.Net生成带Logo的二维码

写在前面 这是ZXing.Net类库的系列文章&#xff0c;实现在二维码中间插入一个logo图标 C# 使用ZXing.Net生成二维码和条码-CSDN博客 C# 使用ZXing.Net识别二维码和条码-CSDN博客 代码实现 该段代码主体来自其他文章&#xff0c;贴在这做个记录 /// <summary> /// 生成…

【教程】Typecho Joe主题开启并修复壁纸相册不显示问题

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 背景说明 Joe主题本身支持“壁纸”功能&#xff0c;其实就是相册。当时还在网上找了好久相册部署的开源项目&#xff0c;太傻了。 但是网上教程很少&#xff0c;一没说如何开启壁纸功能&#xff0c;二没说开启后为…

2023年终总结:耕辍断续应犹在,碌而不暇仍少年

Author&#xff1a;AXYZdong 硕士在读 工科男 有一点思考&#xff0c;有一点想法&#xff0c;有一点理性&#xff01; 定个小小目标&#xff0c;努力成为习惯&#xff01;在最美的年华遇见更好的自己&#xff01; CSDNAXYZdong&#xff0c;CSDN首发&#xff0c;AXYZdong原创 唯…

【JavaWeb】反射与模块化(学习笔记)

一、类加载器 1、类加载 1&#xff09;类加载方式 JVM通过类的加载&#xff0c;类的连接&#xff0c;类的初始化对类进行初始化 类的加载&#xff1a;class文件读入内存&#xff0c;创建java.lang.Class对象 2&#xff09;类的连接 验证&#xff1a;检验类 准备&#xf…

SpringBoot简单整合mybatis

1. maven依赖 <!-- mybatis --> <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.1</version></dependency><!-- mysql驱动 -->&…

华为OD机试 - 螺旋数字矩阵(Java JS Python C)

题目描述 疫情期间,小明隔离在家,百无聊赖,在纸上写数字玩。他发明了一种写法: 给出数字个数 n (0 < n ≤ 999)和行数 m(0 < m ≤ 999),从左上角的 1 开始,按照顺时针螺旋向内写方式,依次写出2,3,....,n,最终形成一个 m 行矩阵。 小明对这个矩阵有些要求:…

精品Nodejs实现的校园疫情防控管理系统的设计与实现健康打卡

《[含文档PPT源码等]精品Nodejs实现的校园疫情防控管理系统的设计与实现[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; 操作系统&#xff1a;Windows 10、Windows 7、Win…