I/O设备模型

I/O设备模型

绝大部分的嵌入式系统都包括一些I/O(Input/Outut,输入/输出)设备,例如仪器上的数据显示屏、工业设备上的串口通信、数据采集设备上用于保存数据的Flash或SD卡,以及网络设备的以太网接口等。

I/O设备模型框架

RT-Thread提供了一套简单的I/O设备模型框架,如图所示,它位于硬件和应用程序之间,共分成三层,从下到下分别是I/O设备管理层、设备驱动框架层、设备驱动层。

在这里插入图片描述
应用程序通过I/O设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层I/O硬件设备进行数据交互。

I/O设备管理层实现了对设备驱动程序的封装。应用程序通过图中的I/O设备管理层提供的标准接口访问底层设备,设备驱动程序的升级、更替不会对上层应用产生影响。这种方式使得设备的硬件操作相关的代码能够独立于应用程序而存在,双方只需关注各自的功能实现,从而降低了代码的耦合型、复杂性,提高了系统的可靠性。

设备驱动框架层是对同类硬件设备驱动的抽象,将不同厂家的同类硬件设备驱动中相同的部分抽取出来,将不同部分留出接口,由驱动程序实现。

设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。
它负责创建和注册I/O设备,对于操作逻辑简单的设备,可以不经过设备驱动框架层,直接将设备注册到I/O设备管理器中。

在这里插入图片描述

  • 设备驱动根据设备模型定义,创建出具备硬件访问能力的设备实例,将该设备通过rt_device_register()接口注册到I/O设备管理器中。
  • 应用程序通过rt_device_find()接口查找到设备,然后使用I/O设备管理接口来访问硬件。

对于另一些设备,如看门狗等,则会将创建的设备实例先注册到对应的设备驱动框架中,再由设备驱动框架向I/O设备管理器进行注册,主要有以下几点:

  • 看门狗设备驱动程序根据看门狗设备模型定义,创建出具备硬件访问能力的看门狗设备实例,并将该看门狗设备通过rt_hw_watchdog_register()接口注册到看门狗设备驱动框架中。
  • 看门狗设备驱动框架通过rt_device_register()接口将看门狗注册到I/O设备管理器中。
  • 应用程序通过I/O设备管理接口来访问看门狗设备硬件。

在这里插入图片描述

I/O设备模型

RT-Thread的设备模型是建立在内核对象模型基础之上的,设备被认为是一类对象,被纳入对象管理器的范畴。每个设备对象都是由基对象派生而来,每个具体设备都可以继承其父类对象的属性,并派生出其私有属性。

在这里插入图片描述

struct rt_object
{char name[RT_NAME_MAX];rt_uint8_t type;rt_uint8_t flag;rt_list_t list;
};
typedef struct rt_object *rt_object_t;
struct rt_device
{struct rt_obejct parent;enum rt_device_class_type type;rt_uint16_t flag;rt_uint16_t open_flag;rt_uint8_t ref_count;rt_uint8_t device_id;/* device call back */rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);/* common device interface */rt_err_t  (*init)   (rt_device_t dev);rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);rt_err_t  (*close)  (rt_device_t dev);rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);void *user_data;
}

I/O设备类型

enum rt_device_class_type
{RT_Device_Class_Char = 0,                           /**< character device */RT_Device_Class_Block,                              /**< block device */RT_Device_Class_NetIf,                              /**< net interface */RT_Device_Class_MTD,                                /**< memory device */RT_Device_Class_CAN,                                /**< CAN device */RT_Device_Class_RTC,                                /**< RTC device */RT_Device_Class_Sound,                              /**< Sound device */RT_Device_Class_Graphic,                            /**< Graphic device */RT_Device_Class_I2CBUS,                             /**< I2C bus device */RT_Device_Class_USBDevice,                          /**< USB slave device */RT_Device_Class_USBHost,                            /**< USB host bus */RT_Device_Class_USBOTG,                             /**< USB OTG bus */RT_Device_Class_SPIBUS,                             /**< SPI bus device */RT_Device_Class_SPIDevice,                          /**< SPI device */RT_Device_Class_SDIO,                               /**< SDIO bus device */RT_Device_Class_PM,                                 /**< PM pseudo device */RT_Device_Class_Pipe,                               /**< Pipe device */RT_Device_Class_Portal,                             /**< Portal device */RT_Device_Class_Timer,                              /**< Timer device */RT_Device_Class_Miscellaneous,                      /**< Miscellaneous device */RT_Device_Class_Sensor,                             /**< Sensor device */RT_Device_Class_Touch,                              /**< Touch device */RT_Device_Class_PHY,                                /**< PHY device */RT_Device_Class_Security,                           /**< Security device */RT_Device_Class_Unknown                             /**< unknown device */
};

