当前位置: 首页 > news >正文

Banana Pi BPI-W3(Armsom W3)RK3588开当板之调试UART

前言

  • 本文主要讲解如何关于RK3588开发板UART的使用和调试方法,包括UART作为普通串口和控制台两种不同使用场景

一. 功能特点

Rockchip UART (Universal Asynchronous Receiver/Transmitter) 基于16550A串口标准,完整模块支持以下功能:

  • 支持5、6、7、8 bits数据位。
  • 支持1、1.5、2 bits停止位。
  • 支持奇校验和偶校验,不支持mark校验和space校验。
  • 支持接收FIFO和发送FIFO,一般为32字节或者64字节。
  • 支持最高4M波特率,实际支持波特率需要芯片时钟分频策略配合。
  • 支持中断传输模式和DMA传输模式。 支持硬件自动流控,RTS+CTS。

二、代码位置

在Linux kernel 中,使用8250串口通用驱动,以下为主要驱动文件:

drivers/tty/serial/8250/8250_core.c # 8250串口驱动核心 drivers/tty/serial/8250/8250_dw.c # Synopsis DesignWare 8250串口驱动 drivers/tty/serial/8250/8250_dma.c # 8250串口DMA驱动 drivers/tty/serial/8250/8250_port.c # 8250串口端口操作 drivers/tty/serial/8250/8250_early.c # 8250串口early console驱动

SDK中提供的UART默认配置已经使用了8250驱动我们就不需要修改

三、硬件原理图

串口功能的硬件上比较简单,这是只附上调试串口的原理图

四、设备树配置

rk平台的设备树修改路径都是在kernel\arch\arm64\boot\dts\rockchip下面,具体哪个文件根据对应开发板来决定,通常描述设备硬件配置在rkxxxx.dtsi中,比如在rk3588s.dtsi中:

uart2: serial@feb50000 {compatible = "rockchip,rk3588-uart", "snps,dw-apb-uart";reg = <0x0 0xfeb50000 0x0 0x100>;interrupts = <GIC_SPI 333 IRQ_TYPE_LEVEL_HIGH>;clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>;clock-names = "baudclk", "apb_pclk";reg-shift = <2>;reg-io-width = <4>;dmas = <&dmac0 10>, <&dmac0 11>;pinctrl-names = "default";pinctrl-0 = <&uart2m1_xfer>;status = "disabled";};

4.1作为普通串口

假入我们想使用w3开发板上40PIN上的uart7 我们在dts可以使用如下配置打开

&uart7 {status = "okay";pinctrl-names = "default";pinctrl-0 = <&uart7m1_xfer>;};

4.2作为调试串口

Rockchip UART作为控制台,使用fiq_debugger流程。 在dts中fiq_debugger节点配置如下。由于fiq_debugger和普通串口互斥,在使能fiq_debugger节点后必须禁用对应的普通串口uart节点。

chosen: chosen {bootargs = "earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0";
};
fiq-debugger {compatible = "rockchip,fiq-debugger";rockchip,serial-id = <2>;rockchip,wake-irq = <0>;/* If enable uart uses irq instead of fiq */rockchip,irq-mode-enable = <1>;rockchip,baudrate = <1500000>; /* Only 115200 and 1500000 */interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_LOW>;pinctrl-names = "default";pinctrl-0 = <&uart2m0_xfer>;status = "okay";
};
&uart2 {status = "disabled";
};
  • rockchip,serial-id:使用的UART编号。修改serial-id到不同UART,fiq_debugger设备也会注册成
  • ttyFIQ0设备。 rockchip,irq-mode-enable:配置为1使用irq中断,配置为0使用fiq中断。
  • interrupts:配置的辅助中断,保持默认即可。
  • pinctrl-0:使用的串口引脚
  • rockchip,baudrate:波特率配置

五、串口相关问题

5.1设备注册

普通串口设备将会根据dts中的aliase来对串口进行编号,对应注册成ttySx设备。注册的节点为/dev/ttyS4,命名规则是通过dts中的aliases来的。

aliases {
serial0 = &uart0;
serial1 = &uart1;
serial2 = &uart2;
serial3 = &uart3;
}

对应uart0注册为ttyS0,uart0注册为ttyS1,如果需要把uart3注册成ttyS1,可以进行以下修改

serial1 = &uart3;  
serial3 = &uart1;

5.2控制台打印相关

