LV.12 D13 UART实验 学习笔记

这篇具有很好参考价值的文章主要介绍了LV.12 D13 UART实验 学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、UART帧格式详解

 UART    

        Universal Asynchronous Receiver Transmitter 即     通用异步收发器,是一种通用的串行、异步通信总线     该总线有两条数据线,可以实现全双工的发送和接收,在嵌入式系统中常用于主机与辅助设备之间的通信。

通信基础 - 并行和串行

        并行通信:总线在传送数据的时候,可以一次性发送多位数据。

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

        

        串行通信:数据线只有一根,逐次传送各位数据

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发 

        在同等条件下,并行比串行的通信速度更快,但并行使用的总线数量较多,会比较浪费资源,布线难度也比较大,不同总线在传输数据时,线和线之间都会有一些信号的干扰。做项目时,使用串行通信多一点

通信基础 - 单工和双工

        单工通信:通信的双方区分为发送器和接收器,数据传输的方向是单向的

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发 

        双工通信: 分为半双工和全双工,它们数据的传输都是双向的。半双工通信时,A和B可以互相发数据,但不能是同时的,而全双工可以同时进行。

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发如果总线的数据线只有一根,一般是半双工的,如果有多根,一般是全双工。

通信基础 - 波特率 

        波特率用于描述UART通信时的通信速度,其单位为bps(bit per second)即每秒钟传送的bit的数量

UART帧格式

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

        起始位表示一次通信的开始,数据位就是通过串口发送的数据,校验位会校验(只能校验,不能修正)数据发送的正确性,停止位表示一次通信的结束。如果想发送多个数据,重复这个步骤就可以

注:  1、数据线在空闲时,数据线上的状态必须是高电平。       

         2、发送数据时,先发低位数据。

        3、串口每次只能发送一个字节, 是为了避免产生累计误差(发送方与接收方的时间误差)。

        串口一般为奇偶校验。奇偶校验(Parity Check)是一种校验代码传输正确性的方法。根据被传输的一组二进制代码的数位中“1”的个数是奇数或偶数来进行校验。采用奇数的称为奇校验,反之,称为偶校验。采用何种校验是事先规定好的。通常专门设置一个奇偶校验位,用它使这组代码中“1”的个数为奇数或偶数。若用奇校验,则当接收端收到这组代码时,校验“1”的个数是否为奇数,从而确定传输代码的正确性。

UART硬件连接

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 两个芯片通信时,要交叉接线,一方的TXD要与另一方的RXD连接,

 

UART控制器

         一般情况下处理器中都会集成UART控制器 我们使用UART进行通信时候只需对其内部的相 关寄存器进行设置即可。

二、Exynos4412下的 UART控制器

引脚功能设置 

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 注:设置引脚功能的实质是让引脚在芯片内部连接到某一个对应的控制器上

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发 

        串口的高低电平信号较弱,极有可能收到干扰,通信距离较短。为了增强串口的信号,在中间加了一个U3芯片,把串口发出来的TTL信号转化为232信号。 

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

         设置引脚功能本质上就是让引脚连接对应的控制器

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发  

        UART在4412内部提供了四个独立的通道,每个通道都包含输入输出端口,这4和通道时Ch0-3,另外还提供了一个专用通道Ch4,用于跟GPS通信(本次使用的是ch2)。所有的端口都可以运行中断和DMA模式。在CPU和串口控制器进行数据传送时,UART既可以产生中断,也可以产生DMA的一些请求。UART支持的波特率最大是4Mbps。每个串口通道都有两个FIFO用于接收和发送数据,

        想要发送数据时,只需要将发送内容写入FIFO(队列)就会自动发送,接收时也只需要读取FIFO(队列)内容即可。

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发 

串口的波特率是可以编程的

支持红外传输,(无线)

1位或者两位停止位

数据位可以是5-8位,还可以有校验位

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 波特率发生器、发送器、接收器、控制单元

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发 

        波特率的发生器使用SCLK_UART时钟,他的频率时100M,发送器和接收器都包含队列和移位器。要被发送的数据会先写到发送队列里,如何数据会被拷贝到发送的移位器。数据通过发送数据的引脚被移出去。接收的数据从接收数据的引脚移进来,被拷贝到接收的缓冲区里。