其中字符设备、块设备是常用的设备类型,它们的分类依据是设备数据与系统之间的传输处理方式。
字符模式设备允许非结构的数据传输,即通常数据传输采用串行的形式,每次一个字节。字符设备通常是一些简单设备,如串口、按键。

块设备每次传输一个数据块,例如每次传输512个字节数据。这个数据块是硬件强制性的,数据块可能使用某类数据接口或某些强制性的传输协议,否则就可能发生错误。
因此,有时块设备驱动程序对读或写操作必须执行附加的工作。
在这里插入图片描述
当系统服务于一个具有大量数据的写操作时,设备驱动程序必须首先将数据分为多个包,每个包采用设备指定的数据尺寸。
而在实际过程中,最后一部分数据尺寸有可能小于正常的设备块尺寸。
如上图中每个块使用单独的写请求写入到设备中,头3个直接进行写操作。
但最后一个数据块尺寸小于设备块尺寸,设备驱动程序必须使用不同于前3个块的方式处理最后的数据块。
通常情况下,设备驱动程序需要首先执行相对应的设备块的读操作,然后把写入数据覆盖到读出数据上,然后再把这个“合成”的数据块作为一整个块写回到设备中。

例如块4,驱动程序需要先把块4所对应的设备块读出来,然后将需要写入的数据覆盖至从设备块读出的数据上,使其合并成一个新的块,最后再写回到块设备中。

创建和注册I/O设备

驱动层负责创建设备实例,并注册到I/O设备管理器中,可以通过静态声明的方式创建设备实例,也可以用下面的接口进行动态创建:

rt_device_t rt_device_create(int type, int attach_size)
{int size;rt_device_t device;size = RT_ALIGN(sizeof(struct rt_device), RT_ALIGN_SIZE);attach_size = RT_ALIGN(attach_size, RT_ALIGN_SIZE);size += attach_size;device = (rt_device_t)rt_malloc(size);if(device){rt_memset(device, 0x0, sizeof(struct rt_device));device->type = (enum rt_device_class_type)type;}return device;
}

调用该接口时,系统会从动态堆内存中分配一个设备控制块,大小为struct rt_device和attach_size的和,设备的类型由参数type设定。
设备被创建后,需要实现它访问硬件的操作方法。

struct rt_device_ops
{/* common device interface */rt_err_t  (*init)   (rt_device_t dev);rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);rt_err_t  (*close)  (rt_device_t dev);rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
};
  • init:初始化设备。设备初始化完成后,设备控制块的flag会被置成已激活状态(RT_DEVICE_FLAG_ACTIVATED)。如果设备控制块中的flag标志已经设置成激活状态,那么再运行初始化接口就会立刻返回,而不会重新进行初始化。
rt_err_t rt_device_init(rt_device_t dev)
{rt_err_t result = RT_EOK;if(dev->init != RT_NULL){result = dev->init(dev);if (result != RT_EOK){RT_DEBUG_LOG(RT_DEBUG_DEVICE, ("To initialize device:%s failed. The error code is %d\n",dev->parent.name, result));}else{dev->flag |= RT_DEVICE_FLAG_ACTIVATED;}}
}
  • open。打开设备。有些设备并不是系统一启动就已经打开开始运行,或者设备需要进行数据收发,但如果上层应用还未准备好,设备也不应该默认已经使能并开始接收数据。所以建议在写底层驱动程序时,在调用open接口时才使能设备。
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
{rt_err_t result = RT_EOK;RT_ASSERT(dev != RT_NULL);RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);if(!(dev->flag & RT_DEVICE_FLAG_ACTIVATED)){if(device_init != RT_NULL){result = device_init(dev);if (result != RT_EOK){RT_DEBUG_LOG(RT_DEBUG_DEVICE, ("To initialize device:%s failed. The error code is %d\n",dev->parent.name, result));return result;}}dev->flag |= RT_DEVICE_FLAG_ACTIVATED;}/* 如果设备是一个独立的设备并且已经打开 */if ((dev->flag & RT_DEVICE_FLAG_STANDALONE) &&(dev->open_flag & RT_DEVICE_OFLAG_OPEN)){return -RT_EBUSY;}/* call device_open interface */if (device_open != RT_NULL){result = device_open(dev, oflag);}else{dev->open_flag = (oflag & RT_DEVICE_OFLAG_MASK);}/* set open flag */if (result == RT_EOK || result == -RT_ENOSYS){dev->open_flag |= RT_DEVICE_OFLAG_OPEN;dev->ref_count++;/* don't let bad things happen silently. If you are bitten by this assert,* please set the ref_count to a bigger type. */RT_ASSERT(dev->ref_count != 0);}return result;
}
  • close:关闭设备,在打开设备时,设备控制块会维护一个打开计数,在打开设备时进行+1操作,在关闭设备时进行-1操作,当计数器变为0时,才会进行真正的关闭操作。
