Zigbee +PC上位机 无线控制二维云台开发笔记

今日尝试开发一款简单好学的PC上位机无线控制二维云台的小试验品:

主要开发环境与工具介绍:

 单片机 STM32F103C8T6 使用标准库函数编程

 Visual Studio 2022软件C# Winform 开发 上位机控制软件

 DL_20 无线串口模块 + USB-TTL 模块 实现无线通信功能

文章提供完整代码解释、设计点解释、测试效果图、完整工程下载

目录

主要用到的知识如下:

C# Winform上位机的编程:

窗体设计:

Form1初始化:

打开串口 控件函数:

串口接收 控件函数:

串口发送 控件函数:

头部/底部开始移动 控件函数:

一键归位 控件函数:

测试连接 控件函数:

创建日志委托 函数:

清除日志区 控件函数:

注意事项:

C# Winform 整体测试工程下载:

STM32F10xx 单片机的编程:

OLED的驱动显示:

PWM控制舵机运动:

初始化TIM3为舵机控制定时器:

设置TIM3占空比控制舵机运转:

串口接收与串口中断服务函数的编写:

STM32F103C8T6测试工程下载:

测试视频与图片:


主要用到的知识如下:

DL_20无线串口模块_dl20无线串口模块-CSDN博客

C#学习笔记10:winform上位机与西门子PLC网口通信_中篇_winform的窗口操作设计、日志的添加使用_c#网口通信界面-CSDN博客

C# Winform上位机的编程:

窗体设计:

主要用到的控件有Listview、imaginelist、button、checkbox、combobox、label 、serialport

Form1初始化:

       //创建这个窗体的addlog ,需要绑定一个实际方法
       private AddLog myaddlog;
       bool Form1_FClosing = false;//用于防止二次Form1_FormClosing()事件发生的
       string formattedLogMessage; //用于临时拼接字符串
       bool OPEN_SERIAL_flag = false;//打开串口标志 false:未打开
       int angle;                  //角度

       public Form1()
       {
           InitializeComponent();
           this.Load += Form1_Load;
           myaddlog = this.AddLog;//绑定方法
           serialPort1.Encoding = Encoding.GetEncoding("GB2312");     //串口接收编码
           Control.CheckForIllegalCrossThreadCalls = false;
       }

       private void Form1_Load(object sender, EventArgs e)
       {
           设置第一列的宽度=整个宽度 减去 第0页宽度
           lstInfo.Columns[1].Width = lstInfo.ClientSize.Width - lstInfo.Columns[0].Width;

           for (int i = 1; i < 10; i++)//初始化串口 号下拉框内容
           {
               comboBox4.Items.Add("COM" + i.ToString()); //添加串口
           }

           for (int H = 0; H < 5; H++)//初始化串口 波特率下拉框内容
           {
               switch (H)
               {
                   case 0: comboBox5.Items.Add("2400"); break;
                   case 1: comboBox5.Items.Add("4800"); break;
                   case 2: comboBox5.Items.Add("9600"); break;
                   case 3: comboBox5.Items.Add("115200"); break;
               }
           }

           //停止位 下拉框内容
           for (int j = 0; j < 3; j++)
           {
               switch (j)
               {
                   case 0: comboBox7.Items.Add("1"); break;
                   case 1: comboBox7.Items.Add("1.5"); break;
                   case 2: comboBox7.Items.Add("2"); break;
               }
           }

           comboBox4.Text = "COM1";//端口下拉框初始值
           comboBox5.Text = "9600";//波特率下拉框初始值
           comboBox7.Text = "1";//停止位
           comboBox6.Text = "8";//数据位

           serialPort1.Close();   //关闭串行端口连接
       }

