提供一种刷新X410内部EMMC存储器的方法

USRP X410内部采用了16G的EMMC存储器,内有内核和文件系统。官方站[注1]提供了多个版本的EMMC映像文件,并提供了多种刷新方法[注2]。

1,如果内核还能运行只是文件系统破坏,可以从外接USB盘,之后使用mount挂载U盘,使dd指令恢复映像到EMMC。

2,如果EMMC损坏无法进入LINUX系统,按照官方的说明,将USB-OTG和调试口的TYPEC接口都接入电脑,可以使用JTAG模式下载并运行U-BOOT,输入指令使X410的内部EMMC存储器映射成一个移动硬盘,之后将映像烧写到这个影射的盘里。这个烧写方式再linux下可以使用dd指令,在win下可以使用专门烧写软件。

官方网页还提供了其他更加上层一些的烧写方式,我没有仔细分析。

无论第一种方法还是第二种方法,我们都是直接EMMC的扇区进行写,不同是第一种是X410进行写,而第二种是外接的用户电脑进行写。我就在想我们完全可以把X410当做一个开发板,之后自己写程序读扇区进行写,而要写的内容可以通过网口收过来。

我分析了X410的原理图,觉得应该可以像使用开发板一样玩起来实现上述功能。我首先配置了DDR4内存以及串口,对DDR4进行了检测,看到没有问题。之后配置了EMMC和GMAC,使用SDK的的LWIP例子运行起来了tcp echo server.这里注意X410使用的PHY收发器是KSZ9031,我从网上搜索了一下才找到了修改寄存器以支持SDK的LWIP的配置。

这里上几张X410的PS配置的截图。

下图是DDR4的配置。

下图是EMMC的配置,注意SD1实际可以不配置,因为在X410里面SD卡部分电路实际没有焊接~

串口和TTC设置:

网口设置

其中网口用到的PHY芯片是KSZ9031,来我们所做的X410克隆版本的原理图。

SDK中默认代码不支持,经过网络搜索我找到了支持的代码如下:


u32_t phymapemac0[32];
u32_t phymapemac1[32];
static u32_t get_KSZ_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)
{xil_printf("Start PHY autonegotiation get_KSZ_phy_speed  \r\n");u16_t temp;u16_t control;u16_t status;u16_t status_speed;u32_t timeout_counter = 0;u32_t temp_speed;u32_t phyregtemp;u16_t phy_clk_delay_reg;u16_t phy_rx_delay_reg;xil_printf("Start PHY autonegotiation \r\n");XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_CONTROL_REG, 0x0002);//set up register address for MMD-Device Address 2hXEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x0008);//select register 08h for MMD-Device address 2hXEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_CONTROL_REG, 0x4002);//select register data for MMD-Device Address 2h,Register 08hXEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x01ef);//defaultXEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, &phy_clk_delay_reg);XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_CONTROL_REG, 0x0002);//set up register address for MMD-Device Address 2hXEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x0005);//select register 05h for MMD-Device address 2hXEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_CONTROL_REG, 0x4002);//XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x0000);//-0.42ns//XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0xcccc);//+0.3nsXEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, 0x7777);//defaultXEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_MMD_ACCESS_ADDRESS_DATA_REG, &phy_rx_delay_reg);xil_printf("The clk delay register is:%x\r\n",phy_clk_delay_reg);xil_printf("The rx delay register is:%x\r\n",phy_rx_delay_reg);//Auto-negotiation Advertisement regXEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, &control);//reg 0x04control |= IEEE_ASYMMETRIC_PAUSE_MASK;//0x0800 流控control |= IEEE_PAUSE_MASK;//0x0400control |= ADVERTISE_100;control |= ADVERTISE_10;XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_AUTONEGO_ADVERTISE_REG, control);//1000Basic-T Control regXEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,&control);control |= ADVERTISE_1000;XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_1000_ADVERTISE_REG_OFFSET,control);//basic controlXEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);//reg 00control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE;  //bit12control |= IEEE_STAT_AUTONEGOTIATE_RESTART; //bit9XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);//basic controlXEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);control |= IEEE_CTRL_RESET_MASK;//software PHY reset,XEmacPs_PhyWrite(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, control);while (1) {XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_CONTROL_REG_OFFSET, &control);if (control & IEEE_CTRL_RESET_MASK)//this bit is self-cleared after a "1" is written to itcontinue;elsebreak;}XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);xil_printf("Waiting for PHY to complete autonegotiation.\r\n");while ( !(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE) ) {sleep(1);XEmacPs_PhyRead(xemacpsp, phy_addr,IEEE_COPPER_SPECIFIC_STATUS_REG_2,  &temp);xil_printf("Link Status is:%x \r\n",temp);timeout_counter++;if (timeout_counter == 30) {xil_printf("Auto negotiation error \r\n");return ;}XEmacPs_PhyRead(xemacpsp, phy_addr, IEEE_STATUS_REG_OFFSET, &status);}xil_printf("autonegotiation complete \r\n");XEmacPs_PhyRead(xemacpsp, phy_addr,0x1f,&status_speed);if ( (status_speed & 0x40) == 0x40)/* 1000Mbps */return 1000;else if ( (status_speed & 0x20) == 0x20)/* 100Mbps */return 100;else if ( (status_speed & 0x10) == 0x10)/* 10Mbps */return 10;elsereturn 0;return XST_SUCCESS;}