rt_err_t rt_device_close(rt_device_t dev)
{rt_err_t result = RT_EOK;/* parameter check */RT_ASSERT(dev != RT_NULL);RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);if(dev->ref_count == 0)return -RT_ERROR;dev->ref_count--;if(dev->ref_count != 0)return RT_EOK;if(dev->close != RT_NULL)result = dev->close(dev);/* set open flag */if (result == RT_EOK || result == -RT_ENOSYS)dev->open_flag = RT_DEVICE_OFLAG_CLOSE;return result;
}
  • read。从设备读取数据。参数pos是读取数据的偏移量,但是有些设备并不一定需要指定偏移量,例如串口设备,驱动程序应忽略这个参数。而对于块设备来说,pos以及size都是以块设备的数据块大小为单位的。
    例如块设备的数据块大小是512,而参数中pos=10,size=2,那么驱动程序应该返回设备中第10个块(从第0个块作为起始),共计2个块的数据。这个接口返回的类型是rt_size_t,即读到的字节数或块数目。正常情况下应该返回参数中size的数值,如果返回零需要设置对应的errno值。
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{/* parameter check */RT_ASSERT(dev != RT_NULL);RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);if (dev->ref_count == 0){rt_set_errno(-RT_ERROR);return 0;}/* call device_read interface */if(device_read != RT_NULL)return dev->read(dev, pos, buffer, size);rt_set_errno(-RT_ENOSYS);return 0;
}
  • write:向设备写入数据。参数pos是写入数据的偏移量。与读操作类似,对于块设备来说,pos以及size都是以块设备的数据块大小为单位的。这个接口返回的类型是rt_size_t,即真实写入数据的字节数或块数目。正常情况下应该会返回参数中 size 的数值,如果返回零请设置对应的 errno 值。
rt_size_t rt_device_write(rt_device_t dev,rt_off_t    pos,const void *buffer,rt_size_t   size)
{/* parameter check */RT_ASSERT(dev != RT_NULL);RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);if (dev->ref_count == 0){rt_set_errno(-RT_ERROR);return 0;}/* call device_write interface */if (device_write != RT_NULL){return device_write(dev, pos, buffer, size);}/* set error code */rt_set_errno(-RT_ENOSYS);return 0;
}
RTM_EXPORT(rt_device_write);
  • control:根据cmd命令控制设备。命令往往是由底层各类设备驱动自定义实现。例如参数RT_DEVICE_CTRL_BLK_GETGEOME,意思是获取块设备的大小信息。
rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg)
{/* parameter check */RT_ASSERT(dev != RT_NULL);RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device);/* call device_write interface */if (device_control != RT_NULL){return device_control(dev, cmd, arg);}return -RT_ENOSYS;
}
RTM_EXPORT(rt_device_control);
  • 当一个动态创建的设备不再需要使用时可以通过如下函数来销毁。
void rt_device_destroy(rt_device_t dev)
{rt_object_detach(&(dev->parent));rt_free(dev);
}
  • 设备被创建后,需要注册到I/O设备管理器中,应用程序才能够访问。
rt_err_t rt_device_register(rt_device_t dev, const char *name, rt_uint16_t flags)
{if (dev == RT_NULL)return -RT_ERROR;if (rt_device_find(name) != RT_NULL) //应当避免重复注册已经注册的设备,已经注册相同名字的设备。return -RT_ERROR;rt_object_init(&(dev->parent), RT_Object_Class_Device, name);dev->flag = flags;dev->ref_count = 0;dev->open_flag = 0;return RT_EOK;
}

flags参数支持下列参数:

#define RT_DEVICE_FLAG_RDONLY       0x001 /* 只读 */
#define RT_DEVICE_FLAG_WRONLY       0x002 /* 只写  */
#define RT_DEVICE_FLAG_RDWR         0x003 /* 读写  */
#define RT_DEVICE_FLAG_REMOVABLE    0x004 /* 可移除  */
#define RT_DEVICE_FLAG_STANDALONE   0x008 /* 独立   */
#define RT_DEVICE_FLAG_SUSPENDED    0x020 /* 挂起  */
#define RT_DEVICE_FLAG_STREAM       0x040 /* 流模式  */
#define RT_DEVICE_FLAG_INT_RX       0x100 /* 中断接收 */
#define RT_DEVICE_FLAG_DMA_RX       0x200 /* DMA 接收 */
#define RT_DEVICE_FLAG_INT_TX       0x400 /* 中断发送 */
#define RT_DEVICE_FLAG_DMA_TX       0x800 /* DMA 发送 */

