第3章单片机C语言程序设计课件.ppt

上传人:牧羊曲112 文档编号:1610619 上传时间:2022-12-10 格式:PPT 页数:80 大小:707.50KB
返回 下载 相关 举报
第3章单片机C语言程序设计课件.ppt_第1页
第1页 / 共80页
第3章单片机C语言程序设计课件.ppt_第2页
第2页 / 共80页
第3章单片机C语言程序设计课件.ppt_第3页
第3页 / 共80页
第3章单片机C语言程序设计课件.ppt_第4页
第4页 / 共80页
第3章单片机C语言程序设计课件.ppt_第5页
第5页 / 共80页
点击查看更多>>
资源描述

《第3章单片机C语言程序设计课件.ppt》由会员分享,可在线阅读,更多相关《第3章单片机C语言程序设计课件.ppt(80页珍藏版)》请在三一办公上搜索。

1、3 单片机的C语言程序设计,2,教学目的,熟悉C51的程序结构掌握C51的数据类型、存储类型及函数等概念了解C51与标准C的不同之处熟悉C51的开发工具Keil C51,如何学习,3,本章内容,C51的程序结构数据类型、存储类型及存储模式运算符、函数及程序流程控制C51的构造数据类型C51实例分析及混合编程Keil C51简介,.1 51的程序结构,与汇编语言相比,C51在功能、结构性、可读性、可维护性及可移植性上有明显的优势 。C51程序结构与一般C语言没有差别。,5,C51的特点:1C51在吸取了汇编语言精华的基础上又有所改进 2C51继承和发扬了高级语言的长处 3C51的可移植性好 4生

2、成的代码质量高,在代码效率方面可以和汇编语言相媲美,51的程序结构,3.151的程序结构,6,C51程序的结构一个C51程序大体上是一个函数定义的集合,在这个集合中仅有一个名为main的函数(主函数)。 函数定义由类型、函数名、参数表和函数体四部分组成。函数的一般格式如下: 类型 函数名(参数表) 参数说明; 数据说明部分; 执行语句部分; ,51的程序结构,3.151的程序结构,7,编写C51程序时应注意以下几点:1.一个C51程序总是从main函数开始执行的,而不论main函数在整个程序中的位置如何。2C语言程序的书写格式十分自由。一条语句可以写成一行,也可以写成几行,还可以在一行内写多条

