STM32F4的USB Slave驱动移植详细步骤及问题解决

news2025/1/8 4:46:51

文章目录

  • 1、USB库下载
  • 2、移植(slave)
    • 2.1、工程准备
    • 2.2、USB库准备
  • 2.3、移植
      • 1、新建USB相关文件夹
      • 2、拷贝
      • 3、添加USB相关代码
      • 4、添加头文件路径
      • 5、修改相关内容
        • 1、修改usb_conf.h
        • 2、定义全局宏USE_USB_OTG_FS
        • 3、修改usbd_storage_msd.c
        • 4、修改usbd_usr.c
        • 5、修改usb_bsp.c
      • 4、修改主函数

1、USB库下载

访问官网:https://www.st.com,搜索UM1021
在这里插入图片描述

选择STSW-STM32046
在这里插入图片描述
下拉到下面,获取下载即可
在这里插入图片描述
下载下来解压后的文件如下图所示:
在这里插入图片描述

2、移植(slave)

2.1、工程准备

准备基础工程一份,这里以W25Qxx工程为例,直接拷贝原子的工程过来

2.2、USB库准备

打开库文件,定位到当前目录
在这里插入图片描述
这三个从上到下分别为USB从机驱动库,USB主机驱动库,USB_OTG内核驱动代码

2.3、移植

1、新建USB相关文件夹

打开工程,新建USB文件夹并拷贝USB驱动库,在拷贝的目录下并新建一个USB_APP文件夹
在这里插入图片描述

2、拷贝

MSC相关代码到USB_APP文件夹内相关代码,即:en.stm32_f105-07_f2_f4_usb-host-device_libProjectUSB_Device_ExamplesMSCsrc 下的部分代码:usb_bsp.c 、usbd_storage_msd.c、usbd_desc.c和 usbd_usr.c等4 个.c 文件,
同时拷贝 en.stm32_f105-07_f2_f4_usb-host-device_libProjectUSB_Device_ExamplesMSCinc 下面的:usb_conf.h、usbd_conf.h 和 usbd_desc.h 等三个文件,添加完成后如下图所示:
在这里插入图片描述

3、添加USB相关代码

工程添加USB相关代码,如下图所示:
根据ST官方的MSC例程,新建USB_OTG组,存放USB_OTG内核驱动代码,新建USB_DEVICE组,存放USB从机与MSC相关代码,新建USB_APP存放用户配置相关驱动代码
在这里插入图片描述

USB_OTG组代码路径为USB\STM32_USB_OTG_Driver\src
USB_DEVICE组代码路径为USB\STM32_USB_Device_Library\Core\src以及USB\STM32_USB_Device_Library\Class\msc\src
USB_APP组代码路径为USB\USB_APP内

我们重点要修改的就是 USB_APP 文件夹下面的代码。
其他代码(USB_OTG 和 USB_DEVICE 文件夹下的代码)一般不用修改。
usb_bsp.c 提供了几个 USB 库需要用到的底层初始化函数,包括:IO 设置、中断设置、VBU配置以及延时函数等,需要我们自己实现。USB Device(Slave)和 USB Host 共用这个.c 文件。
usbd_desc.c 提供了 USB 设备类的描述符,直接决定了 USB 设备的类型、断点、接口、字符串、制造商等重要信息。这里:usbd_desc.c 里面的:usbd 即 device 类,同样:usbh 即 host 类
usbd_usr.c 提供用户应用层接口函数,即 USB 设备类的一些回调函数,当 USB 状态机处理完不同事务的时候,会调用这些回调函数,我们通过这些回调函数,就可以知道 USB 当前状态, 比如:是否枚举成功了?是否连接上了?是否断开了?等
usbd_storage_msd.c 提供一些磁盘操作函数,包括支持的磁盘个数,以及每个磁盘的初始化
和读写等函数。

4、添加头文件路径

在这里插入图片描述

5、修改相关内容

编译代码,根据报错提示,修改相关内容

1、修改usb_conf.h

①,usb_conf.h,include部分,使用如下代码替代:

#include "stm32f4xx.h"

在这里插入图片描述
另外,屏蔽#define VBUS_SENSING_ENABLED,关闭VBUS检测
在这里插入图片描述

2、定义全局宏USE_USB_OTG_FS

②,在C/C++选项卡,定义全局宏:USE_USB_OTG_FS,选择使用USB OTG FS,如图所示:
在这里插入图片描述

3、修改usbd_storage_msd.c

3.1,添加头文件

#include "w25qxx.h"