设备流模式RT_DEVICE_FLAG_STREAM参数用于向串口终端输出字符串,当输出字符是"\n"时,自动在前面补一个“\r”。

当设备注销后,设备将从设备管理器中移除,但不会释放设备控制块占用的内存。

看门狗设备注册示例

struct rt_device_ops
{/* common device interface */rt_err_t  (*init)   (rt_device_t dev);rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);rt_err_t  (*close)  (rt_device_t dev);rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
};const static struct rt_device_ops wdt_ops =
{rt_watchdog_init,rt_watchdog_open,rt_watchdog_close,RT_NULL,RT_NULL,rt_watchdog_control
};rt_err_t rt_hw_watchdog_register(struct rt_watchdog_device *wtd,const char                *name,rt_uint32_t                flag,void                      *data)
{struct rt_device *device;RT_ASSERT(wtd != RT_NULL);device = &(wtd->parent);device->type        = RT_Device_Class_Miscellaneous;device->rx_indicate = RT_NULL;device->tx_complete = RT_NULL;device->ops         = &wdt_ops;device->user_data   = data;/* register a character device */return rt_device_register(device, name, flag);
}

访问I/O设备

应用程序通过I/O设备管理接口来访问硬件设备,当设备驱动程序实现后,应用程序就可以访问该硬件。
在这里插入图片描述
在这里插入图片描述
设备驱动框架层在components/drivers里面查找。
设备驱动层在libraries/HAL_Drivers

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

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

相关文章

【人工智能Ⅰ】实验8:DBSCAN聚类实验

实验8 DBSCAN聚类实验 一、实验目的 学习DBSCAN算法基本原理&#xff0c;掌握算法针对不同形式数据如何进行模型输入&#xff0c;并结合可视化工具对最终聚类结果开展分析。 二、实验内容 1&#xff1a;使用DBSCAN算法对iris数据集进行聚类算法应用。 2&#xff1a;使用DBS…

macOS Sonoma 14.2RC(23C63)发布

系统介绍 黑果魏叔12 月 6 日消息&#xff0c;苹果今日向 Mac 电脑用户推送了 macOS 14.2 RC更新&#xff08;内部版本号&#xff1a;2323C633&#xff09;&#xff0c;本次更新距离上次发布隔了 49 天。 预计正式版会在下周到来。届时用户可以打开“设置”->“通用”->…

ATFX汇市:美联储12月利率决议来袭,或将连续第三次暂停加息

ATFX汇市&#xff1a;明日3:00&#xff0c;美联储将公布12月份利率决议结果&#xff0c;市场普遍预期联邦基金利率上限将维持在5.5%不变&#xff0c;美联储将连续第三次暂停加息。上周五公布的11月非农就业报告显示&#xff0c;新增非农就业人口19.9万人&#xff0c;远高于前值…

windows禁用系统更新

1.在winr运行框中输入services.msc&#xff0c;打开windows服务窗口。 services.msc 2.在服务窗口中&#xff0c;我们找到Windows update选项&#xff0c;如下图所示&#xff1a; 3.双击windows update服务&#xff0c;我们把启动类型改为禁用&#xff0c;如下图所示&#xff…

图片怎么去文字水印?分享三个简单无痕方法

图片怎么去文字水印&#xff1f;大家在遇到好看的图片时&#xff0c;是否曾想过将其用作自己的头像&#xff1f;然而&#xff0c;很多时候从网上保存的图片会带有平台或作者的水印&#xff0c;这会严重影响头像的整体视觉效果&#xff0c;导致我们不得不放弃使用这张图片&#…

代驾系统开发:驶向未来的智能交通服务

随着科技的迅速发展&#xff0c;代驾系统的开发成为改善出行体验和提升交通服务智能化的重要一环。本文将聚焦于代驾系统开发的技术创新&#xff0c;为读者呈现其中涉及的一些令人振奋的技术代码。 1. 区块链技术的运用&#xff1a; 区块链技术被引入代驾系统&#xff0c;可…

11.机器人系统仿真搭建gazebo环境、仿真深度相机、雷达、RGB相机