这里多了几个专门寄存器的设置,之后在获取速度的函数调用一下:

static u32_t get_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)
{u16_t phy_identity;u32_t RetStatus;XEmacPs_PhyRead(xemacpsp, phy_addr, PHY_IDENTIFIER_1_REG,&phy_identity);if (phy_identity == PHY_TI_IDENTIFIER) {RetStatus = get_TI_phy_speed(xemacpsp, phy_addr);} else if (phy_identity == PHY_REALTEK_IDENTIFIER) {RetStatus = get_Realtek_phy_speed(xemacpsp, phy_addr);} else {//RetStatus = get_Marvell_phy_speed(xemacpsp, phy_addr);RetStatus = get_KSZ_phy_speed(xemacpsp, phy_addr); //liwei}return RetStatus;
}

对扇区的读写用到下面的代码。

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xsdps.h"static XSdPs SdIn;#define EMMC1_SD0 0int sdio_init(int id )
{static int init = 0 ;if (init != 0) return 0  ; init = 1;XSdPs_Config *SdConfig;int Status;if(id==0) SdConfig = XSdPs_LookupConfig(XPAR_XSDPS_0_DEVICE_ID);else SdConfig = XSdPs_LookupConfig(XPAR_XSDPS_1_DEVICE_ID);if (NULL == SdConfig) {printf("error XSdPs_LookupConfig\r\n");return XST_FAILURE;}Status = XSdPs_CfgInitialize(&SdIn, SdConfig,SdConfig->BaseAddress);if (Status != XST_SUCCESS) {printf("error XSdPs_CfgInitialize\r\n");return XST_FAILURE;}Status = XSdPs_CardInitialize(&SdIn);if (Status != XST_SUCCESS) {printf("error XSdPs_CardInitialize\r\n");return XST_FAILURE;}
}void read_sector(uint32_t sector, uint8_t*buffer)
{sdio_init(0);int Status = XSdPs_ReadPolled(&SdIn, sector, 1,buffer);if(Status != XST_SUCCESS){printf("Error readingsector %d\r\n",sector);}
}void write_sector( uint32_t sector, const uint8_t *data)
{sdio_init(0);int Status = XSdPs_WritePolled(&SdIn, sector, 1, data);if(Status != XST_SUCCESS){printf("Error writingsector  %d\r\n",sector);}
}

关于EMMC或者SD卡扇区的读写,我同事小周试验成功并写过一篇blog[注4]。

以上硬件和板级支持软件都做好后就开始写正式的应用了。

我们再次梳理一下思路:

1,为了简单起见,我们使用TCP协议,这样就可以不必关心流的控制。

2,PC段负责读映像文件,不断地发送给X410。

3,X410每收到512就顺序写入扇区。

4,设置一个计数器,每写一个扇区就加一。这个计数器也需要清零。我们规定在最初连接上发发送一个32字节指示X410进行计数器清零,并要求得到X410的恢复确认。

思路很简单,我们看PC端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define SERVER_IP "192.168.5.136"  // 服务器 IP 地址
#define SERVER_PORT 7         // 服务器端口
#define BUFFER_SIZE 1024        // 缓冲区大小#include <stdio.h>
#include <stdlib.h>#define SECTOR_SIZE 512
#define N 1
//printf("sector=%d\r",sector++);unsigned char * get_one_block( int len  )
{static  unsigned  int  buff[1024 ] ;static  unsigned  int  sbuff[1024 ] = {0} ;static FILE *fp=NULL;/// = fopen(file_name, "rb");static  unsigned int sector = 0 ;if (fp==NULL) {fp = fopen("usrp_x4xx_fs.sdimg", "rb");if (fp == NULL) {perror("Error opening file");return NULL;}}size_t bytesRead;if  ((bytesRead = fread(buff, 1, len, fp)) > 0) {return  buff ;}elsereturn NULL ;
}int  rcv_blocking ( int this_sock )
{unsigned char b [2048] ;int bytes_received = recv(this_sock, b, 2047, 0);if (bytes_received < 0) {perror("Receive failed");return -1;}return 0 ;
}int main()
{int sock;struct sockaddr_in server_addr;char buffer[BUFFER_SIZE];// 创建 socketif ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("Socket creation failed");exit(EXIT_FAILURE);}// 设置服务器地址结构memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);// 将 IPv4 地址从文本转换为二进制if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {perror("Invalid address/ Address not supported");exit(EXIT_FAILURE);}// 连接到服务器if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("Connection failed");exit(EXIT_FAILURE);}// 发送消息long long int i,d=0,m=-1,sm=0;int  bytes_received ;unsigned char *p;static unsigned char b[1024] ;send(sock,  b,32, 0);  //  reset sector counterrcv_blocking(sock) ;for(;;) {for (i=0; i<N; ++i) {p=get_one_block(SECTOR_SIZE);if (p==NULL)  exit(1);send(sock,  p,SECTOR_SIZE, 0);}d+=N*512 ;m= d/(1024*1024*10) ;if (sm!=m)  {sm=m;printf(  "%dM \n",m*10);}//   rcv_blocking(sock) ;}close(sock);return 0;
}

这里设置了N本打算发送多块之后再集中写入EMMC后应答给PC,后来觉得TCP自动实现应答确认,上位机一味发就可以,没必要考虑流控。

X410上我们使用SDK的LWIP TCP ECHO 服务器修改过来的,只需要修改一下收到数据的回调函数。

err_t recv_callback(void *arg, struct tcp_pcb *tpcb,struct pbuf *p, err_t err)
{/* do not read the packet if we are not in ESTABLISHED state */if (!p) {tcp_close(tpcb);tcp_recv(tpcb, NULL);return ERR_OK;}/* indicate that the packet has been received */tcp_recved(tpcb, p->len);if ((p->len!=1024 ) )	printf("len=%d not  1024! \n",p->len );if (p->len==32 ){  sector = 0 ; printf("len=%d\r" , p->len); tcp_write(tpcb, p->payload, 4, 1);pbuf_free(p); return ERR_OK;}if (p->len>=512 )write_sector( sector ++ , p->payload   ) ;if ( p->len==1024)	write_sector( sector ++ , p->payload+512   ) ;
///	printf("sector=%d  \r" ,sector);pbuf_free(p);return ERR_OK;
}

连续发送512的数据包,我在这个回调函数里面收到第一个发送过来的512字节的包,周都是1024的包,是两个包组合在一起发送了。(这期间我尝试修改修改成别的发送长度连续发送,最终每包也是1024字节,这个没有继续实验,在这里提一下。)

注意调试时候保留了上述代码的printf,打印出正在写的扇区号。发现这个占用了时间拉低了烧写速度。我用的是100M的交换机,保留printf烧写速度是2Mb/s,注释掉烧写速度是6Mb/s .

这个实现烧写速度比较慢,16G的映像文件烧写实测用了5个小时的样子(100M局域网,如果用更1G网应该快一点,但是估计也有限,考虑主要消耗在TCP的对话导致通讯效率比较低)。烧写完毕后确实启动成了。如果要求更高速度可以使用UDP,但是那要自己设计对话实现流控制。

设置X410运行在JTAG模式的命令:

1,SCU的命令里先输入reboot关掉ps.此时power灯是黄色。

2,输入zynqmp bootmode jtag.

3,按一下开关键,此时power灯是绿色,表示PS部分已经启动并进入jtag模式。

4,如果要退出jtag模式,我使用的方法是SCU命令执行reboot.之后从先上电。

这里所说的SCU就是X410实现BIOS功能的STM32,官方叫SCU。具体可以看看我另外一篇BLOG[注3]。

by :李伟

通过网盘分享的文件:BRUN_EMMC_RJ45.rar
链接: https://pan.baidu.com/s/15ci9CLgbKOnkPM8MkOYdgg 提取码: 66u1 

注:

1,files.ettus.com:/binaries/cache/x4xx/ 内有EMMC映像的官方链接。

2,USRP X410/X440 Getting Started Guide - Ettus Knowledge Base X410上手介绍

3,X410启动过程串口的显示-CSDN博客 

4,TQRFSOC开发板47DR :EMMC和SD卡扇区读写_rfsoc47dr 开发语言-CSDN博客

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

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

相关文章

CTFSHOW-WEB入门-命令执行29-32

题目&#xff1a;web 29 题目&#xff1a;解题思路&#xff1a;分析代码&#xff1a; error_reporting(0); if(isset($_GET[c])){//get一个c的参数$c $_GET[c];//赋值给Cif(!preg_match("/flag/i", $c)){eval($c);//if C变量里面没有flag&#xff0c;那么就执行C…

探索AI(chatgpt、文心一言、kimi等)提示词的奥秘

大家好&#xff0c;我是老六哥&#xff0c;我正在共享使用AI提高工作效率的技巧。欢迎关注我&#xff0c;共同提高使用AI的技能&#xff0c;让AI成功你的个人助理。 "AI提示词究竟是什么&#xff1f;" 这是许多初学者在接触AI时的共同疑问。 "我阅读了大量关于…

【CS61A 2024秋】Python入门课,全过程记录P4(Week7 Generators开始,更新于2025/1/29)

文章目录 关于基本介绍&#x1f44b;新的问题更好的解决方案Week7Mon Generators阅读材料Lab 05: Iterators, MutabilityQ1: WWPD: List-MutationQ2: Insert Items 关于 个人博客&#xff0c;里面偶尔更新&#xff0c;最近比较忙。发一些总结的帖子和思考。 江湖有缘相见&…

使用 OpenResty 构建高效的动态图片水印代理服务20250127

使用 OpenResty 构建高效的动态图片水印代理服务 在当今数字化的时代&#xff0c;图片在各种业务场景中广泛应用。为了保护版权、统一品牌形象&#xff0c;动态图片水印功能显得尤为重要。然而&#xff0c;直接在后端服务中集成水印功能&#xff0c;往往会带来代码复杂度增加、…

【MySQL — 数据库增删改查操作】深入解析MySQL的 Update 和 Delete 操作

1. 测试数据 mysql> select* from exam1; ----------------------------------------- | id | name | Chinese | Math | English | ----------------------------------------- | 1 | 唐三藏 | 67.0 | 98.0 | 56.0 | | 2 | 孙悟空 | 87.0 | 78.…

webAPI -DOM 相关知识点总结(非常细)

title: WebAPI语法 date: 2025-01-28 12:00:00 tags:- 前端 categories:- 前端WEB API 了解DOM的结构并掌握其基本的操作&#xff0c;体验 DOM 在开发中的作用 API简介 就是使用js来操作html和浏览器 什么是DOM? 就是一个文档对象模型&#xff0c;是用来呈现预计于任意htm…

图论——最小生成树的扩展应用

最小生成树相关原理 acwing1146.新的开始 假设存在一个“超级发电站” 在每一个矿井修发电站相当于从这个“超级发电站”到各个矿井连一条长度为 v [ i ] v[i] v[i]的边。 这样一来这就是一个最短路的模板题。 #include <iostream> #include <cstring> using na…

K8S中高级存储之PV和PVC

高级存储 PV和PVC 由于kubernetes支持的存储系统有很多&#xff0c;要求客户全都掌握&#xff0c;显然不现实。为了能够屏蔽底层存储实现的细节&#xff0c;方便用户使用&#xff0c; kubernetes引入PV和PVC两种资源对象。 PV&#xff08;Persistent Volume&#xff09; PV是…

宝塔面板SSL加密访问设置教程

参考:https://www.bt.cn/bbs/thread-117246-1-1.html 如何快速使用证书加密访问面板 因早期默认未开启https访问所以没有相关的风险提醒&#xff0c;现面板默认已开启https加密访问、提升安全性 由于采用的是服务器内部本身签发证书&#xff0c;不被公网浏览器信任请参考以下步…

深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用

title: 深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用 date: 2025/1/26 updated: 2025/1/26 author: cmdragon excerpt: 在现代数据库管理系统中,索引技术是提高查询性能的重要手段。当数据量不断增长时,如何快速、有效地访问这些数据成为了数据库设计的核…

21.2-工程中添加FreeRTOS(掌握) 用STM32CubeMX添加FreeRTOS

这个是全网最详细的STM32项目教学视频。 第一篇在这里: 视频在这里 STM32智能小车V3-STM32入门教程-openmv与STM32循迹小车-stm32f103c8t6-电赛 嵌入式学习 PID控制算法 编码器电机 跟随 **V3:HAL库开发、手把手教学下面功能&#xff1a;PID速度控制、PID循迹、PID跟随、遥控、…

【越学学糊涂的Linux系统】Linux指令篇(二)

一、pwd指令&#xff1a; 00x0:打印该用户当前目录下所属的文件路径 看指令框可以看出我用的是一个叫sw的用户&#xff0c;我们的路径就是在一个home目录下的sw目录下的class113文件路径。 也可以说是指出当前所处的工作目录 补充&#xff1a;&#x1f386;​​​​​​​Wi…

LangGraph系列-1:用LangGraph构建简单聊天机器人

在快速发展的人工智能和大型语言模型&#xff08;llm&#xff09;世界中&#xff0c;开发人员不断寻求创建更灵活、更强大、更直观的人工智能代理的方法。 虽然LangChain已经改变了这个领域的游戏规则&#xff0c;允许创建复杂的链和代理&#xff0c;但对代理运行时的更复杂控制…

进程池的制作(linux进程间通信,匿名管道... ...)

目录 一、进程间通信的理解 1.为什么进程间要通信 2.如何进行通信 二、匿名管道 1.管道的理解 2.匿名管道的使用 3.管道的五种特性 4.管道的四种通信情况 5.管道缓冲区容量 三、进程池 1.进程池的理解 2.进程池的制作 四、源码 1.ProcessPool.hpp 2.Task.hpp 3…

Linux学习笔记——用户管理

一、用户管理命令 useradd #用户增加命令 usermod #用户修改命令 passwd #密码修改命令 userdel #用户删除命令 su #用户提权命令 1、useradd命令&#xff08;加用户&#xff09;&#xff1a; 创建并设置用户信息&#xff0c;使用us…

怎样在PPT中启用演讲者视图功能?

怎样在PPT中启用演讲者视图功能&#xff1f; 如果你曾经参加过重要的会议或者演讲&#xff0c;你就会知道&#xff0c;演讲者视图&#xff08;Presenter View&#xff09;对PPT展示至关重要。它不仅能帮助演讲者更好地掌控演讲节奏&#xff0c;还能提供额外的提示和支持&#…

【Unity3D】实现2D小地图效果

目录 一、玩家脚本Player 二、Canvas组件设置 三、小地图相关 四、GameLogicMap脚本修改 基于&#xff1a;【Unity3D】Tilemap俯视角像素游戏案例-CSDN博客 2D玩家添加Dotween移动DOPath效果&#xff0c;移动完成后进行刷新小地图&#xff08;小地图会顺便刷新大地图&…

四.3 Redis 五大数据类型/结构的详细说明/详细使用( hash 哈希表数据类型详解和使用)

四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09; 文章目录 四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09;2.hash 哈希表常用指令(详细讲解说明)2.1 hset …

C#通过3E帧SLMP/MC协议读写三菱FX5U/Q系列PLC数据案例

C#通过3E帧SLMP/MC协议读写三菱FX5U/Q系列PLC数据案例&#xff0c;仅做数据读写报文测试。附带自己整理的SLMP/MC通讯协议表。 SLMP以太网读写PLC数据20191206/.vs/WindowsFormsApp7/v15/.suo , 73216 SLMP以太网读写PLC数据20191206/SLMP与MC协议3E帧通讯协议表.xlsx , 10382…

【算法】经典博弈论问题——威佐夫博弈 python

目录 威佐夫博弈(Wythoff Game)【模板】 威佐夫博弈(Wythoff Game) 有两堆石子&#xff0c;数量任意&#xff0c;可以不同&#xff0c;游戏开始由两个人轮流取石子 游戏规定&#xff0c;每次有两种不同的取法 1)在任意的一堆中取走任意多的石子 2)可以在两堆中同时取走相同数量…