Rockchip UART打印通常包括DDR阶段、Miniloader阶段、TF-A (Trusted Firmware-A)阶段、OP-TEE阶段、Uboot阶段和Kernel阶段,我们平时主要关注的是uboot阶段和kernel阶段的打印,在这两个阶段我们可以尝试关闭所有打印或切换所有打印到其他UART,RK平台默认的调试串口是uart2_m0这一组引脚,假如现在我将打印换成其他串口,可以尝试以下做法。

5.2.1DDR Loader修改方法

DDR Loader中关闭或切换打印,需要修改DDR Loader中的UART打印配置,修改文件rkbin/tools/ddrbin_param.txt中的以下参数:

uart id= # UART控制器id,配置为0xf为关闭打印 uart iomux= # 复用的IOMUX引脚 uart baudrate= # 115200 or 1500000

修改完成后,使用以下命令重新生成ddr.bin固件。

./ddrbin_tool ddrbin_param.txt rk3588_ddr_lp4_2112MHz_lp5_2736MHz_v1.09.bin

5.2.2Uboot修改方法

Uboot中关闭打印,需要在menuconfig中,打开配CONFIG_DISABLE_CONSOLE,保存到.config文件 Uboot中切换打印,由传参机制决定,不需要进行额外修改。uboot解析传参机制相关代码在arch/arm/mach-rockchip/board.c的board_init_f_init_serial()函数中。

5.2.3kernel修改方法

去掉打印需要在menuconfig中,关闭配置CONFIG_SERIAL_8250_CONSOLE。

Device Drivers ---> Character devices ---> Serial drivers ---> [ ]Console on 8250/16550 and compatible serial port

在dts配置中找到类似以下内容,并去掉UART基地址和console相关配置参数

chosen: chosen {bootargs = "earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=PARTUUID=614e0000-0000 rw rootwait";};

将0xfeb50000 console=ttyFIQ0 去掉,然后找到fiq-debugger节点,修改serial-id为0xffffffff,去掉UART引脚复用相关配置。注意,需要保持fiq�debugger节点使能,保持fiq-debugger流程系统才能正常启动

fiq_debugger: fiq-debugger {compatible = "rockchip,fiq-debugger";rockchip,serial-id = <0xffffffff>;rockchip,wake-irq = <0>;/* If enable uart uses irq instead of fiq */rockchip,irq-mode-enable = <1>;rockchip,baudrate = <1500000>;  /* Only 115200 and 1500000 */interrupts = <GIC_SPI 423 IRQ_TYPE_LEVEL_LOW>;status = "okay";};

切换打印串口例如将Kernel打印从UART2切换到UART3,在dts配置中找到类似以下内容,将UART基地址由UART2改为UART3.

bootargs = "earlycon=uart8250,mmio32,0xfe670000 console=ttyFIQ0";

0xfe670000是UART3基地址,然后找到fiq-debugger节点,修改serial-id为3,修改UART3引脚复用配置pinctrl-0 = <&uart3m0_xfer>。注意,同时需要将切换为打印串口的UART3作为普通串口的节点禁用。

六、串口测试