3.2,修改:#define STORAGE_LUN_NBR的值为2,以支持2个磁盘,这里仅使用W25Q一个,所以默认为1即可。
3.3,添加USB_STATUS_REG变量,记录磁盘操作状态。

自己定义的一个标记USB状态的寄存器///
//bit0:表示电脑正在向其写入数据
//bit1:表示电脑正从内部读出数据
//bit2:写数据错误标志位
//bit3:读数据错误标志位
//bit4:1,表示电脑有轮询操作(表明连接还保持着)
vu8 USB_STATUS_REG=0;

3.4,修改STORAGE_Inquirydata数组,和上面3.2的磁盘个数相对应,有几个就新增几个磁盘的描述,比如下面是两个的磁盘描述符,本次只有一个,所以就不用修改:

//USB Mass storage 标准查询数据(每个lun占36字节)
const int8_t  STORAGE_Inquirydata[] = {   
	/* LUN 0 */ 
	0x00,		
	0x80,		
	0x02,		
	0x02,
	(USBD_STD_INQUIRY_LENGTH - 4),
	0x00,0x00,0x00,	  
	'A', 'L', 'I', 'E', 'N', 'T', 'E', 'K', ' ',//Vendor Identification,9字节 
	'S', 'P', 'I', ' ', 'F', 'l', 'a', 's', 'h',' ','D', 'i', 's', 'k', ' ',//Product Identification,15字节 
	'1', '.', '0', ' ',							//Product Revision Level,4字节		
	/* LUN 1 */
	0x00,
	0x80,		
	0x02,		
	0x02,
	(USBD_STD_INQUIRY_LENGTH - 4),
	0x00,0x00,0x00,	  
	'A', 'L', 'I', 'E', 'N', 'T', 'E', 'K',' ',	//Vendor Identification,9字节 
	'S', 'D', ' ', 'F', 'l', 'a', 's', 'h', ' ','D', 'i', 's', 'k', ' ',' ',//Product Identification,15字节 
	'1', '.', '0' ,' ',                      	//Product Revision Level,4字节
};

3.5,修改STORAGE_Init函数为:

int8_t STORAGE_Init (uint8_t lun)
{
	W25QXX_Init();
	return 0; 
} 

在这里插入图片描述
3.6,修改STORAGE_GetCapacity函数为:

int8_t STORAGE_GetCapacity (uint8_t lun, uint32_t *block_num, uint32_t *block_size)
{     
		*block_size=512;  
		*block_num=1024*1024*12/512;//SPI FLASH的前面12M字节,文件系统用
	return 0; 
} 

在这里插入图片描述

3.7,修改STORAGE_IsReady函数为:

int8_t  STORAGE_IsReady (uint8_t lun)
{ 
	USB_STATUS_REG|=0X10;//标记轮询
	return 0;
} 

3.8,修改STORAGE_Read函数为:

int8_t STORAGE_Read (uint8_t lun,uint8_t *buf,uint32_t blk_addr,uint16_t blk_len)
{
	int8_t res=0;
	USB_STATUS_REG|=0X02;//标记正在读数据
	W25QXX_Read(buf,blk_addr*512,blk_len*512);
	return res;
}

3.9,修改STORAGE_Write函数为:

int8_t STORAGE_Write (uint8_t lun,uint8_t *buf,uint32_t blk_addr,uint16_t blk_len) 
{
	int8_t res=0;
	USB_STATUS_REG|=0X01;//标记正在写数据
		W25QXX_Write(buf,blk_addr*512,blk_len*512);
	return res; 
}

4、修改usbd_usr.c

其实就是将LCD显示信息修改为printf输出
4.1,删除lcd_log.h,并添加usb_dcd_int.h等,如下所示:

#include "usbd_usr.h" 
#include "usb_dcd_int.h"
#include <stdio.h> 
#include <usart.h> 

4.2,添加bDeviceState变量,记录USB连接状态,并添加USB中断处理函数(官方例程在stm32fxxx_it.c里面处理)到本.c文件,如下所示

//表示USB连接状态
//0,没有连接;
//1,已经连接;
vu8 bDeviceState=0;		//默认没有连接  
extern USB_OTG_CORE_HANDLE  USB_OTG_dev;	//在main函数定义!!
//USB OTG 中断服务函数
//处理所有USB中断
void OTG_FS_IRQHandler(void)
{
  	USBD_OTG_ISR_Handler(&USB_OTG_dev);
} 

4.3,修改USBD_USR_Init函数为:

void USBD_USR_Init(void)
{
	printf("USBD_USR_Init\r\n");
} 

在这里插入图片描述
官方的又很多的其他初始化,以及LCD的信息显示,直接替换为我们使用的代码即可。
4.4,修改USBD_USR_DeviceReset函数为:

void USBD_USR_DeviceReset (uint8_t speed)
{
	switch (speed)
	{
		case USB_OTG_SPEED_HIGH:
			printf("USB Device Library v1.1.0  [HS]\r\n");
			break; 
		case USB_OTG_SPEED_FULL: 
			printf("USB Device Library v1.1.0  [FS]\r\n");
			break;
		default:
			printf("USB Device Library v1.1.0  [??]\r\n"); 
			break;
	}
}

4.4,修改USBD_USR_DeviceConfigured函数为:

void USBD_USR_DeviceConfigured (void)
{
	printf("MSC Interface started.\r\n"); 
} 

4.5,修改USBD_USR_DeviceSuspended函数为:

void USBD_USR_DeviceSuspended(void)
{
	printf("Device In suspend mode.\r\n");
} 

4.6,修改USBD_USR_DeviceResumed函数为:

void USBD_USR_DeviceResumed(void)
{ 
	printf("Device Resumed\r\n");
}

4.7,修改USBD_USR_DeviceConnected函数为:

void USBD_USR_DeviceConnected (void)
{
	bDeviceState=1;
	printf("USB Device Connected.\r\n");
}

4.8,修改USBD_USR_DeviceDisconnected函数为:

void USBD_USR_DeviceConnected (void)
{
	bDeviceState=1;
	printf("USB Device Connected.\r\n");
}

5、修改usb_bsp.c

5.1,新增sys.h和delay.h,,如下所示:

#include "usb_bsp.h"
#include "usbd_conf.h" 
#include "sys.h"  
#include "delay.h" 

5.2,添加USB_HOST_PWRCTRL宏,用于控制USB HOST接口的对外供电(本次是slave,所以可以忽略此步骤),如下所示:

//USB主机电源控制口
#define USB_HOST_PWRCTRL 	PAout(15)	//PA15

5.3,修改USB_OTG_BSP_Init函数为:

void USB_OTG_BSP_Init(USB_OTG_CORE_HANDLE *pdev)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
	RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS, ENABLE);//使能USB OTG时钟
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;//PA11/12复用功能输出

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PA15推挽输出		
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出功能
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
	USB_HOST_PWRCTRL=1;		//开启USB HOST电源供电
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_OTG_FS);//PA11,AF10(USB)
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_OTG_FS);//PA12,AF10(USB)
}

5.4,修改USB_OTG_BSP_EnableInterrupt函数为:

void USB_OTG_BSP_EnableInterrupt(USB_OTG_CORE_HANDLE *pdev)
{
	NVIC_InitTypeDef   NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_IRQn; 
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//抢占优先级0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能通道
	NVIC_Init(&NVIC_InitStructure);//配置  
}

5.5,修改USB_OTG_BSP_uDelay函数为:

void USB_OTG_BSP_uDelay (const uint32_t usec)
{ 
   	delay_us(usec);
}

5.6,修改USB_OTG_BSP_mDelay函数为:

void USB_OTG_BSP_mDelay (const uint32_t msec)
{  
	delay_ms(msec);
}

4、修改主函数

添加头文件

#include "usbd_msc_core.h"
#include "usbd_usr.h"
#include "usbd_desc.h"
#include "usb_conf.h" 

USB相关定义声明

USB_OTG_CORE_HANDLE USB_OTG_dev;
extern vu8 USB_STATUS_REG;		//USB状态
extern vu8 bDeviceState;		//USB连接 情况

主函数