打开串口 控件函数:


        //打开/关闭串口
        private void button6_Click(object sender, EventArgs e)
        {
            if(OPEN_SERIAL_flag==false)
            {
                try
                {
                    serialPort1.PortName = comboBox4.Text;//设置端口号
                    serialPort1.BaudRate = Convert.ToInt32(comboBox5.Text);//设置端口波特率
                    serialPort1.StopBits = (StopBits)Convert.ToInt32(comboBox7.Text);//设置停止位
                    serialPort1.DataBits = Convert.ToInt32(comboBox6.Text);//设置数据位
                    serialPort1.ReceivedBytesThreshold = 1;
                    serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
                    serialPort1.Open();                   //打开串口
                    OPEN_SERIAL_flag = true;   //标记打开了串口
                    myaddlog(0, "当前串口有设备连接,串口已成功打开");
                    button6.Text = "关闭串口";
                }
                catch
                {
                    myaddlog(1, "错误警告: 端口无设备连接");
                    button6.Text = "打开串口";
                }

            }
            else if(OPEN_SERIAL_flag == true)
            {
                try
                {
                    serialPort1.Close(); //关闭串口        
                    myaddlog(0, "已关闭串口 ");
                    OPEN_SERIAL_flag = false;
                    button6.Text = "打开串口";
                }
                catch { }
            } 
        }

串口接收 控件函数:

用到的全局变量:

        string formattedLogMessage; //用于临时拼接字符串

        //串口接收
        //一个接收数据事件获取串口发送来的数据
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            //处理事件这块可以加上延时确保不定数的数据可以全部收到缓冲后,才去读缓冲内容--单位: 毫秒
            Thread.Sleep(50);
            //如果16进制转换没被勾选
            if (!checkBox1.Checked)
            {
                myaddlog(0, serialPort1.ReadExisting());
                myaddlog(0, "串口消息接收回传:");
            }
            //如果16进制转换被勾选了
            else
            {
                try
                {
                    //定义缓冲区数组大小为串口缓冲区数据的字节数
                    //因为串口事件触发时有可能收到不止一个字节
                    byte[] data = new byte[serialPort1.BytesToRead];
                    serialPort1.Read(data, 0, data.Length);
                  
                    foreach (byte Member in data)  //遍历用法
                    {
                        string str = Convert.ToString(Member, 16).ToUpper();
                        formattedLogMessage = string.Format("0x" + (str.Length == 1 ? "0" + str : str) + " ");
                        myaddlog(0, formattedLogMessage);
                    }
                    myaddlog(0, "串口消息接收回传:");
                }
                catch { }
            }
        }

串口发送 控件函数:


        //串口测试发送:
        private void button7_Click(object sender, EventArgs e)
        {
            byte[] Data = new byte[1];                                                         //单字节发数据     
            if (serialPort1.IsOpen)
            {
                if (textBox1.Text != "")
                {
                    //如果不是16进制发送,就直接string形式发送
                    if (!checkBox2.Checked)
                    {
                        try
                        {
                            serialPort1.Write(textBox1.Text);
                            myaddlog(0, "单条发送成功");
                            //serialPort1.WriteLine();                             //字符串写入
                        }
                        catch
                        {
                            myaddlog(1, "串口数据写入错误");
                        }
                    }
                    else                                                                    //数据模式
                    {
                        try                                                                 //如果此时用户输入字符串中含有非法字符(字母,汉字,符号等等,try,catch块可以捕捉并提示)
                        {
                            for (int i = 0; i < (textBox1.Text.Length - textBox1.Text.Length % 2) / 2; i++)//转换偶数个
                            {
                                Data[0] = Convert.ToByte(textBox1.Text.Substring(i * 2, 2), 16);           //转换
                                serialPort1.Write(Data, 0, 1);
                            }
                            if (textBox1.Text.Length % 2 != 0)
                            {
                                //单独处理最后一个字符
                                Data[0] = Convert.ToByte(textBox1.Text.Substring(textBox1.Text.Length - 1, 1), 16);
                                serialPort1.Write(Data, 0, 1);//写入
                            }
                            //Data = Convert.ToByte(textBox2.Text.Substring(textBox2.Text.Length - 1, 1), 16);
                            myaddlog(0, "单条发送成功");
                        }
                        catch
                        {
                            myaddlog(1, "数据转换错误,请输入数字。");
                        }
                    }
                }

            }
        }

