634|1

501

帖子

4

TA的资源

纯净的硅(高级)

楼主
 

【得捷电子Follow me第3期】+开灯提醒器 [复制链接]

 

一.视频

 
二.总结报告
  1. 硬件环境
配件如下
  1. 主板Seeed Studio XIAO ESP32C3
  2. 扩展板Seeed Studio Expansion Board Base for XIAO(包含屏幕、RTC、蜂鸣器、按钮)
  3. 天线RF ANT 2.4GHZ/5.5GHZ PCB TRACE-IPEX
  4. 排针7-PIN MALE HEADER FOR XIAO 5PACK
  5. 线缆CABLE A PLUG TO C PLUG 3.28'
  6. 温湿度传感器Grove - AHT20 I2C Industrial Grade Temperature and Humidity Sensor
    AHT20
    IIC接口地址0x38
  7. 光线传感器Grove - Light Sensor v1.2
光敏电阻GL5528(亮电阻10-20k,暗电阻1M)和60k电阻分压,lm358电压跟随输出。
ADC采集
焊接排针装好之后

参考
https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/

  1. 任务1:使用MicroPython系统
熟悉Seeed Studio XIAO ESP32C3开发板基本操作,安装esptool,并给开发板刷写MicroPython系统,完成入门程序的运行
搭配器件:Seeed Studio XIAO ESP32C3

3.1 Seeed Studio XIAO ESP32C3开发板熟悉

通过参考链接熟悉开发板,比如熟悉下引脚,原理图等等

3.2 使用esptool工具烧录Micropython固件

下载esptool工具
git clone
链接已隐藏,如需查看请登录或者注册
下载固件
这里最新版是ESP32_GENERIC_C3-20231005-v1.21.0.bin
将下载的bin文件放置于esptool工具的目录esptool/下
安装python库,前提是已经安装了python
右键->在终端中打开,输入如下指令安装对应的库
pip install intelhex
pip install pyserial
按住如下BOOT BUTTON按键,开发板重启进入boot模式
输入如下指令下载固件
python ./esptool.py --chip esp32c3 --port COM9 --baud 921600 --before default_reset --after hard_reset --no-stub write_flash --flash_mode dio --flash_freq 80m 0x0 ESP32_GENERIC_C3-20231005-v1.21.0.bin
其中COM9为对应设备管理器中看到的串口号
ESP32_GENERIC_C3-20231005-v1.21.0.bin是下载的固件名

3.3 Micropython+Thonny体验

1.安装Thonny

以下地址下载安装4.0.2版本https://github.com/thonny/thonny/releases/tag/v4.0.2
注意最新的4.1.3版本不能安装库。
  1. 连接开发板
    打开thonny

    菜单栏点击 运行->配置解释器
    按如下配置

    连接后可以命令行操作,如下
  2. 体验入门程序
    输入如下内容,点击保存
    import time
    from time import sleep
    import machine
    from machine import Pin, SoftI2C
    # Buzzer settings
    buzzer_pin = machine.Pin(5, machine.Pin.OUT)
    buzzer = machine.PWM(buzzer_pin)
    buzzer.freq(1047)
    # Buzzer working
    while True:
    buzzer.duty(10)
    time.sleep(1)
    buzzer.duty(0)
    time.sleep(1)

    选择micropython设备

    存储为boot.py

    点击如下图标运行,可以听到蜂鸣器一秒响一下

    四.任务2:驱动扩展板上的OLED屏幕
    使用扩展板上的OLED屏幕显示文字和图形
    搭配器件:Seeed Studio XIAO ESP32C3Seeed Studio Expansion Board Base for XIAO
  3. 1安装库ssd1306
菜单栏点击工具->管理包
输入库名搜索,安装,如下
网络问题,可能需要多试几次

4.2 显示文字

输入如下代码,保存为boot.py运行
import time
from machine import Pin, SoftI2C
import ssd1306
import math
i2c = SoftI2C(scl=Pin(7), sda=Pin(6)) # Adjust the Pin numbers based on your connections
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
oled.fill(0) # Clear the screen
oled.text("Follow me 3!", 10, 15)
oled.show() # Show the text
效果如下

4.3 显示图形

