32单片机基础:对射式红外传感器计次

接线如下图: 

在HardWare建立两个文件:如图

COuntSensor.c

如何配置外部中断,根据下面图,我们需要把外部中断从GPIO到NVIC这一路出现的外设模块都配置好。把这条信号打通就OK了。

1.配置RCC:把我们这里涉及的外设时钟都打开,不打开时钟,外设是没法工作的

2.配置GPIO,选择我们的端口为输入模式

3.配置AFIO,选择我们用的这一路的GPIO,连接到后面的EXTI

4.配置EXTI,选择边沿触发方式,比如上升沿,下降沿,或者双边沿,选择触发响应方式,可以选择中断响应和事件响应,

5.配置NVIC,给我们的中断设置一个合适的优先级,

最后,通过NVIC,外部中断信号就能进入CPU了,这样CPU才能收到中断信号,才能跳转到中断函数里执行中断程序。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB的时钟

 这里注意一点,GPIOB是APB2的外设,这里的参数是APB2Periph_GPIOB,函数也要用APB2的这个开启时钟函数,如果用了APB1或者AHB的函数,然后填上APB2Periph_GPIOB的参数,程序不会报错,所以这里细心一点,注意函数和参数的这个APB2,APB1和AHB要对应起来

AFIO也是APB2的外设,也是相同的配置。你如果不确定哪一个外设是接在哪个总线上的,可以转到这个函数的定义,看一下参数列表

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//配置AFIO

然后是EXTI和NVIC两个外设,这两个外设的时钟一直都是打开的,不需要我们再开启时钟了。

NVIC是内核的外设,内核的外设都是不需要开启时钟的,人家跟CPU住在一起,都是住在皇宫里的,而RCC管的是内核外的外设,所以RCC管不着NVIC,

ok,第一步配置时钟完成,下一步配置GPIO

    GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

第三步:配置AFIO外设,

这个AFIO外设,ST公司并没有给它分配专门的库函数文件,他的库函数是与GPIO在一个文件里的

这些就是与AFIO有关的库函数

void GPIO_AFIODeInit(void);图片没给出,是在上面几行,

这个函数是 用来复位AFIO外设的,调用一下这个函数,AFIO外设的配置就会全部清除,

void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

这个函数是用来锁定GPIO配置的,调用这个函数,参数指定某个引脚,那这个引脚的配置就会被锁定,防止意外更改,这个也是GPIO的函数,用的不多,了解即可

void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);

这两个函数是用来配置AFIO的事件输出功能的,用的不多,了解即可

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);(重要)

这个函数可以用来进行引脚重映射,第一个参数选择你要重映射的方式,第二个参数是新的状态,使用还是非常简单的。但是我们目前还是没有学习到需要重映射引脚的外设,所以实际调用的话,我们之后博文见。

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);(重要)

这个是我们本节要用到的外部中断函数调用这个函数,就可以配置AFIO的数据选择器,来选择我们想要的中断引脚

void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

这个函数是和以太网相关的,我们这个芯片没有以太网外设,所以也用不到。

好,那这个AFIO库函数,我们就了解差不多了。我们操作一下,调用函数需要的参数,

 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//配置AFIO

 上面的代码,AFIO外部中断引脚选择配置我们就完成了,就这一个函数就OK了

当执行完这个函数后,AFIO的第14个数据选择器就拔好了,跟下面这个图对应起来。

第四步:配置EXTI

我们先看一下EXTI的库函数文件,看一下EXTI都有哪一些库函数可以用。 

void EXTI_DeInit(void);

调用它,就可以把EXTI的配置都清楚,恢复成上电默认的状态

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

调用这个函数,就可以根据这个结构体里的参数配置EXTI外设。我们初始EXTI主要用的就是这个函数,使用方法与GPIO_Init也是一样的,这个应该好理解

void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);

调用这个函数,可以把参数传递的结构体变量赋一个默认值,

像前面三个函数,基本所以外设都有,就像是库函数的模版函数一样,基本每一个外设都需要这些类型的函数,这些模版函数使用的方法和意思也都是一样的,会使用一个之后,再见到这种函数,就会很容易上手,所以,当你学GPIO的时候,你会觉得为啥要用结构体来初始化模块呢。还得定义结构体,结构体赋值,然后再传递结构体的地址。简直太麻烦了,当你继续学习其他外设之后,你会发现,外部中断也是使用结构体初始化的方式,定时器也是,ADC也是,串口也是。

都是一个套路,而且结构体可以看到参数的名字,参数也是可以复制粘贴来的,根本不用查看寄存器,随便选选参数就配置好了,从这个角度看,STM32的库函数是不是比寄存器方便多了。这就是库函数的好处

void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);