头部/底部开始移动 控件函数:

       //头部开始移动
       private void button1_Click(object sender, EventArgs e)
       {
           bool success;//用于检查文本框textbox输入规范用

           //先检查串口是否打开
           if (serialPort1.IsOpen == false)
           {
               myaddlog(1, "无法发送内容,请检查 串口是否打开!");
           }

           else
           {
               //尝试转换 textBox2 角度的输入数值,看是否失败
               success = int.TryParse(textBox2.Text.Trim(), out angle);
               if (success == false && serialPort1.IsOpen)
               {
                   myaddlog(1, "无法将框中内容转换,请检查 设定移动角度 的输入。");
               }
               else
               {
                   if (Headleft.Checked && Headright.Checked)
                   {
                       myaddlog(1, "错误!头部移动方向 不可多选!");
                   }
                   else if (Headleft.Checked == false && Headright.Checked == false)
                   {
                       myaddlog(1, "错误!头部移动方向 并未选择!");
                   }
                   else if (Headleft.Checked == true && Headright.Checked == false)
                   {
                       //此处添加串口发送数据:
                       formattedLogMessage = string.Format("HL{0}&", textBox2.Text);
                       serialPort1.Write(formattedLogMessage);
                       formattedLogMessage = string.Format("已发送头部 移动方向为左 角度为{0}", textBox2.Text);
                       myaddlog(0, formattedLogMessage);
                   }
                   else if (Headleft.Checked == false && Headright.Checked == true)
                   {
                       //此处添加串口发送数据:
                       formattedLogMessage = string.Format("HR{0}&", textBox2.Text);
                       serialPort1.Write(formattedLogMessage);
                       formattedLogMessage = string.Format("已发送头部 移动方向为右 角度为{0}", textBox2.Text);
                       myaddlog(0, formattedLogMessage);
                   }
               }
           }

       }

       //底座开始移动
       private void button2_Click(object sender, EventArgs e)
       {
           bool success;//用于检查文本框textbox输入规范用

           //先检查串口是否打开
           if (serialPort1.IsOpen == false)
           {
               myaddlog(1, "无法发送内容,请检查 串口是否打开!");
           }
           else
           {
               //尝试转换 textBox3 角度的输入数值,看是否失败
               success = int.TryParse(textBox3.Text.Trim(), out angle);
               if (success == false)
               {
                   myaddlog(1, "无法将框中内容转换,请检查 设定移动角度 的输入。");
               }
               else
               {
                   if (Buttomleft.Checked && Buttomright.Checked)
                   {
                       myaddlog(1, "错误!底部移动方向 不可多选!");
                   }
                   else if (Buttomleft.Checked == false && Buttomright.Checked == false)
                   {
                       myaddlog(1, "错误!底部移动方向 并未选择!");
                   }
                   else if (Buttomleft.Checked == true && Buttomright.Checked == false)
                   {
                       //此处添加串口发送数据:
                       formattedLogMessage = string.Format("BL{0}&", textBox3.Text);
                       serialPort1.Write(formattedLogMessage);
                       formattedLogMessage = string.Format("已发送底座 移动方向为左 角度为{0}", textBox3.Text);
                       myaddlog(0, formattedLogMessage);
                   }
                   else if (Buttomleft.Checked == false && Buttomright.Checked == true)
                   {
                       //此处添加串口发送数据:
                       formattedLogMessage = string.Format("BR{0}&", textBox2.Text);
                       serialPort1.Write(formattedLogMessage);
                       formattedLogMessage = string.Format("已发送底座 移动方向为右 角度为{0}", textBox3.Text);
                       myaddlog(0, formattedLogMessage);
                   }
               }
           }

       }

一键归位 控件函数:

       //一键归位
       private void button3_Click(object sender, EventArgs e)
       {
           if (serialPort1.IsOpen)
           {
               formattedLogMessage = "RS&";
               serialPort1.Write(formattedLogMessage);
               myaddlog(0, "已发送归位测试字符串RS");
           }
           else
           {
               myaddlog(1, "无法发送内容,请检查 串口是否打开!");
           }
       }