在开发板上跑一套应用程序,可以发送数据,可以接收数据,测试方法可以短接TX_RX

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termio.h>
#include <time.h>
#include <pthread.h>int read_data(int fd, void *buf, int len);
int write_data(int fd, void *buf, int len);
int setup_port(int fd, int baud, int databits, int parity, int stopbits);
void print_usage(char *program_name);pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER;
int data_available = 0;void *read_thread(void *arg) {int fd = *(int *)arg;char buffer[1024]; // 存储读取的数据while (1) {int bytes_read = read_data(fd, buffer, sizeof(buffer));if (bytes_read > 0) {printf("Read Thread: Read %d bytes: %s\n", bytes_read, buffer);} else {// 处理读取错误或设备关闭的情况break;}}pthread_exit(NULL);
}void *write_thread(void *arg) {int fd = *(int *)arg;char input[1024]; // 存储用户输入的数据while (1) {printf("Enter data to write (or 'q' to quit): ");fgets(input, sizeof(input), stdin);if (strcmp(input, "q\n") == 0 || strcmp(input, "Q\n") == 0) {// 用户输入 'q' 或 'Q',退出循环break;}int len = strlen(input);int bytes_written = write_data(fd, input, len);if (bytes_written > 0) {printf("Write Thread: Wrote %d bytes: %s\n", bytes_written, input);}}pthread_exit(NULL);
}int main(int argc, char *argv[]) //./a.out /dev/ttyS4 115200 8 0 1
{int fd;int baud;int len;int count;int i;int databits;int stopbits;int parity;if (argc != 6) {print_usage(argv[0]);return 1;}baud = atoi(argv[2]);if ((baud < 0) || (baud > 921600)) {fprintf(stderr, "Invalid baudrate!\n");return 1;}databits = atoi(argv[3]);if ((databits < 5) || (databits > 8)) {fprintf(stderr, "Invalid databits!\n");return 1;}parity = atoi(argv[4]);if ((parity < 0) || (parity > 2)) {fprintf(stderr, "Invalid parity!\n");return 1;}stopbits = atoi(argv[5]);if ((stopbits < 1) || (stopbits > 2)) {fprintf(stderr, "Invalid stopbits!\n");return 1;}fd = open(argv[1], O_RDWR, 0);if (fd < 0) {fprintf(stderr, "open <%s> error %s\n", argv[1], strerror(errno));return 1;}if (setup_port(fd, baud, databits, parity, stopbits)) {fprintf(stderr, "setup_port error %s\n", strerror(errno));close(fd);return 1;}pthread_t read_tid, write_tid;int ret;// 创建读取线程ret = pthread_create(&read_tid, NULL, read_thread, &fd);if (ret != 0) {fprintf(stderr, "Failed to create read thread\n");return 1;}// 创建写入线程ret = pthread_create(&write_tid, NULL, write_thread, &fd);if (ret != 0) {fprintf(stderr, "Failed to create write thread\n");return 1;}// 等待读取线程和写入线程结束pthread_join(read_tid, NULL);pthread_join(write_tid, NULL);close(fd);return 0;
}static int baudflag_arr[] = {B921600, B460800, B230400, B115200, B57600, B38400,B19200,  B9600,   B4800,   B2400,   B1800,  B1200,B600,    B300,    B150,    B110,    B75,    B50
};
static int speed_arr[] = {921600,  460800,  230400,  115200,  57600,  38400,19200,   9600,    4800,    2400,    1800,   1200,600,     300,     150,     110,     75,     50
};int speed_to_flag(int speed)
{int i;for (i = 0;  i < sizeof(speed_arr)/sizeof(int);  i++) {if (speed == speed_arr[i]) {return baudflag_arr[i];}}fprintf(stderr, "Unsupported baudrate, use 9600 instead!\n");return B9600;
}static struct termio oterm_attr;int setup_port(int fd, int baud, int databits, int parity, int stopbits)
{struct termio term_attr;if (ioctl(fd, TCGETA, &term_attr) < 0) {return -1;}memcpy(&oterm_attr, &term_attr, sizeof(struct termio));term_attr.c_iflag &= ~(INLCR | IGNCR | ICRNL | ISTRIP);term_attr.c_oflag &= ~(OPOST | ONLCR | OCRNL);term_attr.c_lflag &= ~(ISIG | ECHO | ICANON | NOFLSH);term_attr.c_cflag &= ~CBAUD;term_attr.c_cflag |= CREAD | speed_to_flag(baud);term_attr.c_cflag &= ~(CSIZE);switch (databits) {case 5:term_attr.c_cflag |= CS5;break;case 6:term_attr.c_cflag |= CS6;break;case 7:term_attr.c_cflag |= CS7;break;case 8:default:term_attr.c_cflag |= CS8;break;}switch (parity) {case 1:  term_attr.c_cflag |= (PARENB | PARODD);break;case 2:  term_attr.c_cflag |= PARENB;term_attr.c_cflag &= ~(PARODD);break;case 0:  default:term_attr.c_cflag &= ~(PARENB);break;}switch (stopbits) {case 2:  term_attr.c_cflag |= CSTOPB;break;case 1:  default:term_attr.c_cflag &= ~CSTOPB;break;}term_attr.c_cc[VMIN] = 1;term_attr.c_cc[VTIME] = 0;if (ioctl(fd, TCSETAW, &term_attr) < 0) {return -1;}if (ioctl(fd, TCFLSH, 2) < 0) {return -1;}return 0;
}int read_data(int fd, void *buf, int len)
{int count;int ret;ret = 0;count = 0;//while (len > 0) {ret = read(fd, (char*)buf + count, len);if (ret < 1) {fprintf(stderr, "Read error %s\n", strerror(errno));//break;}count += ret;len = len - ret;//}*((char*)buf + count) = 0;return count;
}int write_data(int fd, void *buf, int len)
{int count;int ret;ret = 0;count = 0;while (len > 0) {ret = write(fd, (char*)buf + count, len);if (ret < 1) {fprintf(stderr, "Write error %s\n", strerror(errno));break;}count += ret;len = len - ret;}return count;
}void print_usage(char *program_name)
{fprintf(stderr,"*************************************\n""  A Simple Serial Port Test Utility\n""*************************************\n\n""Usage:\n  %s <device> <baud> <databits> <parity> <stopbits> \n""       databits: 5, 6, 7, 8\n""       parity: 0(None), 1(Odd), 2(Even)\n""       stopbits: 1, 2\n""Example:\n  %s /dev/ttyS4 115200 8 0 1\n\n",program_name, program_name);
}