数据通过FIFO->移位器->引脚实现发送,通过引脚->移位器->FIFO实现接收。

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发 

三、UART寄存器详解 

先用tar xvf命令解压出一个新工程

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

1、将GPA1_0和GPA1_1分别设置成UART2的接收引脚和发送引脚 GPA1CON[7:0] 

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

2、设置UART2的帧格式 ULCON2 (8位数据位、1位停止位、无校验位、正常模式)

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发 

 回环模式就是将发送端和用户端短接,一般用于测试。

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

中断模式:有消息通知CPU

轮询模式:CPU不断的读取缓冲区,查看有没有数据

DMA:自动传送给内存,解放CPU

 AFC:自动流控制,这次实验就是普通的收发暂时不需要

3、设置UART2的接收和发送模式为轮询模式 UCON2[3:0]

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 只读,[1]表示发送的队列是空的,[0]表示接收的队列有数据

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

把要发送的数据写到UTXHn寄存器的[7:0]位

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

接收器接收到的数据会放入URXHn寄存器的[7:0]位

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

  UBRDIVn寄存器的[15:0]位和UFRACVALn寄存器的[3:0]位是设置波特率的

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

4、设置UART2的波特率为115200 (UBRDIV2 / UFRACVAL2) 

四、UART编程

#include "exynos_4412.h"

int main()
{
	/*1.将GPA1_0和GPA1_1设置成UART2的接收引脚和发送引脚 GPA1CON[7:0]*/
	GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);

	/*2.设置UART2的帧格式 ULCON2 (8位数据位、1位停止位、无校验位、正常模式)*/
	UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0);
	
	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:2]*/
	UART2.UCON2 = UART2.UCON2 & (~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 (UBRDIV2 / UFRACVAL2)*/
	UART2.UBRDIV2 = 53;
	UART2.UFRACVAL2 = 4;

	while(1)
	{
		/*将发送的数据写入发送寄存器 UTXH2*/
		UART2.UTXH2 = 'A';
	}
	return 0;
}

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 

#include "exynos_4412.h"

int main()
{
	/*1.将GPA1_0和GPA1_1设置成UART2的接收引脚和发送引脚 GPA1CON[7:0]*/
	GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);

	/*2.设置UART2的帧格式 ULCON2 (8位数据位、1位停止位、无校验位、正常模式)*/
	UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0);
	
	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:2]*/
	UART2.UCON2 = UART2.UCON2 & (~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 (UBRDIV2 / UFRACVAL2)*/
	UART2.UBRDIV2 = 53;
	UART2.UFRACVAL2 = 4;

	while(1)
	{
		/*将发送的数据写入发送寄存器 UTXH2*/
		UART2.UTXH2 = 'A';
		UART2.UTXH2 = 'B';
		UART2.UTXH2 = 'C';
		UART2.UTXH2 = 'D';
	}
	return 0;
}

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 并没有输出想要的结果,因为CPU的执行速度是1GHz,而发送器的速度是115200,两者的速度不一样,所以发送的字符是随机的。

#include "exynos_4412.h"

int main()
{
	/*1.将GPA1_0和GPA1_1设置成UART2的接收引脚和发送引脚 GPA1CON[7:0]*/
	GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);

	/*2.设置UART2的帧格式 ULCON2 (8位数据位、1位停止位、无校验位、正常模式)*/
	UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0);
	
	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:2]*/
	UART2.UCON2 = UART2.UCON2 & (~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 (UBRDIV2 / UFRACVAL2)*/
	UART2.UBRDIV2 = 53;
	UART2.UFRACVAL2 = 4;

	while(1)
	{
		/*将发送的数据写入发送寄存器 UTXH2*/
		while(!(UART2.UTRSTAT2 & (1 << 1)));
		UART2.UTXH2 = 'A';
		while(!(UART2.UTRSTAT2 & (1 << 1)));
		UART2.UTXH2 = 'B';
		while(!(UART2.UTRSTAT2 & (1 << 1)));
		UART2.UTXH2 = 'C';
		while(!(UART2.UTRSTAT2 & (1 << 1)));
		UART2.UTXH2 = 'D';
	}
	return 0;
}

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