输入如下代码,保存为boot.py运行
import time
from machine import Pin, SoftI2C
import ssd1306
import math
i2c = SoftI2C(scl=Pin(7), sda=Pin(6))
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
center_x = oled_width // 2
center_y = oled_height // 2
square_size = 6 # Size of each square
num_squares = 12 # Number of squares
angle_increment = 2 * math.pi / num_squares
while True:
oled.fill(0) # Clear the screen
for i in range(num_squares):
angle = i * angle_increment
x = int(center_x + (center_x - square_size-30) * math.cos(angle))
y = int(center_y + (center_x - square_size-30) * math.sin(angle))
# Draw all squares
for j in range(num_squares):
angle_j = j * angle_increment
x_j = int(center_x + (center_x - square_size-30) * math.cos(angle_j))
y_j = int(center_y + (center_x - square_size-30) * math.sin(angle_j))
oled.fill_rect(x_j, y_j, square_size, square_size, 1) # Draw the square
oled.fill_rect(x, y, square_size, square_size, 0) # Erase the current square
oled.show()
time.sleep_ms(100) # Pause before next iteration
效果如下

五.任务3:控制蜂鸣器播放音乐

使用Seeed Studio XIAO ESP32C3控制蜂鸣器发出不同频率的声音,并播放一段音乐

5.1 蜂鸣器驱动

输入如下代码,保存为boot.py运行
import time
from time import sleep
import machine
from machine import Pin, SoftI2C
# Buzzer settings
buzzer_pin = machine.Pin(5, machine.Pin.OUT)
buzzer = machine.PWM(buzzer_pin)
buzzer.freq(1047)
# Buzzer working
while True:
buzzer.duty(10)
time.sleep(1)
buzzer.duty(0)
time.sleep(1)

5.2 蜂鸣器播放音乐原理

不同频率对应不同声调,不同占空比对应不同的响度,演奏时间对应节拍。

5.3 蜂鸣器播放音乐

输入如下代码,保存为boot.py运行
import machine
import time
# Buzzer settings
buzzer_pin = machine.Pin(5, machine.Pin.OUT)
buzzer = machine.PWM(buzzer_pin)
buzzer.freq(1047)
# Defining frequency of each music note
NOTE_C4 = 262
NOTE_D4 = 294
NOTE_E4 = 330
NOTE_F4 = 349
NOTE_G4 = 392
NOTE_A4 = 440
NOTE_B4 = 494
NOTE_C5 = 523
NOTE_D5 = 587
NOTE_E5 = 659
NOTE_F5 = 698
NOTE_G5 = 784
NOTE_A5 = 880
NOTE_B5 = 988
# Music notes of the song, 0 is a rest/pulse
notes = [
NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0,
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0,
NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0,
NOTE_A4, NOTE_G4, NOTE_A4, 0,
NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0,
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0,
NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0,
NOTE_A4, NOTE_G4, NOTE_A4, 0,
NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0,
NOTE_A4, NOTE_C5, NOTE_D5, NOTE_D5, 0,
NOTE_D5, NOTE_E5, NOTE_F5, NOTE_F5, 0,
NOTE_E5, NOTE_D5, NOTE_E5, NOTE_A4, 0,
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0,
NOTE_D5, NOTE_E5, NOTE_A4, 0,
NOTE_A4, NOTE_C5, NOTE_B4, NOTE_B4, 0,
NOTE_C5, NOTE_A4, NOTE_B4, 0,
NOTE_A4, NOTE_A4,
#Repeat of first part
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0,
NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0,
NOTE_A4, NOTE_G4, NOTE_A4, 0,
NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0,
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0,
NOTE_C5, NOTE_D5, NOTE_B4, NOTE_B4, 0,
NOTE_A4, NOTE_G4, NOTE_A4, 0,
NOTE_E4, NOTE_G4, NOTE_A4, NOTE_A4, 0,
NOTE_A4, NOTE_C5, NOTE_D5, NOTE_D5, 0,
NOTE_D5, NOTE_E5, NOTE_F5, NOTE_F5, 0,
NOTE_E5, NOTE_D5, NOTE_E5, NOTE_A4, 0,
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_C5, 0,
NOTE_D5, NOTE_E5, NOTE_A4, 0,
NOTE_A4, NOTE_C5, NOTE_B4, NOTE_B4, 0,
NOTE_C5, NOTE_A4, NOTE_B4, 0,
#End of Repeat
NOTE_E5, 0, 0, NOTE_F5, 0, 0,
NOTE_E5, NOTE_E5, 0, NOTE_G5, 0, NOTE_E5, NOTE_D5, 0, 0,
NOTE_D5, 0, 0, NOTE_C5, 0, 0,
NOTE_B4, NOTE_C5, 0, NOTE_B4, 0, NOTE_A4,
NOTE_E5, 0, 0, NOTE_F5, 0, 0,
NOTE_E5, NOTE_E5, 0, NOTE_G5, 0, NOTE_E5, NOTE_D5, 0, 0,
NOTE_D5, 0, 0, NOTE_C5, 0, 0,
NOTE_B4, NOTE_C5, 0, NOTE_B4, 0, NOTE_A4
]
# Durations (in ms) of each music note of the song
# Quarter Note is 250 ms when songSpeed = 1.0
durations = [
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 375, 125,
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 375, 125,
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 125, 250, 125,
125, 125, 250, 125, 125,
250, 125, 250, 125,
125, 125, 250, 125, 125,
125, 125, 375, 375,
250, 125,
#Rpeat of First Part
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 375, 125,
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 375, 125,
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 250, 125, 125,
125, 125, 125, 250, 125,
125, 125, 250, 125, 125,
250, 125, 250, 125,
125, 125, 250, 125, 125,
125, 125, 375, 375,
#End of Repeat
250, 125, 375, 250, 125, 375,
125, 125, 125, 125, 125, 125, 125, 125, 375,
250, 125, 375, 250, 125, 375,
125, 125, 125, 125, 125, 500,
250, 125, 375, 250, 125, 375,
125, 125, 125, 125, 125, 125, 125, 125, 375,
250, 125, 375, 250, 125, 375,
125, 125, 125, 125, 125, 500
]
def play_song():
total_notes = len(notes)
for i in range(total_notes):
current_note = notes
wait = durations
if current_note != 0:
buzzer.duty(512) # Set duty cycle for sound
buzzer.freq(current_note) # Set frequency of the note
else:
buzzer.duty(0) # Turn off the sound
time.sleep_ms(wait)
buzzer.duty(0) # Turn off the sound
while True:
# Play the song
play_song()