int main(void)
{        
	u8 offline_cnt=0;
	u8 tct=0;
	u8 USB_STA;
	u8 Divece_STA;


	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
	delay_init(168);  //初始化延时函数
	uart_init(115200);		//初始化串口波特率为115200
	LED_Init();					//初始化LED  
 	KEY_Init();					//按键初始化  
	W25QXX_Init();				//初始化W25Q128  
  
	if(W25QXX_ReadID()!=W25Q128)printf("W25Q128 Error!\r\n");	//检测W25Q128错误
	else //SPI FLASH 正常
	{   														 
		printf("SPI FLASH Size:12MB\r\n");	 
	}  
 	printf("USB Connecting...\r\n");//提示正在建立连接 	    
	USBD_Init(&USB_OTG_dev,USB_OTG_FS_CORE_ID,&USR_desc,&USBD_MSC_cb,&USR_cb);
	delay_ms(1800);	
	while(1)
	{	
		delay_ms(1);				  
		if(USB_STA!=USB_STATUS_REG)//状态改变了 
		{	 						   	  	   
			if(USB_STATUS_REG&0x01)//正在写		  
			{
				LED1=0;
				printf("USB Writing...\r\n");//提示USB正在写入数据	 
			}
			if(USB_STATUS_REG&0x02)//正在读
			{
				LED1=0;
				printf("USB Reading...\r\n");//提示USB正在读出数据  		 
			}	 										  
			if(USB_STATUS_REG&0x04)printf("USB Write Err \r\n");//提示写入错误
	  
			if(USB_STATUS_REG&0x08)printf("USB Read  Err \r\n");//提示读出错误
			
			USB_STA=USB_STATUS_REG;//记录最后的状态
		}
		if(Divece_STA!=bDeviceState) 
		{
			if(bDeviceState==1)printf("USB Connected    \r\n");//提示USB连接已经建立
			else printf("USB DisConnected \r\n");//提示USB被拔出了
			Divece_STA=bDeviceState;
		}
		tct++;
		if(tct==200)
		{
			tct=0;
			LED1=1;
			LED0=!LED0;//提示系统在运行
			if(USB_STATUS_REG&0x10)
			{
				offline_cnt=0;//USB连接了,则清除offline计数器
				bDeviceState=1;
			}else//没有得到轮询 
			{
				offline_cnt++;  
				if(offline_cnt>10)bDeviceState=0;//2s内没收到在线标记,代表USB被拔出了
			}
			USB_STATUS_REG=0;
		}
	};  
}