测试连接 控件函数:

       //测试连接
       private void button4_Click(object sender, EventArgs e)
       {
           if (serialPort1.IsOpen)
           {
               formattedLogMessage = "TEST&";
               serialPort1.Write(formattedLogMessage);
               myaddlog(0, "已发送测试字符串TEST");
           }
           else
           {
               myaddlog(1, "无法发送内容,请检查 串口是否打开!");
           }
       }

创建日志委托 函数:

 创建委托函数需要放置的位置:

    //info 表示报警级别 ,log 表示报警信息
    public delegate void AddLog(int info, string log);
       //写入日志委托方法
       //创建委托
       private void AddLog(int info, string Log)
       {
           if (!lstInfo.InvokeRequired)
           {
               //创建ListViewItem ,将时间与info放进去
               ListViewItem lst = new ListViewItem("   " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), info);
               lst.SubItems.Add(Log);
               lstInfo.Items.Insert(0, lst);
           }
           else
           {
               Invoke(new Action(() =>
               {
                   ListViewItem lst = new ListViewItem("   " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), info);
                   lst.SubItems.Add(Log);
                   lstInfo.Items.Insert(0, lst);
               }));
           }
       }

清除日志区 控件函数:

        //清除日志区
        private void button8_Click(object sender, EventArgs e)
        {
            lstInfo.Items.Clear();         //清除日志listview 的内容
            MessageBox.Show("已成功清除日志区", "清除接收区");
        }

注意事项:

1、要检查各个控件操作可能出现的错误连接的情况:串口未打开、字符输入非法等,并设置报错日志

2、日志委托写入listview控件,别忘了编辑列

3、

 

C# Winform 整体测试工程下载:

https://download.csdn.net/download/qq_64257614/89368716?spm=1001.2014.3001.5503

STM32F10xx 单片机的编程:

OLED的驱动显示:

有关于OLED的驱动就不多赘述,这里只介绍在哪里刷新了哪些显存,具体配置是有关IIC通信的相关文章贴出如下:

STM32 F103C8T6学习笔记9:0.96寸单色OLED显示屏—自由取模显示—显示汉字与图片_stm32f103c8t6 oled显示文字-CSDN博客

STM32 F103C8T6学习笔记11:RTC实时时钟—OLED手表日历_stm32f103c8t6显示实时时间-CSDN博客

STM32 F103C8T6学习笔记16:1.3寸OLED的驱动显示日历-CSDN博客

PWM控制舵机运动:

初始化TIM3为舵机控制定时器:

        底座舵机:     Signal: PA7
        头部舵机:     Signal: PA6 

设置TIM3占空比控制舵机运转:

这里为了防止舵机运转过快出问题,我使用定时器控制其占空比更新频率不过快,并对占空比输出限幅:然后再主函数调用TIM_SetCompare X();函数来落实占空比的设置:


//通用定时器 定时器1 中断服务函数
void TIM1_UP_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET)
	{	
		if(++t==70)		  //每70ms设置一次舵机占空比
		{
			t=0;
			t1=MIDDLE + t1_receive;
			if(t1>=260) {t1=260;}
			if(t1<150)  {t1=150;}
			
			
			t2=MIDDLE + t2_receive;			
			if(t2>=260) {t2=260;}
			if(t2<150)  {t2=150;}
		}
		
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);//清出中断寄存器标志位,用于退出中断
	}
}

串口接收与串口中断服务函数的编写:

这部分的设计有些麻烦,串口接收是一件比较麻烦的事,

这里为了开发迅速,就不自己编写 状态机+结构体 这样比较规范的串口接收方式了,

我选择了简单的 定义接收buff[]数组缓冲区+末尾接收字符检验 的方式进行串口接收校验了,这种方式好编程,但缺点也很多很明显!

定义的诸多变量如下:


int t,t1,t2,t1_receive,t2_receive; //辅助配置占空比
int Receive[20]; //提取 串口接收数组 字符串里的 所有数字 
int temp_Receive;