六.任务4:连接WiFi网络)

将Seeed Studio XIAO ESP32C3连接到WiFi网络,并访问互联网信息

6.1 连接WIFI

输入如下代码,保存为boot.py运行
import network
import urequests
import utime as time
# Network settings
wifi_ssid = "qiqiqiqi"
wifi_password = "cqmygysdss"
def scan_and_connect():
station = network.WLAN(network.STA_IF)
station.active(True)
print("Scanning for WiFi networks, please wait...")
for ssid, bssid, channel, RSSI, authmode, hidden in station.scan():
print("* {:s}".format(ssid))
print(" - Channel: {}".format(channel))
print(" - RSSI: {}".format(RSSI))
print(" - BSSID: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}".format(*bssid))
print()
while not station.isconnected():
print("Connecting...")
station.connect(wifi_ssid, wifi_password)
time.sleep(10)
print("Connected!")
print("My IP Address:", station.ifconfig()[0])
# Execute the functions
scan_and_connect()

6.2 获取网络时间

from machine import Pin, SoftI2C
import ssd1306
from time import sleep
import time
import network
import urequests
import ujson
# ESP32 Pin assignment
# i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
# ESP8266 Pin assignment
i2c = SoftI2C(scl=Pin(7), sda=Pin(6)) # Adjust the Pin numbers based on your connections
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
station = network.WLAN(network.STA_IF)
station.active(True)
# Network settings
wifi_ssid = "qiqiqiqi"
wifi_password = "cqmygysdss"
url = "http://worldtimeapi.org/api/timezone/America/New_York"
print("Scanning for WiFi networks, please wait...")
authmodes = ['Open', 'WEP', 'WPA-PSK' 'WPA2-PSK4', 'WPA/WPA2-PSK']
for (ssid, bssid, channel, RSSI, authmode, hidden) in station.scan():
print("* {:s}".format(ssid))
print(" - Channel: {}".format(channel))
print(" - RSSI: {}".format(RSSI))
print(" - BSSID: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}".format(*bssid))
print()
# Continually try to connect to WiFi access point
while not station.isconnected():
# Try to connect to WiFi access point
print("Connecting...")
station.connect(wifi_ssid, wifi_password)
time.sleep(10)
# Display connection details
print("Connected!")
print("My IP Address:", station.ifconfig()[0])
while True:
# Perform HTTP GET request on a non-SSL web
response = urequests.get(url)
# Check if the request was successful
if response.status_code == 200:
# Parse the JSON response
data = ujson.loads(response.text)
# Extract the "datetime" field for New York
ny_datetime = data["datetime"]
# Split the date and time components
date_part, time_part = ny_datetime.split("T")
# Get only the first two decimal places of the time
time_part = time_part[:8]
# Get the timezone
timezone = data["timezone"]
# Clear the OLED display
oled.fill(0)
# Display the New York date and time on separate lines
oled.text("New York Date:", 0, 0)
oled.text(date_part, 0, 10)
oled.text("New York Time:", 0, 20)
oled.text(time_part, 0, 30)
oled.text("Timezone:", 0, 40)
oled.text(timezone, 0, 50)
# Update the display
oled.show()
else:
oled.text("Failed to get the time for New York!")
# Update the display
oled.show()

