LV.12 D13 UART实验 学习笔记

一、UART帧格式详解

 UART    

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

通信基础 - 并行和串行

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

        

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

 

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

通信基础 - 单工和双工

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

 

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

如果总线的数据线只有一根,一般是半双工的,如果有多根,一般是全双工。

通信基础 - 波特率 

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

UART帧格式

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

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

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

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

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

UART硬件连接

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

 

UART控制器

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

二、Exynos4412下的 UART控制器

引脚功能设置 

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

 

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

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

  

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

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

 

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

支持红外传输,(无线)

1位或者两位停止位

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

 

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

 

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

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

 

三、UART寄存器详解 

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

 

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

 

 

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

 

 

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

 

中断模式:有消息通知CPU

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

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

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

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

 

 

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

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

 

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

 

 

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

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;
}

 

#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;
}

 并没有输出想要的结果,因为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;
}

#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;
}

 输入什么返回什么加一(以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;
}

 

#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;
}

 

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

作业

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

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

 

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

注:在终端上输入‘2’,LED2点亮,再次输入‘2’,LED2熄灭... ...

#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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

API文档自动生成

API文档自动生成 背景smart-doc配置效果图 背景 对于API接口文档自动生成&#xff0c;可能大家&#xff0c;最新想到的是用swagger&#xff0c;但是有以下问题&#xff1a; 对代码侵入太强版本升级&#xff0c;并不兼容不能生成各种类型的文档 所以&#xff0c;今天给大家推荐…

Python Django 之模板继承详解(extends)

文章目录 1 概述1.1 目的1.2 标签&#xff1a;block、extends1.3 目录结构 2 templates 目录2.1 base.html&#xff1a;父页面2.2 login.html&#xff1a;子页面 3 其它代码3.1 settings.py3.2 views.py3.3 urls.py 1 概述 1.1 目的 模板继承 和 类继承 的目的是一样的&#…

【已解决】PPT不能转换成PDF文档怎么办?

PPT可以转换成PDF文档&#xff0c;只需要点击PPT菜单页面中的【文件】选项&#xff0c;再点击【导出】即可转换&#xff0c;如果转换时发现【导出】选项不可选&#xff0c;无法完成转换怎么办&#xff1f;以下3种方法可以试试&#xff01; 出现上面这种情况&#xff0c;我们可以…

HWebkit库的程序示例

HWebkit库的爬虫程序&#xff0c;并且能够使用指定的服务器。以下是代码的每一行的中文解释&#xff1a; import HWebkit import Network.HTTP.Client import Network.HTTP.Client.TLS import Data.Text.Encoding ​ -- 创建服务器的连接&#xff0c;使用的端口和主机是 proxy…

计算机服务器中了mallad勒索病毒怎么办,勒索病毒解密恢复

警惕&#xff0c;警惕&#xff0c;企业主多警惕&#xff0c;新型网络勒索病毒出来了&#xff0c;近期&#xff0c;云天数据恢复中心&#xff0c;接到了某企业的求助&#xff0c;企业的计算机服务器中了mallad后缀勒索病毒&#xff0c;导致企业计算机系统瘫痪&#xff0c;无法正…

Vue3.0 reactive与ref :VCA模式

简介 Vue3 最大的一个变动应该就是推出了 CompositionAPI&#xff0c;可以说它受ReactHook 启发而来&#xff1b;它我们编写逻辑更灵活&#xff0c;便于提取公共逻辑&#xff0c;代码的复用率得到了提高&#xff0c;也不用再使用 mixin 担心命名冲突的问题。 ref 与 reactive…

yolov7模型轻量化改进之MobileOne骨干替换

本文在之前文章yolov7分割训练的基础上进行改进: https://blog.csdn.net/qq_41920323/article/details/129464115?spm=1001.2014.3001.5502 具体GitHub工程下载,环境安装配置,数据准备等,请借鉴之前的文章,此处只介绍如何进行改进。 MobileOne地址:https://github.com…

通付盾Web3专题 | 智能账户:数字时代基础单元

2008年10月31日&#xff0c;中本聪&#xff08;Satoshi Nakamoto&#xff09;在P2P foundation 网站发布比特币白皮书《比特币&#xff1a;一种点对点的电子现金系统》。转眼距比特币白皮书发布已过去15年。2009年1月比特币网络正式推出&#xff0c;当时每个比特币的价格仅为0.…

设置防火墙

1.RHEL7中的防火墙类型 防火墙只能同时使用一张,firewall底层调用的还是lptables的服务: firewalld:默认 &#xff0c;基于不同的区域做规则 iptables: RHEL6使用&#xff0c;基于链表 Ip6tables Ebtables 2.防火墙的配置方式 查看防火墙状态: rootlinuxidc -]#systemct…

