当前位置: 首页 > news >正文

012——LED模块驱动开发(基于I.MX6uLL)

目录

一、 硬件原理图

二、 驱动程序

三、 应用程序

四、 Makefile

五、操作


一、 硬件原理图

        又是非常经典的点灯环节 ,每次学新语言第一步都是hello world,拿到新板子或者学习新的操作系统,第一步就是点灯。

        LED 的驱动方式,常见的有四种。
① 使用引脚输出 3.3V 点亮 LED,输出 0V 熄灭 LED。
② 使用引脚拉低到 0V 点亮 LED,输出 3.3V 熄灭 LED。
有的芯片为了省电等原因,其引脚驱动能力不足,这时可以使用三极管驱动。
③ 使用引脚输出 1.2V 点亮 LED,输出 0V 熄灭 LED。
④ 使用引脚输出 0V 点亮 LED,输出 1.2V 熄灭 LED。
由此,主芯片引脚输出高电平/低电平,即可改变 LED 状态,而无需关注 GPIO
引脚输出的是 3.3V 还是 1.2V。所以简称输出 1 或 0:
⚫ 逻辑 1-->高电平
⚫ 逻辑 0-->低电平
        SOC级别的芯片通常电压都比较低,像我们之前用的exynos4412他是1.8V的,我们的i.MX6ULL则是可以做到更低的逻辑1,1.2V。现在最新的技术好像是0.8V的。电压降低的好处就是我们的功耗大幅减小。MCU为什么不降低呢,因为它是控制器需要高电压的驱动环境,所以一般都是3.3V和5V的。

这是板子上的LED的原理图

6ull的GPIO是这样描述的

看上面的原理图我找到了

        第五组GPIO的第三个也就是4*32+4-1 = 131

        每组GPIO有32个,我们0开始所以就是128+3 ,131就是我们的GPIO号

        知道这个就差不多可以写驱动程序了,这就是由操作系统和无操作系统的区别,裸机开发的话我们还要找到其它的寄存器,上面说到的那八个都要找到,但是因为GPIO是通用外设,操作系统已经处理过了,所以我们用的话就会很轻松,甚至可以直接给dev下的GPIO设备写值来控制。

然后我们就可以写代码了

二、 驱动程序