//定义串口程序需要用到的变量
char USART0_save[20];  //存字符串命令的数组
char USART0_xb=0;			 //帮助数组下标位移
char USART0_flag=0;    //接收完成标志

//定义命令字符串,用于与接收进行比较 ,不可修改
const char str1_order[]="TEST&"; //测试命令
const char str2_order[]="RS&";   //归位命令


//定义响应字符串,用于响应不同的命令
char str1_receive[]="Cotact OVER";
char str2_receive[]="NTM: Hello,STM32F1xx !";
char error_receive[]="ERROR CMD!";

串口中断服务函数:

//串口1中断服务函数 
void USART1_IRQHandler(void)
{
 
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
	{USART_ClearFlag(USART1, USART_FLAG_RXNE);}
		USART0_save[USART0_xb++]=USART_ReceiveData(USART1);
		if(USART0_xb== 20){USART0_xb=0;	}							      //下标最大不超过20
		if(USART0_save[USART0_xb-1]=='&') {USART0_flag=1;}  //命令以&结尾
	
}

串口接收buff[]缓冲处理函数:

void Handle_Uart_Receive(void)
{
		int i;
				if(USART0_flag==1)
				{
					printf("STM32 confirm Receive : %s",USART0_save);            //先重复接受到的字符串
				
				//先判断命令长度,再根据其判断是否为接受到的命令字符串,根据情况发送不同回应
					if(strncmp(USART0_save,str1_order,5)==0) printf("%s",str1_receive);
					else if(strncmp(USART0_save,str2_order,3)==0) 
						{
								 t1_receive=0;t2_receive=0;
								 printf("%s",str2_receive);
							}
						
						//如果是头部舵机转动的命令头	
					if(USART0_save[0]=='H')
							{
								extractDigitsFromStringArray();//提取 USART0_save 接收数组中的数字								
								//循环拼接提取到的每个数字
								for (i = 0; i < USART0_xb -3; i++) 
								{  
											temp_Receive = temp_Receive*10+Receive[i];  
								}
								//判断向左向右
								if(USART0_save[1]=='L'){t1_receive=0-temp_Receive;}
								if(USART0_save[1]=='R'){t1_receive=0+temp_Receive;}
							}
							
								//如果是底部舵机转动的命令头	
							if(USART0_save[0]=='B')
							{
								extractDigitsFromStringArray(); //提取 USART0_save 接收数组中的数字
								//循环拼接提取到的每个数字
								for (i = 0; i < USART0_xb -3; i++) 
								{  
											temp_Receive = temp_Receive*10+Receive[i];  
								}
								//判断向左向右
								if(USART0_save[1]=='L'){t2_receive=0+temp_Receive;}
								if(USART0_save[1]=='R'){t2_receive=0-temp_Receive;}								
							}
							
						}	
						memset(USART0_save,0,sizeof(USART0_save)); //处理完命令别忘了将数组清零,以便接收下个命令
						temp_Receive=0;
						USART0_xb=0;                               //重置数组下标	
						USART0_flag=0;								             //清理标志位
}
	

缓冲处理辅助函数:

这是一些缓冲处理的辅助函数,主要是C语言的基础,对数据类型的处理判断:其中一些基础函数需要添加头文件

#include <ctype.h>   
#include <string.h>  
#include <stdbool.h>  

//从一个数组中提取数字到另一个数组
void extractDigitsFromStringArray(void)
{
	int i=0,j=0;
	for(i=0;i<=sizeof(USART0_save);i++)
	{
		if(isStringNumeric(USART0_save[i])==true)
		{
			Receive[j]=USART0_save[i] - '0';
			j++;
		}
	}
}

// 辅助函数:检查字符是否是数字  
bool isStringNumeric(char str)
{  
    if (str == NULL || str == '\0') 
		{  
        // 空字符或NULL指针,不是数字
        return false;  
    }  
		
    if (!isdigit((unsigned char)str)) 
				{  
            //发现非数字字符,则返回false  
            return false;  
        }  
 
    //字符是数字 
    return true;  
}