#include "exynos_4412.h"

void UART_Init(void)
{
	/*1.将GPA1_0和GPA1_1设置成UART2的接收引脚和发送引脚 GPA1CON[7:0]*/
	GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);

	/*2.设置UART2的帧格式 ULCON2 (8位数据位、1位停止位、无校验位、正常模式)*/
	UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0);
	
	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:2]*/
	UART2.UCON2 = UART2.UCON2 & (~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 (UBRDIV2 / UFRACVAL2)*/
	UART2.UBRDIV2 = 53;
	UART2.UFRACVAL2 = 4;
}

void UART_Send_Byte(char Dat)
{
		/*等待发送寄存器寄存器为空*/
		while(!(UART2.UTRSTAT2 & (1 << 1)));
		/*将发送的数据写入发送寄存器 UTXH2*/
		UART2.UTXH2 = Dat;
}

char UART_Rec_Byte(void)
{
	char Dat;
	/*判断接收寄存器是否接收到了数据*/
	if(UART2.UTRSTAT2 & 1)
	{
		Dat = UART2.URXH2;
		return Dat;
	}
	else
	{
		return 0;
	}
}

int main()
{
	char RecDat = 0;
	UART_Init();
	while(1)
	{
		RecDat = UART_Rec_Byte();
		if(RecDat == 0)
		{

		}
		else
		{
			RecDat = RecDat + 1;
			UART_Send_Byte(RecDat);
		}
	}
	return 0;
}

LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

 输入什么返回什么加一(以ASCII计算)

 

 五、输入输出重定向

#include "exynos_4412.h"

void UART_Init(void)
{
	/*1.将GPA1_0和GPA1_1设置成UART2的接收引脚和发送引脚 GPA1CON[7:0]*/
	GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);

	/*2.设置UART2的帧格式 ULCON2 (8位数据位、1位停止位、无校验位、正常模式)*/
	UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0);
	
	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:2]*/
	UART2.UCON2 = UART2.UCON2 & (~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 (UBRDIV2 / UFRACVAL2)*/
	UART2.UBRDIV2 = 53;
	UART2.UFRACVAL2 = 4;
}

void UART_Send_Byte(char Dat)
{
		/*等待发送寄存器寄存器为空*/
		while(!(UART2.UTRSTAT2 & (1 << 1)));
		/*将发送的数据写入发送寄存器 UTXH2*/
		UART2.UTXH2 = Dat;
}

char UART_Rec_Byte(void)
{
	char Dat;
	/*判断接收寄存器是否接收到了数据*/
	if(UART2.UTRSTAT2 & 1)
	{
		Dat = UART2.URXH2;
		return Dat;
	}
	else
	{
		return 0;
	}
}

void UART_Send_Str(char * pstr)
{
	while(*pstr != '\0')
		UART_Send_Byte(*pstr++);
}

int main()
{
	char RecDat = 0;
	UART_Init();
	while(1)
	{  
		UART_Send_Str("Hello World\n");
	}
	return 0;
}

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

#include "exynos_4412.h"
 
void UART_Init(void)
{
	GPA1.CON = GPA1.CON & (~(0xFF)) | (0x22);
	UART2.ULCON2 = UART2.ULCON2 & (~(0x7F)) | (0x3);
	UART2.UCON2 = UART2.UCON2 & (~(0xF)) | (0x5);
	UART2.UBRDIV2 = 53;
	UART2.UFRACVAL2 = 4;
}
 
void uart_send_byte(char Dat)
{	
	while(!(UART2.UTRSTAT2 & (1 << 1)));
	UART2.UTXH2 = Dat;
}
 
char uart_recv_byte(void)
{
	char Dat = 0;
	if(UART2.UTRSTAT2 & 1)
	{
		Dat = UART2.URXH2;
		return Dat;
	}
	else
    {
		return 0;
    }
}
 