目录 1 gazebo仿真环境搭建 1.1 直接添加内置组件创建仿真环境 1.2 urdf、gazebo、rviz的综合应用 2 ROS_control 2.1 运动控制实现流程(Gazebo) 2.1.1 已经创建完毕的机器人模型&#xff0c;编写一个单独的 xacro 文件&#xff0c;为机器人模型添加传动装置以及控制器 …

Postswigger 靶场 XSS 通关

文章目录 PostSwigger靶场XSS通关学徒&#xff1a;第一关学徒&#xff1a;第二关学徒&#xff1a;第三关学徒&#xff1a;第四关学徒&#xff1a;第五关学徒&#xff1a;第六关学徒&#xff1a;第七关学徒&#xff1a;第八关学徒&#xff1a;第九关 PostSwigger靶场XSS通关 靶…

插入排序——折半插入排序

1、简述&#xff1a; 折半插入排序&#xff08;binary insertion sort&#xff09;是对插入排序算法的一种改进&#xff0c;由于排序算法过程中&#xff0c;就是不断的依次将元素插入前面已排好序的序列中。由于前半部分为已排好序的数列&#xff0c;这样我们不用按顺序依次寻…

项目部署阿里云服务器详细流程

1.购买域名 官网 域名的作用的是为了DNS解析&#xff0c;这样被人访问的不再是一连串的IP地址&#xff0c;而是形如 www.xxx.com 这样的网站 域名购买流程较长&#xff0c;还需备案&#xff0c;所以能提前准备好&#xff0c;10块左右买个.top域名

Python—KNN分类算法

原文: https://zhuanlan.zhihu.com/p/143092725 1. 概述 KNN 可以说是最简单的分类算法之一&#xff0c;同时&#xff0c;它也是最常用的分类算法之一。注意&#xff1a;KNN 算法是有监督学习中的分类算法&#xff0c;它看起来和另一个机器学习算法 K-means 有点像&#xff0…

百安居的数字化之路:找到用户的“连接器”

从线下客流遭遇悬崖式下跌到逐步回升&#xff0c;越来越多企业认识到&#xff0c;用户消费习惯的改变已经不可逆地影响着各行各业&#xff0c;只有让组织、人员、系统、产品等元素产生新的连接和协同&#xff0c;才能不断强化企业对抗风险的能力。这个过程&#xff0c;也是数字…

改进的A*算法的路径规划(3)

5.4实验结果与讨论 为验证本文算法的有效性&#xff0c;在模拟越野环境中完成路径规划&#xff0c;并通过仿真对比 实验验证了本文改进算法的可行性和综合性能的优越性。 5.4.1 与 传 统A*实验对比 为了验证改进A* 算法与传统A* 的优越性&#xff0c;建立了7050的栅格地图(并…

如何制作AI数字人高清模型?

数字人是什么&#xff1f;重新下一个定义&#xff1a;"把人数字化&#xff0c;以行人的职责“它是用AI技术根据你的真人形象克隆出一个数字人&#xff0c;跟真人的相似度可以达到100%以上的&#xff0c;像你的动作、表情还有声音&#xff0c;都是可以被克隆出来。克隆出来…

SpringBoot程序打包失败处理

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…

最新鸿蒙HarmonyOS4.0开发登陆的界面2

登陆功能 代码如下&#xff1a; import router from ohos.router; Entry Component struct Index {State message: string XXAPP登陆State userName: string ;State password: string ;build() {Row() {Column({space:50}) {Image($r(app.media.icon)).width(200).interpol…

josef约瑟 静态电压继电器 HWY-41B 19-240V 导轨式安装

HWY-40系列无辅源静态电压继电器 HWY-41A无辅源静态电压继电器 HWY-42A无辅源静态电压继电器 HWY-43A无辅源静态电压继电器 HWY-44A无辅源静态电压继电器 HWY-45A无辅源静态电压继电器 HWY-41B无辅源静态电压继电器 HWY-42B无辅源静态电压继电器 HWY-43B无辅源静态电压继电器 …

Qt之QSlider和QProgressBar

Qt之QSlider和QProgressBar 实验结果 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget) {ui->setupUi(this);connect(ui->dial,&QDial::valueChanged,this,&Widget::do_val…

腾讯云:AI云探索之路

随着科技的飞速发展&#xff0c;人工智能(AI)云计算领域日益显现出其巨大的潜力和价值。在这个充满挑战和机遇的领域&#xff0c;腾讯云凭借其卓越的技术和创新能力&#xff0c;取得了令人瞩目的成果。本文将深入探讨腾讯云在AI云计算领域的优势&#xff0c;以及其为人工智能发…