6.3 WIFI信号强度追踪

import network
import time
from time import sleep
import machine
from machine import Pin, SoftI2C
import ssd1306
import math
# ESP32C3 Pin assignment
i2c = SoftI2C(scl=Pin(7), sda=Pin(6)) # Adjust the Pin numbers based on your connections
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
# Network settings
wifi_ssid = "qiqiqiqi"
wifi_password = "cqmygysdss"
machine.freq(160000000) # Set CPU frequency to 160 MHz (ESP8266 specific)
oled.text("Starting up...", 0, 0)
oled.show()
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(wifi_ssid, wifi_password)
time.sleep(1)
while not station.isconnected():
time.sleep(1)
oled.fill(0)
oled.text("Connecting to", 0, 0)
oled.text(wifi_ssid, 0, 20)
oled.show()
time.sleep(2)
oled.fill(0)
ip_address = station.ifconfig()[0] # Get the IP address
oled.text("Connected! ", 0, 0)
oled.text("IP Address:", 0, 20)
oled.text(ip_address, 0, 40)
oled.show()
time.sleep(2)
# Buzzer settings
buzzer_pin = machine.Pin(5, machine.Pin.OUT)
buzzer = machine.PWM(buzzer_pin)
buzzer.freq(1047)
buzzer.duty(0)
center_x = oled_width // 2
center_y = oled_height // 2
square_size = 6 # Size of each square
num_squares = 12 # Number of squares
angle_increment = 2 * math.pi / num_squares
x_pos = [12, 38, 64, 90]
statuses = ["poor", "normal", "good", "excellent"]
def calculate_block_count(rssi):
# Determine the number of blocks based on RSSI values
if -80 <= rssi < -60:
return 1
elif -60 <= rssi < -40:
return 2
elif -40 <= rssi < -20:
return 3
elif -20 <= rssi <= 10:
return 4
def draw_blocks(count):
for i in range(count):
y_pos = 50 - calculate_block_height(i)
oled.fill_rect(x_pos, y_pos, 24, calculate_block_height(i), 1)
for i in range(count, 4): # Clear unused area
y_pos = 50 - calculate_block_height(i)
oled.fill_rect(x_pos, y_pos, 24, calculate_block_height(i), 0)
def calculate_block_height(index):
return 10 * (index + 1)
loop_count = 0 # Initialize loop count
while loop_count < 2: # Execute the loop 24 times
oled.fill(0) # Clear the screen
for i in range(num_squares):
angle = i * angle_increment
x = int(center_x + (center_x - square_size-30) * math.cos(angle))
y = int(center_y + (center_x - square_size-30) * math.sin(angle))
# Draw all squares
for j in range(num_squares):
angle_j = j * angle_increment
x_j = int(center_x + (center_x - square_size-30) * math.cos(angle_j))
y_j = int(center_y + (center_x - square_size-30) * math.sin(angle_j))
oled.fill_rect(x_j, y_j, square_size, square_size, 1) # Draw the square
oled.fill_rect(x, y, square_size, square_size, 0) # Erase the current square
oled.show()
time.sleep_ms(100) # Pause before next iteration
loop_count += 1 # Increase loop count
oled.fill(0) # Clear the screen after finishing the loops
oled.show()
while True:
oled.fill(0)
station = network.WLAN(network.STA_IF)
time.sleep(0.1)
rssi = station.status('rssi')
rssi_duty = 160 + 2 * int(rssi)
rssi_duty_2 = int(rssi_duty / 2)
rssi_abs = abs(int(rssi)) / 100
block_count = calculate_block_count(rssi)
status = statuses[block_count - 1] # Get the status text based on block count
draw_blocks(block_count)
oled.text(status, 11, 56)
oled.text("RSSI:", 0, 0)
oled.text(str(rssi), 40, 0)
# Update the display
oled.show()
buzzer.duty(rssi_duty)
time.sleep(rssi_abs)
buzzer.duty(0)
time.sleep(rssi_abs)
buzzer.duty(rssi_duty_2)
time.sleep(rssi_abs)
buzzer.duty(0)
time.sleep(rssi_abs)