运行效果如下:

相关文章:

Banana Pi BPI-W3(Armsom W3)RK3588开当板之调试UART

前言 本文主要讲解如何关于RK3588开发板UART的使用和调试方法&#xff0c;包括UART作为普通串口和控制台两种不同使用场景 一. 功能特点 Rockchip UART (Universal Asynchronous Receiver/Transmitter) 基于16550A串口标准&#xff0c;完整模块支持以下功能&#xff1a; 支…...

LeetCode88——合并两个有序数组

LeetCode88——合并两个有序数组 1.题目描述&#xff1a; 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减…...

C++ BinarySercahTree recursion version

for循环版本的&#xff1a;C BinarySercahTree for version-CSDN博客 Inorder()在c BinarySerschTree for verison写了。 还是按照那种嵌套的方式来写递归。 现在来写查找 FindR() bool FindR(){return _FindR(_root);}然后_FindR()函数写递归具体实现&#xff1a; 假设要…...

兑换码生成与解析-个人笔记(java)

1.需求分析 兑换码长度为10字符&#xff0c;包含24个大写字母和8个数字。兑换码需要保证唯一性&#xff0c;不可重复兑换。需要防止猜测和爆刷攻击。兑换码生成和验证的算法需要高效&#xff0c;避免对数据库带来较大的压力。 导航 1.需求分析2.实现方案3.加密过程4.解密过程5…...

2023/10/25MySQL学习

外键约束 在子表添加外键后,不能在主表删除或更新记录,因为存在外键关联 删除外键,注意外键名称时我们添加外键时起的名称 使用cascade操作后,可以操作主表数据,并且子表的外键也会对应改变 set null的话,删除主表对应主键信息后,子表对应外键信息变为空 多表关系 创建中间表 可…...

网络协议--Ping程序

7.1 引言 “ping”这个名字源于声纳定位操作。Ping程序由Mike Muuss编写&#xff0c;目的是为了测试另一台主机是否可达。该程序发送一份ICMP回显请求报文给主机&#xff0c;并等待返回ICMP回显应答&#xff08;图6-3列出了所有的ICMP报文类型&#xff09;。 一般来说&#x…...

如何在 Azure 容器应用程序上部署具有 Elastic Observability 的 Hello World Web 应用程序

作者&#xff1a;Jonathan Simon Elastic Observability 是提供对正在运行的 Web 应用程序的可见性的最佳工具。 Microsoft Azure 容器应用程序是一个完全托管的环境&#xff0c;使你能够在无服务器平台上运行容器化应用程序&#xff0c;以便你的应用程序可以扩展和缩减。 这使…...

JAVA排序

再看各种排序前我们先了解一下什么叫 稳定性 比如一组数据arr[i]下标与arr[j下标]相等,arr[i]在前面,arr[j]在arr[i]后面,排序后这两个数据仍然是arr[i]在arr[j]前面,arr[j]在arr[i]后面,这就叫稳定 插入排序&#xff1a; 优势: 越有序查找速度越快 时间复杂度: O(N^2) 空间复…...

opencalib中lidar2camera安装记录

目录 一、opencalib安装 二、lidar2camera的安装 三、测试运行 四、出现过的问题 一、opencalib安装 代码地址&#xff1a;https://github.com/PJLab-ADG/SensorsCalibration/blob/master/README.md # pull docker image sudo docker pull scllovewkf/opencalib:v1 # Aft…...

整个自动驾驶小车001:概述