编译有三个错,一个是头文件没有定义,屏蔽掉即可,一个是新版本对局部变量定义的位置有先后顺序要求,修改下位置即可,然后就可以下载进去测试,亲测成功
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【蒸汽冷凝器型号和PI控制】具有PID控制的蒸汽冷凝器的动力学模型(MatlabSimulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

离线数仓同步数据3

业务数据_增量表数据同步 1&#xff09;Flume配置概述2&#xff09;Flume配置实操3&#xff09;通道测试4&#xff09;编写Flume启停脚本 1&#xff09;Flume配置概述 Flume需要将Kafka中topic_db主题的数据传输到HDFS&#xff0c;故其需选用KafkaSource以及HDFSSink&#xff…

mysql索引条件下推 、 count(*)、count(1)、IN 、exists等

索引下推 Index Condition Pushdown(ICP)&#xff0c;是一种在存储引擎层使用索引过滤数据的优化方式 如果没有ICP&#xff0c;存储引擎会遍历以定位基表中的行&#xff0c;并将他们返回给mysql服务器&#xff0c;有mysql 服务器评估where 后面的条件是否保留行。启用ICP&…

Debian11安装PostgreSQL+PostGIS+pgRouting ,链接Navicat

船新版本&#xff0c;遵循官网教程 1 准备一个Debian11系统2 从官网安装Postgres2.1 安装Postgres2.2 修改Postgres密码2.3 配置Postgres远程访问 3 安装Postgis、pgRouting4 链接Navicat 1 准备一个Debian11系统 2 从官网安装Postgres 2.1 安装Postgres 1 进入Postgre的官网…

GE IC693CPU374CPU模块

处理能力&#xff1a;IC693CPU374 CPU 模块通常具有高性能的处理器&#xff0c;用于执行复杂的控制逻辑和数据处理任务。 内存容量&#xff1a;它通常具有内置的RAM和Flash存储器&#xff0c;用于存储控制程序、数据和配置信息。 多通信接口&#xff1a;该模块通常具有多个通…

集合的笔记

集合 包装类 泛型类有一个不幸的限制, 不能使用基本类型作为类型参数, 解决办法是使用这个包装类, 每一种基本类型都有对应的包装类 基本类型和它们对应的包装类型之间的转换是自动进行的。在调用 add 方法时&#xff0c;个值为 42 的 Integer 类对象在一种被称为自动装箱的过…

redis(0)-安装实操

1.基本概念 key-value型数据库&#xff0c;秒10万级查询。 2.计算向数据移动 3.安装步骤 3.1总体流程 //源码目录&#xff1a;/home/ftp/redis5 src //安装目录&#xff1a;make install /opt/tang/redis5/bin 只是一些bin文件 //make install 只是把bin 复制到某个路…

数据结构与算法学习(day1)——简化版桶排序

文章目录 前言本章目标简化版桶排序题目一题目二 前言 &#xff08;1&#xff09;我是一个大三的学生&#xff08;准确来说应该是准大三&#xff0c;因为明天才报名哈哈哈&#xff09;。 &#xff08;2&#xff09;最近就想每天闲着没事也刷些C语言习题来锻炼下编程水平&#x…

如何选择靠谱的全景平台?VR全景加盟从哪方面对比?

VR全景行业经过近几年的发展&#xff0c;已经逐渐普及开来&#xff0c;线下各个行业都有实体商家开始引入VR全景去做营销宣传推广了。不少老板也意识到线上线下双渠道的重要性&#xff0c;而VR全景的存在就刚好满足各行各业的需求&#xff0c;从这一点不难看出&#xff0c;VR全…

协议定制 + Json序列化反序列化

文章目录 协议定制 Json序列化反序列化1. 再谈 "协议"1.1 结构化数据1.2 序列化和反序列化 2. 网络版计算器2.1 服务端2.2 协议定制(1) 网络发送和读取的正确理解(2) 协议定制的问题 2.3 客户端2.4 代码 3. Json实现序列化反序列化3.1 简单介绍3.2 使用 协议定制 J…

《低代码指南》——AI低代码维格云技术与部署架构说明

#整体技术架构说明 维格云概念上由两个部分组成:工作台(workbench)和数据表(datasheet)。 工作台(workbench)维护系统集群节点、组织和用户数据,提供文件夹、表格、仪表盘、表单、镜像、审计、权限服务等功能。 数据表(datasheet)为多个协作成员提供实时协作,以便…

变电站自动化监控系统

力安科技变电站自动化监控系统是以箱式变电站为管理对象&#xff0c;加装箱变网关&#xff0c;在完成箱变智能化改造的基础上&#xff0c;依托电易云&#xff0c;构建一体化智慧箱变及运维系统。智能箱式变电站被广泛应用于住宅小区、城市公用变压器、工厂、商场、机场、电站等…

Java语法中一些需要注意的点(仅用于个人学习)

1.当字符串和其他类型相加时&#xff0c;基本都是字符串&#xff0c;这与运算顺序有关。 2.Java中用ctrl d 来结束循环输入。 3.nextLine() 遇到空格不会结束。 4.方法重载 4.1. 方法名必须相同 4.2. 参数列表必须不同(参数的个数不同、参数的类型不同、类型的次序必须不…

【大数据之Kafka】八、Kafka Broker之生产经验

1 节点服役和退役 1.1 服役新节点 新节点准备&#xff1a; &#xff08;1&#xff09;关闭hadoop104&#xff0c;并右键执行克隆操作。 &#xff08;2&#xff09;开启hadoop105&#xff0c;并修改 IP 地址为105。 vim /etc/sysconfig/network-scripts/ifcfg/ens33&#xff…

Edge官方鼠标手势

前言 日期&#xff1a;2023年8月 Edge浏览器目前已自带官方的鼠标手势功能&#xff0c;若要使用首先将浏览器更新至最新版&#xff0c;下文介绍使用方法。 官方鼠标手势 前提 更新Edge至最新版&#xff0c;并关闭其它鼠标手势扩展。 开启鼠标手势 打开Edge浏览器的设置&…

Linux c++开发-04-让Hello World更像一个工程

外层CMakeLists.txt src中的CMakeLists.txt ADD_EXECUTABLE(hello main.cpp)main.cpp 然后 cd build cmake … make ./bin/hello

TCPIP协议学习

TCP协议 连接导向&#xff1a;TCP是一种面向连接的协议&#xff0c;这意味着通信的两端在建立通信之前必须通过握手过程建立连接。握手过程包括三次握手&#xff0c;其中客户端向服务器发送连接请求&#xff0c;服务器回复确认&#xff0c;最后客户端再次确认连接。这样建立的连…

基于springboot+vue的校园失物招领系统-前后端分离(内含文档+源码+教程)

近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日常生活越来越离不开计算机和互联网技术。首先&#xff0c;根据收集到的用户需求分析&#xff0c;对设计系统有一个初步的认识与了解&#xff0c;确定校园失物招领网站的总体功能模块。然后&#xff0c;详细…

InstructPix2Pix(CVPR2023)-图像编辑论文解读

文章目录 1.摘要2.背景3.算法3.1 生成多模态训练集3.1.1生成指令及成对caption3.1.2 依据成对的caption生成成对的图像 3.2 InstructPix2Pix 4.实验结果4.1基线比较4.2消融实验 5.结论 论文&#xff1a; 《InstructPix2Pix: Learning to Follow Image Editing Instructions》 …

什么是系统集成项目管理工程师,证书难考吗?

系统集成项目管理工程师&#xff0c;属于软考三个级别中的 “中级”&#xff0c;相当于中级职称。 系统集成项目管理程师是工信部和人社部举办的软考中新增开的一门考试。软考全称全国计算机技术与 软件专业技术资格&#xff08;水平&#xff09;考试&#xff0c;这门新开的系…