这个函数是用来软件触发外部中断的,调用这个函数,参数给定一个指定的中断线,就能软件触发一次这个外部中断,如果你程序中需要这个功能的话,可以使用这个函数,如果你只需要外部引脚触发中断,那就不需要用这个函数了

FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
剩下这四个函数,也是库函数的模版函数,很多模块都有这四个函数因为在外设运行的过程中,会产生一些状态标志位,比如外部中断来了,串口收到数据定时器时间到,都会置标志位,这些标志位都是放在状态寄存器的,当程序想要看这些标志位时,就可以用到这四个函数,

其中FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);就可以获取指定的标志位是否被置1了。

void EXTI_ClearFlag(uint32_t EXTI_Line);可以对置1的标志位进行清除

那对于这些标志位,有的比较紧急,在置标志位后触发中断。在中断函数里,如果你想查看标志位和清除标志位,那就用

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);//获取中断标志位是否被置1,
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);//清除中断挂起标志位

这两个函数

总结:如果你想在主程序里查看和清除标志位,就用

FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);这两个函数

如果你想在中断里查看和清除标志位,就用

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

这两个函数,本质上,这四个寄存器都是对状态寄存器的读写。

好,我们初始化EXTI;

 EXTI_Init这个函数需要什么参数呢,跳转到函数定义。

可以看出,里面只需要一个参数,就是EXTI初始化的结构体,因为EXTI只有一个,所以不需要像GPIO那样,先指定要配置的哪个EXTI了,

看上面,需要一个指针结构体,所以我们定义一个结构体,按上图所示起名称,然后把成员引入。

     EXTI_InitTypeDef  EXTI_InitStructure;//EXTI配置
	 EXTI_InitStructure.EXTI_Line= EXTI_Line14;
	 EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	 EXTI_InitStructure.EXTI_Mode= EXTI_Mode_Interrupt;
	 EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling;
	 EXTI_Init( &EXTI_InitStructure);

 我们再跳转到成员变量的定义,如下图

ctrl+f搜索一下EXTI_lines,如下图,我们需要14的线路, 

 同理,找到各个参数要的。

 第五步:配置NVIC

因为NVIC是内核外设,所以它的库函数是被ST发配到杂项这里来了,我们打开misc.h文件,

我们来看一下,第一个

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

这个函数是用来中断分组的,参数是中断分组的方式,

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

根据结构体里面指定的参数初始化NVIC,

void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);

设置中断向量表(用的不多)

void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);

系统低功耗配置(用的不多)

所以只需要用上面的两个函数就OK了。在配置中断之前,先指定一下中断的分组。

然后使用NVIC_Init初始化一下NVIC就行了。

看一下void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);这个函数怎么用,

下面写到,配置优先寄存器:先占优先级和从占优先级,这里先占优先级就是抢占优先级,从占优先级就是响应优先级。这个参数可以取下面这个列表里的一个值。

这个具体要选哪个,其实看我们的实际需求来的,一般的话,中断不多,很难导致中断冲突。对优先级分钟来说,就比较随意了,哪个都行。那这里我就选择第二个分组,2位抢占,2位响应,

 注意一下,这个分组方式整个芯片只能用一种,所以按理说这个分组的代码整个工程只需要执行一次就行了。如果你把它放到模块里面进行分组,那你要确保每个模块分组都选择的是同一个。

同理,跳转每个函数了解一下

 

最上面提示我们不在我们这个文件里,我们按CTRL+F选着下图所示 

 我们芯片是MD的。直接展开MD,看一下。

 选择EXTI15_10_IRQn ,STM32的EXTI10到15都是合并到了这个通道里

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC配置
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn ;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init( &NVIC_InitStructure);

最后初始化的程序:

void CountSensor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//配置AFIO
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//配置AFIO
	
	 EXTI_InitTypeDef  EXTI_InitStructure;//EXTI配置
	 EXTI_InitStructure.EXTI_Line= EXTI_Line14;
	 EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	 EXTI_InitStructure.EXTI_Mode= EXTI_Mode_Interrupt;
	 EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling;
	 EXTI_Init( &EXTI_InitStructure);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC配置
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn ;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init( &NVIC_Init

OK,下面写中断的函数,在STM32中,中断函数的名字都是固定的。

每个中断通道都对应一个中函数,中断函数的名字我们可以参考一下启动文件

以IRQHandler结尾的字符串就是我们中断函数的名字

   DCD     EXTI15_10_IRQHandler       ; EXTI Line 15..10

这个就是  EXTI15_10的中断函数,我们复制一下

中断函数都是无参五返回值的,

void EXTI15_10_IRQHandler (void)
 {
	 
 }

然后在中断函数里,一般都是先进行一个中断标志位的判断,确保是我们想要的中断源触发的这个函数,因为这个函数EXTI10到15都能进来,所以要先判断一下是不是我们想要的EXTI14进来的。

 if(EXTI_GetITStatus(EXTI_Line14)==SET)//看一下EXTI14中断标志位是不是为1,返回值是SET和RESET
	 {
		  EXTI_ClearITPendingBit(EXTI_Line14);//清除标志位
	 }

判断一下,看看是不是,如果是的话,我们就可以进入执行中断程序了。

最后,中断程序结束后,一定再调用一下清除中断标志位的函数。

因为中断标志位置1了,程序就会跳转到中断函数。

如果不清除中断标志位,那它就一直申请中断。这样程序就会不断响应中断,执行中断函数,那程序就卡死在中断函数里了。

中断函数不需要声明,因为中断函数不需要调用它是自动执行的。

最后,代码如下:

CountSensor.c

#include "stm32f10x.h"                  // Device header              

uint16_t CountSensor_Count;

void CountSensor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//配置AFIO
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//配置AFIO
	
	 EXTI_InitTypeDef  EXTI_InitStructure;//EXTI配置
	 EXTI_InitStructure.EXTI_Line= EXTI_Line14;
	 EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	 EXTI_InitStructure.EXTI_Mode= EXTI_Mode_Interrupt;
	 EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling;
	 EXTI_Init( &EXTI_InitStructure);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC配置
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn ;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init( &NVIC_InitStructure);
}

uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

 void EXTI15_10_IRQHandler (void)
 {
	 if(EXTI_GetITStatus(EXTI_Line14)==SET)//看一下EXTI14中断标志位是不是为1,返回值是SET和RESET
	 {
		  CountSensor_Count++;
		 EXTI_ClearITPendingBit(EXTI_Line14);
	 }
 }

CountSensor.h

#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H

void CountSensor_Init(void);
uint16_t CountSensor_Get(void);

#endif

main.c

#include "stm32f10x.h"                  // Device header              
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"

int main()
{
	
	OLED_Init();
	CountSensor_Init();
	OLED_ShowString(1,1,"Count:");
	
	while(1)
	{
      OLED_ShowNum(1,7,CountSensor_Get(),5);
	}
}

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

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

相关文章

[算法沉淀记录] 排序算法 —— 冒泡排序

排序算法 —— 冒泡排序 基本概念 冒泡排序是一种简单的排序算法。它重复地遍历要排序的列表,一次比较两个元素,并交换它们的位置,如果它们不是按照升序排列的。这步遍历是重复进行的,直到没有再需要交换,也就是说该…

【MATLAB】 LMD信号分解+FFT傅里叶频谱变换组合算法

有意向获取代码,请转文末观看代码获取方式~ 展示出图效果 1 LMD分解算法 LMD (Local Mean Decomposition) 分解算法是一种信号分解算法,它可以将一个信号分解成多个局部平滑的成分,并且可以将高频噪声和低频信号有效地分离出来。LMD 分解算…

消息中间件篇之RabbitMQ-消息不丢失

一、生产者确认机制 RabbitMQ提供了publisher confirm机制来避免消息发送到MQ过程中丢失。消息发送到MQ以后,会返回一个结果给发送者,表示消息是否处理成功。 当消息没有到交换机就失败了,就会返回publish-confirm。当消息没有到达MQ时&…

打开 Camera app 出图,前几帧图像偏暗、偏色该怎样去避免?

1、问题背景 使用的安卓平台,客户的应用是要尽可能快的获取到1帧图像效果正常的图片。 但当打开 camera 启动出流后,前3-5帧图像是偏暗、偏色的,如下图所示,是抓取出流的前25帧图像, 前3帧颜色是偏蓝的,…

[嵌入式系统-33]:RT-Thread -18- 新手指南:三种不同的版本、三阶段学习路径

目录 前言:学习路径:入门学习-》进阶段学习》应用开发 一、RT-Thread版本 1.1 标准版 1.2 Nano 1.3 Smart版本 1.4 初学者制定学习路线 1.5 RT-Thread在线文档中心目录结构 1.6 学习和使用RT-Thread的三种场景 二、入门学习阶段:内…

架构设计:微服务架构实践

引言 前段时间做项目的时候有客户问到过我,什么微服务?微服务是一种架构风格,其中软件系统被构建为一组小型服务,每个服务都运行在自己的进程中并使用轻量级通信机制(如HTTP或消息队列)进行通信。这些服务…

Spring Boot与Netty:构建高性能的网络应用