材料&#xff1a; 1&#xff0c;树梅派4b&#xff0c;作为主控&#xff0c;这个东西有linux系统&#xff0c;方便 2&#xff0c;HC-S104超声波模块&#xff0c;我有多个&#xff0c;不少于4个&#xff0c;我可以前后左右四个方向都搞一个 3&#xff0c;l298n模块&#xff0c;…...

windows本地搭建mmlspark分布式机器平台流程

文章目录 windows本地搭建mmlspark分布式机器平台流程安装环境pyspark环境spark环境java环境hadoop环境1.修改hadoop配置文件下的jdk地址为自己的实际地址2.修改bin文件离线环境jar包环境1mmlsprk第三方包jar包环境2参考代码我有话说其他问题记录概要参考文献windows本地搭建mm…...

深入探究 Next.js 中的 getServerSideProps 和 getStaticProps 用法及区别

引言&#xff1a; Next.js 是一个流行的 React 框架&#xff0c;它提供了许多强大的功能来简化服务器渲染&#xff08;SSR&#xff09;和静态生成&#xff08;SSG&#xff09;的过程。其中&#xff0c;getServerSideProps 和 getStaticProps 是两个重要的函数&#xff0c;用于在…...

餐饮业如何高效经营?赶紧闭眼抄这个方法!

在现代社会&#xff0c;餐饮业已经成为人们日常生活中不可或缺的一部分。为了提高食堂运营效率&#xff0c;满足不断增长的客户需求&#xff0c;智慧收银系统应运而生。 智慧收银系统帮助食堂业主更好地理解其客户&#xff0c;提高服务质量&#xff0c;优化库存管理&#xff0c…...

餐饮外卖小程序商城的作用是什么

随着互联网及线上餐饮的发展趋势&#xff0c;行业洗牌正在加速&#xff0c;并且对餐饮连锁门店提出更高要求&#xff0c;餐饮数字化转型加快&#xff0c;积极发展线上经营是不少餐饮商家的首选。这其中&#xff0c;餐饮外卖商城成为很多餐饮品牌的线上经营品牌&#xff0c;也是…...

nRF52832 SDK15.3.0 基于ble_app_uart demo FreeRTOS移植

参考资料&#xff1a;Nrf52832 freeOS系统移植_nrf5283操作系统-CSDN博客 这里把移植经验记录下来&#xff0c;供有需要的同学参考&#xff0c;有不对的地方也请大家批评指正。 把FreeRTOS移植到 nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\ble_app_uart工程&#xff…...

电厂数据可视化三维大屏展示平台加强企业安全防范

园区可视化大屏是一种新型的信息化手段&#xff0c;能够将园区内各项数据信息以图像的形式直观呈现在大屏幕上&#xff0c;便于管理员和员工进行实时监控、分析和决策。本文将从以下几个方面介绍园区可视化大屏的作用和应用。 VR数字孪生园区系统是通过将实际园区的各种数据和信…...

2246: 【区赛】【宁波32届小学生】最佳交换

目录 题目描述 输入 输出 样例输入 样例输出 提示 代码 题目描述 星星小朋友和 N-1 个小伙伴一起玩了一上午的纸牌游戏&#xff0c;星星总是能赢&#xff0c;气焰嚣张&#xff0c; 小伙伴们决定出道纸牌问题难倒星星&#xff0c;让他别再狂妄自大了&#xff0c;问题是这…...

Java面试记录

文章目录 1、final关键字2、synchronized关键字&#xff08;1&#xff09;synchronized的功能&#xff1a;&#xff08;2&#xff09;synchronized的底层实现原理&#xff1a; 3、Java中线程同步的实现方法&#xff08;1&#xff09;. 使用synchronized关键字&#xff1a;&…...

【数据库】聚集函数

聚集函数 聚集函数一览AVG() 函数COUNT() 函数MAX() 函数MIN() 函数SUM() 函数 组合聚集函数 聚集函数一览 我们需要汇总数据而不是实际检索&#xff0c;此时我们使用聚集函数进行处理&#xff1b; 聚集函数一览表如下&#xff1a; 函数说明AVG()返回平均值COUNT()返回数量总…...

【单元测试】--编写单元测试

一、编写第一个单元测试 编写第一个单元测试通常包括以下步骤。以下示例以C#和NUnit为例&#xff1a; 创建测试项目&#xff1a; 在Visual Studio中&#xff0c;创建一个新的Class Library项目&#xff0c;这将是你的单元测试项目。在解决方案资源管理器中&#xff0c;右键点…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...