void uart_send_str(char * pstr)
{
    while(*pstr != '\0')
        uart_send_byte(*pstr++);
}
 
int main()
{
    char RecDat = 0;
	UART_Init();

    while(1)
    {
        printf("Hello World\n");
    }
	return 0;
}

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

我们也可以把printf封装一下直接调用,但是这时的输出和以前的不同,以前的是Linux为我们提供的C库,它将输出重定向到显卡,所以我们能在屏幕上看到,而这个printf是我们自己写的,它重定向到了串口,所以我们使用串口软件连接单片机时能打印出来 

作业

 1.若使用UART协议发送一个字节的数据0x63,画出信号线上的时序图

注:8位数据位、无校验位、一位停止位

 LV.12 D13 UART实验 学习笔记,学习,笔记,arm开发

2.编程实现电脑远程控制LED状态

注:在终端上输入‘2’,LED2点亮,再次输入‘2’,LED2熄灭... ...文章来源地址https://www.toymoban.com/news/detail-740967.html

#include "exynos_4412.h"

void UART_Init(void)
{
	/*1.将GPA1_0和GPA1_0_1置成ART2的接收引脚和发送引脚 GPA1CON[7:0]*/
	GPA1.CON = GPA1.CON & (~(0xFF << 0)) | (0x22 << 0);

	/*2.设置UART2的帧格式 ULCON2(8位数据位、1位停止位、无校验位、正常模式)*/
	UART2.ULCON2 = UART2.ULCON2 & (~(0x7F << 0)) | (0x3 << 0);

	/*3.设置UART2的接收和发送模式为轮询模式 UCON2[3:2]*/
	UART2.UCON2 = UART2.UCON2 & (~(0xF << 0)) | (0x5 << 0);

	/*4.设置UART2的波特率为115200 (UBRDIV2 / UFRACVAL2)*/
	UART2.UBRDIV2 = 53;
	UART2.UFRACVAL2 = 4;
}

void GPIO_Init(void)
{
	GPX2.CON = GPX2.CON & (~(0xF << 28)) | (0x1 << 28);
}

void UART_Send_Byte(char Dat)
{
	/*等待发送寄存器寄存器为空*/
	while(!(UART2.UTRSTAT2 & (1 << 1)));
	/*将发送的数据写入发送寄存器 UTXH2*/
	UART2.UTXH2 = Dat;
}

char UART_Rec_Byte(void)
{
	char Dat;
	/*判断接收寄存器是否接收了数据*/
	if(UART2.UTRSTAT2 & 1)
	{
		Dat = UART2.URXH2;
		return Dat;
	}
	else
	{
		return 0;
	}
}

void UART_Send_Str(char * pstr)
{
	while(*pstr != '\0')
		UART_Send_Byte(*pstr++);
}

void OnLED2(void)
{
	GPX2.DAT = GPX2.DAT | (1 << 7);
}

void OffLED2(void)
{
	GPX2.DAT = GPX2.DAT & (~(1 << 7));
}

int main()
{
	char RecDat = 0;
	UART_Init();
	GPIO_Init();
	while(1)
	{   
		RecDat = UART_Rec_Byte();
		if(RecDat == '2')
		{
			if(GPX2.DAT & (1 << 7))
			{
				OffLED2();
			}
			else
			{
				OnLED2();
			}
		}
		else
		{
		}
	}
	return 0;
}