3、语句。但每条语句都必须以分号“;”作为结束符。3C语言对大小字母敏感,C语言编译器编译程序时对同一个字母的大小写作为不同的变量来处理。4可以用/*/对C程序中的任何部分作注释。程序加上必要的注释,可增加程序的可读性。,51的程序结构,3.151的程序结构,.2 数据类型、存储类型及存储模式,数据类型常量和变量C51的存储类型及存储模式特殊功能寄存器、并行接口及位变量的定义,9,数据类型 可分为基本数据类型和构造数据类型。构造数据类型由基本数据类型构造而成。C51的数据类型分类如下所示:,数据类型、存储类型及存储模式,3.2 数据类型、存储类型及存储模式,10,11,常量和变量 C语言中的数据有

4、常量、变量之分。 常量在程序运行的过程中,其值不能改变的量称为常量。C语言支持4种常量:整数常量、浮点数常量、枚举量常量、字符常量。 变量在程序执行过程中,其值能不断变化的量。使用一个变量之前必须进行定义,用一个标识符作为变量名并指出它的数据类型和存储类型,以便编译系统为它分配相应的存储单元。,数据类型、存储类型及存储模式,3.2 数据类型、存储类型及存储模式,12,C51的存储类型及存储模式 51系列单片机的存储器采用哈佛结构。即其程序存储器和数据存储器分开,并有各自的寻址机构和寻址方式。以AT89C52为例,其具体结构如下图所示。,数据类型、存储类型及存储模式,3.2 数据类型、存储类型及

5、存储模式,13,C51的存储类型及存储模式AT89C52单片机在物理上有四个存储空间:1片内程序存储器空间2片外程序存储器空间3片内数据存储器空间4片外数据存储器空间 片内数据存贮区是存放临时性传递变量或使用频率较高的变量的。访问片内数据存贮器速度较快,经常使用的变量置于片内数据存贮器,而将不常用的置于片外数据存贮器中。寻址方式可使用直接和间接寻址,数据类型、存储类型及存储模式,3.2 数据类型、存储类型及存储模式,14,C51在定义变量、常量时,将通过不同的存储类型(data、bdata、idata、pdata、xdata、code)将它们定位在不同的存储区中。存储类型与AT89C52单片机

6、实际存储空间的对应关系如下所示。data 直接寻址内部数据存储区,访问速度快(128B),007FHbdata 可位寻址内部数据存储区,允许位与字节混合访问(16B)idata 间接寻址内部数据存储区,可访问片内全部RAM地址空间(256B),00FFHpdata 分页寻址外部数据存储区(256B),由MOVX Ri访问(高位P2)xdata 外部数据存储区(64KB),由MOVX DPTR访问code 代码存储区(64KB)程序由MOVC A+DPTR访问,C51的存储类型,3.2 数据类型、存储类型及存储模式,一定要熟悉噢!,15,当使用code存贮类型定义数据时,C51编译器会将其定义在

7、代码空间或FLASH访问片内数据存贮器data、bdata、idata比访问片外数存相对要快一些,因此,可将经常使用的变量置于片内数存,16,例:,code uchar table =1,2,3,”help”,oxff;idata uint temp;data char var; /char data var;等价尽量用后一种static unsigned long xdata array100 ; 静态变量extern float idata x,y,z;模块化编程uint pdata dimension;uchar xdata vector 1044;char bdata flags;,1

8、7,如果省略掉数据类型存储模式,将自动决定变量的默认存储类型作为编译的选项。由编译器选用的存储模式决定。存储模式决定了变量的默认存储类型,参数传递区和无明确存储类型说明变量的存储类型。,18,存储模式说明,SMALL :可直接寻址的内部数据存储区COMPACT: 分页外部数据存储区LARGE :外部数据存储区参数和局部变量放入Cx51允许在变量类型定义前,指定存储类型 data char x 与char data x 等价,但应尽量使用后一种,19,特殊功能寄存器的定义 AT89C52单片机中除了程序计数器PC和4组通用寄存器组(R0-R7)之外,其他所有寄存器均称为特殊功能寄存器(SFR),

9、它们分散在片内RAM区的高128B中,它们只能采用直接寻址方式访问。 为了能直接访问这些SFR,C51扩充了类型说明符sfr、sfr16、sbit。利用它们可在C语言源程序中直接对AT89C52单片机的特殊功能寄存器进行定义。,数据类型、存储类型及存储模式,3.2 数据类型、存储类型及存储模式,20,21,定义方法如下:sfr 特殊功能寄存器名=地址常数sfr16特殊功能寄存器名=地址常数注:1、可以与变量名一样,用SFR名去存取SFR。 2、定义时标示符必须使用SFR名,且必须把原来分配好的绝对地址赋给预定义标示符例:sfr Acc=0 xE0; sfr P0=0 x80;,22,由于51系

10、列中不同的单片机的寄存器数量与类型不同,所以可采用头文件。 头文件reg52.h中有所有89C52的SFR及可位寻址的位的定义,只要 # include ,23,程序的开头可以都加上以下三行#include#define uchar unsigned char#define uint unsigned int头文件reg52.h中有所有89C52的SFR 及可位寻址位的定义,24,SFR一般按8位存取,在新的8051系列产品中有按16位存取的(如,8052有T2),它们需用sfr16类型来说明(*) sfr16 T2=0 xCC,25,定义SFR的位,Sbit可以访问位寻址对象1、sbit S

11、FR的位标示符=可按位寻址 的SFR 名常量 sfr PSW=0 xD0; sbit OV=PSW2; sbit Cy=PSW7;,26,2、 sbit SFR的位标示符=可按位寻址 的SFR 的绝对地址常量其中,绝对地址位于80HFFH之间,且该地址能被8整除 sbit OV=0 xD02; sbit Cy=0 xD07;,27,3、 sbit SFR的位标示符=可按位寻址 的SFR 的绝对位地址 该绝对位地址位于80HFFH之间 sbit OV=0 xD2; sbit Cy=0 xD7; 特殊功能位代表了一个独立的定义类,不能与其他位定义和位域互换,28,并行接口的定义 AT89C52单片

12、机内部有4个8位并行口,即SFR中的P0-P3。这些内部并行口既可进行字节寻址,也可进行位寻址。 除了内部4个并行口之外,AT89C52单片机还可以在片外扩展硬件I/O口,这些I/O口大多只能进行字节寻址。 AT89C52单片机没有专用的I/O指令,它的口地址是和数据存储器统一编址的。即把一个口看作是数据存储器的一个单元。程序采用访问数据存储器的指令来访问I/O口。,数据类型、存储类型及存储模式,3.2 数据类型、存储类型及存储模式,29,51系列并行接口及其Cx51定义,编程时,片内I/O口与片外扩展I/O口可统一在头文件中定义,也可在程序的开始位置定义,方法如下:1、片内I/O口(用sfr

13、定义) sfr P0=0 x80 sfr P1=0 x90,30,2、片外扩展I/O口,根据其硬件译码地址,将其视为片外数据存储器的一个单元,用define定义。#include#define PORTA XBYTE0 xffc0 i=PORTA; PORTA=i;,31,定义口地址的目的,便于Cx51编译器按51单片机实际硬件结构建立I/O口变量名与其实际地址的联系,以便使用软件模拟51单片机的硬件操作,32,位变量(bit)及其Cx51定义,bit类似于unsigned int等,用来说明变量类型一、定义位变量1、bit check_bit ; /*将变量check_bit定义为 位变量*

14、/,33,位变量总位于8051内部RAM中(*)bdata bit display_flag ; /*正确*/data bit display_flag ; /*正确*/idata bit display_flag ; /*正确*/ bit display_flag ; /*存储模式为small,正确*/pdata bit display_flag ; /*错*/static bit display_flag ; /*正确*/extern bit display_flag ; /*正确*/ bit *bptr ; /*错*/ bit b_array3; /*错*/注:位变量不能定义成一个指针和