STM32F103C8T6测试工程下载:

https://download.csdn.net/download/qq_64257614/89368723?spm=1001.2014.3001.5503

测试视频与图片:

本次小试验品开发前后总共耗时不到俩天,按小时计算的话就少于一天了......

Zigbee +PC上位机 无线控制二维云台开发

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用 Scapy 库编写 Ping of Death 攻击脚本

一、介绍 1.1 概述 Ping of Death&#xff08;PoD&#xff09;攻击是一种历史悠久的拒绝服务&#xff08;DoS&#xff09;攻击&#xff0c;攻击者通过发送特制的畸形ICMP Echo请求数据包&#xff0c;导致目标系统无法正确处理&#xff0c;从而导致系统崩溃、重启或无法响应正…

用于相似图片搜索引擎的Python OpenCV图像直方图

图像直方图 那么&#xff0c;图像直方图到底是什么&#xff1f; 图片 图像的构成是由像素点构成的&#xff0c;每个像素点的值代表着该点的颜色&#xff08;灰度图或者彩色图&#xff09;。所谓直方图就是对图像的中的这些像素点的值进行统计&#xff0c;得到一个统一的整体的…

手眼标定学习笔记

目录 标定代码&#xff1a; 手眼标定原理学习 什么是手眼标定 手眼标定的目的 eye in hand eye to hand AXXB问题的求解 标定代码&#xff1a; GitHub - pumpkin-ws/HandEyeCalib 推荐博文&#xff1a; https://zhuanlan.zhihu.com/p/486592374 手眼标定原理学习 参…

YOLOv8: 标注石头、识别边缘及计算面积的方案

YOLOv8: 标注石头、识别边缘及计算面积的方案 引言 YOLO&#xff08;You Only Look Once&#xff09;是一种非常有效的实时目标检测算法&#xff0c;自其首次发布以来就受到了广泛的关注和应用。YOLOv8 是这一系列算法的最新版本&#xff0c;继承了之前版本的高效性和准确性&a…

如何在一台电脑上安装多个版本的JDK并且切换使用?

如何在一台电脑上安装多个版本的JDK并且切换使用&#xff1f; 文章目录 如何在一台电脑上安装多个版本的JDK并且切换使用&#xff1f;1.目录管理2.下载JDK3.配置环境变量4. 验证 1.目录管理 我们需要首先对不同版本的JDK进行版本管理&#xff0c;如下所示&#xff0c;我在C盘路…

vue3状态管理,pinia的使用

状态管理 我们知道组件与组件之间可以传递信息&#xff0c;那么我们就可以将一个信息作为组件的独立状态&#xff08;例如&#xff0c;单个组件的颜色&#xff09;或者共有状态&#xff08;例如&#xff0c;多个组件是否显示&#xff09;在组件之传递&#xff0c;这样的话我们希…

STM32作业实现(七)OLED显示数据

目录 STM32作业设计 STM32作业实现(一)串口通信 STM32作业实现(二)串口控制led STM32作业实现(三)串口控制有源蜂鸣器 STM32作业实现(四)光敏传感器 STM32作业实现(五)温湿度传感器dht11 STM32作业实现(六)闪存保存数据 STM32作业实现(七)OLED显示数据 STM32作业实现(八)触摸按…

最新h5st(4.7.2)参数分析与纯算法还原(含算法源码)

文章目录 1. 写在前面2. 加密分析3. 算法还原 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…

HALCON-从入门到入门-最常用的算子-二值化

1.废话 图像处理中的二值化是一种将灰度图像转换为只有两种可能值&#xff08;通常是0和255&#xff0c;分别代表黑色和白色&#xff09;的过程。这个过程在数字图像处理中非常常见&#xff0c;因为它可以简化图像数据&#xff0c;突出图像的主要特征&#xff0c;并降低后续处…

5.25.12 数字组织病理学的自我监督对比学习

