完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
|
|
相关推荐
1个回答
|
|
第二部分、随堂记录 1.5.1.Keil软件的介绍和安装 1.5.1.1、IDE概念 (1)IDE(Integrated Development Environment )就是集成开发环境,就是一套用来开发的完整的软件系统。 1.5.1.2、Keil uvision介绍 (1)发展: Keil介绍 (2)咱们使用keil v5,一般做单片机和嵌入式的都说是K5 (3)安装包在哪里 网络下载、开发板光盘 1.5.1.3、安装和破解(win7以上版本在破解前一定记得管理员身份运行) 链接:安装和破解 本地指导文件:开发板光盘资料课程配套ARM3.0开发板光盘资料Keil C51安装和破解包 1.5.1.4、Keil和MDK (1)本来只能用来开发51单片机,叫Keil (2)后来ARM公司收购了Keil软件,基于Keil扩展了ARM的开发,主要用来开发ARM Cortex-M系列单片机的程序(譬如STM32),软件名改成了MDK (3)刚才我们安装的是Keil C51,只能用来开发51单片机程序。如果要开发ARM Cortex-M单片机要扩展安装MDK安装包,大家装不装都可以。C51开发不需要Pack。 所以说以后说Keil就是开发51单片机的,MDK是开发ARM单片机的。 记住下面这个链接:KEIL 5 pack离线包 Stm32f1/f2/f3/f4 做ARM单片机的时候有用。 1.5.2.Keil的基本使用演示 1.5.2.1、使用Keil打开已有工程项目 (1)IDE(Keil)来开发软件,需要首先创建一个工程项目(Project),Keil软件需要一些工程项目文件来管理项目中的代码。 (2)别人事先创建好的工程项目,我可以使用Keil软件直接打开。打开方法有2个 第一个:进入项目中后双击(项目名.uvproj)文件来打开。没有拓展名的打开拓展名。 第二个:通过快捷图标直接打开Keil软件后,在软件中再打开工程项目(菜单栏的open project)。当我们直接双击快捷方式图标打开keil软件时,会默认打开上次关闭时正在打开的工程项目(如果上次关闭时并未打开工程则本次打开时也未打开任何工程)。 1.5.2.2、编译工程 (1)工程项目中有很多文件,简单的分为3类:工程文件、源文件、目标文件 工程文件就是Keil软件工作需要的文件,和我们写程序没关系 源文件就是我们写的源代码,就是我们编程编出来的 目标文件是Keil中的编译器等工具把我们源文件编译后生成的文件,最终向单片机中烧录时需要目标文件来烧录进去。 (2)我们工程刚创建好(空工程)时只有工程文件,此时我们要去编写添加源代码,代码写好后就有了工程文件和源文件,此时点编译操作就可以得到目标文件。 (3)编译的时候有可能会报错(Errors)和报警告(Warnnings),错误就是有很严重的问题,此时编译无效并不能生成最终需要的可烧录的程序文件,必须去排除错误重新编译才可以;警告是轻微问题,有时候可以忽略有时候不行,具体要凭经验。 (4)建议编译时直接点快捷图标栏里面的Rebuild进行编译。 1.5.2.3、Keil中建立新工程(可以看"普中51单片机开发攻略--A7.pdf")都要学会! (1)建立新工程前建议先关闭之前的工程 (2)菜单栏:Projcet->new uVision Project,选择一个合适的目录用来保存将来的工程项目,并且输入一个项目名 (3)选择CPU,如果Keil5,一般都选择Microchip->AT89C52/AT89C51,如果Keil4,选择Atmel->AT89C52/AT89C51最好使用Search搜索,会快一点,点击OK。弹出来选择“是否添加标准的8051的起始代码”,选择否(用处不大,后面简单说一下!),确定就ok了。这里感谢博主柏明的文章:关于KEIL5最新版没有ATMEL(含89C51芯片)的情况 (4)现在可以自己开始写代码,或者直接复制一个已经写好的代码文件进来,这里我们新建一个文件,编写代码,命名为"main.c" (5)但是编写的main.c并没有添加到工程文件,选择Source Group1右键选择"Add Existing Files to...",然后把main.c就添加到工程项目中了,以后别人给你的源文件,不是只放在文件夹下就行了,记得要添加到工程文件下! (6)点Build编译,发现一个问题:没有可以用来烧录的.hex文件。编译时生成的文件有很多,但是只有这个.hex文件才是我们最终需要的,可以用来烧录的文件。其他都属于中间文件(杂碎) 解决方案:是再多一步配置,打开快速菜单栏中“Target Options”,在弹出的多选框里,点击Output菜单,点选下面的Creat HEX File,然后关闭菜单,重新rebuild即可。 (7)最后就是烧录下载程序了! 1.5.3.C语言基本介绍 1.5.3.1、汇编语言与C语言 (1)汇编语言(机器语言,考虑寄存器)编程比C语言难 (2)用汇编或者用C语言都能完成任务,写出程序 (3)推荐学习路线;用C语言入门学会单片机,然后去扩展学习汇编 1.5.3.2、标准C语言和Keil C51的C语言 (1)C51 中定义的库函数和标准 C 语言定义的库函数不同。 (2)标准C语言就是独立于各种应用领域而独立成为标准的C语言,和各种平台的具体的C语言有微小差异。C51 中的数据类型与标准 C 的数据类型也有一定的区别,在 C51 中还增加了几种针对 51 单片机特有的数据类型;(比如***it) (3)C51 与标准 C 的输入输出处理不一样,C51 中的输入输出是通过 51 串行口来完成的,输入输出指令执行前必须要对串行口进行初始化; (4)C51 与标准 C 在函数使用方面也有一定的区别,C51 中有专门的中断函数。 1.5.3.3、学习方法和思路 (1)学过C语言基本概念的,很容易上手 (2)彻底没接触过编程语言的,刚开始甚至可以靠死记硬背来上手(记忆可以帮助理解,理解可以促进记忆) 刚开始简单的概念记住,就慢慢理解了!读书百遍,其意自见! (3)C语言关键字是英文字母缩写,可以辅助记忆(char--character,int--interger) (4)先学C语言基本概念,其余的用到再解释 1.5.4.C语言基本概念(自学+上课捋一遍) 看开发攻略第四章 C语言基础 1.5.4.1 数据类型 (1)C51 能够识别的数据类型如下图所示 (2)指针类型(*) 指针型本身就是一个变量,用来存放地址,在C51中一般占用1~3字节的内存单元。 (3)特殊功能寄存器型 这是 C51 扩充的数据类型,分 sfr 和 sfr16 两种类型.
(4)位类型 用于访问 51 单片机中的可寻址的位单元。 C51 支持两种位类型:bit 型和 ***it 型。它们在内存中都只占一个二进 制位,其值可以是“1”或“0”。 用 bit 定义的位变量在 C51 编译器编译时,在不同的时候位地址是可以变化的 用 ***it 定义的位变量必须与 51 单片机的一个可以寻址位单元或可位寻址的字节单元中的某一位联系在一起,在 C51 编译器编译时,其对应的位地址是不可变化的。 (5)数据类型隐式转换 隐式转换的优先级顺序如下:Bit→char→int→long→float→signed→unsigned 1.5.4.2 C51 运算量 1.常量 (1)整型常量 十进制整型:10,15,-2 十六进制整型:0x12---->12H 长整型:123L,占四个字节 (2)浮点型常量 十进制表示形式:0.123 指数表示形式:123.0e-3 (3)字符型常量 字符型常量是用单引号引起的字符,如‘A’,‘B’+转义字符't,n...' (4)字符串型常量 字符串型常量由双引号“”括起的字符组成,如“ABCD”。 2.变量 一个变量由两部分组成:变量名和变量值。在 C51 中,变量在使用前必须对变量进行定义,指出变量的数据类 型和存储模式。以便编译系统为它分配相应的存储单元。 定义的格式如下: [存储种类] 数据类型说明符 [存储器类型] 变量名 1[=初值],变量名2[初值]…; (1)存储种类 是指变量在程序执行过程中的作用范围。 C51的存储种类分为四种,分别是自动(auto)、外部(extern)、静态(static)和寄存器(register),如果省略,默认为auto。 auto:定义的变量作用范围仅在定义它的函数体或复合语句内部有效。 extern:定义的变量称为外部变量,其作用范围为整个程序。 static:定义的变量称为静态变量。其作用范围仅在定义的函数体内有效,一直存在,再次进入该函数时,变量的值为上次结束函数时的值。 register:定义的变量称为寄存器变量,处理速度快,但数目少。C51编译器编译时能自动识别程序中使用频率最高的变量,并自动将其作为寄存器变量,用户无需专门声明。 (2)数据类型说明符 在定义变量时必须通过数据类型说明符指明变量的数据类型 指明变量在存储器中占用的字节数。可以是基本数据类型说明符,也可以是组合数据类型说明符,还可以是用typedef和#define定义的类型别名。别名要按用户自定义标识符的原则命名。例如:使用“#define uchar unsigned char”定义了“uchar",则可以使用这个类型定义变量。 3)存储器类型 是用于指明变量所处的单片机的存储器区域情况。 省略则默认为data类型,即片内前128字节的RAM; bdata为可位寻址内部数据存储器,定义的变量可以用***it定义位变量访问其中的二进制位; idata可以访间51的内部256字节的RAM; code定义的变量存储在程序存储器,只能读出不能写入,相当于常量。 4)变量名 是C51区分不同变量,为不同变量取的名称,也就是用户自定义标识符,要遵循标识符的命名原则。 在 C51 中规定变量名可以由字母、数字和下划线三种字符组成,且第一个字母必须为字母或下划线。变量名有两种:普通变量名和指针变量名。它们的区别是指针变量名前面要带“*”号。 5) 特殊功能寄存器变量 51 系列单片机片内有许多特殊功能寄存器,通过这些特殊功能寄存器可以控制 51 系列单片机的定时器、计数器、串口、I/O 及其它功能部件,每一个特殊功能寄存器在片内 RAM 中都对应于一个字节单元或两个字节单元。在 C51 中,允许用户对这些特殊功能寄存器进行访问,访问时须通过 sfr 或sfr16 类型说明符进行定义,定义时须指明它们所对应的片内 RAM 单元的地址。 格式如下: sfr 或 sfr16 特殊功能寄存器名=地址; 例:
在 C51 中,允许用户通过位类型符定义位变量。位类型符有两个:bit 和 ***it。可以定义两种位变量。 bit 位类型符用于定义一般的可位处理位变量。它的格式如下: bit 位变量名; ***it 位类型符用于定义在可位寻址字节或特殊功能寄存器中的位,定义时须指明其位地址,可以是位直接地址,可以是可位寻址变量带位号,也可以是特殊功能寄存器名带位号。格式如下: ***it 位变量名=位地址; 如位地址为位直接地址,其取值范围为 0x00~0xff;如位地址是可位寻址变量带位号或特殊功能寄存器名带位号,则在它前面须对可位寻址变量或特殊功能寄存器进行定义。字节地址与位号之间、特殊功能寄存器与位号之间一般用“^”作间隔。如定义 51 单片机管脚: ***it LED=P1^0;//是将发光二极管 LED 接 P1口 0 位端,用以控制 LED 的亮灭。这里的P1可以认为是地址 参考资料:***it的使用说明 可以再看一下reg52.h看一下具体应用 1.5.4.3 C51运算符 1.赋值运算符 ‘=’ :变量=表达式; 2.算术运算符:+,-,*,/,% 注:相除的两个数为浮点数,则运算的结果也为浮点数,如相除的两个数为整数,则运算的结果也为整数,即 为整除。如 25.0/20.0 结果为 1.25,而 25/20 结果为 1。 3.关系运算符:>,<,>=,<=,==,!= 格式:表达式 1 关系运算符 表达式 2 成立为真(1),不成立为假(0). 4.逻辑运算符: C51 有 3 种逻辑运算符: || 逻辑或 && 逻辑与 ! 逻辑非 5.位运算符 位运算是按位对变量进行运算,但并不改变参与运算的变量的值。如果要求按位改变变量的值,则要利用相应的赋值运算。C51 中位运算符只能对整数进行操作,不能对浮点数进行操作。C51 中的位运算符有: & 按位与 | 按位或 ^ 按位异或 ~ 按位取反 << 左移 >> 右移 【例】设 a=0x45=01010100B,b=0x3b=00111011B,则 a&b、a|b、a^b、~a、a<<2、b>>2 分别为多少?6. 复合赋值运算符 C51 语言中支持在赋值运算符“=”的前面加上其它运算符,组成复合赋值运算符。下面是 C51 中支持的复合赋值运算符 += 加法赋值 ?+ 减法赋值 *= 乘法赋值 /= 除法赋值 %= 取模赋值 &= 逻辑与赋值 |= 逻辑或赋值 ^= 逻辑异或赋值 ~= 逻辑非赋值 >>= 右移位赋值 <<= 左移位赋值 先把变量与后面的表达式进行某种运算,然后将运算的结果赋给前面的变量。 7. 逗号运算符 在 C51 语言中,逗号“,”是一个特殊的运算符,可以用它将两个或两个以上的表达式连接起来,称为逗号表达式。逗号表达式的一般格式为: 表达式 1,表达式 2,……,表达式 n 按从左至右的顺序依次计算出各个表达式的值,而整个逗号表达式的值是最右边的表达式(表达式 n)的值。例如: x=(a=3,6*3)结果 x 的值为 18。 8.条件运算符 条件运算符“?:”是 C51 语言中唯一的一个三目运算符 逻辑表达式?表达式 1:表达式 2 其功能是先计算逻辑表达式的值,当逻辑表达式的值为真(非 0 值)时,将计算的表达式 1 的值作为整个条件表达式的值;当逻辑表达式的值为假(0 值)时,将计算的表达式 2 的值作为整个条件表达式的值。 9. 指针与地址运算符 * 指针运算符 & 取地址运算符 1.5.4.4 C51 基本结构和相关语句 1.顺序结构 2.选择结构(if-else,switch-case) 3.循环结构(while,for,do..while) continue,break. 1.5.4.5 函数 1.函数定义的一般格式如下: 函数类型 函数名(形式参数表) [reentrant][interrupt m][using n] 形式参数说明 { 局部变量定义 函数体 } 2.reentrant修饰符 3.interrupt m 修饰符 interrupt m 是 C51 函数中非常重要的一个修饰符,这是因为中断函数必须通过它进行修饰。在 C51 程序设计中,当函数定义时用了 interrupt m 修饰符,系统编译时把对应函数转化为中断函数,自动加上程序头段和尾段,并按 51 系统中断的处理方式自动把它安排在程序存储器中的相应位置。在该修饰符中,m 的取值为 0~31,对应的中断情况如下: 0——外部中断 0 1——定时/计数器 T0 2——外部中断 1 3——定时/计数器 T1 4——串行口中断 5——定时/计数器 T2 其它值预留。 1.5.4.6 数组 1.一维数组 2.字符数组 写不出来程序很正常, 第一步看懂代码,第二步是自己能模仿着写出来,第三步是对原始程序的更改创新,第四步才是自己写代码! 1.5.5.整体流程梳理(其实就跟点亮小灯的流程一样) 1.5.5.1、程序开发过程 (1)源代码编写(以后的工作重点) (2)编译生成可执行程序(点一下rebuild) (3)烧录(ISP烧录) 1.5.5.2、单片机工作流程(运行) (1)单片机上电 (2)时钟模块(晶振+晶振电路)起振,CPU有了时钟节拍,单片机内部各模块开始工作 (3)CPU从ROM中逐条读取可执行程序来执行,RAM存储变量(对应C语言程序中定义的变量)配合程序执行。直到断电关机。 注:单片机中的程序是执行不完的,所以while(1)是必须的,类似于轮询。不断给CPU喂指令,只要开机,每个时钟周期它就得执行指令,不然就跑飞了! 1.5.5.3、程序是调出来的 (1)程序一次写好达到要求很难,几乎不太可能! (2)先解决编译时错误(例:忘记分号“;”) (3)再解决运行时问题(这种相当于功能没实现,要分析,一定要注重逻辑) 一定要学会分析和解决的方法,要学会自己解决,而不是抱怨,我教大家的主要是思考的思路! 1.5.6.跟着数据手册学单片机1 1.5.6.1、先说这么几点 (1)数据手册干嘛的:其实就是单片机芯片使用说明书 是给软件/硬件工程师来看的细节! (2)数据手册内容结构 开始学习之前,要简单看一下,然后后面当字典用(后几节) (3)数据手册怎么用:先概括读、然后按需读模块 1.5.6.2、STC90C51简单介绍 (1)参考数据手册第6页 (2)12T和6T和1T的问题: 典型的51单片机都是12T的 12T的意思就是51单片机内部会对外部时钟频率进行12分频后再给CPU(如果外部晶振是12MHz,则外部时钟频率就是12MHz,内部CPU的主频就是12MHz/12=1MHz) 6T的含义就是内部CPU时钟频率=外部时钟频率/61T的含义就是内部CPU时钟频率=外部时钟频率/1。所以1T单片机是最快的。 (3)后边的看手册 3.工作频率范围:就是可以在6T和12T之间切换 ISP和IAP的区别、EEPROM、看门狗、定时器、UART 都是外部外设,这些看不懂怎么办?百度啊!提前了解下! 1.5.7.跟着数据手册学单片机2 1.5.7.1、STC90C51内部结构框图 P7 框图:示意图,从逻辑上告诉我们单片机内部都有什么东西是如何工作的。 (1)总线,单片机内部模块与模块之间的通信线(RAM、ROM、定时器... 单片机:城市 模块:各个建筑 总线:路) (2)模块,单片机内部各自独立具有一定功能的单元;重点会学:UART、定时器、EEPROM... (3)要求:大家能看懂图例,知道图中分别表示的是什么。当我们对CPU内部原件模块理解很少的时候其实从框图中看不出什么。框图中实际上传达了很多信息,将来回顾的时候能看出来就行了。往后学,慢慢就看懂了! 1.5.7.2、单片机管脚图 P8 (1)DIP(直插封装)封装和SMD(贴片封装)封装 (2)引脚编号和名字 每一个引脚都有个编号(1/2/3),这个编号主要是看文档时文档里用来描述这个引脚的作用时做标记用的,跟编程是无关的。所以一般引脚编号并不重要。 每一个引脚除了编号外还有个名字(p0.0,p0.2),这个引脚的名字比较重要。因为这引脚名字和我们将来在编程中控制这个引脚时的名字是一样的,所以引脚名字和编程有关。 1.5.7.3、关于仿真器和ISP (1)仿真器。早期开发单片机软件时的辅助设备,早期的单片机很多只能烧录一次,或者不提供调试功能,软件开发难度很大。后来单片机厂商就专门发明了一个设备叫仿真器,仿真器能够仿真出单片机的效果,可以用来调试程序。所以那时候买了单片机之后还要买仿真器,用仿真器来开发,好了后烧录到单片机中运行。 缺陷:1、一个仿真器对应1个单片机型号。2、仿真器很贵。 (2)因为仿真器很贵,所以有些公司开发出了软件仿真器,叫软仿真。譬如keil中就自带了软件仿真功能。 (3)后来仿真器被放弃,升级成了调试器。调试器的思路是:程序开发还是在单片机中,只不过我们用一个专用的调试器可以在单片机中直接完成仿真。典型的调试器如Jlink。调试器比仿真器来说更便宜,所以是更好的解决方案。现在的单片机大多都支持调试器方案。可以调试! (4)有时候开发程序并不使用调试器进行单步调试,而是直接开发了程序后通过串口将程序下载到单片机中(ISP方式下载)去运行,然后根据运行的现象来直接判断定位问题,然后解决问题,最后完成程序软件开发。这种方式下不需要专用的硬件仿真器和调试器,有时候会配合使用软件仿真功能。还可以配合使用串口打印,或者LED灯的指示等方式来进行调试。 总结:3和4是我们现在开发软件的主流方法。相对来说,没经验的人更喜欢3(有经验的喜欢4);越往后(单片机到嵌入式到物联网)3用的越少,4用的越多; 我们课程的方向是:51单片机阶段用软件仿真(Keil)结合ISP下载方式来调试程序,STM32学习阶段是调试器和ISP下载两种方式相结合,到了嵌入式阶段就又回到4的方式,以后往后都不会再用Jlink等调试器了。(调试器就类似辅助工具,慢慢的摆脱它,水平才能提高)。 1.5.8.跟着数据手册学单片机3 1.5.8.1、选型表一览表 P9 电压差异、51/52/53差在flash上(决定你最大的程序大小:code)、SRAM、I/O; 根据选型表可以自主按需选择单片机。 1.5.8.2、单片机最小系统介绍(开发攻略-第五章) + 数据手册P10 (1)单片机最小系统,就是指的单片机加上最少的外围电路,然后还可以工作。 (2)最小系统中有3部分电路:供电电路、上电复位电路、晶振电路。如果我们要给系统更新程序即烧入程序,就得加上程序下载电路。如下图: 1.5.8.3、管脚定义 P12 重点看管脚和说明!大概扫一下! (1)电源等管脚 单片机上面有一些管脚是用来支持单片机工作的,譬如VCC和GND、RST等,这些引脚和编程无关,程序也无法操控这些引脚。这些引脚软件工程师不用管,硬件工程师很在意(因为外面都要接电路)。 (2)IO端口。 IO端口是单片机和外部电路进行交互的窗口,外部电路通过IO向单片机内部输入(input)信息,单片机通过IO端口向外部输出(output)信息。单片机中大部分的引脚都是IO(P开头的引脚都是IO),将来单片机内部的程序运行就会通过操作这些IO来和外部电路交互,从而实现程序目的。(比如点亮LED灯,程序控制IO,IO输出给LED灯) (3)管脚复用。 普通引脚是一个引脚只有一个名字,一种作用; 有些引脚有2个名字(譬如P1.0/T2,P3.0/RxD),这种引脚就有2种作用(意思不是说这个引脚可以同时做2件事情,而是说这个引脚在不同的时候可以工作在不同的2种模式下),可以通过软件编程让这个引脚在某个时间工作在某种模式下。一个引脚的2种模式之间没有关联,配置工作在A模式下则和B模式一点关系都没有。 为什么要管脚复用?纯粹是为了省引脚。降低成本!后面会讲如何切换模式,有什么用。 1.5.9.跟着数据手册学单片机4 1.5.9.1、封装尺寸 P14 看手册。 1.5.9.2、命名规则 出错很正常! 1.5.10.二进制和IO端口 1.5.10.1、二进制和bit位 计算机中最小的存储单位是二进制位(binary digit),也叫比特, bit 只能够存储 0 或 1 ; 1.5.10.2、位(bit,b)和字节(byte,B)经常用到 (1)1字节=8位 (2)一个字符=2字节 比如家里的光纤,100Mb/s,最高下载速度差不多12.5MB/s 参考链接:字节、位、bit、byte、KB、B、字符之间有什么关系? 自己去了解! 1.5.10.3、初识IO口 (1)什么是IO(input/output),单片机的IO物理上表现为单片机的引脚,是电子设备,可以input/output电平。 (2)IO对单片机的重要意义。 (a)你按下按键,单片机能够感受到你的按键(按下和抬起有电平变化)----input (b)向外output,电灯是电路导通,靠的就是output。 (3)单片机有多少IO 是可以定制的,当然多了功能更强大! (4)IO的组织形式。首先一个单片机的众多IO分为多个端口(port),一个端口由若干个IO引脚(很多时候都是8个)组成。可以理解为一个端口就是一组IO引脚。P1叫端口,P1.0,P1.2就是IO引脚。一个端口就是一组IO引脚 (5)如何编程控制IO 这是后面重点! P0 = 0x00;//把P0端口的8个引脚全部置成0;灯就亮了,为什么?看电路 比如: 当I/O口输出高电平时,LED两端的电位相同,因此电压为0V,不能构成电流回路,所以LED不亮。当I/O口输出低电平时,LED左侧电位为0,而右侧则在R1的上拉作用下电位提高,因此LED两端有正向电压,可以点亮发光。 MCS-51单片机的I/O口具有比较强的灌电流能力,但拉电流能力却很弱,所以并不适合用输出高电平的方法点亮LED,大多采用这种负逻辑的驱动方法。 |
|
|
|
只有小组成员才能发言,加入小组>>
2555 浏览 0 评论
1154浏览 2评论
753浏览 1评论
506浏览 0评论
270浏览 0评论
439浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-25 04:05 , Processed in 1.827651 second(s), Total 79, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号