15、数组,34,2、函数可包含类型为bit的参数,也可将其作为返回值 bit func(bit b0, bit b1).return(b1),35,3、可位寻址对象 指可以字节或位寻址的对象。该对象应位于89C52片内可位寻址RAM中。允许数据类型为idata的对象放入89C52片内可位寻址RAM区中。定义可分为两步:1)先定义变量的数据类型和存储类型(*) bdata int ibase ; bdata char bary4;,36,2) 然后可使用sbit定义可独立寻址访问的对象位sbit mybit0=ibase0;sbit mybit15=ibase15;sbit Ary07=bary07

16、;sbit Ary37=bary37; 对象ibase和bary也可以字节寻址 Ary37=0;/*位寻址*/ bary3=a;/*字节寻址*/注:1、Sbit定义要求基址对象的存储类型为bdata,否则 只有绝对的特殊位(SFR中的位定义)定义是合 法的。 2、位置(操作符)后的最大值依赖于指定的基类 型,37,例:判浮点数的符号为是否为正union float long 高地址 float bdata f ; long bdata l ;fl ; 低地址sbit float_sign=fl.f31 ; if (! float_sign) /*符号位为正*/ . else .,7,0,3.3

17、 运算符、函数及程序流程控制,C51的运算符C51的函数C51的流程控制语句,39,C51的运算符1.C51的赋值运算符和算术运算符(+、-、*、/、%)2.C51的关系运算符(、=、 )5.自增减运算6.复合运算符(10种),运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,40,C51的函数 函数是C语言中的一种基本模块,一个C语言程序就是由若干个模块化的函数所构成的。 1.函数的分类从用户使用角度划分,函数分为标准库函数和用户自定义函数。用户自定义函数是指用户根据任务自己编写的函数。从函数定义的形式上可划分为三种形式:无参数函数、有参数函数和空函数。,运算符、函数及程序流程控