无监督学习可以弥补标记数据集的稀缺性。 无监督学习的一个有前途的子类是自监督学习&#xff0c;其目的是使用原始输入作为学习信号来学习显著特征。在这项工作中&#xff0c;我们解决了在没有任何监督的情况下学习领域特定特征的问题&#xff0c;以提高数字组织病理学界感兴…

【vue实战项目】通用管理系统:作业列表

目录 目录 1.前言 2.后端API 3.前端API 4.组件 5.分页 6.封装组件 1.前言 本文是博主前端Vue实战系列中的一篇文章&#xff0c;本系列将会带大家一起从0开始一步步完整的做完一个小项目&#xff0c;让你找到Vue实战的技巧和感觉。 专栏地址&#xff1a; https://blog…

【Linux】Linux工具——gcc/g++

1.使用vim更改信用名单——sudo 我们这里来补充sudo的相关知识——添加信任白名单用户 使用sudo就必须将使用sudo的那个账号添加到信用名单里&#xff0c;而且啊&#xff0c;只有超级管理员才可以添加 信用名单在/etc/sudoers里 我们发现它的权限只是可读啊&#xff0c;所以…

MD中 面料的物理属性参数

该图片是Marvelous Designer软件中"Fabric Physical Properties"(面料物理属性)面板的截图,用于调整面料在弯曲、折叠时的硬度(Buckling Stiffness)。 目标部分解释了调整Buckling Stiffness的作用:通过调整该百分比值来决定面料角落处的硬度。进入80%的Buckling St…

Linux网络编程:应用层协议|HTTPS

目录 1.预备知识 1.1.加密和解密 1.2.常见加密方式 1.2.1.对称加密 1.2.2.非对称加密 ​编辑 1.3.数据摘要&#xff08;数据指纹&#xff09;和数据签名 1.4.证书 1.4.1.CA认证 1.4.2.证书和数字签名 2.HTTPS协议 2.1.自行设计HTTPS加密方案 2.1.1.只使用对称加密 …

java调用科大讯飞离线语音合成SDK --内附完整项目

科大讯飞语音开放平台基础环境搭建 1.用户注册 注册科大讯飞开放平台账号 2.注册好后先创建一个自己的应用 创建完成后进入应用选择离线语音合成&#xff08;普通版&#xff09;可以看到我们开发需要的SDK,选择windows MSC点击下载。 3.选择你刚刚创建的应用&#xff0c;选择…

python安装pystan教程

简介 PyStan是Stan编程语言的Python接口&#xff0c;Stan是一种用于统计建模和数据分析的概率编程语言。PyStan使用户能够在Python环境中定义、编译和采样Stan模型。 安装步骤 首先&#xff0c;需要先安装 Cython pip install Cython -i https://mirrors.aliyun.com/pypi/sim…

Java学习20——Map接口

目录 一.Map&#xff1a; 1.基本介绍&#xff1a; 2.Map常用方法&#xff1a; 3.Map的遍历方法&#xff1a; 4.HashMap: 1.基本介绍&#xff1a; 2.HashMap底层扩容机制&#xff1a; 5.Hashtable&#xff1a; 1.基本介绍&#xff1a; 2.HashMap和Hashtable的对比&…

计算机储存容量单位都有哪些?

这些单位在高中职的计算机概论似乎都学过了&#xff0c;不过我以前的书本好像也只有教到 GB&#xff0c;现在的教科书可能有教到 TB 或 PB 吧&#xff0c;但我不确定&#xff0c;不过在不久的将来可能又会有更大的单位有机会用到&#xff0c;所以想了解一下。 电脑的最小单位为…

Springboot校园食堂智能排餐系统-计算机毕业设计源码85935

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对校园食堂智能排餐系统等问题&#xff0c;对…

word如何锁定样式不被修改(CSDN_20240602)

在写材料合稿时&#xff0c;交上来word样式千奇百怪&#xff0c;直接合稿可能会把大稿子格式搞乱&#xff0c;所以希望发模板时&#xff0c;写死文档样例&#xff0c;不允许修改样式。 1. 创建模板样式&#xff0c;如下图所示。 2. 点击“审阅”-->"限制编辑”&#x…