基于App控制的STM32温湿度控制系统(WIFI模块)
ESP8266WIFI模块
在这个项目中我选用的正点原子的WIFI模块(ATK-ESP8266),这个模块采用串口(LVTTL)与MCU(或其他串口设备)通信,内置了TCP/IP的协议栈,可以实现串口与WIFI直接的转换,通过ATK-SEP8266模块,传统的串口设备只是需要简单的串口配置,即可通过网络(WIFI)传输自己的数据。
模块使用说明
其实挺简单的,拿出你的模块看,VCC就接3.3V或者5V,GND就接GND啦,然后WIFI模块与串口设备TX接RX,RX接TX,RST的复位是低电平有效。IO-0适用于固件烧写的,低电平就是烧写模式,高电平时运行模式(默认态)
RST和IO-0一般是不用接的(一般使用只用接前4个就好了)!
常用AT指令
指令 | 描述 |
---|
AT+CWMODE | 选择 WIFI 应用模式 | AT+CWJAP | 加入 AP | AT+CWLAP | 列出当前可用 AP | AT+CWQAP | 退出与 AP 的连接 | AT+CWSAP | 设置 AP 模式下的参数 | AT+CWLIF | 查看已接入设备的 IP | AT+CWDHCP | 设置 DHCP 开关 | AT+CWAUTOCONN | 设置 STA 开机自动连接到 wifi | AT+CIPSTAMAC | 设置 STA 的 MAC 地址 | AT+CIPAPMAC | 设置 AP 的 MAC 地址 | AT+CIPSTA | 设置 STA 的 IP 地址 | AT+CIPAP | 设置 AP 的 IP 地址 | AT+CWSTARTSMART | 启动智能连接 | AT+CWSTOPSMART | 停止智能连接 | AT+WPS | 设置 WPS 功能 | AT+MDNS | 设置 MDNS 功能 | AT+CWHOSTNAME | 设置 ATK-ESP-01 Station 的主机名字 |
具体AT指令的使用还是得去看下模块的用户手册,这里就不做过多的赘述了。
DHT11温湿度传感器
简介
参数 | Value |
---|
工作电压 | 3.3V~5V | 湿度测量范围 | 20%~95% | 温度测量范围 | 0°~50° |
传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。(如下图所示)
模块数据的发送流程
首先主机发送开始信号,即:拉低数据线,保持t1(至少18ms)时间,然后拉高数据线t2(20-40us)时间,然后读取DHT11的响应,正常的话,DHT11会拉低数据线,保持t3(40-50us)时间,作为响应信号,然后DHT11拉高数据线,保持t4(40-50us)时间后,开始输出数据。
代码实现
void DHT11_PIN_MODE(uint8_t mode)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(mode==Intput)
{
GPIO_InitStruct.Pin = DHT11_DATA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_DATA_GPIO_Port, &GPIO_InitStruct);
}else
{
GPIO_InitStruct.Pin = DHT11_DATA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_DATA_GPIO_Port, &GPIO_InitStruct);
}
}
static uint8_t DHT11_ReadByte ( void )
{
DHT11_PIN_MODE(Intput);
uint8_t i, temp=0;
for(i=0;i<8;i++)
{
while(Read_DHT11_DATA()==GPIO_PIN_RESET);
HAL_Delay_us(40);
if(Read_DHT11_DATA()==GPIO_PIN_SET)
{
while(Read_DHT11_DATA()==GPIO_PIN_SET){}
temp|=(uint8_t)(0x01<<(7-i));
}else
{
temp&=(uint8_t)~(0x01<<(7-i));
}
}
return temp;
}
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{
if(Frame_Flag.dht11flag++ >= 1000 )
Frame_Flag.dht11flag = 0;
uint8_t temp;
uint16_t humi_temp;
DHT11_PIN_MODE(Output);
DHT11_DATA_RESET();
Delay_ms(18);
DHT11_DATA_SET();
HAL_Delay_us(30);
DHT11_PIN_MODE(Intput);
if(Read_DHT11_DATA()==GPIO_PIN_RESET)
{
while(Read_DHT11_DATA()==GPIO_PIN_RESET);
while(Read_DHT11_DATA()==GPIO_PIN_SET);
DHT11_Data->humi_high8Bit= DHT11_ReadByte();
DHT11_Data->humi_low8bit = DHT11_ReadByte();
DHT11_Data->temp_high8bit= DHT11_ReadByte();
DHT11_Data->temp_low8bit = DHT11_ReadByte();
DHT11_Data->check_sum = DHT11_ReadByte();
DHT11_PIN_MODE(Output);
DHT11_DATA_SET();
humi_temp=DHT11_Data->humi_high8Bit*100+DHT11_Data->humi_low8bit;
DHT11_Data->humidity =(float)humi_temp/100;
humi_temp=DHT11_Data->temp_high8bit*100+DHT11_Data->temp_low8bit;
DHT11_Data->temperature=(float)humi_temp/100;
temp = DHT11_Data->humi_high8Bit + DHT11_Data->humi_low8bit +
DHT11_Data->temp_high8bit+ DHT11_Data->temp_low8bit;
if(DHT11_Data->check_sum==temp)
{
return SUCCESS;
}else
{
return ERROR;
}
}else
{
return ERROR;
}
}
这里需要说明的是,如果用的是FreeRTOS的话就需要开多一个定时器用来进行μs级别的计时,同时不能放在任务中跑,这样任务很容易阻塞导致系统的暂停运行。
μs级的延时配置
这里选用的TIM6用来做计时器
HAL库配置
由于TIM6是挂载在APB1上的,所以要APB1的值 因为我APB1Timer clocks是90MHz 所以89+1/90=1MHz,也就是1μs计数一次。
代码实现
void HAL_Delay_us(uint16_t us)
{
uint16_t differ = 0xffff-us-5;
HAL_TIM_Base_Start(&htim6);
__HAL_TIM_SetCounter(&htim6,differ);
while(differ < 0xffff-5)
{
differ = __HAL_TIM_GetCounter(&htim6);
}
HAL_TIM_Base_Stop(&htim6);
}
项目总框架
讲完这个项目所用的两个外设了,现在来说一下整个项目的总体设计思路。
总框架图
如上图所示,首先我们利用AT指令初始化WIFI模块,然后WIFI模块等待APP建立TCP连接。建立完连接之后,便利用TCP间客户机与服务机之间的通信进行数据的传输。DHT11模块接收到温湿度信号后发送脉冲给单片机的IO口,IO口读取电平后将其转化成数字,再由单片机的串口发送到WIFI模块回显在APP上面。
单片机控制流程图
单片机首先初始化板上的资源(IO口、串口、WIFI模块),然后开启了串口中断,DMA传输,以及创建控制任务。开启了串口中断后就开始接收数据。如果没有接收到数据,则返回重新接收,如果有,就把数据存进缓冲区,存进缓冲区之后,程序会对缓冲区的数据进行解包处理后存进结构体对应的成员变量内,然后通过读取温度与湿度的成员变量,通过串口发送给WIFI模块,APP对数据进行回显。
APP控制流程图
在打开APP的时候,在界面中输入服务器IP地址和端口号,点击CONNECT按钮就会跟服务器建立TCP连接。然后在控制界面有开灯、关灯、播放音乐的按钮,点击可以触发对应的事件然后向WIFI模块发送信息。WIFI模块收到信息之后通过AT指令会发送数据给APP,APP处理之后可以回显到界面上。
项目代码实现
WIFI模块
首先需要利用单片机给WIFI模块通过串口传输发送以下AT指令进行初始化:
char rst[] = "AT+RESTORE\r\n";
char cipmode[] = "AT+CIPMODE=0\r\n";
char mux[] = "AT+CIPMUX=1\r\n";
char server[] = "AT+CIPSERVER=1\r\n";
当APP(从机)与WIFI模块(主机建立连接后)需要发送如下指令:
char cipsend[] = "AT+CIPSEND=0,30\r\n";
STM32单片机(主机)
初始化WIFI模块
HAL_UART_Transmit(&huart7,(uint8_t *)rst,strlen(rst),0xFFFF);
HAL_Delay(5000);
memset(ReceiveBuff,'\0',PackSize);
HAL_Delay(1000);
HAL_UART_Transmit(&huart7,(uint8_t *)cipmode,strlen(cipmode),0xFFFF);
HAL_Delay(2000);
memset(ReceiveBuff,'\0',PackSize);
HAL_Delay(1000);
HAL_UART_Transmit(&huart7,(uint8_t *)mux,strlen(mux),0xFFFF);
HAL_Delay(2000);
memset(ReceiveBuff,'\0',PackSize);
HAL_Delay(1000);
HAL_UART_Transmit(&huart7,(uint8_t *)server,strlen(server),0xFFFF);
HAL_Delay(2000);
memset(ReceiveBuff,'\0',PackSize);
控制任务
void Control_Task(void *argument)
{
portTickType xLastWakeTime;
xLastWakeTime = xTaskGetTickCount();
switch(data_wifi.control_light)
{
case 'W':
LIGHT_CLOSE();
Send_PackCage.light_status = 'n';
break;
case 'Q':
LIGHT_ON();
Send_PackCage.light_status = 'y';
break;
default:
break;
}
switch(data_wifi.control_music)
{
case 'A':
LittleStar_InitMusic();
Send_PackCage.music_status = 'l';
break;
case 'S':
HappySong_InitMusic();
Send_PackCage.music_status = 'h';
break;
case 'D':
SuperMario_InitMusic();
Send_PackCage.music_status = 's';
break;
case '#':
musicflag = 1;
break;
default:
HAL_TIM_PWM_Stop(&htim12, TIM_CHANNEL_1);
Send_PackCage.music_status = 'n';
break;
}
if(Frame_Flag.controlflag++ >= 1000 )
Frame_Flag.controlflag = 0;
vTaskDelayUntil(&xLastWakeTime, 50 / portTICK_RATE_MS);
}
}
DHT11数据发送给WIFI模块
if(Count_Data.dht11_count >= TASK_DHT11)
{
DHT11_Read_TempAndHumidity(&DHT11_Data);
sprintf(str,"%.2f",DHT11_Data.temperature);
strcat(str,fuhao);
sprintf(humi,"%f",DHT11_Data.humidity);
strcat(str,humi);
if(Init_FLAG == 1)
{
HAL_UART_Transmit(&huart7,(uint8_t *)cipsend,strlen(cipsend),0xFFFF);
HAL_Delay(200);
HAL_UART_Transmit(&huart7,(uint8_t *)str,strlen(str),0xFFFF);
}
Count_Data.dht11_count = 0;
}
APP的编写
布局文件
内容偏长,需要的可以复制,不需要的直接跳过就好了
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_gravity="center"
android:layout_marginTop="40dp"
android:background="@drawable/wolf"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Smart Home Center"
android:textStyle="bold"
android:textColor="@color/white"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:paddingLeft="50dp">
<TextView
android:id="@+id/textip"
android:layout_width="70dp"
android:layout_height="40dp"
android:text="设备IP:"
android:gravity="center"
android:textColor="@color/white" />
<EditText
android:id="@+id/ip"
android:layout_width="250dp"
android:layout_height="40dp"
android:layout_toRightOf="@+id/textip"
android:gravity="center"
android:maxLines="1"
android:textColor="@color/white" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:paddingLeft="50dp">
<TextView
android:layout_width="70dp"
android:layout_height="40dp"
android:text="Port:"
android:id="@+id/textport"
android:gravity="center"
android:textColor="@color/white"
android:inputType="number"
/>
<EditText
android:id="@+id/port"
android:layout_width="250dp"
android:layout_height="40dp"
android:layout_toRightOf="@+id/textport"
android:maxLines="1"
android:textColor="@color/white" />
</RelativeLayout>
<Button
android:layout_width="150dp"
android:layout_height="40dp"
android:background="#00B8D4"
android:layout_gravity="center"
android:textColor="@color/white"
android:textStyle="bold"
android:text="Connect"
android:onClick="Connect"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:paddingLeft="50dp">
<TextView
android:id="@+id/textTem"
android:layout_width="180dp"
android:layout_height="20dp"
android:text="Home Of Temperature:"
android:textColor="@color/white"
android:textSize="15sp"
/>
<TextView
android:id="@+id/temperature"
android:layout_width="60dp"
android:layout_height="20dp"
android:layout_toRightOf="@+id/textTem"
android:textColor="@color/white"
android:textSize="15sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:paddingLeft="50dp">
<TextView
android:id="@+id/texthumi"
android:layout_width="180dp"
android:layout_height="20dp"
android:text="Home Of Humidity:"
android:textColor="@color/white"
android:textSize="15sp" />
<TextView
android:id="@+id/humidity"
android:layout_width="35dp"
android:layout_height="20dp"
android:layout_toRightOf="@+id/texthumi"
android:textColor="@color/white"
android:textSize="15sp" />
<TextView
android:layout_width="20dp"
android:layout_height="20dp"
android:id="@+id/fuhao"
android:layout_toRightOf="@id/humidity"
android:textColor="@color/white"
android:textSize="15sp"/>
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Device_Control"
android:textColor="@color/white"
android:layout_marginTop="10dp"
android:textSize="20sp"
android:layout_gravity="center"
android:textStyle="bold"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=" —————————————————————————"
android:textColor="@color/white"
android:layout_marginTop="5dp"
/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_gravity="center"
>
<Button
android:id="@+id/online"
android:layout_width="120dp"
android:layout_height="50dp"
android:background="#0AD9F6"
android:text="JudgeOnline"
android:textColor="@color/white"
android:textSize="12dp"
android:textStyle="italic" />
<TextView
android:id="@+id/status"
android:layout_width="200dp"
android:layout_height="50dp"
android:layout_toRightOf="@+id/online"
android:gravity="center"
android:text="Wait"
android:textColor="@color/white"
android:textSize="20dp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_gravity="center"
>
<Button
android:id="@+id/lighton"
android:layout_width="150dp"
android:layout_height="50dp"
android:background="#0AD9F6"
android:text="LIGHTON"
android:textColor="@color/white"
android:textStyle="italic" />
<Button
android:id="@+id/lightclose"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_toRightOf="@+id/lighton"
android:layout_marginLeft="20dp"
android:background="#0AD9F6"
android:gravity="center"
android:text="LIGHTCLOSE"
android:textColor="@color/white" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_gravity="center"
>
<Button
android:id="@+id/music1"
android:layout_width="120dp"
android:layout_height="50dp"
android:background="#0AD9F6"
android:text="MUSIC1"
android:textColor="@color/white"
android:textStyle="italic" />
<Button
android:id="@+id/music2"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_toRightOf="@id/music1"
android:layout_marginLeft="10dp"
android:background="#0AD9F6"
android:text="MUSIC2"
android:textColor="@color/white"
android:textStyle="italic" />
<Button
android:id="@+id/music3"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_toRightOf="@id/music2"
android:layout_marginLeft="10dp"
android:background="#0AD9F6"
android:text="MUSIC3"
android:textColor="@color/white"
android:textStyle="italic" />
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:layout_gravity="center"
>
<TextView
android:layout_width="120dp"
android:layout_height="50dp"
android:gravity="center"
android:text="No Music"
android:textColor="@color/white"
android:textSize="20sp"
android:id="@+id/musicname"
/>
<Button
android:layout_width="100dp"
android:layout_height="20dp"
android:layout_alignBottom="@+id/musicname"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="-12dp"
android:id="@+id/shutdown"
/>
</RelativeLayout>
</LinearLayout>
效果图 小黄鸭除外😿!!
逻辑代码
package com.example.smart_homeapp;
import androidx.appcompat.app.AppCompatActivity;
import android.icu.util.Output;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ThreadLocalRandom;
public class MainActivity extends AppCompatActivity {
Client_Thread client;
Socket ClientSocket;
InputStream is;
OutputStream os;
EditText et_ip,et_port;
TextView tv_online,tv_music,tv_temperature,tv_humidity,tv_fuhao;
Button btn_online,btn_lighton,btn_lightclose,btn_music1,btn_music2,btn_music3,btn_sd;
ButtonListener buttonListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Viewinit();
Listenerinit();
}
public void Viewinit(){
et_ip = (EditText) findViewById(R.id.ip);
et_port = (EditText)findViewById(R.id.port);
tv_online = (TextView) findViewById(R.id.status);
tv_music = (TextView)findViewById(R.id.musicname);
tv_temperature = (TextView)findViewById(R.id.temperature);
tv_humidity = (TextView)findViewById(R.id.humidity);
tv_fuhao = (TextView)findViewById(R.id.fuhao);
btn_online = (Button) findViewById(R.id.online);
btn_lighton = (Button) findViewById(R.id.lighton);
btn_lightclose = (Button) findViewById(R.id.lightclose);
btn_music1 = (Button) findViewById(R.id.music1);
btn_music2 = (Button) findViewById(R.id.music2);
btn_music3 = (Button) findViewById(R.id.music3);
btn_sd = (Button) findViewById(R.id.shutdown);
}
public void Connect(View view) {
String ip = et_ip.getText().toString();
int port = Integer.parseInt(et_port.getText().toString());
Toast.makeText(MainActivity.this,"Wait a second...",Toast.LENGTH_LONG).show();
client = new Client_Thread(ip,port);
client.start();
Toast.makeText(MainActivity.this, "Connect Successfully", Toast.LENGTH_SHORT).show();
}
class ButtonListener implements View.OnClickListener{
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.online:
send("##C");
tv_online.setText("Already done");
Toast.makeText(MainActivity.this, "Please..Wait...", Toast.LENGTH_SHORT).show();
break;
case R.id.lighton:
send("Q##");
Toast.makeText(MainActivity.this, "The Light is openned", Toast.LENGTH_SHORT).show();
break;
case R.id.lightclose:
send("W##");
Toast.makeText(MainActivity.this, "The Light is closed", Toast.LENGTH_SHORT).show();
break;
case R.id.music1:
send("#A#");
Toast.makeText(MainActivity.this, "Little_Start Music Begin", Toast.LENGTH_SHORT).show();
tv_music.setText("Little_Start");
break;
case R.id.music2:
send("#S#");
Toast.makeText(MainActivity.this, "Happy_Song Music Begin ", Toast.LENGTH_SHORT).show();
tv_music.setText("Happy_Song");
break;
case R.id.music3:
send("#D#");
Toast.makeText(MainActivity.this, "Mario_Song Music Begin", Toast.LENGTH_SHORT).show();
tv_music.setText("Mario_Song");
break;
case R.id.shutdown:
send("###");
Toast.makeText(MainActivity.this, "Shut down All!!", Toast.LENGTH_SHORT).show();
tv_music.setText("No Music");
}
}
}
public void Listenerinit(){
buttonListener = new ButtonListener();
btn_online.setOnClickListener(buttonListener);
btn_lighton.setOnClickListener(buttonListener);
btn_lightclose.setOnClickListener(buttonListener);
btn_music1.setOnClickListener(buttonListener);
btn_music2.setOnClickListener(buttonListener);
btn_music3.setOnClickListener(buttonListener);
btn_sd.setOnClickListener(buttonListener);
}
public void send(String msg){
new SendThread(msg).start();
}
class SendThread extends Thread{
String msg;
public SendThread(String msg){
this.msg = msg;
}
@Override
public void run() {
try {
if(os == null)
{
return;
}
os.write(msg.getBytes());
os.flush();
}catch (IOException e){
e.printStackTrace();
}
}
}
String str;
class Client_Thread extends Thread{
String ip;
int port;
public Client_Thread(String ip,int port){
this.ip = ip;
this.port = port;
}
public void run() {
try {
ClientSocket = new Socket(ip,port);
is = ClientSocket.getInputStream();
os = ClientSocket.getOutputStream();
while(true){
final byte[] buf = new byte[1024];
final int len = is.read(buf);
str = new String(buf,0,len);
runOnUiThread(()->{
String[] strs = str.split(",");
tv_temperature.setText(strs[0]+"°");
tv_humidity.setText(strs[1]);
tv_fuhao.setText("%");
Log.e("TAG",str);
});
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
这里创建了两个线程,一个是客户端线程,用来接收服务器发来的数据,一个是发送线程,用来发送APP点击触发的事件需要发送的信息。在客户端线层中用了split来进行分隔接受回来的数据。类似于去除帧头帧尾。
BUG记录
WIFI模块的发送
这个真的很坑!如果你用WIFI模块作为服务器的话,也就是说WIFI模块被设置成AP模式的话,那么就得先把WIFI模块的透传模式关了,不然的话CIPSEND发送一直都是返回ERROR的。 解决方法:按照我上图的初始化逻辑就好了,至于CIPSEND,看个人需求放不同的位置就好了。
开任务之后main.c里的while不跑了
md这个问题就很灵性,由于在任务里面没有us级的延时(可能有,但是我不知道),但是处理DHT11模块返回来的数据需要用us级的延时等待脉冲的产生再进行置位。因为潜意识里在任务里面除了用osDelay外其他的Delay类似于HAL_Delay这些会对任务造成阻塞然后程序就死掉了。 解决方法:把main.c里的任务创建注释了然后全部把代码放在while里面利用delay计时跑个假的任务处理。(虽然是在摆烂,但是确实能解决ddl带来的焦虑) 打算尝试把us级的自定义延时函数放在任务里面跑,看会不会阻塞。
|