到了这里,关于LV.12 D13 UART实验 学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • lv7 嵌入式开发-网络编程开发 13 UNIX域套接字

    目录 1 UNIX 域流式套接字 2 UNIX 域数据报套接字 UNIX 域流式套接字(UNIX domain stream socket)是一种在同一台主机上的进程之间进行通信的机制。它不依赖于网络协议栈,而是使用文件系统作为通信的基础。 UNIX 域流式套接字提供可靠的、双向的、面向连接的通信方式。与传统的

    2024年02月07日
    浏览(49)
  • 物联网||不一样的点灯实验(2)|通过使用CMSIS库函数实现点灯实验-学习笔记(12)

    根据 Enable the GPIO AHB clock using the following function: __HAL_RCC_GPIOx_CLK_ENABLE(). 初始化需首先调用 __HAL_RCC_GPIOx_CLK_ENABLE() 需要将头文件包含在代码中去。 加入初始化stm32f4xx_hal_gpio.c中的void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init),增加函数:HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_Ini

    2024年02月14日
    浏览(50)
  • ARM系统下的postgis12 和postgis13安装

    目录 一、环境 二、安装 1、安装docker 2、安装postgis(里面包含postgres) 三、测试 四、使用 五、问题 linux的系统在终端输入: uname -a  Linux host-10-208-254-221 4.19.90-2112.8.0.0131.oe1.aarch64 #1 SMP Fri Dec 31 19:53:20 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux 搞不懂是什么系统??? 在网上搜索arm下

    2024年02月02日
    浏览(70)
  • arm学习串口uart数据帧传输

    main.c uart4.h uart4.c

    2024年02月12日
    浏览(36)
  • 用于HUD平视显示器的控制芯片:S2D13V40

    一款利用汽车抬头显示技术用于HUD平视显示器的控制芯片:S2D13V40。HUD的全称是Head Up Display,即平视显示器,以前应用于军用飞机上,旨在降低飞行员需要低头查看仪表的频率。起初,HUD通过光学原理,将驾驶相关的信息投射在飞行员的头盔上,使得飞行员能在保证正常驾驶的

    2024年04月13日
    浏览(34)
  • 物联网|按键实验---学习I/O的输入及中断的编程|读取I/O的输入信号|中断的编程方法|轮询实现按键捕获实验-学习笔记(13)

    1按键功能及用途,常见按键举例 2按键的工作原理 3按键的电路表示 课后小作业: 请大家课后百度现在流行的触摸式按键的相关信息,并考虑如何使用我们开发板的CPU如何操作触摸按键。 了解STM32F407中IO输入信号读取的原理. 了解STM32F407中外部中断的原理 .学习利用CMSIS实现I

    2024年02月14日
    浏览(40)
  • web开发学习笔记(13.mybatis基于注解配置)

    1.使用mybatis基本步骤 2.引入依赖 3.配置application.properties   配置数据库连接信息 4.编写实体类和接口 5.编写测试类 6.或者使用stream流的方式进行输出 7.设置查看mapper层里的提示信息,方便操作 8.  使用jdbc连接数据库 9.数据库连接池 10.lombok,在编译时,会自动生成对应的java代

    2024年01月23日
    浏览(49)
  • 嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第四天-ARM Linux编程之IIC与uart (物联技术666)

    链接:https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd=1688 提取码:1688 教学内容: 1 、 I2C 总线: I2C(Inter-Integrated Circuit),PHILIPS公司开发的两线式半双工同步串行总线;可以用来连接存储器(EEPROM、FLASH)、A/D、D/A转换器、LCD驱动器、传感器等等。 I2C总线有两根信号线:双向数据线

    2024年02月19日
    浏览(45)
  • 【ARM64 常见汇编指令学习 13 -- ARM 汇编 ORG 伪指令学习】

    上篇文章:ARM64 常见汇编指令学习 12 – ARM 汇编函数 的学习 下篇文章:ARM64 常见汇编指令学习 14 – ARM 汇编 .balign,.balignw,.balign 伪指令学习 在ARM汇编中,\\\" org \\\"是一个汇编器伪指令,用于设置下一条指令的装入地址。\\\" org \\\"后面跟着的是一个表达式,这个表达式的值就是下一条

    2024年02月14日
    浏览(50)
  • QT学习笔记-oracle oci数据库驱动交叉编译并移植到ARM开发板

    在上一文《QT学习笔记-QT安装oracle oci驱动》中介绍了在Windows环境下使用QT访问oracle数据库时遇到驱动无法加载问题的解决办法,大体思路是对QT源码中数据库驱动的源码oci进行编译,要想通过编译需要依赖对应数据库的头文件和库(可以通过下载oracle instant client),编译通过

    2024年02月13日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包