点击下载《Spring Boot与Netty:构建高性能的网络应用》 1. 前言 本文将详细探讨如何在Spring Boot应用中集成Netty,以构建高性能的网络应用。我们将首先了解Netty的原理和优势,然后介绍如何在Spring Boot项目中集成Netty,包括详…

代码随想录算法训练营第三天

● 自己看到题目的第一想法 203.移除链表元素 方法一: 思路: 设置虚拟头节点 dummyhead 设置临时指针 cur 遍历 整个链表 循环: 如果 cur !nullptr &&cur->next !nullptr 则 遍历链表 否则结束遍历 如果 cur->next val 则…

C++ //练习 8.4 编写函数,以读模式打开一个文件,将其内容读入到一个string的vector中,将每一行作为一个独立的元素存于vector中。

C Primer(第5版) 练习 8.4 练习 8.4 编写函数,以读模式打开一个文件,将其内容读入到一个string的vector中,将每一行作为一个独立的元素存于vector中。 环境:Linux Ubuntu(云服务器&#xff09…

装修避坑干货|阳台洗衣柜洗衣机一体柜设计。福州中宅装饰,福州装修

装修的时候常常会在洗衣柜中嵌入洗衣机,其实阳台柜的安装并不像看起来的那么简单,下面给大家说说几个注意事项‼️ 01.水电位置 在安装阳台柜之前,务必确认水电管道的位置。确保阳台柜不会阻碍水电管道的使用,以免造成不必要的麻…

U盘乱码与文件丢失:恢复指南与预防策略

U盘乱码文件丢失是一种常见的技术问题,通常表现为存储在U盘中的文件名显示为不可识别的字符或文件无法正常打开,有时甚至文件会完全消失。这种情况可能由多种原因引起,包括但不限于文件系统损坏、不正确的拔插操作、病毒感染、兼容性问题等。…

花生壳内网穿透教程(图文并茂)

目录 前言: 使用教程: 1.注册账号 2.软件下载及安装: 3.账号绑定及花生壳的使用 4.内网穿透的配置(重点) 4.2 新增映射页面: 4.3 上面几种映射的区别: 4.4 上面TCP类型的区别:…

Linux进程信号 ----- (信号保存)

前言 信号从产生到执行,并不会被立即处理,这就意味着需要一种 “方式” 记录信号是否产生,对于 31 个普通信号来说,一个 int 整型就足以表示所有普通信号的产生信息了;信号还有可能被 “阻塞”,对于这种多状…

鸿蒙中的九种布局概述

鸿蒙中的九种布局概述 概述 鸿蒙开发中包含就种布局,分别为线性布局、层叠布局、弹性布局、相对布局、栅格布局、媒体布局、列表、网格、轮播。 线性布局 线性布局通过Row和Column进行构建,是其他布局的基础。其中Row是水平方向排列,Colu…

电路设计(25)——4位数字频率计的multisim仿真及PCB设计

1.设计要求 使用4位数码管,显示输入信号的频率。完成功能仿真后,用AD软件,画出原理图以及PCB。 2.电路设计 输入信号的参数为: 可见,输入为168HZ,测量值为170HZ,误差在可接受的范围内。 3.PCB设…

Jenkins解决Host key verification failed (2)

Jenkins解决Host key verification failed 分析原因情况 一、用OpenSSH的人都知ssh会把你每个你访问过计算机的公钥(public key)都记录在~/.ssh/known_hosts。当下次访问相同计算机时,OpenSSH会核对公钥。如果公钥不同,OpenSSH会发出警告,避免…

【Java程序设计】【C00299】基于Springboot的仓库管理系统(有论文)

基于Springboot的仓库管理系统(有论文) 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的仓库管理系统,本系统有管理员角色权限; 系统整体功能有:个人信息管理、仓库管理、物资管理、物资…

matplotlib子图绘制

文章目录 子图组合网格布局GridSpec matplotlib教程: 初步 子图 从绘图流程出发,【plt】图像有三个层级,依次是窗口、坐标系以及图像。在一个坐标系中可以有多条曲线,即表示多个图像;相应地在一个窗口中,…

文心一言 VS 讯飞星火 VS chatgpt (201)-- 算法导论15.2 6题

六、用go语言,证明:对 n 个元素的表达式进行完全括号化,恰好需要 n-1 对括号。 文心一言,代码正常运行: 在Go语言中,证明对n个元素的表达式进行完全括号化恰好需要n-1对括号,可以通过数学归纳…

将yolov8权重文件转为onnx格式并在c#中使用

yolo模型转ONNX 在yolov8中,我们将训练结果的.pt权重文件转换为onnx格式只需要使用ultralytics库中的YOLO类,使用pip安装ultralytics库,然后执行下面python代码 from ultralytics import YOLO# 加载YOLOv8模型 model YOLO("best.pt&q…
最新文章