范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

GD32开发实战指南第4章GD32启动流程详解(Keil版)

  开发环境:
  MDK:Keil 5.30
  开发板:GD32F207I-EVAL
  MCU:GD32F207IK
  对于我们常用的桌面操作系统而言,我们在开发应用时,并不关心系统的初始化,绝大多数应用程序是在操作系统运行后才开始运行的,操作系统已经提供了一个合适的运行环境,然而对于嵌入式设备而言,在设备上电后,所有的一切都需要由开发者来设置,这里处理器是没有堆栈,没有中断,更没有外围设备,这些工作是需要软件来指定的,而且不同的CPU类型、不同大小的内存和不同种类的外设,其初始化工作都是不同的。本文将以GD32F207IK (基于Cortex-M3)为例进行讲解。
  在开始正式讲解之前,你需要了解ARM寄存器、汇编以及反编译相关的知识,这些可以参考笔者博文。
  深入理解ARM寄存器:https://bruceou.blog.csdn.net/article/details/117866186
  ARM汇编入门:https://bruceou.blog.csdn.net/article/details/117897496
  Keil反编译入门(一):https://bruceou.blog.csdn.net/article/details/118314875
  Keil反编译入门(二):https://bruceou.blog.csdn.net/article/details/118400368
  下面我们就来具体看一下用户从Flash启动GD32的过程,主要讲解从上电复位到main函数的过程。主要有以下步骤:
  1.初始化堆栈指针 SP=_initial_sp,初始化 PC 指针=Reset_Handler
  2.初始化中断向量表
  3.配置系统时钟
  4.调用 C 库函数_main 初始化用户堆栈,然后进入 main 函数。
  在开始讲解之前,我们需要了解GD32的启动模式。4.1 GD32的启动模式
  首先要讲一下GD32的启动模式,因为启动模式决定了向量表的位置,GD32有三种启动模式:
  1)主闪存存储器(Main Flash)启动:从GD32内置的Flash启动(0x0800 0000-0x0807 FFFF),一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。以0x08000000 对应的内存为例,则该块内存既可以通过0x00000000 操作也可以通过0x08000000 操作,且都是操作的同一块内存。
  2)系统存储器(System Memory)启动:从系统存储器启动(0x1FFFF000 - 0x1FFF F7FF),这种模式启动的程序功能是由厂家设置的。一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的ISP程序中,提供了串口下载程序的固件,可以通过这个ISP程序将用户程序下载到系统的Flash中。以0x1FFFFFF0对应的内存为例,则该块内存既可以通过0x00000000 操作也可以通过0x1FFFFFF0操作,且都是操作的同一块内存。
  3)片上SRAM启动:从内置SRAM启动(0x2000 0000-0x3FFFFFFF),既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。SRAM 只能通过0x20000000进行操作,与上述两者不同。从SRAM 启动时,需要在应用程序初始化代码中重新设置向量表的位置。
  用户可以通过设置BOOT0和BOOT1的引脚电平状态,来选择复位后的启动模式。如下图所示:
  启动模式只决定程序烧录的位置,加载完程序之后会有一个重映射(映射到0x00000000地址位置);真正产生复位信号的时候,CPU还是从开始位置执行。
  值得注意的是GD32上电复位以后,代码区都是从0x00000000开始的,三种启动模式只是将各自存储空间的地址映射到0x00000000中。
  Bootloader 存放在系统(System)存储内,可以在 MCU 启动后对 Flash 进行再编程。在GD32F20x 系列产品中,Bootloader 通过 USART0 与外界交互。
  GD32F20x芯片支持嵌入式引导程序通过多种接口方式来更新Flash。可以有1或2个USART端口和标准USB端口用于GD32F205xx和GD32F207xx互联型产品。如下表所示。
  产品线
  产品
  支持串行外设
  互联型
  GD32F205xx
  USART0(PA9 PA10)
  USART1(PD5 PD6)
  USB(PA9 PA10 PA11 PA12)
  GD32F207xx
  USART0(PA9 PA10)
  USART1(PD5 PD6)
  USB(PA9 PA10 PA11 PA12)
  4.2 GD32的启动文件分析
  因为启动过程主要是由汇编完成的,因此GD32的启动的大部分内容都是在启动文件里。笔者的启动文件是startup_gd32f20x_cl.s。4.2.1堆栈定义
  1. Stack栈
  栈的作用是用于局部变量,函数调用,函数形参等的开销,栈的大小不能超过内部SRAM 的大小。当程序较大时,需要修改栈的大小,不然可能会出现的HardFault的错误。
  第43行:表示开辟栈的大小为 0X00000400(1KB),EQU是伪指令,相当于C 中的 define。
  第45行:开辟一段可读可写数据空间,ARER 伪指令表示下面将开始定义一个代码段或者数据段。此处是定义数据段。ARER 后面的关键字表示这个段的属性。段名为STACK,可以任意命名;NOINIT 表示不初始化;READWRITE 表示可读可写,ALIGN=3,表示按照 8 字节对齐。
  第46行:SPACE 用于分配大小等于 Stack_Size连续内存空间,单位为字节。
  第47行: __initial_sp表示栈顶地址。栈是由高向低生长的。
  2.Heap堆
  堆主要用来动态内存的分配,像 malloc()函数申请的内存就在堆中。
  开辟堆的大小为 0X00000200(512 字节),名字为 HEAP,NOINIT 即不初始化,可读可写,8字节对齐。__heap_base 表示对的起始地址,__heap_limit 表示堆的结束地址。4.2.2向量表
  向量表是一个WORD( 32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。向量表在地址空间中的位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。在复位后,该寄存器的值为 0。因此,在地址 0 (即 FLASH 地址 0)处必须包含一张向量表,用于初始时的异常分配。
  值得注意的是这里有个另类:0号类型并不是什么入口地址,而是给出了复位后 MSP 的初值,后面会具体讲解。
  ……
  第66行:定义一块代码段,段名字是RESET,READONLY 表示只读。
  第67-69行:使用EXPORT将3个标识符申明为可被外部引用,声明 __Vectors、__Vectors_End 和__Vectors_Size 具有全局属性。这几个变量在Keil分散加载时会用到。
  第71行:__Vectors 表示向量表起始地址,DCD 表示分配 1 个 4 字节的空间。每行 DCD 都会生成一个 4 字节的二进制代码,中断向量表 存放的实际上是中断服务程序的入口地址。当异常(也即是中断事件)发生时,CPU 的中断系统会将相应的入口地址赋值给 PC 程序计数器,之后就开始执行中断服务程序。在60行之后,依次定义了中断服务程序的入口地址。
  第179行:__Vectors_End 为向量表结束地址。
  第181行:__Vectors_Size则是向量表的大小,向量表的大小是通过__Vectors 和__Vectors_End 相减得到的。
  上述向量表可以在《GD32F20x_User_Manual_EN_Rev2.4》中找到的,笔者这里只截取了部分。
  笔者只截取了部分。4.2.3复位程序
  复位程序是系统上电后执行的第一个程序,复位程序也是中断程序,只是这个程序比较特殊,因此单独提出来讲解。
  第186行:定义了一个服务程序,PROC表示程序的开始。
  第187行:使用EXPORT将Reset_Handler申明为可被外部引用,后面WEAK表示弱定义,如果外部文件定义了该标号则首先引用该标号,如果外部文件没有声明也不会出错。这里表示复位程序可以由用户在其他文件重新实现。
  第188-189行:表示该标号来自外部文件,SystemInit()是一个库函数,在system_gd32f10x.c中定义的,__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈,这个是由编译器完成的,该函数最终会调用我们自己写的main函数,从而进入C世界中。
  第190行:这是一条汇编指令,表示从存储器中加载SystemInit到一个寄存器R0的地址中。R0~R3 寄存器通常用于函数入参出参或子程序调用。
  第191行:汇编指令,表示跳转到寄存器R0的地址,并根据寄存器的 LSE 确定处理器的状态,还要把跳转前的下条指令地址保存到 LR。
  第192行:和190行是一个意思,表示从存储器中加载__main到一个寄存器R0的地址中。
  第193行:和191稍微不同,这里跳转到至指定寄存器的地址后,不会返回。
  第194行:和PROC是对应的,表示程序的结束。
  值得注意的是,这里的__main和C语言中的main()不是一样东西,__main是C lib中的函数,也就是在Keil中自带的;而main()函数是C的入口,main()会被__main调用。4.2.4中断服务程序
  我们平时要使用哪个中断,就需要编写相应的中断服务程序,只是启动文件把这些函数留出来了,但是内容都是空的,真正的中断复服务程序需要我们在外部的 C 文件里面重新实现,这里只是提前占了一个位置罢了。
  这部分没啥好说的,和服务程序类似的,只需要注意‘B .’语句,B表示跳转,这里跳转到一个‘.’,即表示无线循环。4.2.5堆栈初始化
  堆栈初始化是由一个IF条件来实现的,MICROLIB的定义与否决定了堆栈的初始化方式。
  这个定义是在Options->Target中设置的。
  如果没有定义__MICROLIB , 则会使用双段存储器模式,且声明了__user_initial_stackheap 具有全局属性,这需要开发者自己来初始化堆栈。
  这部分也没啥讲的,需要注意的是,ALIGN表示对指令或者数据存放的地址进行对齐,缺省表示4字节对齐。4.2.6其他
  第62行:PRESERVE8 用于指定当前文件的堆栈按照 8 字节对齐。
  第63行:THUMB 表示后面指令兼容 THUMB 指令。现在 Cortex-M 系列的都使用 THUMB-2 指令集,THUMB-2 是 32 位的,兼容 16 位和 32 位的指令,是 THUMB 的超集。4.3 GD32的启动流程实例分析
  有了前面的分析,接下来就来具体看看GD32启动流程的具体内容。4.3.1初始化SP、PC、向量表
  当系统复位后,处理器首先读取向量表中的前两个字(8 个字节),第一个字存入 MSP,第二个字为复位向量,也就是程序执行的起始地址。
  这里通过J-Flash打开hex文件。
  硬件这时自动从0x0800 0000位置处读取数据赋给栈指针SP,然后自动从0x0800 0004位置处读取数据赋给PC,完成了复位操作,SP= 0x0200 2008,PC = 0x0800 01BD。
  初始化SP、PC紧接着就初始化向量表,如果感觉看HEX文件抽象,我们看看反汇编文件吧。
  是不是更容易些,是不是和《GD32F20x_User_Manual_EN_Rev2.4》中的向量表对应起来了。其实看反汇编文件更好理解GD32的启动流程,只是有些抽象。
  生成反汇编的方法如下。
  在KEIL的User选项中,如下图添加这两项:
  fromelf --bin --output=GD32F207I_EVAL.bin ../Output/GD32F207I_EVAL.axf
  fromelf --text -a -c --output=GD32F207I_EVAL.dis ../Output/GD32F207I_EVAL.axf
  然后重新编译,即可得到二进制文件GD32F207I_EVAL.bin(以后会分析)、反汇编文件GD32F207I_EVAL.dis。
  如下图所示:
  4.3.2设置系统时钟
  细心的朋友可能发现,PC=0x080001BC的地址是没有对齐的。然后在反汇编文件中却是这样的:
  这里是硬件自动对齐到 0x080001BC,并执行SystemInit函数初始化系统时钟。
  当然也可通过硬件调试来确认上面的分析:
  接下来就会进入SystemInit函数中。
  SystemInit函数内容如下:/*!     brief      setup the micro-controller system, initialize the system     param[in]  none     param[out] none     retval     none */ void SystemInit(void) {     /* reset the RCC clock configuration to the default reset state */     /* enable IRC8M */     RCU_CTL |= RCU_CTL_IRC8MEN;      RCU_CFG0 &= ~RCU_CFG0_SCS;     /* reset HXTALEN, CKMEN, PLLEN bits */     RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN);      /* reset SCS, AHBPSC, APB1PSC, APB2PSC, ADCPSC, CKOUT0SEL bits */     RCU_CFG0 &= ~(RCU_CFG0_SCS | RCU_CFG0_AHBPSC | RCU_CFG0_APB1PSC | RCU_CFG0_APB2PSC |                   RCU_CFG0_ADCPSC | RCU_CFG0_ADCPSC_2 | RCU_CFG0_CKOUT0SEL);      /* reset HXTALEN, CKMEN, PLLEN bits */     RCU_CTL &= ~(RCU_CTL_HXTALEN | RCU_CTL_CKMEN | RCU_CTL_PLLEN);      /* Reset HXTALBPS bit */     RCU_CTL &= ~(RCU_CTL_HXTALBPS);      /* reset PLLSEL, PREDV0_LSB, PLLMF, USBFSPSC bits */     RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLMF |                   RCU_CFG0_USBFSPSC | RCU_CFG0_PLLMF_4);      /* reset PLL1EN and PLL2EN bits */     RCU_CTL &= ~(RCU_CTL_PLL1EN | RCU_CTL_PLL2EN);      /* reset CFG1 register */     RCU_CFG1 = 0x00000000U;      /* reset INT register */     RCU_INT = 0x00FF0000U;      /* reset CFG2 register */     RCU_CFG2 = 0x00000000U;      /* reset PLLTCTL register */     RCU_PLLTCTL &= (~RCU_PLLTCTL_PLLTEN);      /* reset PLLTINT register */     RCU_PLLTINT = 0x00400000U;      /* reset PLLTCFG register */     RCU_PLLTCFG = 0x20003010U;      /* configure the system clock source, PLL multiplier, AHB/APBx prescalers and flash settings */     system_clock_config(); }
  前面部分是配置时钟的,具体参考手册吧。
  system_clock_config()函数用于时钟初始化,这里使用的是HXTAL,HXTAL进过通过PLL锁相环后为120MHz。
  system_clock_config()函数调用的system_clock_120m_hxtal()函数,其如下:/*!     brief      configure the system clock to 120M by PLL which selects HXTAL(8M) as its clock source     param[in]  none     param[out] none     retval     none */ static void system_clock_120m_hxtal(void) {     uint32_t timeout = 0U;     uint32_t stab_flag = 0U;      /* enable HXTAL */     RCU_CTL |= RCU_CTL_HXTALEN;      /* wait until HXTAL is stable or the startup time is longer than HXTAL_STARTUP_TIMEOUT */     do {         timeout++;         stab_flag = (RCU_CTL & RCU_CTL_HXTALSTB);     } while((0U == stab_flag) && (HXTAL_STARTUP_TIMEOUT != timeout));      /* if fail */     if(0U == (RCU_CTL & RCU_CTL_HXTALSTB)) {         while(1) {         }     }      /* HXTAL is stable */     /* AHB = SYSCLK */     RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;     /* APB2 = AHB/1 */     RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;     /* APB1 = AHB/2 */     RCU_CFG0 |= RCU_APB1_CKAHB_DIV2;      /* CK_PLL = (CK_PREDIV0) * 10 = 120 MHz */     RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PREDV0_LSB | RCU_CFG0_PLLSEL);     RCU_CFG0 |= (RCU_PLLSRC_HXTAL | RCU_PLL_MUL10);      /* CK_PREDIV0 = (CK_HXTAL) / 5 * 12 /5 = 12 MHz */     RCU_CFG1 &= ~(RCU_CFG1_PREDV0SEL | RCU_CFG1_PLL1MF | RCU_CFG1_PREDV1 | RCU_CFG1_PREDV0);     RCU_CFG1 |= (RCU_PREDV0SRC_CKPLL1 | RCU_PLL1_MUL12 | RCU_PREDV1_DIV5 | RCU_PREDV0_DIV5);      /* enable PLL1 */     RCU_CTL |= RCU_CTL_PLL1EN;     /* wait till PLL1 is ready */     while((RCU_CTL & RCU_CTL_PLL1STB) == 0U) {     }      /* enable PLL */     RCU_CTL |= RCU_CTL_PLLEN;      /* wait until PLL is stable */     while(0U == (RCU_CTL & RCU_CTL_PLLSTB)) {     }      /* select PLL as system clock */     RCU_CFG0 &= ~RCU_CFG0_SCS;     RCU_CFG0 |= RCU_CKSYSSRC_PLL;      /* wait until PLL is selected as system clock */     while(0U == (RCU_CFG0 & RCU_SCSS_PLL)) {     } }4.3.3初始化堆栈并进入main
  执行指令LDR R0, =__main,然后就跳转到__main程序段运行,当然这里指标准库的__main函数。
  这中间初始化了栈区。
  这段代码是个循环(BCC 0x080001e6),实际运行时候循环了两次。第一次运行的时候,读取"加载数据段的函数"的地址并跳转到该函数处运行(注意加载已初始化数据段和未初始化数据段用的是同一个函数);第二次运行的时候,读取"初始化栈的函数"的地址并跳转到该函数处运行。
  最后就进入C文件的main函数中,至此,启动过程到此结束。
  最后,总结下GD32 从flash的启动流程。
  MCU上电后从0x0800 0000处读取栈顶地址并保存,然后从0x0800 0004读取中断向量表的起始地址,这就是复位程序的入口地址,接着跳转到复位程序入口处,初始向量表,然后设置时钟,设置堆栈,最后跳转到C空间的main函数,即进入用户程序。
  启动流程1(使用标准库,不使用Microlib)如下图:
  启动流程2(使用Microlib)如下图:

