donatello1996

  • 2021-02-19
  • 发表了主题帖: 【Perf-V评测】流水灯&Verilog基本语法

    本帖最后由 donatello1996 于 2021-2-19 20:21 编辑        春节放假在家有空的时候学习了一下VIVADO开发环境的基本使用和Verilog语言基本语法,用着学到的知识写了个简单的流水灯,颇有收获。先说下VIVADO环境,要在这个环境下写流水灯代码,需要做、 -源码编写 -run synthesis(综合)  -run implementation(执行/落实) -implementation design指定硬件管脚 -生成bit文件 -烧录bit文件到DDR中或FLASH中 五步,其中run implementation依赖run synthesis步骤,生成bit文件依赖run implementation步骤,还有就是将FPGA芯片的硬件管脚和源码做绑定在run implementation步骤的implementation design功能中完成,因为源码里面所有的输入输出变量都没有绑定任何管脚。如果源码中的输入输出变量发生变化(变量名/变量个数发生变化),则需要重新运行上述所有步骤;如果只是想重新指定若干硬件管脚而输入输出变量没变的话,则不需要重新运行run synthesis步骤。先来看看源码编写部分: module led(     input sw1,     input sw2,     input sw3,     input sw4,     input clk,     output D0,     output D1,     output D2,     output D3,     output reg[8:0] D456     );     assign D0 = sw1;     assign D1 = sw2;     assign D2 = sw3;     assign D3 = sw4;     parameter T = 10000000;     reg [31:0] cnt;     reg [31:0] state;     reg [8:0] led_bits [8:0];     integer flag = 1;     always @ (posedge clk)     begin         if(flag == 1)         begin             flag <= 0;             state <= 0;             led_bits[0] <= 9'b111111110;             led_bits[1] <= 9'b111111101;             led_bits[2] <= 9'b111111011;             led_bits[3] <= 9'b111110111;             led_bits[4] <= 9'b111101111;             led_bits[5] <= 9'b111011111;             led_bits[6] <= 9'b110111111;             led_bits[7] <= 9'b101111111;             led_bits[8] <= 9'b011111111;         end         cnt <= cnt + 1;         if(cnt == T)         begin             cnt <= 0;             D456 <= led_bits[state];             state <= state + 1;             if(state == 10)             begin                 state <= 0;             end         end     end endmodule 一行一行进行解读,生怕忽略掉细节:   -begin/end类似C语言的大括号{},为一段包含性代码;   -module led(...);写法类似C语言的main函数,是Verilog语言的主体执行部分,小括号内是对于输入输出变量的声明,小括号后要加分号,module写完之后要用endmodule结尾,类似C语言函数结束的}右大括号 module里面一般写input输入变量和output输出变量,如果没有reg或者中括号[8:0]关键字,则默认为bool类型变量,变量值为1或0,比如input sw1就是一位宽输入变量sw1,output D0就是一位宽输出变量D0,output reg[8:0] D456就是9位宽输出变量D456; -assign D0 = sw1;这行语句充分说明了Verilog语言是描述性语言而不是过程性语言,这行语句说明了输出变量D0的值完全要与输入变量sw1的值相等,只要FPGA开始上电运行代码,D0的状态就必须跟随sw1状态,与执行时间无关,不受其它过程性因素和其它变量的影响;   -parameter T = 10000000; T是一个值为10000000的常量,不能在源码的其它地方被修改,类似C语言const;   -reg [31:0] cnt;cnt是一个位宽为32位的变量,类似C语言的int cnt,reg变量位宽可自由指定;   -reg [8:0] led_bits [8:0];led_bits是一个成员个数为9,成员位宽为9的一维数组,reg关键字后是成员个数,变量名后是位宽,reg数组位宽可自由指定;   -integer flag = 1;用法与C语言int flag = 1;完全相同;   -always @ (posedge clk)是循环语法,类似while(),其中变量posedge clk的意思是clk输入变量/输入信号的上升沿,整句语句的意思是clk输入信号每产生一个上升沿,则always内部语句执行一次,从这里可以看出来,Verilg的循环语法完全可以由硬件信号进行控制,而C语言中while(1)/while(i++)等语句由CPU机器周期进行控制,虽然机器周期和硬件信号有特定代数关系,但是还是有本质区别的。这里还说明一点,FPGA芯片可以接入一个或多个时钟源,至少接入一个,不同的always语句可以由不同频率时钟源进行控制,在always语句内部通过计数器溢出方式对接入时钟源进行分频,代码非常简单粗暴,看到这我想起了STM32单片机自带的TIMER定时器,但Verilog语句明显比TIMER直接多了;   -        if(flag == 1)         begin             flag <= 0;             ...         end 这个是上电之后执行一次的语句,因为Verilg语法规定所有变量赋值语句必须在实体模块中执行,always语句就是最常用的实体模块;   -上电后对led_bits数组的每一个成员进行赋值,从111111110依次到011111111按序赋值,也就是流水灯的效果;   -D456 <= led_bits[state];将数组的值赋给D456输出变量,D456刚好就是一个9位的变量,对应板子上的三个RGBLED灯。   代码写好,检查无语法错误,开始进行run synthesis和run implementation,快捷键F11:   然后指定硬件管脚,这里指定D456输出变量的管脚为开发板实际RGBLED灯管脚,一共九个管脚,依次配置,除此之外还有D0~D3,sw1~sw4对应的四盏红色LED灯和四个开关:   配置完毕之后生成bit文件,点击上方工具栏按钮即可,注意生成bit文件的时候有几率会报错,提示Error(s) found during DRC. Bitgen not run,解决方法参考: https://blog.csdn.net/weixin_43065256/article/details/86557378即写一个name.tcl脚本文件,内容为: set_property SEVERITY {Warning} [get_drc_checks NSTD-1] set_property SEVERITY {Warning} [get_drc_checks RTSTAT-1] set_property SEVERITY {Warning} [get_drc_checks UCIO-1] 将脚本指定到Generate Bitstram功能的右键菜单Settings设置中,在tcl.pre一栏填写刚刚指定的name.tcl文件: 然后重新生成bit文件不会有任何问题。 这个应该算XILINX官方的bug,后来官方论坛也对此做出反应: https://forums.xilinx.com/t5/Vivado-TCL-Community/Error-during-Bitgen-Vivado-12-1345-Error-s-found-during-DRC/td-p/333171   bit文件生成完毕就是下载文件到板子的DDR上面了,直接用Hardware Manager功能即可。 下到板子上,板子的RGBLED会有流水灯效果: 拨动开关会有对应红灯亮灭:   如果要下载到SPI FLASH上,则还会遇到第二个问题:ERROR: [Writecfgmem 68-20] SPI_BUSWIDTH property is set to "1" on bitfile 这个方法也好解决,在下方终端中输入 set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design] 指令,重新生成bit文件并烧录MCS文件即可。   除此之外,VIVADO环境还有一个RTL功能可以让开发者参考,以验证代码书写,器件搭建是否有误: 最后,VIVADO2020运行过程中有可能会出现与2018版本驱动不兼容的情况,可以用VIVADO路径下data\xicom\cable_drivers\nt64\digilent的路径下的驱动:

  • 2021-02-15
  • 回复了主题帖: 【Perf-V评测】点灯实验

    你哪里是小菜鸟,Verilog我看都看不懂呢,啥子玩意噢

  • 2021-02-14
  • 发表了主题帖: 【Perf-V评测】上电&固件烧录

          板子拿到手快一个月了,最近春节放假有时间把玩一下,不过烧录这板子的固件还是稍微花了点时间的。        澎峰科技开发板主控为XILINX Artix7系列的XC7A35T,属于低端产品线(Artix7)的低端型号(35T): X——        XILINX C——        纯FPGA,不带ARM内核 7A——      Artix7产品线,往上有K7和V7,以及带ARM内核的MPSOC系列 35T——    35T为低端型号,往上有50T和100T,其中100T是核心裸露的高端型号   另外,从澎峰科技公司信息中了解到贵司同时出品了100T型号的开发板,很是期待。澎峰科技XC7A35T开发板整体简约整洁,除去主控外,有Arduino GPIO扩展接口/四个拨码开关/四个按键/三个RGB灯,JTAG和USER_JTAG两组十针烧录接口,还有用于存储固件的SPI FLASH,运行程序的DDR3存储颗粒,以及背面用于连接HDMI视频输出扩展板的FPC排母,澎峰科技将HDMI视频输出做成扩展板的用意,除了商业上的考虑,更多是考虑了35T低端型号外接视频接口的项目效果并不理想。        开发板拿到手之后我发现并没有开发板并没有蜂鸟软核固件,是不可以对软核进行编程的,要进行软核编程必须先下载XILINX VIVADO开发环境将固件固化到开发板DDR内存中或SPI FLASH中,彭峰科技厂商提供的说明文档要求使用VIVADO 2018版本,但我从网上下载最新的2020.2版本也是没有任何问题的,可以进行固件烧录。(注意:下载VIVADO软件需要注册XILINX官网账号)使用VIVADO打开厂商提供的工程文件,即一个名为35T的压缩包,工程是VIVADO2018版本的,使用2020.2版本打开会提示向上升级兼容性: 然后使用左下角的Open Hardware Manager功能连接开发板,开发板要通过JTAG接口连接仿真器,仿真器再连接USB电脑,能成功识别板子的主控XC7A35T: 将工程文件夹内的project_1.runs\impl_1的system.bit二进制文件烧录到板子的DDR3内存内,这个文件就是蜂鸟软核,也就是开发固件,由于是烧录到DDR3内存中,所以板子掉电固件会丢失,要永久固化固件到SPI FLASH中并开机启动需要使用FLASH固件下载工具。 在VIVADO开发环境中,支持烧录到FLASH的固件后缀名为MCS,可以用工具生成,烧录地址为0,接口为SPIX4,大小为128MB: 并且2020.2版本更为先进,能直接识别与主控连接的FLASH颗粒,右键烧录MCS文件,等待烧录完毕(一分钟左右): 固件烧录完毕之后就可以使用彭峰科技厂商的Windows IDE开发环境或者Ubuntu虚拟机进行基于软核的编程了,我选择后者虚拟机,因为他们提供的Windows IDE是基于Eclipse定制的,我最讨厌的就是Eclipse系开发环境,没有之一,而用Linux虚拟机有个好处就是可以直接用VSCode进行代码编辑,非常方便。使用VMWARE软件打开彭峰科技提供的虚拟机,终端进入/home/a/Desktop/fengniao/e200_opensource/Perf-V-e-sdk目录,使用 make software PROGRAM=demo_gpio BOARD=Perf-V-creative-board 指令进行编译,使用 make upload PROGRAM=demo_gpio BOARD=Perf-V-creative-board 进行基于软核的代码上传,注意开发板与JTAG仿真器的连接要用USER_JTAG接口: 如果板子上没有蜂鸟软核,这一步烧录会提示JTAG仿真器无法找到目标设备,即: 烧录成功之后开发板的LED灯和RGBLED灯会进行规律闪烁。

  • 2021-01-15
  • 回复了主题帖: 搞事情预警~~~~啦啦啦~快进帖前排了解

    米尔FZ5

  • 2021-01-12
  • 回复了主题帖: 测评入围名单:免费评测澎峰Artix 7 FPGA 开发套件

    个人信息无误,确认可以完成评测计划。

  • 2020-09-08
  • 回复了主题帖: 【STM32F769Discovery开发板试用】便捷的SD卡读取BMP文件&QSPI读写&SDRAM读写

    freebsder 发表于 2020-9-8 16:03 图片不错。搞了这么多事情,楼主算是把F769摸透了
    一个月之前的帖子...

  • 回复了主题帖: 【STM32F769Discovery开发板试用】便捷的SD卡读取BMP文件&QSPI读写&SDRAM读写

    littleshrimp 发表于 2020-9-8 09:25 这个板子的屏幕不错,H7好像也是这块屏,看你的代码简单改一下,弄成TF卡加载JPG文件就可以实现一个电子相 ...
    读取JPG文件之前有帖子写了,并不顺利

  • 2020-08-11
  • 回复了主题帖: 【STM32F769Discovery开发板试用】串口空闲中断不定长接收&PWM输出方波

    freebsder 发表于 2020-8-11 20:54 挺好啊,写的很详细,幸好我转手了,要不和你比就相形见绌了。
    谢谢道长,道长过奖了

  • 回复了主题帖: 【STM32F769Discovery开发板试用】串口空闲中断不定长接收&PWM输出方波

    freebsder 发表于 2020-8-10 18:59 你这是想搞到100分啊?
    哈哈道长见笑了,反正这段时间都有在玩这板,写一下心得罢了

  • 2020-08-10
  • 回复了主题帖: 【STM32F769Discovery开发板试用】串口空闲中断不定长接收&PWM输出方波

    初步推测,能输出更高频率PWM方波跟定时器本身有关系,试下换TIM2 TIM3这些更常用的定时器,看看能能不能输出更高频率的方波,做这个PWM实验的一大难题是开发板引出的GPIO引脚极其稀少,大部分都被FMC和LTDC两个接口的外设占用掉了,Arduino接口上引出的那些引脚每一个都是非常宝贵的资源。

  • 回复了主题帖: 又有板子开箱!这回是兆易GD32307E-START开发板

    接口啥的不介意,反正搞嵌入式的是不允许出现缺数据线的情况的,比较介意的地方是外设太少了,光秃秃一块核心板,不说液晶屏SDRAM那些,至少传感器焊几粒啊,真的没兴趣

  • 回复了主题帖: 【STM32F769Discovery开发板试用】能用但并不好用的TCP服务器通信代码简单尝试

    freebsder 发表于 2020-8-6 22:16 啥问题?简单说。。。
    使用echo方式测试TCP,第一,接收TCP报文的次数有限,接收了几条之后就哑火了,没法接续接收了,第二,TCP服务器在接入一段时间之后自动断开了

  • 发表了主题帖: 【STM32F769Discovery开发板试用】串口空闲中断不定长接收&PWM输出方波

    本帖最后由 donatello1996 于 2020-8-10 12:01 编辑        串口空闲中断和PWM方波输出都是实际产品项目中非常常用的功能,串口空闲中断的好处在于可以使用不轮询方式,不占用CPU资源的前提下进行不定长串口字符串接收,因为触发方式是使用中断。使用空闲中断实现不定长接收的方式非常简单,只需要两点,一个是开启空闲中断,一个是开启接收DMA,这边我使用开发板的Arduino接口上面的串口6(USART6)来进行: 接收DMA初始化使用CubeMX自动一键生成: UART_HandleTypeDef huart6; DMA_HandleTypeDef hdma_usart6_rx; void UART6_Init(int baud) { __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_USART6_CLK_ENABLE(); __HAL_RCC_DMA2_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF8_USART6; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); huart6.Instance = USART6; huart6.Init.BaudRate = baud; huart6.Init.WordLength = UART_WORDLENGTH_8B; huart6.Init.StopBits = UART_STOPBITS_1; huart6.Init.Parity = UART_PARITY_NONE; huart6.Init.Mode = UART_MODE_TX_RX; huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart6.Init.OverSampling = UART_OVERSAMPLING_16; huart6.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; huart6.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; HAL_UART_Init(&huart6); hdma_usart6_rx.Instance = DMA2_Stream1; hdma_usart6_rx.Init.Channel = DMA_CHANNEL_5; hdma_usart6_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart6_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart6_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart6_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart6_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart6_rx.Init.Mode = DMA_CIRCULAR; hdma_usart6_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart6_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&hdma_usart6_rx); __HAL_LINKDMA(&huart6,hdmarx,hdma_usart6_rx); __HAL_UART_ENABLE_IT(&huart6,UART_IT_IDLE); HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn); HAL_NVIC_SetPriority(USART6_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART6_IRQn); } void USART6_IRQHandler() { int temp; if(__HAL_UART_GET_FLAG(&huart6, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart6); HAL_UART_DMAStop(&huart6); temp=__HAL_DMA_GET_COUNTER(&hdma_usart6_rx); rx6_len=BUFFERSIZE-temp; uart6_recv_end_flag=1; } } 主循环中使用轮询处理代码处理空闲中断中接收到的数据,这里是只有触发了空闲中断才会处理数据并重新开启DMA,所以是不会占用CPU轮询资源的: #define BUFFERSIZE 255 unsigned char rx6_buf[BUFFERSIZE],rx6_len=0,uart6_recv_end_flag; void UART6_DMA_Get() { if(uart6_recv_end_flag==1) { uart6_recv_end_flag=0; printf("%s %d\n",rx6_buf,rx6_len); HAL_UART_Receive_DMA(&huart6, (unsigned char*)rx6_buf, BUFFERSIZE); } } 将串口6接入一个定时打印数值的传感器,就可以采集数据了,不过值得一提的是,串口空闲中断本质上仍是中断,如果空闲中断触发次数过于频繁,会严重影响CPU正常轮询工作,所以选用的串口传感器不能是每一帧发送间隔太短,频繁触发空闲中断的传感器。 然后是PWM,这里我使用Arduino接口的PF6即TIM10的通道1,可以直接参考原子代码来修改进行初始化: TIM_HandleTypeDef TIM10_Handler; TIM_OC_InitTypeDef TIM10_CH1Handler; void TIM10_PWM_Init(int arr,int psc) { __HAL_RCC_TIM10_CLK_ENABLE(); __HAL_RCC_GPIOF_CLK_ENABLE(); GPIO_InitTypeDef GPIO_Initure; GPIO_Initure.Pin=GPIO_PIN_6; GPIO_Initure.Mode=GPIO_MODE_AF_PP; GPIO_Initure.Pull=GPIO_PULLUP; GPIO_Initure.Speed=GPIO_SPEED_HIGH; GPIO_Initure.Alternate=GPIO_AF3_TIM10; HAL_GPIO_Init(GPIOF,&GPIO_Initure); TIM10_Handler.Instance=TIM10; TIM10_Handler.Init.Prescaler=psc; TIM10_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; TIM10_Handler.Init.Period=arr; TIM10_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&TIM10_Handler); TIM10_CH1Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1 TIM10_CH1Handler.Pulse=arr/2; //设置比较值,此值用来确定占空比, //默认比较值为自动重装载值的一半,即占空比为50% TIM10_CH1Handler.OCPolarity=TIM_OCPOLARITY_LOW; HAL_TIM_PWM_ConfigChannel(&TIM10_Handler,&TIM10_CH1Handler,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&TIM10_Handler,TIM_CHANNEL_1); } void TIM_SetTIM10Compare1(int compare) { TIM10->CCR1=compare; } 输出效果: 这里可以提一下TIM10的两个初始化参数,一个是重装载值,一个是分频值,重装载值的作用是设置一个PWM比较阈值,比如256,在0~256范围内占空比是0%~100%变化,而分频值就是PWM输出方波的频率,分频值越小,PWM频率越高,输出一个方波周期的时间就越短,这里我设置TIM10分频值为1,PWM波的输出间隔达到了1us级别,即1MHz,不过这样仍然算是频率较低的,之后我再继续探讨看看怎么输出更高频率的方波。 其它参数中,向上计数模式即为增量模式,即TIM10的计数器越计越多,模式为PWM1即占空比数值越大,一个周期内的低电平时间越长。

  • 2020-08-06
  • 回复了主题帖: 【STM32F769Discovery开发板试用】USB-HS HID收发简单测评

    freebsder 发表于 2020-8-6 22:02 深挖的有点意思。
    道长见笑了

  • 回复了主题帖: 【STM32F769Discovery开发板试用】USB-HS HID收发简单测评

    littleshrimp 发表于 2020-8-6 19:54 USBD_CUSTOM_HID_SendReport函数有返回值 你试一下,判断返回值为USBD_OK时再操作GPIO 测量一下波 ...
    好的,这周我有空试试,我也想知道F769的USBHID性能如何

  • 回复了主题帖: 【STM32F769Discovery开发板试用】USB-HS HID收发简单测评

    littleshrimp 发表于 2020-8-6 17:37 单从USB2.0的参数看,最大480Mbps即使不考虑协议最大速率不会超过60MB/S 你再去看USB3320的数据手册最大 ...
    有图有真相,令人信服,点赞!既然官方手册都说了不超过60MB/s的话,那就肯定不会超过,但具体应该是多少数值我得写个上位机验证一下,这点是必须要实事求是的。我跟楼上那位争论的重点在于,不应该把USB时钟和每秒发送数据量混在一起讨论,我认为这是两个完全不同的概念,发送数据量是跟USB时钟有很小的关联,但绝对不是简单的1对1关系,F769主控的USB控制器从USB时钟源获取工作时钟,在规定的周期内从缓冲区把一帧数据发出,那么一秒发送的数据量具体有多少,跟一帧数据大小和占用时钟周期有关系,这是一个二元关系,不是简单的1对1关系

  • 加入了学习《TouchGFX 设计》,观看 sylar^z的TouchGFX作品

  • 2020-08-05
  • 回复了主题帖: 【STM32F769Discovery开发板试用】USB-HS HID收发简单测评

    cruelfox 发表于 2020-8-4 21:28 你那只是函数返回而已,数据并没有发送出去。 HID报文是Interrupt Transfer类型的,只有主机polling的 ...
    数据有没有成功发送出去,需要电脑上位机那边接收并做计数,不是STM32这边需要关心的问题,先不说polling到底间隔多长,反正成功发送多少数据量肯定跟60MHz的时钟没有半毛钱关系。

  • 2020-08-04
  • 回复了主题帖: 【STM32F769Discovery开发板试用】USB-HS HID收发简单测评

    cruelfox 发表于 2020-8-4 17:49 扯了,你这么用示波器抓只是判断 USBD_CUSTOM_HID_SendReport(&hUsbDeviceHS,buf,sizeof(buf)); 这 ...
    时钟60MHz跟可以发送的数据量速率128MB/s有什么关系呢?60MHz只不过是USB时钟电平的频率,一秒钟内时钟可以翻转60M次,只要在时钟翻转一次电平的时候CPU往USBHS缓冲区送入一次64字节的数据包就代表发送成功了,接收端能否正常,准确无误接收这些数据包跟发送端有啥关系呢?

  • 回复了主题帖: 27家运营商“弃用华为”!美国公布“5G干净网络”名单

    搞协议栈的我默默路过。。。看到几个熟悉的牌子,中华电信是香港应科院的,台湾之星没听说过,至于KT和SKT就比较熟悉了,都是辣鸡韩棒子的牌子,反正我对SKT没啥好印象,LOL季中赛一选亮老鼠,biss!

学过的课程

最近访客

< 1/6 >

统计信息

已有215人来访过

  • 芯币:752
  • 好友:1
  • 主题:50
  • 回复:81
  • 课时:--
  • 资源:--

留言

你需要登录后才可以留言 登录 | 注册


早晨五点 2018-7-13
在吗哥,我想请教您一个问题。我用HAL库的接收中断接收数据为什么接收到的数据总是变换次序?比如第2个数据变成第一个,第三个编程第二个。。。我应该怎么办呢?希望您能帮助我一下
查看全部