idea集成测试插件替代postman

idea集成测试插件替代postman 兄弟萌&#xff0c;你再测试接口是否无bug是否流畅的时候是否还在使用“postman”来回切换进行测试呢&#xff1f; 页面切换进行测试&#xff0c;有没有感觉很麻烦呢&#xff1f; 打开postman&#xff0c;输入接口地址&#xff0c;有没有感觉很麻烦…

拓扑排序专题1 拓扑排序

题目&#xff1a; 样例&#xff1a; 输入 4 5 0 1 0 2 0 3 1 2 3 2 输出 0 1 3 2 思路&#xff1a; 拓扑序列含义 一个由图中所有点构成的序列 A 满足&#xff1a;对于图中的每条边 (x,y)(x,y)&#xff0c; x 在 A 中都出现在 y 之前&#xff0c;则称 A 是该图的一个拓扑序列…

阿里云无影升级2.0 云电脑解决方案时代到来

10月31日&#xff0c;杭州云栖大会上&#xff0c;阿里云宣布无影全新升级2.0&#xff1a;从云电脑到云上解决方案&#xff0c;帮助中小企业更便捷地构建云上办公&#xff0c;并开放无影产品及解决方案能力&#xff0c;为生态合作伙伴提供企业云平台&#xff0c;帮助其打造定制化…

Python小试牛刀:GUI(图形界面)实现计算器UI界面(二)

上一篇&#xff1a;Python小试牛刀&#xff1a;GUI&#xff08;图形界面&#xff09;实现计算器UI界面&#xff08;一&#xff09;-CSDN博客 在上一篇文章中介绍了Python GUI常用的库&#xff0c;以及运用GUI标准库tkinter仅设计了计算器的UI界面。 而在本篇文章&#xff0c;…

「Java开发指南」如何用MyEclipse搭建Spring MVC应用程序?(一)

本教程将指导开发者如何生成一个可运行的Spring MVC客户应用程序&#xff0c;该应用程序实现域模型的CRUD应用程序模式。在本教程中&#xff0c;您将学习如何&#xff1a; 从数据库表的Scaffold到现有项目部署搭建的应用程序 使用Spring MVC搭建需要MyEclipse Spring或Bling授…

本章内容的重点是对各种电子式电动机保护器电路的原理分析和故障维修指导,对电子式电动机保护器以下简称为电动机保护器。

上世纪八十年代之前&#xff0c;电子技术的应用尚处于初级阶段&#xff0c;对电动机的保护任务多由热继电器承担&#xff0c;国内型号为为JR20-XX系列、JR36-XX系列等。其保护机理如下&#xff1a;热继电器由发热元件、双金属片、触点及一套传动和调整机构组成。发热元件是一段…

【Linux】第八站:gcc和g++的使用

文章目录 一、解决sudo命令的问题二、Linux编译器-gcc/g1.gcc的使用2.g的使用 三、gcc编译链接过程1.预处理2.编译&#xff08;生成汇编&#xff09;3.汇编&#xff08;生成机器可识别代码&#xff09;4.链接&#xff08;生成可执行文件或库文件&#xff09;5.一些选项的意义 四…

SQLITE3 函数接口

简述 sqlite3 接口的核心元素: 两大对象&#xff0c;八大函数&#xff1b; 其中两个对象指的是: sqlite3 数据库连接对象 数据库的连接句柄(数据库的文件描述符) 代表你打开的那个 sqlite3 的数据库文件,后序对数据库的操作都需要用到这个对象 sqlite3_stmt SQL 语句对象…

从「码农」到管理者,E人程序员的十年蜕变

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩北京录音间 当我们谈论程序员创业时&#xff0c;常常会首先想到一些传统观念认为的挑战&#xff1a;沟通技巧不佳、逻…

各种爱心特效代码免费分享

「链接&#xff1a;https://pan.xunlei.com/s/VNi9l3Mqp9oEflga1T6M-ZUOA1?pwdsam3# 提取码&#xff1a;sam3”复制这段内容后打开手机迅雷App&#xff0c;查看更方便」 「链接&#xff1a;https://pan.xunlei.com/s/VNi9lWqdFIwdtD5sdCDZFamoA1?pwdka8b# 提取码&#xff1a;…

集简云x slack(自建)无需API开发轻松连接OA、电商、营销、CRM、用户运营、推广、客服等近千款系统

slack是一个工作效率管理平台&#xff0c;让每个人都能够使用无代码自动化和 AI 功能&#xff0c;还可以无缝连接搜索和知识共享&#xff0c;并确保团队保持联系和参与。在世界各地&#xff0c;Slack 不仅受到公司的信任&#xff0c;同时也是人们偏好使用的平台。 官网&#x…