#include "asm-generic/errno-base.h"
#include "asm-generic/gpio.h"
#include "asm/uaccess.h"
#include <linux/module.h>
#include <linux/poll.h>#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>struct gpio_desc{int gpio;int irq;char *name;int key;struct timer_list key_timer;
} ;static struct gpio_desc gpios[2] = {{131, 0, "led0", },//{132, 0, "led1", },
};/* 主设备号                                                                 */
static int major = 0;
static struct class *gpio_class;/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{char tmp_buf[2];int err;int count = sizeof(gpios)/sizeof(gpios[0]);if (size != 2)return -EINVAL;err = copy_from_user(tmp_buf, buf, 1);if (tmp_buf[0] >= count)return -EINVAL;tmp_buf[1] = gpio_get_value(gpios[tmp_buf[0]].gpio);err = copy_to_user(buf, tmp_buf, 2);return 2;
}static ssize_t gpio_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{unsigned char ker_buf[2];int err;if (size != 2)return -EINVAL;err = copy_from_user(ker_buf, buf, size);if (ker_buf[0] >= sizeof(gpios)/sizeof(gpios[0]))return -EINVAL;gpio_set_value(gpios[ker_buf[0]].gpio, ker_buf[1]);return 2;    
}/* 定义自己的file_operations结构体                                              */
static struct file_operations gpio_key_drv = {.owner	 = THIS_MODULE,.read    = gpio_drv_read,.write   = gpio_drv_write,
};/* 在入口函数 */
static int __init gpio_drv_init(void)
{int err;int i;int count = sizeof(gpios)/sizeof(gpios[0]);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);for (i = 0; i < count; i++){		/* set pin as output */err = gpio_request(gpios[i].gpio, gpios[i].name);if (err < 0) {printk("can not request gpio %s %d\n", gpios[i].name, gpios[i].gpio);return -ENODEV;}gpio_direction_output(gpios[i].gpio, 1);}/* 注册file_operations 	*/major = register_chrdev(0, "100ask_led", &gpio_key_drv);  /* /dev/gpio_desc */gpio_class = class_create(THIS_MODULE, "100ask_led_class");if (IS_ERR(gpio_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_led_class");return PTR_ERR(gpio_class);}device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "100ask_led"); /* /dev/100ask_gpio */return err;
}/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数*/
static void __exit gpio_drv_exit(void)
{int i;int count = sizeof(gpios)/sizeof(gpios[0]);printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(gpio_class, MKDEV(major, 0));class_destroy(gpio_class);unregister_chrdev(major, "100ask_led");for (i = 0; i < count; i++){gpio_free(gpios[i].gpio);		}
}/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */module_init(gpio_drv_init);
module_exit(gpio_drv_exit);MODULE_LICENSE("GPL");

三、 应用程序


#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>static int fd;//int led_on(int which);
//int led_off(int which);
//int led_status(int which);/** ./led_test <0|1|2|..>  on * ./led_test <0|1|2|..>  off* ./led_test <0|1|2|..>*/
int main(int argc, char **argv)
{int ret;char buf[2];int i;/* 1. 判断参数 */if (argc < 2) {printf("Usage: %s <0|1|2|...> [on | off]\n", argv[0]);return -1;}/* 2. 打开文件 */fd = open("/dev/100ask_led", O_RDWR);if (fd == -1){printf("can not open file /dev/100ask_led\n");return -1;}if (argc == 3){/* write */buf[0] = strtol(argv[1], NULL, 0);if (strcmp(argv[2], "on") == 0)buf[1] = 0;elsebuf[1] = 1;ret = write(fd, buf, 2);}else{buf[0] = strtol(argv[1], NULL, 0);ret = read(fd, buf, 2);if (ret == 2){printf("led %d status is %s\n", buf[0], buf[1] == 0 ? "on" : "off");}}close(fd);return 0;
}

四、 Makefile

韦东山老师的makefile写的有点太潦草了,我们来优化一下

CC := $(CROSS_COMPILE)gcc
FILE_NAME = led_test
DRIVER_NAME = led_drv
# 定义NFS根文件系统目录  
FS_FILE = ~/nfs_rootfsKERN_DIR =  /home/book/program/100ask_imx6ull_mini-sdk/Linux-4.9.88 # 板子所用内核源码的目录# all:
# 	make -C $(KERN_DIR) M=`pwd` modules 
# 	$(CROSS_COMPILE)gcc -o led_test led_test.c
# 默认目标  
all:  @echo "Starting build process..."  @echo "Building kernel modules..."  make -C $(KERN_DIR) M=$(PWD) modules  @echo "Building $(FILE_NAME) test program..."  $(CC) -o $(FILE_NAME) $(FILE_NAME).c  # 安装目标  
install:  @echo "Installing $(DRIVER_NAME).ko to $(FS_FILE)..."  cp ./$(DRIVER_NAME).ko $(FS_FILE)  @echo "$(DRIVER_NAME).ko installed."  @echo "Installing $(FILE_NAME) to $(FS_FILE)..."  cp ./$(FILE_NAME) $(FS_FILE)  @echo "$(FILE_NAME) installed."  clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.order  led_test# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.oobj-m += led_drv.o
# 声明伪目标  
.PHONY: all clean install

五、操作

每次都要重新配置网络很难受所以我这面写了个脚本上机自动配置ip并且挂载nfs 

#!/bin/bash  # 定义变量  
NFS_SERVER="192.168.5.10"  
NFS_SHARE="/home/book/nfs_rootfs"  
IPADDR="192.168.5.110"
MOUNT_POINT="/mnt"
INTERFACE="eth0"  
# 设置本机IP
sleep 1
ifconfig $INTERFACE $IPADDR
sleep 1# 测试与NFS服务器的连通性  
ping -c 1 $NFS_SERVER > /dev/null 2>&1  
if [ $? -eq 0 ]; then  echo "NFS服务器 $NFS_SERVER 连通性正常"  
else  echo "无法与NFS服务器 $NFS_SERVER 建立连接"  exit 1  
fi  # 检查挂载点是否存在,如果不存在则创建  
if [ ! -d "$MOUNT_POINT" ]; then  mkdir -p "$MOUNT_POINT"  
fi  # 尝试挂载NFS共享  
mount -t nfs -o nolock,vers=3 $NFS_SERVER:$NFS_SHARE $MOUNT_POINT  
if [ $? -eq 0 ]; then  echo "NFS共享已成功挂载到 $MOUNT_POINT"  
else  echo "无法挂载NFS共享到 $MOUNT_POINT"  exit 1  
fi

最后我们上传一下

相关文章:

012——LED模块驱动开发(基于I.MX6uLL)

目录 一、 硬件原理图 二、 驱动程序 三、 应用程序 四、 Makefile 五、操作 一、 硬件原理图 又是非常经典的点灯环节 &#xff0c;每次学新语言第一步都是hello world&#xff0c;拿到新板子或者学习新的操作系统&#xff0c;第一步就是点灯。 LED 的驱动方式&#xff0…...

基于springboot实现房屋租赁管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现房屋租赁系统演示 摘要 房屋是人类生活栖息的重要场所&#xff0c;随着城市中的流动人口的增多&#xff0c;人们对房屋租赁需求越来越高&#xff0c;为满足用户查询房屋、预约看房、房屋租赁的需求&#xff0c;特开发了本基于Spring Boot的房屋租赁系统。 …...

168.乐理基础-中古调式概述

如果到这五线谱还没记住还不认识的话去看102.五线谱-高音谱号与103.五线谱-低音谱号这两个里&#xff0c;这里面有五线谱对应的音名&#xff0c;对比着看 如果不认识调号去看112.五线谱的调号&#xff08;一&#xff09;、113.五线谱的调号&#xff08;二&#xff09;、114.快…...

【项目实战】【Docker】【Git】【Linux】部署V2rayA项目

今天着手了一个全新领域的项目&#xff0c;从完全没有头绪到成功运行&#xff0c;记录一下具体的部署流程 github项目链接V2rayA 一开始拿到以后完全没有抓手&#xff0c;去阅读了一下他的帮助文档 写着能用docker运行&#xff0c;就去下载了一个Docker配置了一下 拉取代码到…...

mac 切换 jdk

查看 mac 上都有哪些版本 /usr/libexec/java_home -V看准版本切换 按前缀切换 比如 export JAVA_HOME/usr/libexec/java_home -v 1.8这样会随机一个 1.8 的 如果想再确定一个比如 openjdk export JAVA_HOME/usr/libexec/java_home -v 1.8.0_292这个方式是临时的&#xff0c…...

MD5加密返回32位密文字符串

前言&#xff1a; 项目中需要调用其他系统的 api 接口&#xff0c;接口使用的是按一定规则生成 MD5 密文作为签名来进行身份验证&#xff0c;本文仅记录 32 位 MD5 密文的生成方式&#xff0c;仅供参考。 什么是MD5 加密&#xff1f; MD5 加密是一种加密算法&#xff0c;MD5…...

npm常用命令技巧

NPM (Node Package Manager) 是 JavaScript 的包管理工具&#xff0c;广泛用于管理项目中的依赖。无论是前端项目还是Node.js后端项目&#xff0c;NPM 都扮演着重要的角色。本文将介绍 NPM 中常用的几个命令&#xff0c;并提供相应的代码示例。 1. 初始化项目&#xff1a;npm …...

intellij idea 使用git撤销(取消)commit

git撤销(取消) 未 push的 commit Git&#xff0c;选择分支后&#xff0c;右键 Undo Commit &#xff0c;会把这个 commit 撤销。 git撤销(取消) 已经 push 的 commit 备份分支内容&#xff1a; 选中分支&#xff0c; 新建 分支&#xff0c;避免后续因为操作不当&#xff0c;导…...

【计算机网络】四层负载均衡和七层负载均衡

前言 1、分层方式 首先我们知道&#xff0c;在计算机网络中&#xff0c;常用的协议分层方式&#xff1a;OSI和TCP/IP&#xff0c;以及实际生产中使用的协议划分方式。 在OSI中&#xff0c;各层的职责如下&#xff1a; 应用层&#xff1a;对软件提供接口以使程序能使用网络服…...

IP-guard WebServer 任意文件读取漏洞复现

0x01 产品简介 IP-guard是由溢信科技股份有限公司开发的一款终端安全管理软件,旨在帮助企业保护终端设备安全、数据安全、管理网络使用和简化IT系统管理。 0x02 漏洞概述 由于IP-guard WebServer /ipg/static/appr/lib/flexpaper/php/view.php接口处未对用户输入的数据进行严…...

【IoTDB 线上小课 01】我们聊聊“金三银四”下的开源

关于 IoTDB&#xff0c;关于物联网&#xff0c;关于时序数据库&#xff0c;关于开源...你是否仍有很多疑问&#xff1f; 除了自己钻研文档&#xff0c;群里与各位“大佬”的沟通&#xff0c;你是否还希望能够有个学习“捷径”&#xff1f; 天谋科技发起社区小伙伴&#xff0c;正…...

2024053期传足14场胜负前瞻

2024053期售止时间为4月6日&#xff08;周六&#xff09;21点00分&#xff0c;敬请留意&#xff1a; 本期深盘多&#xff0c;1.5以下赔率1场&#xff0c;1.5-2.0赔率8场&#xff0c;其他场次是平半盘、平盘。本期14场难度中等。以下为基础盘前瞻&#xff0c;大家可根据自身判断…...

C语言------冒泡法排序

一.前情提要 1.介绍 冒泡法排序法&#xff1a; 1)冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它重复地遍历要排序的列表&#xff0c;一次比较相邻的两个元素&#xff0c;并且如果它们的顺序错误就将它们交换过来。重复这个过程直到没有需…...

C#(C Sharp)学习笔记_Enum枚举类型【十三】

什么是枚举类型 枚举类型(Enum) 是由基础整型数值类型的一组命名常量定义的值类型。枚举包含自己的值&#xff0c;但不能继承或传递继承。 语法 // enum enum_name // enum_name variable enum_name.enum_value// 定义一个枚举类型——例如&#xff1a; enum enum_name {va…...

乐知付-如何制作html文件可双击跳转到指定页面?

标题: 乐知付-如何制作html文件可双击跳转到指定页面&#xff1f; 标签: [乐知付, 乐知付加密, 密码管理] 分类: [网站,html] 为了便于买家理解使用链接进行付费获取密码&#xff1b;现开发个小工具&#xff0c;将支付链接转为浏览器可识别的文件&#xff0c;双击打开即可跳转到…...

电工技术学习笔记——直流电路及其分析方法

一、直流电路 电路的组成 1. 电压和电流的参考方向 电压&#xff08;Voltage&#xff09;&#xff1a;电压是电场力对电荷产生的作用&#xff0c;表示为电荷单位正电荷所具有的能量。在电路中&#xff0c;电压通常被定义为两点之间的电势差&#xff0c;具有方向性&#xff0c;…...

详解python中的迭代

如果给定一个list或tuple&#xff0c;我们可以通过for循环来遍历这个list或tuple&#xff0c;这种遍历我们称为迭代&#xff08;Iteration&#xff09;。 在Python中&#xff0c;迭代是通过for ... in来完成的&#xff0c;而很多语言比如C语言&#xff0c;迭代list是通过下标完…...

机器学习模型——集成算法(三)

前面我们说了bagging算法和Boosting算法 接下来我们学习Adaboost算法 Adaboost基本概念&#xff1a; AdaBoost &#xff08;Adaptive Boosting&#xff0c;自适应提升&#xff09;: 算法原理是将多个弱学习器进行合理的结合&#xff0c;使其成为一个强学习器。 Adaboost采用…...

企业微信企业主体变更认证介绍

企业微信变更主体有什么作用&#xff1f; 说一个自己亲身经历的事情&#xff0c;当时我在一家教育公司做运营&#xff0c;公司所有客户都是通过企业微信对接的。后来行业整顿&#xff0c;公司不得不注销&#xff0c;换了营业执照打算做技能培训&#xff0c;但发现注销后原来的企…...

如何降低AI功耗? —— 超低功耗的仿生硬件

一、背景 仿生硬件&#xff08;Bionic Hardware&#xff09;这一术语通常指的是受自然界的生物系统启发而设计制造的电子或机械硬件设备&#xff0c;它们在功能、结构、材料、能源效率等方面模仿生物体的特性&#xff0c;以实现更高效、智能、自适应或环境友好的性能。在人工智…...

原型模式详解

原型模式简单的理解来说,就是复制品,用一个已经做好的成品作为原型,然后通过复制它得到新的产品。就好像细胞分裂一样。用Java来说,就是用对象创建对象,而不是通过类来创建对象。 原型模式的目的是从原型实例克隆出新的实例 ,对于那些有非常复杂的初始化过程的对象或者是…...

嵌入式开发中状态模式实现

文章目录 状态模式代码实现代码解释小结 状态模式 状态模式&#xff08;State Pattern&#xff09;是一种行为设计模式&#xff0c;它允许对象在内部状态改变时改变它的行为。在嵌入式系统中&#xff0c;状态模式尤其适用于那些根据外部事件或内部条件频繁改变状态并且每种状态…...

目标检测——车牌数据集

一、重要性及意义 交通安全与管理&#xff1a;车牌检测和识别技术有助于交通管理部门快速、准确地获取车辆信息&#xff0c;从而更有效地进行交通监控和执法。例如&#xff0c;在违规停车、超速行驶等交通违法行为中&#xff0c;该技术可以帮助交警迅速锁定违规车辆&#xff0…...

FOC算法中为啥用PWM触发ADC中断

在FOC&#xff08;Field Oriented Control&#xff0c;场向量控制&#xff09;算法中&#xff0c;为什么要使用PWM&#xff08;Pulse Width Modulation&#xff0c;脉宽调制&#xff09;触发ADC&#xff08;Analog-to-Digital Converter&#xff0c;模数转换器&#xff09;中断…...

2024.4.5学习记录————嵌入式学习内容

2024.4.5学习记录————嵌入式学习内容 C语言 指针&#xff0c;位操作&#xff0c;结构体字节对齐&#xff0c;位段&#xff0c;宏的高级运用 C 基础特性&#xff1a;类&#xff0c;对象&#xff0c;派生&#xff0c;继承&#xff0c;运算符重载&#xff0c;友元&#xf…...

k8s 部署 canal 集群,RocketMQ 模式

k8s 部署 canal 集群&#xff0c;RocketMQ 模式 k8s 部署 canal 集群&#xff0c;RocketMQ 模式前提MySQLRocketMQ制作 canal-admin、canal-server 镜像 部署 zookeeper部署 canal-admin部署 canal-server测试 k8s 部署 canal 集群&#xff0c;RocketMQ 模式 前提 MySQL 开启…...

Android+OnnxRuntime+Opencv+Onnx模型操作图片擦除多余内容

今年来AI的发展非常迅速,在工业、医疗等等行业逐渐出现相应的解决方案,AI也逐渐成为各行业基础设施建设重要的一环,未来发展的大趋势,不过这也需要一个漫长的过程,需要很多技术型人才加入其中,除了工业设施的基础建设,在娱乐方向也有很多有趣的能力,不如图片/视频换背景…...

【CANoe】CAPL_E2E测试-验证报文中的CRC值是否正确

文章目录 一、背景二、CRC校验算法实现_dll制作三、CAPL脚本编写四、测试结果4.1、Write输出窗口4.2、测试报告截图一、背景 在嵌入式软件开发过程中,对于一些报文,需要实现安全发送与安全接收,这就涉及到CRC和RollingCounter。整车和MCU通讯的报文需要对方进行校验才能正确…...

Linux云计算之网络基础8——IPV6和常用网络服务

目录 一、IPV6基础 IPV6详解 IPv6数据报的基本首部 IPv6数据报的扩展首部 IPv6地址的表示方法 IPv6地址分类 网际控制报文协议ICMPv6 二、cisco基于IPV6的配置 cisco基于IPV6的配置步骤 模拟配置 三、HTML基础介绍 文档的结构 动手操作一下 四、常用网络服务介绍…...

页面刚加载的时候显示自己定义的{{***}}然后一闪而过

这时候别用插值表达式语法了&#xff0c;直接用v-text或者v-html就能解决这个问题 但是有个问题&#xff0c;如下图所示&#xff1a; 具体bind使用方式&#xff0c;如下图所示&#xff1a; 但是v-bind也可以进行简写&#xff0c;就是去掉v-bind&#xff0c;直接写&#xff1a…...

越南网站怎么做/制作网站的网址

前言 PowerShell是为系统管理员设计的基于任务的自动化命令行外壳(Shell)和相关脚本环境。 它建立在.NET框架上。 它是比命令提示符(cmd)强大的命令行解释器&#xff0c;可以解释PowerShell和批处理命令。 powershell一直都是内网渗透的大热门&#xff0c;微软是真正的在推行…...

网站跳出率怎么算/今日热点新闻

文章目录Pygame实战&#xff08;一&#xff09;&#xff1a;随机抽位置一、 概述1、 简介2、 设计思路3、 成果展示二、 开始编程1、 配置文件2、 程序界面2.1 读取配置2.2 工具类2.3 显示区2.4 操作区2.5 随机矩形3、 事件处理4、 总程序三、 程序打包1、 规范文件2、 打包运行…...

wordpress广告位设置/宣传推广方案模板

导读&#xff1a;sql存储是数据库操作过程中比较重要的一个环节&#xff0c;对于一些初学者来说也是比较抽象难理解的&#xff0c;本文我将通过几个实例来解析数据库中的sql存储过程&#xff0c;这样就将抽象的事物形象化&#xff0c;比较容易理解。 例1&#xff1a; create pr…...

网站制作公司知道万维科技/网站一级域名和二级域名

Android之路 – 3步完成多语言支持 转自&#xff1a; http://www.cnblogs.com/JamesL/archive/2011/06/08/2075763.htmlGoogle不愧是跨国企业&#xff0c;连开发出的系统都能轻松支持多语言支持。 以英文和简体中文两种语言为例&#xff0c;3步完成&#xff1a; 1. 新建项目后…...

工信部网站备案查询系统/学网络运营需要多少钱

time简介 time命令 用于 打印出一条命令或一个程序的执行时间 time 命令以秒为单位将一条命令执行期间所用的时间、系统时间和 time 命令的执行时间打印在标准错误中。 用法&#xff1a;time [ -p ] Command [ Argument ... ] 举例&#xff1a;想知道 列举home文件夹这一命令 花…...

网站开发域名/站长工具ip地址查询

这个例子显示了如何使用QPainter渲染一个简单的QWindow。 值得学习的内容 <QtGui>头文件 #include <QtGui>就可以使用Qt GUI模块中的所有类&#xff0c;当然&#xff0c;愿意的话也可以分开各个include。 QBackingStore与绘制 用于管理基于QPainter的图形的窗口后缓…...