18、制,3.3运算符、函数及程序流程控制,41,C51的函数 2.函数的定义无参数函数的定义返回值类型 函数名() 函数体语句有参数函数的定义返回值类型 函数名(形式参数列表) 形式参数类型说明 函数体语句空函数的定义返回值类型 函数名() ,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,42,C51的函数 3.函数的参数和返回值形式参数和实际参数形参:定义函数时,位于函数名后面圆括号中的变量名称为“形式参数”。实参:调用函数时,主调用函数名后面括号中的表达式称为“实际参数”。参数的返回值通过函数调用使主调用函数获得一个确定的值,这就是函数的返回值。函数的返回值是通过return语

19、句获得的。,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,43,C51的函数 4.函数的调用调用的形式为:函数名(实际参数表列); 函数的调用方式有三种:1.函数调用语句:即把被调用函数名作为调用函数的一个语句,如func( ) 2.被调用函数作为表达式的运算对象:如x=2*func(a,b) 3.被调用函数作为另一个函数的实际参数:如x=min(a,func(a,b);,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,44,C51的函数 5.对被调用函数的说明在调用一个函数之前,应对该函数的类型进行说明,即“先说明,后调用”。被调用函数必须是已经存在的函数(库函

20、数或用户自定义函数)。6.函数变量的存储方式函数变量按其有效作用范围可划分为局部变量和全局变量。变量按其存储方式可分为四种存储种类,分别是自动变量(auto)、外部变量(extern)、静态变量(static)和寄存器变量(register),这些存储种类与变量的关系为:内部变量可定义为自动变量、静态变量或寄存器变量,外部变量可定义为全局变量或静态变量。,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,45,C51的流程控制语句 C语言有三种基本结构:顺序结构、选择结构与循环结构。其中选择结构又派生出串行多分支结构和并行多分支结构,循环结构又分为“while”型循环结构和“do

21、while”型循环结构。,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,顺序结构流程图,选择结构流程图,46,C51的流程控制语句 C语言有三种基本结构:顺序结构、选择结构与循环结构。其中选择结构又派生出串行多分支结构和并行多分支结构,循环结构又分为“while”型循环结构和“do while”型循环结构。,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,循环结构流程图,47,C51的流程控制语句 1.选择语句ifC语言提供了3种形式的if语句:(1)if (条件表达式) 语句;(2)if (条件表达式) 语句1; else 语句2;(3)if (条件表达式1)

22、语句1; else if (条件表达式2) 语句2; else if (条件表达式3) 语句3; else if (条件表达式m) 语句m; else 语句n;,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,48,C51的流程控制语句 2. switch/case语句 该语句形式如下:switch (表达式) case 常量表达式1: 语句1; break; case 常量表达式2: 语句2; break; case 常量表达式n: 语句n; break; default : 语句n+1; ,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,49,C51的流程控制语

23、句 3. 循环语句while语句一般形式为:while (条件表达式) 语句; /*循环体*/do while 语句一般形式如下:do 语句; /*循环体*/ while (条件表达式);,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,50,C51的流程控制语句 3. 循环语句for语句一般形式如下:for (初值设定表达式;循环条件表达式;更新表达式) 语句; /*循环体*/,运算符、函数及程序流程控制,3.3运算符、函数及程序流程控制,3.4 C51的构造数据类型,数组结构联合枚举指针,52,数组一维数组一维数组的定义方式类型说明符 数组名整型表达式初始化 二维数组二维数组

24、的定义方式类型说明符 数组名常量表达式 常量表达式二维数组的初始化,C51的构造数据类型,3.4 C51的构造数据类型,53,结构结构的定义 结构是结构是一种数据类型,通常定义结构数据类型后再用它去定义相关变量。定义结构类型的方法如下: struct 结构名 结构成员说明;结构成员说明的格式为:类型标识符 成员名;用结构类型去定义变量的方法有三种:先定义结构类型再定义类型为该结构的变量 定义结构类型的同时,定义该结构的变量 直接定义结构类型变量 结构变量的引用引用的方式为:结构变量名.成员名,C51的构造数据类型,3.4 C51的构造数据类型,54,联合 又称为共用体。它也是C语言中的一种构造

25、数据类型。在一个联合中可以包含多个不同类型的数据元素。其定义格式为:union 联合类型名 类型说明符 变量名; ; 联合与结构的区别在于:联合所包含的各个成员只能分时共享同一存储空间。定义联合类型变量的方法类似于定义结构变量,同样有三种。引用联合成员的一般格式为:联合变量名.联合元素 或:联合变量名-联合元素,C51的构造数据类型,3.4 C51的构造数据类型,55,枚举 枚举数据类型是一个有名字的某些整数型常量的集合。这些整数型常量是该类型变量可取的所有的合法值。枚举定义应当列出该类型变量的可取值。定义枚举类型变量的一般格式为:enum 枚举名 枚举值表列 变量表列;,C51的构造数据类型

26、,3.4 C51的构造数据类型,56,指针 指针是C语言的一个重要概念,也是特色之一。使用指针可以有效地表示复杂的数据结构,直接处理内存地址,而且可以更有效地使用数组。 指针的概念 变量的指针就是变量的地址 指针变量的定义指针变量定义的一般形式如下:类型标示符 *指针变量名指针变量的引用 应注意的是:“*”在指针变量定义时和在指针运算时所代表的含义是不同的。在进行指针变量定义时,“*”是指针变量类型说明符。而在进行指针运算时,“*”是指针运算符,表示引用指针所指向的变量。,C51的构造数据类型,3.4 C51的构造数据类型,57,5.2.1 指针的基本概念 变量的指针就是变量的地址。 指向变量

27、的指针变量:若有一个变量专门来存放另一个变量的地址,则该变量称为指向变量的指针变量。1、指针变量的定义 类型标示符 *指针变量名 例:int *ap ;/*定义ap为指针变量,指 向整型变量*/,58,2、指针变量的引用(*) int a; int *ap; ap=&a;/*ap指向变量a,&为取地址符*/ 指针运算符“*”,*ap和a等价 *ap表示ap所指向的变量 x=*ap;/*ap所指向的变量赋值给x*/ *ap=0 xff;/*a=0 xff*/,59,关于KEIL C51的指针类型,支持“基于存贮器的”指针和“一般”指针两种类型。基于存贮器的指针 在编译时一般被“行内”编码,无须库

28、调用。即指针所指的对象所在的存储空间由定义时c源代码中的存储器类型决定。该指针只需12字节一般指针 一般指针包括3个字节。2字节偏移(即指针的地址)和1字节存储器类型。为了表示这种指针必须用长整数来定义存贮类型。,60,指针定义(*) static data unsigned char data *标示符 auto idata char idata register pdata unsigned int pdata extern xdata int xdata code unsinged long code bdata long bdata float struct union,61,第二列指

29、出指针变量存放在何处,缺省时决定于编译用存储模式第四列指明指针所指的对象存放在何处,缺省时为三字节的通用指针。,62,1、基于存贮器的指针 由C源代码中的存贮类型决定。用这种指针可高效访问对象且只需1至2字节1个字节idata*, data *,pdata*2个字节code *,xdata*例:char xdata *px 其中,xdata为指针指向的对象变量定位存储空间,指针长度为2个字节,所指的对象是个字符型,指针自身在默认存储器区(决定于编译模式),63,char idata *px ;所指的对象变量在idata 中,指针为1个字节 data char xdata *px; 除指明指针所

30、指对象 在xdata中,还指明指 针自身存放在data中 char xdata *data px; 与上式等价 与早期C51版本兼容,64,例(*) struct time char hour; char min; char sec; struct time xdata *pxtime struct time idata *px; px-pxtime-hour=12;,65,2、一般指针 即定义时未指明所指对象的存储空间共3字节:1个存放存储器类型,2个存放地 址偏移量,低,高,指针的高位和低位地址偏移量,66,存储器类型编码(*),67,指针 对比一般指针和基于存储器的指针,可得出以下结论:

31、基于存储器的指针长度短,运行速度快。而一般指针由于其所指对象变量的存储器空间位置只有在运行期间才能确定,编译器在编译期间无法优化存储方式,必须生成一般代码以保证能对任意空间的对象进行存取,所以一般指针所产生的代码运行速度较慢。一般指针可用于存取任何变量而不必考虑变量在AT89C52存储器空间的位置,函数可利用一般指针来存取位于任何存储空间的数据。因此一般指针的兼容性较好。而基于存储器的指针所指的对象有确定的存储器空间,因此缺乏兼容性。,C51的构造数据类型,3.4 C51的构造数据类型,例:清零程序 (将2000H20FFH的内容清零) 汇编语言程序 ORG 0030HSE01: MOV R0

32、,#00H MOV DPTR,#2000H ;(0000H)送DPTRLOO1: CLR A MOVX DPTR,A ;0送(DPTR) INC DPTR ;DPTR+1 INC R0 ;字节数加1 CJNE R0,#00H,LOO1 ;不到FF个字节再清LOOP: SJMP LOOP,69,清零程序 (将2000H20FFH的内容清零) C51程序 #include main( ) int i; unsigned char xdata *p=0 x2000; /* 指针指向2000H单元 */ for(i=0;i256;i+) *p=0; p+; /*清零2000H-20FFH单元*/ ,

33、汇编语言程序 ORG 0030HL00: MOV R0,#10H ;查找16个字节 MOV R1,#00H MOV DPTR,#2000HL11: MOVX A,DPTR CJNE A,#00H,L16 ;取出内容与00H相等吗? INC R1 ;取出个数加1L16: INC DPTR DJNZ R0,L11 ;未完继续 MOV DPTR,#2100H MOV A,R1 MOVX DPTR,A ;相同数个数送2100HL1E: SJMP L1E,例:查找零的个数(在2000H-200FH中查出有几个字 节是零,把个数放在2100H单元中),查找零的个数C51程序#include main (

34、) unsigned char xdata *p=0 x2000;/*指针p指向2000H单元*/ int n=0,i; for(i=0;i16;i+) if(*p=0) n+; /* 若该单元内容为零,则n+1 */ p+; /* 指针指向下一单元 */ p=0 x2100; /* 指针p指向2100H单元 */ *p=n; /* 把个数放在2100H单元中 */,3.5 C51实例分析及混合编程,C51实例分析混合编程,73,C51实例分析 设计一个程序来实现以下功能:使用拨动开关模拟从P3.0口输入一个脉冲,控制P1口按16进制加一方式变化。在这里,我们选用51系列的AT89C52型号单