南昌超标电摩能上牌么?警惕!超标电摩说能上牌是骗人的!近段时间以来,南昌街头出现了越来越多的电动摩托车(下简称电摩)。令人奇怪的是,这些体积庞大声音轰鸣,外形酷似踏板摩托车且时速动辄在80码以上的电摩,坐月子不能吃水果?3种水果,催乳还能消脂,促进宝宝发育,是哪3种?坐月子期间不吃水果很容易便秘的,经常吃一些油腻高能量食物,又很少运动,所以非常容易消化不良,造成便秘,坐月子期间一定要伴有水果,有的水果对催乳有很好的效果,而且还可以消脂,所以坐月宝宝眼屎多又黄怎么办?宝宝眼屎多又黄怎么办?宝宝眼屎多又黄考虑为细菌性结膜炎,可滴抗菌眼药水抗感染治疗。宝宝眼屎多又黄,很可能是因为眼部不卫生,引起了细菌性结膜炎,家长可以用棉棒蘸取温开水生理盐水,将宝麒麟960和660比谁好?谢谢邀请。简单来说,骁龙660和麒麟960对比,理论上肯定是麒麟960要好一些,毕竟这也是华为上一代旗舰处理器。骁龙660与麒麟960区别对比从参数来看,海思麒麟960拥有8个核心有人说女生最好别在朋友圈炫耀,会遭到身边朋友嫉恨,别光看表面,对吗?女生要是在朋友圈里时常晒自已吃了什么什么的,穿的又多么的好,又在什么地方消费了一番,用的又是什么高级化妆品,或许也是出于无心,而总让别人感觉有点轻浮,是故意显摆弦耀自已活的多么的潇五十五岁了,想带着老伴去摩旅,ADV和太子哪个更合适?谢邀!我是骠骑府!资深摩托车媒体编辑,评论员!欢迎关注!55岁的年纪,想带着老伴摩行天下,领略祖国的大好河山,骠骑府先向题主致以敬意!不错,生活就该是这样的,这个年纪了,儿女也都大湖北省内所有高校中综合实力排名是怎样的?说实话湖北省重点院校数目真的不少,单单985院校就有两所,分别是武汉大学以及华中科技大学另外就是关于湖北省211院校的情况,一共有7所,分别是中南财经政法大学华中科技大学中国地质大大家身边有没有靠炒股发财的人?我讲两个我认识的人。一个是我的前同事。以前在证券公司做业务,这位女同事是坐柜台负责开户登记和资料整理,平时不太爱说话,因此虽然天天碰面,但基本是脸熟人不熟的状态。由于证券公司是这样双十一,天猫京东都超过三千亿,可我身边基本没人买,数据正常吗?8000亿除以14亿人口,人均差不多600元。这要包括所有的老人小孩和其它低收入人群。而高收入人群又有多少愿意在线上购买可能是假货的高单价物品?群东西卖给谁了?反正我家别说人均60如何获得清华领军,北大博雅计划资格或加分?几不像的东西!学西方,没人家的人文环境。想自创,全国的基数太大,不可能做到相对公平。国民脑袋里满是封建残余,就连一些老师和校领导的境界也不够,这人才选拔变成了命运淘汰,老是出不了多戏剧中,在艺术水平上有能超越样板戏的吗?重赏之下必有勇夫,精雕之下必有精品。暂时撇开学术,纯从一种作品细粗来说,样板戏曾经众人之下集中编剧,做到逐字逐句唱腔凝炼,焉能不精益求精?如今有哪些作品会享受如此待遇?众人捧柴火焰
按照这3招改造您的早餐,一天血糖更稳定很多糖友午餐后晚餐后血糖控制得很好,早餐后血糖却很难稳定,即使降下来也比午餐后和晚餐后血糖高。您知道这是为什么吗?如何调整更有效吗?为什么早餐后血糖比午餐后晚餐后要高1。早餐进食不心静神平体安内经云阴平阳秘,精神乃治,两虚相得,乃克其形。主要是从形的角度论述了如何才能保持健康和生病的先决条件是什么。对于神的描述,现在多指精神意识思维活动,知道了基本概念,更要明白调神御神吃什么养肝如果早上起来后发现有黑色素沉着,疲惫不堪,口臭,眼睛干涩等。那就一定要注意养肝了,人的肝脏一旦受损,就容易中毒或感染疾病,今天给大家推荐8种最佳的养肝水果。1无花果含有机酸和多种酶脾虚的人如何健脾?李医生教你补脾五法,学会!补脾不迷路大家好,我是李医生!中医养生中,脾胃有很高的地位,原因无他,只因为脾胃是后天之本,气血生化之源。但是如果说脾胃虚弱了,气血生化无源,那自然也就无法濡养其他脏腑了,就会出现一系列身体21,米兰险胜!临危不惧的皮奥利,硬气的下半场,吉鲁制胜球头条创作挑战赛与桑普多利亚的比赛,皮奥利做出两处调整,克亚尔替换托莫里,波贝加替换本纳赛尔,两人得到了首发的机会。其余位置上都是老熟人,吉鲁领衔攻击群,身后是CDK莱奥梅西亚斯迈尼60!欧冠5冠王踢疯了开季6场大胜3线高歌,新援连入9球巴萨因为疫情和财务影响,沉寂了两年时间,持续在西甲和欧冠表现低迷。2022年夏天,巴萨终于重回最佳状态,找回了欧冠5冠王的实力,阵容配置和即战力全部追上了皇马,再次与皇马在西甲形成最大的前途就是没有前途?京张奥运会之后,张家口到底路在何方?一场15天的奥运会不足以成就一座城市,尤其是张家口。作为一年刮两次风,一次刮半年的城市,张家口给人的第一印象就是空气质量好,好到那些年,冀中南甚至北京的冬天都阴霾密布监测爆表时,张丢人,男足差点输给缅甸范大将军真的是一位神奇的预言家,若干年前有曾语言先输越南菲律宾,再输缅甸。结果在刚刚过去的U19比赛中,差一点就输给了缅甸。真的是脸都不要了。中国足球真的是一年不如一年,一届不如一身体乏力,五脏亏虚?五种中成药,补足心肝脾肺肾之气大家好,我是贾医生,如果你总是觉得自己身体很乏力,做什么都提不起精神,没有力气,那么你就要注意了,千万不要忽视,这种情况多见是由于我们的五脏亏虚所致,今天贾医生就来给大家分享五种中秋败丝瓜胜良药!入秋后多吃丝瓜,分享5种做法,润秋燥特养人俗话说秋败茄子似毒药,秋败丝瓜胜良药,丝瓜一直被称为美人瓜,汁水多且美白,非常适合现在的秋燥天食用。孔子曰不时,不食,我们应该顺应四季和节气调整饮食,秋高气爽的天气里,大家可以多吃18天鏖战!赛前,谁能想到他们会夺冠?北京时间9月19日,2022年男篮欧锦赛的来到了最后决赛的争夺。在决赛当中,赛前不被看好的西班牙一路过关斩将,没有了加索尔兄弟,没有了卢比奥能打出这样的成绩让人十分惊艳,而他们的对