七.任务5:使用外部传感器

连接环境光传感器或温湿度传感器,获取传感器的数值,并转换成真实的物理量
  1. 获取光线传感器值
    光线传感器使用图中A0,GPIO2


    光线充足时2.36V

    光线暗时420mV

    from machine import ADC
    from machine import Pin
    import time
    while True:
    adc = ADC(Pin(2),atten=ADC.ATTN_11DB) # create an ADC object acting on a pin
    val = adc.read_u16() # read a raw analog value in the range 0-65535
    print("ADC:"+str(val))
    val = adc.read_uv() # read an analog value in microvolts
    print("Vol:"+str(val/1000)+"mV")
    time.sleep(1)
    光线充足和暗时采集值分别如下

  2. 获取温湿度值
搜索安装AHT20库micropython-ahtx0
SDA引脚为GPIO6
SCL引脚为GPIO7
import utime
from machine import Pin, I2C
import ahtx0
i2c = I2C(scl=Pin(7), sda=Pin(6))
# Create the sensor object using I2C
sensor = ahtx0.AHT20(i2c)
while True:
print("\nTemperature: %0.2f C" % sensor.temperature)
print("Humidity: %0.2f %%" % sensor.relative_humidity)
utime.sleep(5)
测试结果如下

八.任务6:

基于前面的任务,这里实现分任务3:开灯提醒器

  1. 屏幕可以显示温湿度
  2. 读取光线传感器的值,在光线不足时屏幕和蜂鸣器提示开灯
  3. 注意判断的回滞处理避免抖动
代码如下
import utime
from machine import Pin, I2C, PWM
import ahtx0
from machine import SoftI2C
import ssd1306
import math
from machine import ADC
# Buzzer settings
buzzer_pin = Pin(5, Pin.OUT)
buzzer = PWM(buzzer_pin)
buzzer.freq(1047)
i2coled = SoftI2C(scl=Pin(7), sda=Pin(6)) # Adjust the Pin numbers based on your connections
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2coled)
i2caht20 = I2C(scl=Pin(7), sda=Pin(6))
# Create the sensor object using I2C
sensor = ahtx0.AHT20(i2caht20)
state = 0
while True:
oled.fill(0) # Clear the screen
print("\nTemperature: %0.2f C" % sensor.temperature)
print("Humidity: %0.2f %%" % sensor.relative_humidity)
oled.text("T:"+str(sensor.temperature), 10, 10)
oled.text("H:"+str(sensor.relative_humidity), 10, 20)
adc = ADC(Pin(2),atten=ADC.ATTN_11DB)
val = adc.read_uv() # read an analog value in microvolts
val = val / 1000;
print("Vol:"+str(val)+"mV")
oled.text("L:"+str(val), 10, 30)
# 回滞处理,避免抖动
if state==0: #初始直接判断
if val < 2000:
state = 1
oled.text("Light On", 10, 50)
buzzer.duty(10)
else:
state = 2
oled.text("Light Off", 10, 50)
buzzer.duty(0)
elif state==1: #等待达到高阈值
if val > 2200:
state = 2
oled.text("Light Off", 10, 50)
buzzer.duty(0)
else:
oled.text("Light On", 10, 50)
#buzzer.duty(10)
elif state==2: #等待达到低阈值
if val < 1800:
state = 1
oled.text("Light On", 10, 50)
buzzer.duty(10)
else:
oled.text("Light Off", 10, 50)
#buzzer.duty(0)
else:
state = 0
oled.show() # Show the text
print(state)
utime.sleep(0.5)
详见视频
 

总结

基于上述多个任务熟悉了Seeed Studio XIAO ESP32C3的开发, 并综合实现了一个开灯提醒器的Demo。本次活动非常有意思,也体验了microython开发的便利性。希望后面能继续参加。

 

三.代码

最新回复

楼主这个配个智能开关 可以发个指令自动开灯了。   详情 回复 发表于 2023-11-19 20:57
点赞 关注
 
 

回复
举报

6069

帖子

4

TA的资源

版主

沙发
 

楼主这个配个智能开关 可以发个指令自动开灯了。

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表