35、片机。,C51实例分析及混合编程,3.5 C51实例分析及混合编程,74,程序如下:,#include sbit P30=P30;void DELAY(); /*声明延迟函数*/void main() unsigned char i=1; P1=0 xff; /* 初始化P1口,LED全灭*/ while(1) while(P30); /* 若P3.0 = 1,则等待*/ DELAY(); /*调用延时函数用来消除抖动,判断P3.0 是否真正为1*/,75,while(!P30); /* 若P3.0 = 0,则等待*/ DELAY(); /*调用延时函数用来消除抖动,判断P3.0是否真正为 0

36、*/ P1=!i; i+; /*P3.0经过一个高电平和一个低电平,认为来了一 个脉冲,则让P1加一并输出相应数值*/ void DELAY(void) unsigned char i=0; for (i=0;i255;i+) ;,76,混合编程在C51程序中直接嵌入汇编在C文件中直接嵌入汇编代码可以用下面的方式:#pragma ASM 汇编代码#pragma ENDASM 用此方法可以在C源代码的任意位置嵌入汇编语句。但要注意的是在直接使用形参时要小心,在不同的优化级别下产生的汇编代码有所不同。,C51实例分析及混合编程,3.5 C51实例分析及混合编程,3.6 Keil C51简介,项目文件的建立、设置与目标文件的获得程序的调试,78,项目文件的建立、设置与目标文件的获得1.启动Vision2并创建一个项目2.创建新的源文件3.项目的详细设置4.编译和链接程序,Keil C51简介,3.6 Keil C51简介,79,程序的调试1.常用调试命令2.在线汇编3. 断点设置4.程序调试窗口存储器窗口观察窗口反汇编窗口串行窗口,Keil C51简介,3.6 Keil C51简介,80,本章小结,熟悉C51的程序结构掌握C51的数据类型、存储类型及函数等概念了解C51与标准C的不同之处熟悉C51的开发工具Keil C51,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号