基于32单片机的RS485综合土壤传感器检测土壤PH、氮磷钾的使用(超详细)
1-3为RS485综合土壤传感器的基本内容
4-5为基于STM32F103C8T6单片机使用RS485传感器检测土壤PH、氮磷钾并显示在OLED显示屏的相关配置内容
注意:本篇文件讲解使用的是PH、氮磷钾四合一RS485综合土壤传感器,但里面的讲解内容适配市面上的所有多合一的RS485综合土壤传感器
一、RS485综合土壤传感器概述
1.1 产品概述
本产品性能稳定灵敏度高,响应快,输出稳定,适用于各种土质。是观测和研究盐渍土的发生、演变、改良以及水盐动态的重要工具。通过测量土壤的介电常数,能直接稳定地反映各种土壤的真实水分含量。可测量土壤水分的体积百分比,是符合目前国际标准的土壤水分测量方法。可长期埋入土壤中,耐长期电解,耐腐蚀,抽真空灌封,完全防水。
适用于土壤墒情监测、科学试验、节水灌溉、温室大棚、花卉蔬菜、草地牧场、土壤速测、植物培养、污水处理、精细农业等场合的温湿度、电导率、PH值测试。
1.2 功能特点
■ 门槛低,步骤少,测量快速,无需试剂,不限检测次数。
■ 电极采用特殊处理的合金材料,可承受较强的外力冲击,不易损坏。
■ 完全密封,耐酸碱腐蚀,可埋入土壤或直接投入水中进行长期动态检测。
■ 精度高,响应快,互换性好,探针插入式设计保证测量精确,性能可靠。
■ 也可用于水肥一体溶液、以及其他营养液与基质的电导率。
二、硬件连接
综合土壤传感器使用的RS485通信方式,而RS-485 是一种用于长距离通信的串行通信标准,常用于工业和商业应用。它在噪声干扰的环境中提供了良好的信号完整性,支持多点通信。
在与单片机(如 Arduino、8051、PIC、STM32 等)进行通信时,通常需要将 RS-485 信号转换为 TTL信号。
将 RS-485 信号转换为 TTL 信号是确保传感器和单片机之间能够顺利通信的关键步骤,这样可以解决电平匹配、通信协议、设计简化和电气保护等问题。通常使用专门的 RS-485 到 TTL 的转换器(例如 MAX485、SN75176 等)来实现这一转换。
单片机TTL转RS485模块
土壤综合传感器接线讲解
线色
说明
备注
棕色
电源正
4.5~30V DC
黑色
电源地
GND
黄色
485-A
485-A
蓝色
485-B
485-B
传感器和转换器接线讲解
单片机TTL转RS485模块 单片机和土壤综合传感器 VCC 单片机的5V(建议) TXD A2 RXD A3 GND GND GND GND B 485-B A 485-A
三、单片机和RS485综合传感器的通信配置
综合土壤传感器通过硬件连接后,32单片机和其的通信方式则为串口通信,单片机通过串口发送问询帧给综合土壤传感器,其接收到单片机的发送信息后,会反馈应答帧给单片机,单片机从应答帧接收到对的地址码后,则开始接收对应的信息,后通过转换数据得到我们想要的信息。
要想知道具体的通信细节,我们还得查看商家给我们对应传感器的文档,里面有我们需要的对应通信协议。
四、通信协议 (重要)
4.1 通讯基本参数
注意: 这里的波特率指的是串口通信中使用的波特率,而不同厂家生产的综合土壤传感器出厂默认的波特率也有可能不相同,有的可能为9600bit/s,具体到哪个需要找商家拿到对应的资料文档从而进行查看,本次使用的传感器出厂默认为4800bit/s。
编 码
8位二进制
数据位
8位
奇偶校验位
无
停止位
1位
错误校验
CRC(冗余循环码)
波特率
2400bit/s、4800bit/s、9600 bit/s可设,出厂默认为4800bit/s
4.2 数据帧格式定义
采用ModBus-RTU 通讯规约,格式如下:
初始结构 ≥4 字节的时间
地址码 = 1 字节
功能码 = 1 字节
数据区 = N 字节
错误校验 = 16 位CRC 码
结束结构 ≥4 字节的时间
地址码:为变送器的地址,在通讯网络中是唯一的(出厂默认0x01)。
功能码:本产品用到功能码0x03、0x06、0x10等。
数据区:数据区是具体通讯数据,注意16bits数据高字节在前!
CRC码:二字节的校验码。
其中,主机问询帧结构就是单片机通过串口发送给传感器的信息,而从机应答帧结构就是土壤传感器反馈给单片机的信息
主机问询帧结构:
地址码
功能码
寄存器起始地址
寄存器长度
校验码低位
校验码高位
1字节
1字节
2字节
2字节
1字节
1字节
从机应答帧结构:
地址码
功能码
有效字节数
数据一区
第二数据区
第N数据区
校验码
1字节
1字节
1字节
2字节
2字节
2字节
2字节
4.3 寄存器地址
不同厂家的土壤综合传感器的寄存器地址也可能会各不相同,无需理会,我们只需要读懂其使用方法就行。
4.4 通讯协议示例以及解释(重要)
以官方的文档为例
官方举例是电导率温度水分PH四合一设备的例子,那是不是所有的多合一的综合土壤综合传感器都一样呢?明显不是的,我们不同的综合土壤传感器所发的问询帧和返回的应答帧是不一样的,那具体该怎么发送,怎么修改呢?
问询帧:
地址码、功能码:这两个出厂就有个默认值,而且资料文档也会给出,在4、2的截图有说明,而且其中一般都是默认地址码0x01,功能码0x03
起始地址:两个字节,具体字节是你检测的数据最前的第一个地址,例如上面举例的电导率温度水分PH四合一检测的数据地址最前面的则是000H,所有它的起始地址为0x00 0x00,而本次讲解使用的是PH、氮磷钾四合一,那根据官方给的文档查阅,地址最前的则是PH的地址0003H,则起始地址为0x00 0x3H。
数据长度:就是你发送地址数据的长度。检测多少个数据,看需要检测的数据是多少个以及它的地址字节数,但文档的地址的检测数据都是低字节,所以可以简单的总结为,四合一的数据长度为0x00 0x04,七合一的数据长度:0x00 0x07,只检测一个数据的长度为:0x00 0x01
效验码低字节、效验码高字节:这里的二字节的校验码指的是CRC码。那CRC码又是什么呢?CRC码是一种广泛使用的错误检测码,用于检测数据传输或存储过程中发生的错误。它通过在数据中添加冗余信息,使接收方能够验证数据的完整性。
根据前面的发送数据来计算CRC码,那怎样计算CRC码呢?计算CRC码比较复杂,而且难度高,那我们可以直接通过查阅数据来获取我们的CRC码,地址如下:16进制(CRC16)(MODBUS RTU通讯)校验码在线计算器
举例:
文档电导率温度水分PH四合一发送的地址码、功能码、起始地址、数据长度分为01 03 00 00 00 04,那它的CRC码(MSB-LSB)为:0944,也就是说效验码低字节:0x44,效验码高字节:0x09,这就和我上面列举的官方文档一致了。
那我们本次使用的PH、氮磷钾四合一,那其地址码、功能码、起始地址、数据长度分为01 03 00 03 00 04,那它的CRC码(MSB-LSB)为:09B4,也就是说效验码低字节:0xB4,效验码高字节:0x09。
那假如我四合一传感器只想读取其中一个数据呢,比如我只想读取磷的数据,那我的问询帧前面的格式则为,地址码:0x01 功能码:0x03,起始地址:0x00 0x05 ,数据长度:0x00 0x01,效验码低字节0x94,效验码高字节:0x0B。其中起始地址中的0x00 0x05是磷的寄存器地址。
当然你也可以全部读取所有数据,然后根据你数组存储的数据位置来获取单独的数据也是可以的。
应答帧:
以官方给出的电导率温度水分PH四合一例子讲解:
地址码、功能码:和上面问询帧讲解一样,出厂一般默认为0x01 0x03,具体看文档。
返回有效字节数:具体看从机应答帧结构。例如:四合一土壤综合传感器,那其有效字节数为为你检测的数据之和,也就是水分值+温度值+电导率+PH = 8,有效字节数则是0x08
后面的数据区就是根据你的地址数据从低到高排列的,我们用数组接受到返回的数据后,根据其位置分别读取就行
五、代码例子 (PH、氮磷钾四合一)
单片机通过串口发送问询帧给综合土壤传感器,其接收到单片机的发送信息后,会反馈应答帧给单片机,单片机从应答帧接收到对的地址码后,则开始接收对应的信息,后通过转换数据得到我们想要的信息。
5.1 串口2定时发送问询帧给RS485综合土壤传感器
通过配置串口2通信,并且波特率选择为4800(根据厂家的默认出厂波特率为准),通过1S定时给综合土壤传感器发送问询帧,等待发送完毕。(注意其中串口使用的串口2,定时器使用的定时器3,同学们可以自由选择自己的配置串口和定时器)
串口2配置和发送一字节函数:
void Usart2_Init(u32 bp)
{NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // GPIOA时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //串口2时钟使能USART_DeInit(USART2); //复位串口2//USART2_TX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure); //USART2_RX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = bp;USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART2, &USART_InitStructure); //初始化串口2USART_Cmd(USART2, ENABLE); //使能串口 //使能接收中断USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断 //设置中断优先级NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器}//串口1发送一字节
void Send_Byte(uint8_t data)
{USART_SendData(USART2,data);while(!USART_GetFlagStatus(USART2,USART_FLAG_TXE));//等待发送数据完毕}
定时器3配置:
通过定时一字节一字节的发送询问帧,其中PH、氮磷钾四合一的询问帧为
01 03 00 03 00 04 B4 09(为什么是这个,上面内容有解释,同学们可查看4.4内容)
void Tim3_Init(void) {RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//打开TIM3时钟//配置TIM3时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct={0};TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数TIM_TimeBaseInitStruct.TIM_Period = 10000;//重装载值TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;//预分频数TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE);//使能更新中断//TIM3中优先限配置NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=TIM3_IRQn;//配置TIM3中断源NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;//中断使能NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;//抢断优先级NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //子优先级NVIC_Init(&NVIC_InitStruct);//初始化配置NVIC(中断向量控制寄存器)TIM_Cmd(TIM3, ENABLE);//使能定时器 }u16 time[5]={0}; void TIM3_IRQHandler(void) {if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET){TIM_ClearITPendingBit(TIM3,TIM_IT_Update);time[0]++;if(time[0] == 1){time[0] = 0;delay_ms (1);//延时1msSend_Byte( 0x01);Send_Byte( 0x03);Send_Byte( 0x00);Send_Byte( 0x03);Send_Byte( 0x00);Send_Byte( 0x04);Send_Byte( 0xB4);Send_Byte( 0x09);}} }
5.2 发送完询问帧后,通过串口2中断,接收返回的信息(应答帧)
其中我们根据应答帧第一个地址码为包头,从而接收信息。其中代码的13个字节数据计算方法是根据应答帧结构来的,例如四合一=地址码(1)+功能码(1)+有效字节数(1)+数据区(4x2)+效验码(2) = 13
5.3 完整代码
main.c
#include "main.h" extern char USART_data[20];//串口数据存储数组 float ph; int N; int P; int K; u8 bufss1[20]; u8 bufss2[20]; u8 bufss3[20]; u8 bufss4[20]; int main(void) {NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断组的选择Usart2_Init(4800);//串口2初始化OLED_Init();//OLED初始化Tim3_Init();//定时器初始化 while(1) {ph=(USART_data[3]*16+USART_data[4])/10;N=(USART_data[5]*16+USART_data[6]);P=(USART_data[7]*16+USART_data[8]);K=(USART_data[9]*16+USART_data[10]);sprintf((char *)bufss1,"PH:%.1f",ph);sprintf((char *)bufss2,"N:%d",N);sprintf((char *)bufss3,"P:%d",P);sprintf((char *)bufss4,"K:%d",K);Oled_ShowAll(0,0,bufss1);//显示中英字符串Oled_ShowAll(2,0,bufss2);//显示中英字符串Oled_ShowAll(4,0,bufss3);//显示中英字符串Oled_ShowAll(6,0,bufss4);//显示中英字符串}}
串口2配置
#include "usart.h"void Usart2_Init(u32 bp) {NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // GPIOA时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //串口2时钟使能USART_DeInit(USART2); //复位串口2//USART2_TX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure); //USART2_RX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = bp;USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART2, &USART_InitStructure); //初始化串口2USART_Cmd(USART2, ENABLE); //使能串口 //使能接收中断USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断 //设置中断优先级NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器}char USART_flag;char USART_data[20];int i=0;int USART_Ready=0;//数据接收完成标志 //串口中断 void USART2_IRQHandler(void) {if(USART_GetITStatus(USART2,USART_IT_RXNE))//接收中断标志位{USART_flag = USART_ReceiveData(USART2);if(USART_flag==0x01)//检测包头{USART_Ready=1;}if(USART_Ready==1){USART_data[i]=USART_flag;i++;if(i==12)//捕捉完成18个字节数据{USART_Ready=0;i=0; }}USART_ClearITPendingBit(USART2,USART_IT_RXNE);//清除中断标志位} } //串口2发送一字节 void Send_Byte(uint8_t data) {USART_SendData(USART2,data);while(!USART_GetFlagStatus(USART2,USART_FLAG_TXE));//等待发送数据完毕}//串口2接收一节字u16 Rece_Byte(void) {while(!USART_GetFlagStatus(USART2,USART_FLAG_RXNE))//等待接收数据完毕{}return USART_ReceiveData(USART2); }//回显函数 void Data_Echo(void) {uint16_t date=0;date=Rece_Byte();Send_Byte(date); }//函数功能:printf重定向 int fputc(int c, FILE * stream) {Send_Byte(c);return c; }
定时器3配置:
#include "time.h" /*********************** 函数名: 函数功能:定时器3初始化 形参: 返回值: 函数说明:************************/ void Tim3_Init(void) {RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//打开TIM时钟//配置TIM3时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct={0};TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//向上计数TIM_TimeBaseInitStruct.TIM_Period = 10000;//重装载值TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;//预分频数TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);TIM_ITConfig(TIM3,TIM_IT_Update, ENABLE);//使能更新中断//TIM3中优先限配置NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=TIM3_IRQn;//配置TIM3中断源NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;//中断使能NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;//抢断优先级NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //子优先级NVIC_Init(&NVIC_InitStruct);//初始化配置NVIC(中断向量控制寄存器)TIM_Cmd(TIM3, ENABLE);//使能定时器 }u16 time[5]={0}; void TIM3_IRQHandler(void) {if(TIM_GetITStatus(TIM3,TIM_IT_Update) != RESET){TIM_ClearITPendingBit(TIM3,TIM_IT_Update);time[0]++;if(time[0] == 1){time[0] = 0;delay_ms (1);//延时1msSend_Byte( 0x01);Send_Byte( 0x03);Send_Byte( 0x00);Send_Byte( 0x03);Send_Byte( 0x00);Send_Byte( 0x04);Send_Byte( 0xB4);Send_Byte( 0x09);}} }
oled.c
#include "oled.h" #include "oledfont.h" #include "pic.h"extern const unsigned char Aciss_8X16[];//字符库 extern const unsigned char indexs[][3];//寻找汉字位置 const unsigned char GB2312[];//汉字库/*@brief 初始化OLED与单片机的IO接口@param 无@retval 无*/ static void OLED_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStructure; //定义一个GPIO_InitTypeDef类型的结构体RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); //打开GPIOC的外设时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13; //选择控制的引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //设置为通用开漏输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置输出速率为50MHzGPIO_Init(GPIOB,&GPIO_InitStructure); //调用库函数初始化GPIOAOLED_SCLK_Set(); //设PA5(SCL)为高电平OLED_SDIN_Set(); //设PA6(SDA)为高电平 }/*@brief 模拟IIC起始信号@param 无@retval 无*/ static void OLED_IIC_Start(void) {OLED_SCLK_Set(); //时钟线置高OLED_SDIN_Set(); //信号线置高delay_us(1); //延迟1usOLED_SDIN_Clr(); //信号线置低delay_us(1); //延迟1usOLED_SCLK_Clr(); //时钟线置低delay_us(1); //延迟1us }/*@brief 模拟IIC停止信号@param 无@retval 无*/ static void OLED_IIC_Stop(void) {OLED_SDIN_Clr(); //信号线置低delay_us(1); //延迟1usOLED_SCLK_Set(); //时钟线置高delay_us(1); //延迟1usOLED_SDIN_Set(); //信号线置高delay_us(1); //延迟1us }/*@brief 模拟IIC读取从机应答信号@param 无@retval 无*/ static unsigned char IIC_Wait_Ack(void) {unsigned char ack;OLED_SCLK_Clr(); //时钟线置低delay_us(1); //延迟1usOLED_SDIN_Set(); //信号线置高delay_us(1); //延迟1usOLED_SCLK_Set(); //时钟线置高delay_us(1); //延迟1usif(OLED_READ_SDIN()) //读取SDA的电平ack = IIC_NO_ACK; //如果为1,则从机没有应答elseack = IIC_ACK; //如果为0,则从机应答OLED_SCLK_Clr();//时钟线置低delay_us(1); //延迟1usreturn ack; //返回读取到的应答信息 }static void Write_IIC_Byte(unsigned char IIC_Byte) {unsigned char i; //定义变量for(i=0;i<8;i++) //for循环8次{OLED_SCLK_Clr(); //时钟线置低,为传输数据做准备delay_us(1); //延迟1usif(IIC_Byte & 0x80) //读取最高位OLED_SDIN_Set();//最高位为1elseOLED_SDIN_Clr(); //最高位为0IIC_Byte <<= 1; //数据左移1位delay_us(1); //延迟1usOLED_SCLK_Set(); //时钟线置高,产生上升沿,把数据发送出去delay_us(1); //延迟1us}OLED_SCLK_Clr(); //时钟线置低delay_us(1); //延迟1uswhile(IIC_Wait_Ack()); //从机应答 }/*@brief IIC写入命令@param IIC_Command:写入的命令@retval 无*/ static void Oled_Send_Cmd(unsigned char IIC_Command) {OLED_IIC_Start();Write_IIC_Byte(0x78);//写入从机地址,SD0 = 0Write_IIC_Byte(0x00);//写入命令Write_IIC_Byte(IIC_Command);//数据OLED_IIC_Stop(); //发送停止信号 }/*@brief IIC写入数据@param IIC_Data:数据@retval 无*/ static void Oled_Send_Data(unsigned char IIC_Data) {OLED_IIC_Start();Write_IIC_Byte(0x78); //写入从机地址,SD0 = 0Write_IIC_Byte(0x40); //写入数据Write_IIC_Byte(IIC_Data);//数据OLED_IIC_Stop(); //发送停止信号 }/*@brief 开显示@param 无@retval 无*/ void OLED_Display_On(void) {Oled_Send_Cmd(0X8D); //设置OLED电荷泵Oled_Send_Cmd(0X14); //使能,开Oled_Send_Cmd(0XAF); //开显示 }/*@brief 关显示@param 无@retval 无*/ void OLED_Display_Off(void) {Oled_Send_Cmd(0XAE); //关显示Oled_Send_Cmd(0X8D); //设置OLED电荷泵Oled_Send_Cmd(0X10); //失能,关 } /*@brief 清屏@param 无@retval 无*/ void OLED_Clear(u8 data) { u8 i,j;for(i=0;i<8;i++){Oled_Send_Cmd(0xB0+i);//发送页起始地址Oled_Send_Cmd(0x00);//发送列地址低四位Oled_Send_Cmd(0x10);//发送列地址高四位for(j=0;j<128;j++){Oled_Send_Data(data);}} }void OLED_Init(void) {OLED_GPIO_Init(); //GPIO口初始化delay_ms(200); //延迟,由于单片机上电初始化比OLED快,所以必须加上延迟,等待OLED上复位完成Oled_Send_Cmd(0xAE); //关闭显示Oled_Send_Cmd(0x00); //设置低列地址Oled_Send_Cmd(0x10); //设置高列地址Oled_Send_Cmd(0x40); //设置起始行地址Oled_Send_Cmd(0xB0); //设置页地址Oled_Send_Cmd(0x81); // 对比度设置,可设置亮度Oled_Send_Cmd(0xFF); // 265 Oled_Send_Cmd(0xA1); //设置段(SEG)的起始映射地址;column的127地址是SEG0的地址Oled_Send_Cmd(0xA6); //正常显示;0xa7逆显示Oled_Send_Cmd(0xA8); //设置驱动路数(16~64)Oled_Send_Cmd(0x3F); //64dutyOled_Send_Cmd(0xC8); //重映射模式,COM[N-1]~COM0扫描Oled_Send_Cmd(0xD3); //设置显示偏移Oled_Send_Cmd(0x00); //无偏移Oled_Send_Cmd(0xD5); //设置震荡器分频Oled_Send_Cmd(0x80); //使用默认值Oled_Send_Cmd(0xD9); //设置 Pre-Charge PeriodOled_Send_Cmd(0xF1); //使用官方推荐值Oled_Send_Cmd(0xDA); //设置 com pin configuartionOled_Send_Cmd(0x12); //使用默认值Oled_Send_Cmd(0xDB); //设置 Vcomh,可调节亮度(默认)Oled_Send_Cmd(0x40); 使用官方推荐值Oled_Send_Cmd(0x8D); //设置OLED电荷泵Oled_Send_Cmd(0x14); //开显示Oled_Send_Cmd(0xAF); //开启OLED面板显示OLED_Clear(0); //清屏 // Set_Postion(0,0); //设置数据写入的起始行、列 // OLED_Clear(0); } /*********************** 函数名:Set_Postion 函数功能: 形参: u8 page 页 u8 col 列 返回值:void 函数说明: 页地址:0xb0 列低位地址:0x00 列高位地址:0x10 ************************/ void Set_Postion(u8 page,u8 col) {Oled_Send_Cmd(0xB0+page);//发送页起始地址Oled_Send_Cmd(0x00+(col&0x0f));//发送列地址低四位Oled_Send_Cmd(0x10+((col&0xf0)>>4));//发送列地址高四位 } /*********************** 函数名:Oled_ShowPic 函数功能:OLED显示图片 形参: u8 page 页 u8 col 列 u8 height 图片高度 u8 width 图片宽度 u8 *pic 图片数据 返回值:void 64*64 ************************/void Oled_ShowPic(u8 page,u8 col,u8 height,u8 width,u8 *pic) {u8 i,j;for(i=0;i<height/8;i++)//活得页范围{Set_Postion(page+i,col);//定义框架for(j=0;j<width;j++){Oled_Send_Data(pic[j+i*width]);//往框架输入数据,以列方式}}} //清空某字符 void Oled_clear(u8 page,u8 col,u8 height,u8 width) {u8 i,j;for(i=0;i<height/8;i++)//活得页范围{Set_Postion(page+i,col);//定义框架for(j=0;j<width;j++){Oled_Send_Data(0x00);//往框架输入数据,以列方式}}}/*********************** 函数名:Oled_ShowChar 函数功能:OLED显示字符 形参: u8 page 页 u8 col 列 u8 pic 字符数据 返回值:void 8*16 ************************/void Oled_ShowChar(u8 page,u8 col,u8 eng) {u8 i,j,Char;Char=eng-' ';//获取字符所在位置for(i=0;i<2;i++){Set_Postion(page+i,col);for(j=0;j<8;j++){Oled_Send_Data(Aciss_8X16[j+Char*16+i*8]);}} } /*********************** 函数名:Oled_Showstring 函数功能:OLED显示字符串 形参: u8 page 页 2 u8 col 列 8 u8 *str 字符串 返回值:void 8*16 ************************/ void Oled_Showstring(u8 page,u8 col,u8*str) {while(*str !='\0'){if(col>=128){col=0;page +=2;}if(page>=8){page=0;}Oled_ShowChar(page,col,*str);str++;//字符递加col +=8;//每显示完一个字符,列+8 }}/*********************** 函数名:Oled_ShowChi 函数功能:OLED显示汉字 形参: u8 page 页 u8 col 列 u8 *chi 显示汉字 返回值:void16*16 = 32 byte ************************/void Oled_ShowChi(u8 page,u8 col,u8*chi) {u8 i,j,z;for(i=0;i<sizeof(indexs)/sizeof(indexs[0]);i++){if(*chi == indexs[i][0]&&*(chi+1)==indexs[i][1])//比较汉字在第几个位置{break;}}for(j=0;j<2;j++){Set_Postion(page+j,col);for(z=0;z<16;z++){Oled_Send_Data(GB2312[z+i*32+j*16]);}}}/*********************** 函数名:Oled_ShowAll 函数功能:OLED显示汉英字符串 形参: u8 page 页 u8 col 列 u8 *str 显示汉字 返回值:void8*16;16*16 = 32 byte ************************/void Oled_ShowAll(u8 page ,u8 col ,u8*str) {while(*str != '\0') {if(*str>127){if(col>=120)//放不下字{page +=2;//col=0;}if(page>=8){page=0;}Oled_ShowChi(page,col,str);col +=16;str +=2;}else{if(col>=128)//放不下字{page +=2;//col=0;}if(page>=8){page=0;}Oled_ShowChar(page,col,*str);str++;col +=8;}}}/*@brief OLED滚屏函数,范围0~1页,水平向左@param 无@retval 无*/ void OLED_Scroll(void) {Oled_Send_Cmd(0x2E); //关闭滚动Oled_Send_Cmd(0x27); //水平向左滚动Oled_Send_Cmd(0x00); //虚拟字节Oled_Send_Cmd(0x00); //起始页 0Oled_Send_Cmd(0x00); //滚动时间间隔Oled_Send_Cmd(0x01); //终止页 1Oled_Send_Cmd(0x00); //虚拟字节Oled_Send_Cmd(0xFF); //虚拟字节Oled_Send_Cmd(0x2F); //开启滚动 }
六、结语
到这一步,基本已经配置成功了,是不是很简单,只需要按照程序来做,大家都能顺利成功读取到RS485的综合土壤传感器的数据。需要代码的可以去我的主页获取下载,只需5积分,最后附上代码获取网址:
https://download.csdn.net/download/weixin_52680858/90121983
看到这的同学,如果觉得对你有用,就麻烦大家点点赞和收藏,谢谢!!!!
---------以下内容为打广告-----------
本人承接各种单片机设计,价格实惠,有需要的同学可联系本人QQ1972218606
---------------------------------------------
————————————————
相关文章:
基于32单片机的RS485综合土壤传感器检测土壤PH、氮磷钾的使用(超详细)
1-3为RS485综合土壤传感器的基本内容 4-5为基于STM32F103C8T6单片机使用RS485传感器检测土壤PH、氮磷钾并显示在OLED显示屏的相关配置内容 注意:本篇文件讲解使用的是PH、氮磷钾四合一RS485综合土壤传感器,但里面的讲解内容适配市面上的所有多合一的RS…...
SQL server学习03-创建和管理数据表
目录 一,SQL server的数据类型 1,基本数据类型 2,自定义数据类型 二,使用T-SQL创建表 1,数据完整性的分类 2,约束的类型 3,创建表时创建约束 4,任务 5,由任务编写…...
Windows 系统下 Python 环境安装
一、引言 Python 作为一种广泛应用的编程语言,在数据分析、人工智能等领域发挥着重要作用。本文将详细介绍在 Windows 系统上安装 Python 环境的步骤。 二、安装前准备 系统要求 Windows 7 及以上版本一般都能支持 Python。硬件方面,通常 2GB 内存、几…...
Redis 在Go项目中的集成和统一管理
本节我们在项目中安装和集成 go-redis,让项目能访问Redis,后面实战项目中的用户认证体系会依赖Redis来实现,像Token、Session这些都是在Redis中存储的。 本节大纲如下: Redis的使用场景有不少,不过有一点需要提醒的…...
Hive——HQL数据定义语言
文章目录 Hive HQL数据查询语言更多大数据资源持续更新中。。。学习目标一、HQL数据定义语言(DDL)概述1、DDL语法的作用2、Hive中DDL使用☆ 创建数据库☆ 查询数据库☆ 切换数据库☆ 修改数据库☆ 删除数据库 二、Hive DDL建表基础1、完整建表语法树2、H…...
Python机器视觉的学习
一、二值化 1.1 二值化图 二值化图:就是将图像中的像素改成只有两种值,其操作的图像必须是灰度图。 1.2 阈值法 阈值法(Thresholding)是一种图像分割技术,旨在根据像素的灰度值或颜色值将图像分成不同的区域。该方法…...
使用navicat新旧版本,连接PostgreSQL高版本报错问题图文解决办法
使用navicat新旧版本,连接PostgreSQL高版本报错问题图文解决办法 一、问题现象:二、出现原因三、解决方法:1、升级Navicat版本:2、使用低版本的postgreSQL:3、修改Navicat的dll二进制文件:navicat版本15nav…...
秒杀抢购场景下实战JVM级别锁与分布式锁
背景历史 在电商系统中,秒杀抢购活动是一种常见的营销手段。它通过设定极低的价格和有限的商品数量,吸引大量用户在特定时间点抢购,从而迅速增加销量、提升品牌曝光度和用户活跃度。然而,这种活动也对系统的性能和稳定性提出了极…...
【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数(四)
目录 ARC规则 规则 对象型变量不能作为C语言结构体的成员 显式转换id和void* 属性 数组 ARC规则 规则 在ARC有效的情况下编译源代码必须遵守一定的规则: 主要解释一下最后两条 对象型变量不能作为C语言结构体的成员 要把对象型变量加入到结构体成员中时&a…...
Reactor 响应式编程(第一篇:Reactor核心)
系列文章目录 Reactor 响应式编程(第一篇:Reactor核心) Reactor 响应式编程(第二篇:Spring Webflux) Reactor 响应式编程(第三篇:R2DBC) Reactor 响应式编程(…...
数据挖掘之聚类分析
聚类分析(Clustering Analysis) 是数据挖掘中的一项重要技术,旨在根据对象间的相似性或差异性,将对象分为若干组(簇)。同一簇内的对象相似性较高,而不同簇间的对象差异性较大。聚类分析广泛应用…...
修改uniapp下拉刷新圆圈颜色
直接看图 修改前就是常规的绿色 自定义更符合我们的软件 直接说方法 修改 在App.vue的style样式里添加一行 .uni-page-refresh--refreshing .uni-page-refresh__path{stroke:#FF2442; }我是通过 不执行 uni.stopPullDownRefresh(); 下拉刷新 之后通过F12看出来的 希望可以帮…...
SparkSQL与Hive的整合
文章目录 SparkSQL与Hive的整合1.1. Spark On Hive1.1.1. Hive的准备工作1.1.2. Spark的准备工作1.1.3. Spark代码开发1.1.4. Spark On Hive案例 1.2. Hive On Spark1.3. SparkSQL命令行1.4. SparkSQL分布式查询引擎1.4.1. 开启ThriftServer服务1.4.2. beeline连接ThriftServer…...
电子科技大学考研,计算机与软件专业怎么选择?
电子科技大学在计算机与软件领域具备卓越实力,其毕业生就业前景及薪资水平均颇为可观。因此,学生应依据个人课程专长来选定专业。若各项课程均表现出色,推荐25届考生优先考虑软件专业,因其上岸难度相对较低。 接下来,C…...
MSF(Metasploit Framework)
渗透测试中MSF是一个非常强大的工具,可以用来验证系统漏洞、执行攻击以及开发自定义的漏洞利用代码。以下是使用MSF进行渗透测试的基本步骤: 1.启动MSF 启动MSF控制台。 msfconsole2. 搜索漏洞 在MSF中搜索已知漏洞。 search <vulnerability nam…...
@SpringBootTest 报错: UnsatisfiedDependencyException
Spring Boot Test 报错: UnsatisfiedDependencyException 在使用 SpringBootTest 测试时,出现 UnsatisfiedDependencyException 报错,原因和解决方法如下。 报错原因分析 1. Spring 存在涉及 Bean 没有被添加 Spring Boot 测试中,默认会加…...
QT数据库操作详解
在Qt中,操作数据库通常使用Qt SQL模块,该模块提供了一组类来与数据库进行交互。 数据库连接与查询执行 QSqlDatabase::addDatabase(): 添加一个数据库连接。 QSqlDatabase::open(): 打开数据库连接。 QSqlDatabase::close(): 关闭数据库连接。 QSql…...
Coding Caprice - dynamic programming13
647. 回文子串 class Solution { public:int countSubstrings(string s) {int s_len s.size();vector<int> dp{0};int out(1);for(int i1; i<s_len; i){out;vector<int> dq{i};if(s[i]s[i-1]){dq.push_back(i-1);out;}for(int j:dp){if(j>0&&s[j-1…...
Fastapi教程:使用 aioredis 连接池执行Redis 的高效异步操作
在构建高性能的 Web 应用时,缓存系统是一个至关重要的组成部分。Redis 是最常见的缓存系统之一,它提供了高效的存储与读取机制。然而,在与 Redis 进行频繁交互时,创建和销毁连接可能会成为瓶颈。为了优化这一问题,我们…...
Elasticsearch对象映射
Spring Data Elasticsearch对象映射是将Java对象(域实体)映射到存储在Elasticsearchs中的JSON表示形式并返回的过程。内部用于此映射的类是MappingElasticsearchConverter。 元模型对象映射 基于元模型的方法使用域类型信息对Elasticsearch进行读写操作…...
解决Logitech G hub 无法进入一直转圈的方案(2024.12)
如果你不是最新版本无法加载尝试以下方案:删除AppData 文件夹下的logihub文件夹 具体路径:用户名根据实际你的请情况修改 C:\Users\Administrator\AppData\Local 如果你有通过lua编译脚本,记得备份!! ↓如果你是最新…...
Debezium SchemaNameAdjuster 分析
Debezium SchemaNameAdjuster 分析 目录 1. 概述2. 核心功能3. 实现原理4. 应用场景5. 扩展示例6. 总结1. 概述 SchemaNameAdjuster 是 Debezium 中的一个工具类,主要用于确保 Schema 名称符合 Avro 命名规范。在数据库变更事件被转换为 Kafka 消息时,需要为每个表和字段创…...
聊一下前端常见的图片格式
1. JPEG (JPG) 概述:是一种有损压缩的图像格式,它通过去除图像中一些人类视觉不易察觉的细节来减小文件大小。它支持数百万种颜色,能够很好地呈现照片等色彩丰富的图像内容。优点: 压缩率高:可以在保持相对较好的图像…...
npm : 无法加载文件 D:\nodejs\npm.ps1
问题描述 npm run serve 启动一个Vue项目,报错如下: npm : 无法加载文件 D:\nodejs\npm.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/? LinkID135170 中的 about_Execution_Policies。…...
如何使用 Python 实现 TCP / IP 客户端和服务端通信?
如何使用Python实现TCP/IP客户端和服务端通信? 1. TCP/IP通信基础 TCP/IP(传输控制协议/互联网协议)是互联网的基础协议,用于在网络中的计算机之间进行可靠的数据传输。在Python中,可以使用socket模块来实现TCP/IP通…...
IDEA 可视化使用 git rebase 合并分支步骤 使git分支树保持整洁
模拟环境 dev 分支开发完一个功能,需要合并到 master 分支,如果现在直接 merge 合并的话 git分支树会出现杂乱分叉,先把 master 分支 rebase 到 dev git分支树就会是整洁的一条直线 git rebase介绍 rebase:翻译成中文是重新设定,…...
【指南】03 CSC联系外导
确定外导 课题组有合作关系的国外导师与自己研究方向密切相关的国外导师国外高校官网、谷歌学术、Research Gate等平台检索不可以是中国港澳台的高校科研院所或机构注意外导所在高校排名和科研水平可列表记录注意外国签证政策 发送邮件 自我介绍简要介绍CSC介绍自己的研究对…...
axios请求拦截器和响应拦截器,封装naive-ui的 Loading Bar加载条和useMessage消息提示
接之前的博客设计从0开始边做边学,用vue和python做一个博客,非规范化项目,怎么简单怎么弄,跑的起来有啥毛病解决啥毛病(三),目前已经完成了基本的功能demo,但是请求接口不可能每个页…...
联网功耗电流波形
飞行模式下,Wifi 可连接或不可连接的条件对比: 1. 基电流为 3.5 mA 的环境下, 网络不可连接时,会产生一个持续0.72s,平均电流为 54.8 mA 的电流波形 2. 基电流为 6.8 mA 的环境下, 网络可连接时,会产生一个持续4.64s,平均电流为 73.63 mA 的电流波形 …...
Unity 模板测试透视效果(URP)
可以实现笼中窥梦和PicoVR中通过VST局部透视效果。 使用到的Shader: Shader "Unlit/StencilShader" {Properties{[IntRange]_Index("Stencil Index",Range(0,255))0}SubShader{Tags{"RenderType""Opaque""Queue""Geo…...
C 语言动态爱心代码
C 语言动态爱心代码 代码 #include <stdio.h> #include <math.h> #include <windows.h> #include <tchar.h> float f(float x, float y, float z) {float a x * x 9.0f / 4.0f * y * y z * z - 1;return a * a * a - x * x * z * z * z - 9.0f / …...
Linux服务器磁盘满了,清理步骤命令
Linux服务器磁盘满了,磁盘空间不足,清理步骤 1. 检查磁盘使用情况 了解哪些文件和目录占用了大量空间。 使用 df -h查看所有分区的总体使用情况。 使用 du -sh /directory/* | sort -rh | head -n 10 查找特定目录下占用空间最大的前10个子目录或文件。…...
CTFshow-命令执行(Web41-57)
CTFshow-命令执行(Web41-57) CTFWeb-命令执行漏洞过滤的绕过姿势_绕过空格过滤-CSDN博客 总结rce(远程代码执行各种sao姿势)绕过bypass_远程命令执行绕过-CSDN博客 对比两者的源代码,我们发现,cat指令把flag.php的内容导出后依…...
Batch Norm vs Layer Norm:为什么 Transformer 更适合用 Layer Norm?
Batch Norm vs Layer Norm:为什么 Transformer 更适合用 Layer Norm? 1. Batch Norm 和 Layer Norm 的定义与作用 1.1 Batch Normalization (BN) Batch Norm 是一种归一化方法,主要用于加速深层神经网络的训练。它在每个小批量(b…...
jQuery Mobile页面事件
jQuery Mobile页面事件 jQuery Mobile是一个基于jQuery的移动设备友好的Web应用框架,它提供了一套丰富的页面事件,这些事件在移动应用的交互设计中扮演着重要的角色。本文将详细介绍jQuery Mobile中的页面事件,包括它们的触发时机、用途以及如何使用它们来增强移动应用的交…...
接口测试Day01-HTTP请求
概念 接口:系统之间(外部系统与内部系统,内部系统与内部系统)数据交通的通道。 接口测试:校验 接口回发的 响应数据 与 预期结果 是否一致。 接口测试,可以绕过前端界面。直接对 服务器进行测试!…...
使用枚举实现单例模式,不会反序列化破坏攻击,不会被反射破坏攻击。(附带枚举单例的简单实现)
原因分析 1.反序列化方法 ① jdk8中的Enum源码中对反序列化方法进行重写,抛出异常。 java.lang.Enum#readObject方法截图如下 ②java.io.ObjectInputStream#readObject 方法中的 readEnum 方法处理了枚举类型的反序列化,从而确保了枚举的单例特性。 …...
又细又长的马尾:tail
英语里边有一个单词 tail,意为“尾巴”,这应当是众所周知的事情了。 不过,tail 这条尾巴,并不简单,因为它还是一个词根,也就是说 tail 其实是自由词素。 事实上,tail 最初来自 马尾 这样一个概…...
记录:VB6 直接获取 PictureBox 的图像数据到数组 GetDIBits
记得十几年前写几个游戏辅助工具的时候用过这个功能,这几天想直接把图片控件的数据转换为 PNG 文件不想用存出 BMP 文件交换,直接取得图像数据操作即可,但是忘记了当初是怎么做的了,找到个 2007 年的例子好像不太对,运…...
uboot移植网络驱动过程,无法ping通mx6ull和ubuntu问题解决方案
开发板:mx6ull-ALPHA_V2.4 ubuntu版本:20.04 1.现在虚拟机设置中添加网路适配器用于开启桥接模式 2.在编辑中打开“虚拟网络编辑器” 我的电脑本身只有VMnet1和VMnet8,需要底下“添加网络”,增加这个VMnet0 ,并且进行…...
Ubuntu 安装软件被锁:Could not get lock问题解决
今天刚使用虚拟机安装完Ubuntu系统,想要安装所需要的软件是出现了以下错误信息: 错误信息: E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable) E: Unable to lock the administration directory (/…...
S2CRNet 图像测评笔记 图像融合
空间分离曲线渲染网络用于高效高分辨率图像协调 开源地址: https://github.com/stefanLeong/S2CRNet 效果图: 左边是输入,最右边是效果:效果不是很理想,色差问题还在 本地代码: S2CRNet-demos-main...
Lambda表达式
C Lambda表达式 文章目录 C Lambda表达式基本用法捕获列表返回值mutable Lambda 表达式是一种匿名函数,可以在代码中直接定义并使用。它主要用于简化那些只需要简单操作的函数定义。在 C 中,lambda 表达式的语法结构通常包括以下几个部分: 捕…...
高通 Android12 添加APN信息
1、产品有国外客户,需要添加国外的定制APN信息。 2、路径: SC200E_AP/QCM2290_Android12.0_R02_r004/QSSI.12/vendor/qcom/proprietary/commonsys/telephony-apps/etc/apns-conf.xml在上述路径中将APN信息添加即可。 3、路径 SC200E_AP\QCM2290_Andr…...
探秘 IIC 与 SPI:软件模拟与硬件接口的抉择之谜
一、IIC 软件模拟:受限中的灵活应变 在嵌入式系统的通信世界里,IIC 常采用软件模拟的方式开展工作,这背后有着诸多考量。首先,硬件资源的限制是一个重要因素。不少微控制器并没有内置功能完备的 IIC 硬件模块,甚至压根…...
【ts语法学习】主要数据类型与变量声明时的类型注解
一、ts中的主要数据类型 1.布尔类型 (boolean) 2.数值类型 (number) 3.字符串类型 (string) 4.数组类型 (Array) 5.对象类型 (object) 6.null 和 undefined 7.元组类型 (Tuple) 8.枚举类型 (enum) 9.任意类型 (any) 10.never 11.unknown 12.void TypeScript(简称 …...
论文概览 |《Sustainable Cities and Society》2024.12 Vol.116
本次给大家整理的是《Sustainable Cities and Society》杂志2024年12月第116期的论文的题目和摘要,一共包括52篇SCI论文! 论文1 Enhancing road traffic flow in sustainable cities through transformer models: Advancements and challenges 通过变压…...
Vue3之响应式系统详解
Vue3中的响应式系统是其核心功能之一,它使得数据变化能够自动触发视图更新,从而简化了开发过程,提高了开发效率。本文将详细阐述Vue3中的响应式系统,包括其核心概念、工作原理、实现方式、应用场景以及优势。同时,本文…...
只出现一次的数字(字节面试题 最优解)
题目来源 136. 只出现一次的数字 - 力扣(LeetCode) 题目描述 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问…...
VMware Workstation的有线连接消失了
进入/var/lib目录下 cd /var/lib 查看是否存在NetworkManager 文件 ls 将其删除,然后虚拟机reboot一下。 sudo rm -r NetworkManager reboot 解决了,可以联网...