查看: 5832|回复: 7

IAR平台下实时系统ThreadX在GD32F207Z下的移植

[复制链接]

该用户从未签到

发表于 2016-2-14 23:19:29 | 显示全部楼层 |阅读模式
分享到:
本帖最后由 sellen-314350 于 2016-2-22 09:44 编辑

一、方案名称:  
IAR平台下实时系统ThreadX在GD32F207Z下的移植

二、方案介绍
GD32F207ZE是基于ARM Cortex-M3内核的32位通用MCU,主频为120MHz,是兆易创新推出的MCU中较高端的系列。
本测试方案,为了特别体现F207高速的特点,专门在IAR平台上移植了Express Logic公司的实时操作系统ThreadX。
这是一款已经部署在超过20亿数量终端的嵌入式系统,成熟可靠,稳定快速,当然了,价格同时也是极其昂贵的。
在测试方案中,通过建立8个不同优先级的线程进行切换,足够体现F207高速稳定的优点,其中线程1是优先权最低的,它是传递信号的发出者。
另外,也在测试方案中纳入了常见的按钮中断管理功能。
最后,方案最后通过大话演绎形式,对F207进行了超频试验,成功超频至192MHz,证明该MCU具备很大的超频性能。
整篇方案以实验纪事方式进行,语言表达追求诙谐轻松,思路导向追求散漫飞翔,值得初学者细读学之。

三、方案结构框图
2016-02-21_21-48-42.jpg


四、设计应用描述
1、强大的120MHz时钟的选择
我的任务是移植嵌入式商业实时操作系统,因为在QQ群里面对120MHz这么高的频率产生疑问,被几位大牛好好取笑了一大番,因此我决心努力学习下这个功能,因此在这里要特别体现出GD32F207Z的强大超频性质。
好,闲话少说,我们先来选择下时钟,论坛很多例子,都是利用了GD32F207Z内置的8MHz的HSI来驱动,是个在要求精度不是特别苛刻情况下非常经济节约的一个方法。我看到GD给的demo板子,很大方的外挂了一个25MHz的晶振,所以我想考虑下用HSE来做驱动时钟……好,先探索下最高频率120MHz如何得到先,从官方手册里面,我们看一下以下的时钟图,如图所示,红色部分的路线,不可能得到我们需要的结果,绿色部分的可以也,如图暂且先找到3个简单的组合线路,我的例子是选择了最下面一条线路,虽然复杂了那么一点点,在此以学习为主啊!
2016-02-15_21-14-24.jpg

时钟设置的代码如下:
  1. void RCC_Configuration120MHz(void){
  2.   TypeState HSEStartUpStatus;
  3.   /* Enable HSE */
  4.   RCC_HSEConfig(RCC_HSE_ON);
  5.   
  6.   /* Wait till HSE ready */
  7.   HSEStartUpStatus = RCC_WaitForHSEStartUp();
  8.   if(HSEStartUpStatus == SUCCESS){
  9.     /* CK_AHB = (CK_SYS / 1) = CK_SYS */
  10.     RCC_AHBConfig(RCC_SYSCLK_DIV1);
  11.    
  12.     /* CK_APB2 = (CK_AHB / 1) = CK_SYS */
  13.     RCC_APB2Config(RCC_APB2AHB_DIV1);
  14.    
  15.     /* CK_APB1 = (CK_AHB / 2) = CK_SYS / 2 */
  16.     RCC_APB1Config(RCC_APB1AHB_DIV2);
  17.    
  18.     /* CK_ADC = (CK_APB2 / 12) = CK_SYS / 12 */
  19.     RCC_ADCCLKConfig(RCC_ADCCLK_APB2_DIV12);
  20.    
  21.     /* CK_PLL2 = (HSE / 5) * 8 = 40 MHz */
  22.     RCC_PREDV2Config(RCC_PREDIV2_DIV5);
  23.     RCC_PLL2Config(RCC_PLL2MUL_8);
  24.     /* Enable PLL2 */
  25.     RCC_PLL2_Enable(ENABLE);
  26.     /* Wait till PLL2 is ready */
  27.     while (RCC_GetBitState(RCC_FLAG_PLL2STB) == RESET){ }
  28.    
  29.     /* CK_PLL = (CK_PLL2 / 5) * 9 = 72 MHz */
  30.     /* CK_PLL = (CK_PLL2 / 8) * 24 = 120 MHz */
  31.     RCC_PREDV1Config(RCC_PREDIV1_SOURCE_PLL2, RCC_PREDIV1_DIV8);
  32.     RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_24);
  33.    
  34.     /* Enable PLL */
  35.     RCC_PLL_Enable(ENABLE);
  36.     /* Wait till PLL is ready */
  37.     while(RCC_GetBitState(RCC_FLAG_PLLSTB) == RESET){ }
  38.    
  39.     /* Select PLL as system clock source */
  40.     /* so CK_SYS = CK_PLL = 120 MHz */
  41.     RCC_CK_SYSConfig(RCC_SYSCLKSOURCE_PLLCLK);
  42.    
  43.     /* Wait till PLL is used as system clock source */
  44.     while(RCC_GetCK_SYSSource() != 0x08){ }
  45.   }
  46. }
复制代码
紧接着,需要设置ThreadX的初始化文件tx_initialize_low_level.s,代码如下:
  1. SYSTEM_CLOCK      EQU   120000000
复制代码
该文件其他部分基本不用修改了!
所以说我个人感觉ThreadX是最好移植的一个高性能嵌入式实时操作系统了,各位看官同意不?
到这里或许有看官提出了:能否和电脑CPU一样,加上水冷,超频到200MHz用?
我的建议是:不用问,试试看就知道了,呵呵……

2、是时候做个跑马灯了,120MHz行否?
我就把一步步过程,都抓图上贴,其实也是展示个过程,教导新手而已。
前面我们已经把系统时钟设置为120MHz了,其实里面还多了这几行代码,我们先保留,跳过去看后面先:
  1. /* CK_APB2 = (CK_AHB / 1) = CK_SYS */
  2.     RCC_APB2Config(RCC_APB2AHB_DIV1);
  3.    
  4.     /* CK_APB1 = (CK_AHB / 2) = CK_SYS / 2 */
  5.     RCC_APB1Config(RCC_APB1AHB_DIV2);
  6.    
  7.     /* CK_ADC = (CK_APB2 / 12) = CK_SYS / 12 */
  8.     RCC_ADCCLKConfig(RCC_ADCCLK_APB2_DIV12);
复制代码
首先看看硬件图,发现demo板子的3个LED和2个按钮均是连接到GPIOD,如图:
2016-02-17_13-18-46.jpg

再从官方手册GD32F20x系统架构图中,找到GPIOD属于APB2的外围设备,如图:
2016-02-17_13-24-06.jpg

因此要使用GPIOD,先要配置CK_APB2时钟,我们直接配备成CK_SYS(也就是120MHz咯),也就是前面多的第一句代码。
然后另外新建立一个void SetupHardware(void)函数,如下:
  1. void SetupHardware(void){
  2.     GPIO_InitPara     GPIO_InitStructure;

  3.     /* Enable APB2 Clock */
  4.     RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOD, ENABLE);

  5.     /* Configure led mode */
  6.     GPIO_InitStructure.GPIO_Pin                   = BSP_LED_RED | BSP_LED_GREEN | BSP_LED_YELLOW;
  7.     GPIO_InitStructure.GPIO_Mode                  = GPIO_MODE_OUT_PP;
  8.     GPIO_InitStructure.GPIO_Speed                 = GPIO_SPEED_50MHZ;
  9.     GPIO_Init(GPIOD,&GPIO_InitStructure);
  10. }
复制代码
写到这里,发现GD的函数格式和ST确实不同,不过不用担心,外围驱动的文件分类其实是一样的。
比如说:
ST定义格式是:
  1. GPIO_InitTypeDef    GPIO_InitStructure;
复制代码
GD定义格式变了,是:
  1. GPIO_InitPara         GPIO_InitStructure;
复制代码
其实都是在他们各自的外设头文件里面定义的,ST是stm32fxxx_gpio.h,GD是gd32f20x_gpio.h,我抓了个比较图如下:
2016-02-17_13-54-53.jpg

到这里,估计妈妈再也不用担心各位在移植ST代码到GD时碰到麻烦了。
另外,记得在编译之前,在IAR工程里面增加驱动文件gd32f20x_gpio.c,否则GPIO_Init函数会报错。
就是下图红框框这个文件了!
(其他3个驱动,先不用管,我的代码写了很长,但是这个帖子现在才更新,你懂的)
2016-02-17_14-05-25.jpg

到这里,我们编写个简单main文件,就能轻松点亮LED了:
  1.   /* 点亮led灯 */
  2.   GPIO_SetBits(GPIOD,BSP_LED_RED);
  3.   GPIO_SetBits(GPIOD,BSP_LED_GREEN);
  4.   GPIO_SetBits(GPIOD,BSP_LED_YELLOW);
复制代码
3、应该到了引入ThreadX内核的时候了
时间比较紧急,我加快点步骤了。
先上广告:昨天弄到这里时,发现WIN10对于ULINK的驱动补肾接受,调试时非常不顺利。
于是多了《用STLINK连接GD32F207ZE测试板示意图》这么一个帖子。
还是老流氓做法吧,链接是:https://www.cirmall.com/bbs/thread-46465-1-1.html
在IAR中新建txlib目录,增加ThreadX操作系统中两个重要文件,并修改CPU启动文件startup_gd32f20x_cl.s,替换中断向量表的系统函数地址到ThreadX对应函数中去,如下图红框框部分所示(其中EXTERN  __tx_IntHandler_button外部函数是下面内容提到的,先略过):
2016-02-19_16-12-56.jpg

修改main.c文件:
  1. #include "tx_api.h"

  2. /* 定义函数声明 --------------------------------------------------------------*/
  3. extern void           SetupHardware(void);

  4. /* Define the ThreadX object control blocks...  */
  5. TX_EVENT_FLAGS_GROUP    event_flags_1;
  6. TX_EVENT_FLAGS_GROUP    event_flags_2;
  7. TX_QUEUE                queue_1;
  8. TX_QUEUE                queue_2;
  9. TX_QUEUE                queue_3;
  10. TX_SEMAPHORE            semaphore_1;
  11. TX_SEMAPHORE            semaphore_2;
  12. TX_TIMER                onesec_timer;

  13. TX_THREAD               thread_1;
  14. TX_THREAD               thread_2;
  15. TX_THREAD               thread_3;
  16. TX_THREAD               thread_4;
  17. TX_THREAD               thread_5;
  18. TX_THREAD               thread_6;
  19. TX_THREAD               thread_7;
  20. TX_THREAD               thread_8;

  21. /* Define the counters used in the demo application...  */
  22. ULONG                   thread_1_counter;
  23. ULONG                   thread_2_counter;
  24. ULONG                   thread_3_counter;
  25. ULONG                   thread_4_counter;
  26. ULONG                   thread_5_counter;
  27. ULONG                   thread_6_counter;
  28. ULONG                   thread_7_counter;
  29. ULONG                   thread_8_counter;

  30. /* Define thread prototypes.  */
  31. void    thread_1_entry(ULONG thread_input);
  32. void    thread_2_entry(ULONG thread_input);
  33. void    thread_3_entry(ULONG thread_input);
  34. void    thread_4_entry(ULONG thread_input);
  35. void    thread_5_entry(ULONG thread_input);
  36. void    thread_6_entry(ULONG thread_input);
  37. void    thread_7_entry(ULONG thread_input);
  38. void    thread_8_entry(ULONG thread_input);
  39. void    onesec_timer_entry(ULONG);

  40. /* Define main entry point.  */
  41. int     main(){
  42.   /* 初始化硬件BSP,包括时钟、IO模式、串口等等等等.  */
  43.   SetupHardware();
  44.   /* Enter the ThreadX kernel.  */
  45.   tx_kernel_enter();
  46. }
复制代码
新增ThreadX初始化定义函数:
  1. void    tx_application_define(void *first_unused_memory){
  2.   
  3.   CHAR *pointer;
  4.   
  5.   /* Pickup first free address.  */
  6.   pointer =  (CHAR *) first_unused_memory;
  7.   
  8.   /* Create the event flags groups */
  9.   tx_event_flags_create(&event_flags_1, "event flags 1");
  10.   tx_event_flags_create(&event_flags_2, "event flags 2");
  11.   
  12.   /* Create the message queues*/
  13.   tx_queue_create(&queue_1, "queue 1", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));
  14.   pointer =  pointer + (DEMO_QUEUE_SIZE*sizeof(ULONG));
  15.   tx_queue_create(&queue_2, "queue 2", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));
  16.   pointer =  pointer + (DEMO_QUEUE_SIZE*sizeof(ULONG));
  17.   tx_queue_create(&queue_3, "queue 3", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));
  18.   pointer =  pointer + (DEMO_QUEUE_SIZE*sizeof(ULONG));
  19.   
  20.   /* Create the semaphores */
  21.   tx_semaphore_create(&semaphore_1, "semaphore 1", 0);
  22.   tx_semaphore_create(&semaphore_2, "semaphore 2", 0);
  23.   
  24.   /* Create threads*/
  25.   tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, pointer, DEMO_STACK_SIZE, 10, 10, TX_NO_TIME_SLICE, TX_AUTO_START);
  26.   pointer =  pointer + DEMO_STACK_SIZE;
  27.   tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, pointer, DEMO_STACK_SIZE, 9, 9, TX_NO_TIME_SLICE, TX_AUTO_START);
  28.   pointer =  pointer + DEMO_STACK_SIZE;
  29.   tx_thread_create(&thread_3, "thread 3", thread_3_entry, 3, pointer, DEMO_STACK_SIZE, 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
  30.   pointer =  pointer + DEMO_STACK_SIZE;
  31.   tx_thread_create(&thread_4, "thread 4", thread_4_entry, 4, pointer, DEMO_STACK_SIZE, 7, 7, TX_NO_TIME_SLICE, TX_AUTO_START);
  32.   pointer =  pointer + DEMO_STACK_SIZE;
  33.   tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, pointer, DEMO_STACK_SIZE, 6, 6, TX_NO_TIME_SLICE, TX_AUTO_START);
  34.   pointer =  pointer + DEMO_STACK_SIZE;
  35.   tx_thread_create(&thread_6, "thread 6", thread_6_entry, 6, pointer, DEMO_STACK_SIZE, 5, 5, TX_NO_TIME_SLICE, TX_AUTO_START);
  36.   pointer =  pointer + DEMO_STACK_SIZE;
  37.   tx_thread_create(&thread_7, "thread 7", thread_7_entry, 7, pointer, DEMO_STACK_SIZE, 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);
  38.   pointer =  pointer + DEMO_STACK_SIZE;
  39.   tx_thread_create(&thread_8, "thread 8", thread_8_entry, 8, pointer, DEMO_STACK_SIZE, 3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);
  40.   pointer =  pointer + DEMO_STACK_SIZE;
  41.   
  42.   /* 建立《一秒钟定时器线程》:初始化1秒钟后启动,间隔1秒 */
  43.   tx_timer_create (&onesec_timer, "1s_timer", onesec_timer_entry, 0x00, 100, 100, TX_AUTO_ACTIVATE);
  44. }
复制代码
这两段代码,含有了嵌入式操作系统的各个主要部件和函数。其数量之多,达到变态级别,呵呵。
如有不明白之处,建议先自行购买相应书籍学习先。
在这里极力推荐本群飞鸟兄弟的大作:《嵌入式实时操作系统原理与最佳实践》。
该书以飞鸟内核(就是本次活动的配套系统Colibri_GD32F207啊)作为例子,对嵌入式操作系统进行了非常全面的诠释,非常适合新手入门!
链接是:http://item.jd.com/11545659.html
2016-02-19_16-26-16.jpg


各位可以看到,上面代码最后一行,我们建立了一个《一秒钟定时器线程》,在这里就让小红板秒闪黄色的LED吧。
也就是一句代码就能切换了:
  1. GPIOD->DOR ^= BSP_LED_YELLOW;
复制代码
抓图如下:
2016-02-19_16-34-46.jpg

到这里,编译下载后,效果良好。
至于其他8个线程,它们事实上非常简单,第一个线程传递个信息给第二个,第二个又传递给第三个……以此类推。
我原本意思是做一个虐待GD32F207的代码(测试板嘛,一般都是被虐待的遍体鳞伤,才能证明真实是耐C):
按下按钮后,发送一个信号出去,里面8个线程分别通过板子的几个串口发送和回传数据,甚至其他接口也全部纳入,信号做一个级数增加,1变2,2变4……
看看我们的主角啥时候会自杀?
爽吧,呵呵。

时间太紧迫,先做个简单的内部传递先吧。

4、外部中断的引入及自定义中断处理函数
小红板板载了两个按钮,其原理图在前面展示过,分别连接到GPIOD_PIN_14和GPIOD_PIN_15。
官方附带的样板代码,把这两个按钮作为外部中断输入,分别控制了剩下的两个LED灯状态。
我们也来仿制下:
  1.   /* Configure BSP_BTN_KEYs */
  2.   GPIO_InitStructure.GPIO_Pin                   = BSP_BTN_KEY1 |BSP_BTN_KEY2;
  3.   GPIO_InitStructure.GPIO_Mode                  = GPIO_MODE_IN_FLOATING;//DUE TO PCB HAVE UPLINKED,HERE CAN FLOATING
  4.   GPIO_InitStructure.GPIO_Speed                 = GPIO_SPEED_50MHZ;
  5.   GPIO_Init(GPIOD,&GPIO_InitStructure);

  6.   /* Connect EXTI Line to pin */
  7. //  GPIO_EXTILineConfig(GPIO_PORT_SOURCE_GPIOD, BSP_KEY1_EXTI_PIN_SOURCE | BSP_KEY2_EXTI_PIN_SOURCE);//DO NOT USE THIS
  8.   GPIO_EXTILineConfig(GPIO_PORT_SOURCE_GPIOD, BSP_KEY1_EXTI_PIN_SOURCE);
  9.   GPIO_EXTILineConfig(GPIO_PORT_SOURCE_GPIOD, BSP_KEY2_EXTI_PIN_SOURCE);
  10.   
  11.   /* Configure EXTI line */
  12.   EXTI_DeInit(&EXTI_InitStructure);
  13.   EXTI_InitStructure.EXTI_LINE                  = BSP_KEY1_EXTI_LINE | BSP_KEY2_EXTI_LINE;
  14.   EXTI_InitStructure.EXTI_Mode                  = EXTI_Mode_Interrupt;
  15.   EXTI_InitStructure.EXTI_Trigger               = EXTI_Trigger_Rising_Falling;//TWO BUTTONS,SAME EXIT,ANY INPUT STATES
  16. EXTI_InitStructure.EXTI_LINEEnable            = ENABLE;
  17.   EXTI_Init(&EXTI_InitStructure);

  18.   /* Enable and set EXTI Line Interrupt */
  19.   NVIC_InitStructure.NVIC_IRQ                   = BSP_KEYS_EXTI_IRQChannel;
  20.   NVIC_InitStructure.NVIC_IRQPreemptPriority    = 0x0F;
  21.   NVIC_InitStructure.NVIC_IRQSubPriority        = 0x0F;
  22.   NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
  23.   NVIC_Init(&NVIC_InitStructure);
复制代码
以上代码,和样本程序基本一样的,唯一不同的地方是:
在按钮中断的触发状态定义里:
样本代码是监控了下降沿触发(提示:PCB板子是做了上拉设计):
  1. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling
复制代码
我的代码是监控了上升沿和下降沿两个触发:
  1. EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
复制代码
新增加一个IT.C文件,导入到app目录,新增一个中断处理函数:
  1. void IrqHandler_BTN(void){
  2.   static ULONG  xTimeKeyTriggerRising1, xTimeKeyTriggerFalling1;
  3.   static ULONG  xTimeKeyTriggerRising2, xTimeKeyTriggerFalling2;
  4.   ULONG xTimeKeyBetweenInterrupts1, xTimeKeyBetweenInterrupts2;
  5.   
  6.   if(EXTI_GetIntBitState(BSP_KEY1_EXTI_LINE) != RESET){
  7.   if(GPIO_ReadInputBit(GPIOD, BSP_BTN_KEY1) == 0){
  8.   xTimeKeyTriggerFalling1 = tx_time_get();
  9.   xTimeKeyTriggerRising1 = NULL;
  10.   }
  11.   else if(GPIO_ReadInputBit(GPIOD, BSP_BTN_KEY1) == 1){
  12.   if(xTimeKeyTriggerFalling1 != NULL)
  13.   xTimeKeyTriggerRising1 = tx_time_get();
  14.   }
  15.   if(xTimeKeyTriggerRising1 != NULL && xTimeKeyTriggerFalling1 != NULL){
  16.   xTimeKeyBetweenInterrupts1 = xTimeKeyTriggerRising1 - xTimeKeyTriggerFalling1;
  17.   if(xTimeKeyBetweenInterrupts1 > btnMIN_TIME_BETWEEN_010_PUSH_MS){
  18.   xTimeKeyTriggerRising1 = NULL;
  19.   xTimeKeyTriggerFalling1 = NULL;
  20.   GPIOD->DOR ^= BSP_LED_RED;
  21.   }
  22.   }
  23.   /* Clear the EXTI line pending bit */
  24.   EXTI_ClearIntBitState(BSP_KEY1_EXTI_LINE);
  25.   }
  26.   if(EXTI_GetIntBitState(BSP_KEY2_EXTI_LINE) != RESET){
  27.   if(GPIO_ReadInputBit(GPIOD, BSP_BTN_KEY2) == 0){
  28.   xTimeKeyTriggerFalling2 = tx_time_get();
  29.   xTimeKeyTriggerRising2 = NULL;
  30.   }
  31.   else if(GPIO_ReadInputBit(GPIOD, BSP_BTN_KEY2) == 1){
  32.   if(xTimeKeyTriggerFalling2 != NULL)
  33.   xTimeKeyTriggerRising2 = tx_time_get();
  34.   }
  35.   if(xTimeKeyTriggerRising2 != NULL && xTimeKeyTriggerFalling2 != NULL){
  36.   xTimeKeyBetweenInterrupts2 = xTimeKeyTriggerRising2 - xTimeKeyTriggerFalling2;
  37.   if(xTimeKeyBetweenInterrupts2 > btnMIN_TIME_BETWEEN_010_PUSH_MS){
  38.   xTimeKeyTriggerRising2 = NULL;
  39.   xTimeKeyTriggerFalling2 = NULL;
  40.   GPIOD->DOR ^= BSP_LED_GREEN;
  41.   }
  42.   }
  43.   /* Clear the EXTI line pending bit */
  44.   EXTI_ClearIntBitState(BSP_KEY2_EXTI_LINE);
  45.   }
  46. }
复制代码
需要特别指出的是,两个按钮共用了一个入口EXTI15_10_IRQn,我们需要在中断服务程序里面分别判断并不同处理。
其中KEY1是切换红色灯,KEY2是切换绿色灯
到这里有经验的群友会立马发现:为啥我前面要监控按钮的上升沿和下降沿了。
好处就是可以在中断里面直接抗干扰滤波,甚至在里面嵌入更多功能,干净快捷!

5、串口的实用用法
各位看官,时间剩下两天了,看来今天要终稿。
我看到在小红板的样本代码里面,飞鸟提供了一个简易的串口交互例子,非常值得新手参考学习。
我这里仅点滴提一下,飞鸟例子里面,存储串口交互数据的是一个静态缓冲区:
  1. static char _buffer[256];
复制代码
但如果交互数据比较多,交互非常频繁,为了节约单片机里面非常珍贵的RAM,那必须推荐另外一个更实用和巧妙地方法——环形队列,我在网上找到一张示意图如下:
2016-02-21_10-22-52.jpg


至于如何使用,我在这里提供一个非常适合新手的办法——购买飞鸟的大作,然后就可以名正言顺地要求他提供相应售后服务了嘛!
这是真的,我以前使用另外一款免费RTOS——FreeRTOS时,碰到一个问题,发邮件给他的作者,结果鬼佬很直接地说——你得先买我的正版书籍,看一周后,如果还是不懂,我一定答疑!
于是我就直接用信用卡刷了美金,购买了他的两本电子书,最后问题还真的就这么自己解决了。
这个经验必须得推广。实在是不建议有些群友啥问题都张口要,同学们必须学会自己探索啊,呵呵。
2016-02-21_15-45-46.jpg


6、大话GD32F207ZE的超频
首先声明:
1.在任何量产的电子产品中应用超频技术,会引来额外的风险!
2.本节内容包含的试验方法,并非全部科学准确,仅适合在有限条件下的一种主观测试。
3.本节语言风格散漫飞翔,追求大话演绎效果第一。
好了,念完经书后,我们马上进入话题了。
首先回忆一下我们当年在电脑上是如何超频的吧——大都是进入主板的BIOS,然后把CPU的倍频从1改到1.2,内存倍频从1改到1.2等等等等……
当然了,有些智能主板事先已经提供了几种模式——比如“稳定”、“平常”、“专家”。
只要选择了“专家”模式,整个电脑跑起来那就自动打了鸡血一样活蹦乱跳,你也就自然也变成“超频专家”一个!
在和同学们交流时候,你的声音特别大和兴奋,是吧?
好,先冷静下。各位看官是否还记得,我在本文第一节《强大的120MHz时钟的选择》里面提到,在计算120MHz(官方推荐的最高核心频率)时,提到了3种不同的时钟计算路径。既然我们要超频嘛,还得从这个时钟着手——还是选择最后那条路径,然后还是通过PLL.倍频寻找最大值吧,计算结果如下:
  1. CK_SYS=CK_PLL=CK_PLL2/1*32=HSE/1*20/1*32=25*20*32=16000 MHz
复制代码
好家伙!算出来核心频率是16G啊,足以秒杀INTEL或者AMD的任何一款最顶级的桌面CPU了……
英特尔去年新推出的第六代Core i7-6700K,14nm工艺新架构,四核心八线程,但频率也仅4G而已嘛。
喂!那位把500W电脑电源搬出来的同学!
先不用搬了,GD32F207ZE暂时还用不到这么高功率的电源。
我们一起先修改下代码,从脚开始往上,好好虐待下GD的这位当家MM,如何?
代码伺候!(注意:以下代码进行修改时,也务必同时修改ThreadX初始化文件的频率)
  1.     /* CK_AHB = (CK_SYS / 1) = CK_SYS */
  2.     RCC_AHBConfig(RCC_SYSCLK_DIV1);
  3.    
  4.     /* CK_APB2 = (CK_AHB / 1) = CK_SYS */
  5.     RCC_APB2Config(RCC_APB2AHB_DIV1);
  6.    
  7.     /* CK_PLL2 = (HSE / 5) * 8 = 40 MHz */
  8.     RCC_PREDV2Config(RCC_PREDIV2_DIV5);
  9.     RCC_PLL2Config(RCC_PLL2MUL_8);
  10.     /* Enable PLL2 */
  11.     RCC_PLL2_Enable(ENABLE);
  12.     /* Wait till PLL2 is ready */
  13.     while (RCC_GetBitState(RCC_FLAG_PLL2STB) == RESET){ }
  14.    
  15.     /* CK_PLL = (CK_PLL2 / 5) * 15 = 120 MHz */
  16.     /* CK_PLL = (CK_PLL2 / 5) * 20 = 160 MHz */
  17.     /* CK_PLL = (CK_PLL2 / 5) * 24 = 192 MHz */
  18.     /* CK_PLL = (CK_PLL2 / 5) * 25 = 200 MHz */
  19.     RCC_PREDV1Config(RCC_PREDIV1_SOURCE_PLL2, RCC_PREDIV1_DIV5);
  20.     RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_15);  //120MHz
  21. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_20);  //160MHz
  22. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_24);  //192MHz
  23. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_25);  //200MHz
  24. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_26);  //208MHz
  25. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_27);  //216MHz
  26. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_28);  //224MHz
  27. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_29);  //232MHz
  28. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_30);  //240MHz
  29. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_31);  //248MHz
  30. //  RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_32);  //256MHz

复制代码
上面的代码中,CK_PLL2事先得到是40MHz(注意:我并不是一开始就跑峰值哦),因此下面两句代码,就能算出核心频率是官方推荐的120MHz(即40 / 5 * 15 = 120):
  1. RCC_PREDV1Config(RCC_PREDIV1_SOURCE_PLL2, RCC_PREDIV1_DIV5);
  2. RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_15);  //120MHz
复制代码
眼尖的朋友马上发现,只要修改RCC_PLLConfig函数的第二个参数,就能得到不同的超频效果了,是吧?
于是我就有了以下的虐待笔记:
  1. 120MHz,运行良好
  2. 128MHz,运行良好
  3. 136MHz,运行良好
  4. 144MHz,运行良好
  5. 152MHz,运行良好
  6. 160MHz,运行良好
  7. 168MHz,运行良好
  8. 176MHz,运行良好
  9. 184MHz,运行良好
  10. 192MHz,运行良好
  11. 200MHz,运行出现异常,5次重启,1次成功
  12. 208MHz,运行异常
  13. 。。。
复制代码
写到这里,有必要解释下我前面念过的3句经文了,其中第一句的意思是:
电脑的超频,和GD32F207ZE不同,电脑的CPU,纯粹就是一个运算单元,它外面还需要配备内存啊南桥啊北桥啊啥的。
电脑的超频是否成功,要看CPU所配备的内存啊南北桥啊啥的是否跟得上它快速的频率。
有过电脑超频经验的会明白,为啥我们为了让内存跟得上,有时候还得增加它的供电电压,就是这个道理。
而GD32呢,它不仅内置了RAM,也内置了AHB桥还有各种外设的桥,比如我这里跑马灯用到的APB2桥等等。
我们在这里单纯超了核心频率,但是RAM和各种外设频率会出现跟不上的情况。
我在调试时就发现,在超频至200MHz时,事实上内核仍然在跑动的,按钮中断也表现正常,但是LED始终无法点亮。
是否外设频率APB2太高跟不上呢?我于是又调整了外设的分频器:
  1. RCC_APB2Config(RCC_APB2AHB_DIV2);
复制代码
这算下来,外设APB2也就100MHz,连官方的最高120MHz都达不到。
但事实上下载后,LED仍然无法正常工作。
所以说单片机由于内置了太多的功能,它的超频看来要比电脑CPU更难理解和操作。
在经过多次倍频和多次分频后,时钟波形肯定出现了严重变形。
我这个例子还仅仅是使用了GPIO一个外设,如果你还用到串口啊、I2C啊I2S啊,估计能否保证正常的时序都是个问题。
最终的结论就是:
GD32F207ZE在使用GPIO的情况下,能够稳定超频至192MHz,是官方标称频率的1.6倍,非常可观的结果。
当然了,如果有哪位朋友有更稳定和精密的25MHz的晶振,可以替换下小红板本身这个,或许能够取得更佳的结果了。
看到这里估计有看官提出来了,都是听哥哥你在吹牛,能否发个证明出来看看?
这个我考虑过了,我看过GD32F207的时钟图,下面这个确实可以把核心频率直接通过IO输出来,直接用示波器采集一下,更科学更有说服力啊!但是由于时间关系,先在此搁置咯。
2016-02-21_13-06-48.jpg

五、总结、建议(也要加分
从2月14日至今天2月21日,大概花了一周多的时间(大部分都是晚上),啰里啰唆的,简简单单做了几个试验。
全文至此,算是告一段落了。
总结和建议如下:
  1. 1、GD32F207系列,是一个高频率、稳定可用的芯片,值得在产品中投入使用。
  2. 2、从推广上,建议兆易继续加大推广力度,大肆发放物美价廉的核心demo板子,加大飞鸟内核的附带推广。
  3. 3、从应用上,建议照顾微小企业,推出TSSOP,QFN32,LQFP48等小封装芯片,降低加工和返修难度。
  4. 4、从定位上,建议把130定位在STM8价格,估计能够占据大幅安防市场的空间,当然了,如果配备了摄像头和LAN接口的20X系列,也降价冲击这块市场,估计想不用都很难。
  5. 5、从体验上,建议尽快把GD内核模型,纳入到IAR或KEIL等等IDE的数据库中去,大大降低推广应用的入门难度。(另外一个方法是,大肆提供这两个编程平台的demo代码,也是个好办法)
  6. 6、从发展上,建议下一个版本(GD32F40X系列)的测试板,必须增加一块高清液晶屏幕,理由见下图。
  7. 7、从品牌上,建议推出一个板载正版JLINK OB(仅支持GD内核)的纪念版测试板,大大提高GD的品牌效应,估计人人都喊好!
复制代码
Slide5.png


六、作品实物图+视频(加分
2016-02-21_14-02-28.jpg


七、方案代码+BOM(加分
我在代码里面写了《使用须知》,如下:
1、请事先按照官方文件GD32 MCU IDE Config V2.0.rar,设置好你的IAR编译平台。
2、本代码出自爱板网“GigaDevice GD32 MCU”板块,版权归其所有!
3、爱板网链接如下:https://www.cirmall.com/bbs/forum-147-1.html
4、本代码针对“GD32 Colibri-F207ZE开发板”专门设计。
5、本代码对GD32F207ZE超频至192MHz,并预留了一处陷阱(仅仅删除了一个词而已),编译下载后,两个按钮中断无法正常工作,此处留给各位自行研究探索,我会在稍后公布陷阱答案。
GD32F207_V1_20160221_172342.rar (6.27 MB, 下载次数: 36)
回复

使用道具 举报

该用户从未签到

 楼主| 发表于 2016-2-14 23:24:29 | 显示全部楼层
本帖最后由 sellen-314350 于 2016-2-14 23:25 编辑

抽取情人节宝贵的2个小时的结果!
明天再继续。。。
回复 支持 反对

使用道具 举报

  • TA的每日心情
    开心
    2018-11-19 16:39
  • 签到天数: 2 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2016-2-16 14:00:07 | 显示全部楼层
    亲,可以将内容一并发到经验频道,很有机会获得每月之星的呢http://jingyan.eeboard.com/
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-7-14 10:15
  • 签到天数: 4 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2016-2-19 19:21:12 | 显示全部楼层
    很好的文章。也大度的推荐了trochili rtos,鼓励!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    擦汗
    2019-8-27 21:30
  • 签到天数: 219 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2016-2-19 21:42:34 | 显示全部楼层
    非常棒的分享,感谢楼主!!加油!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2016-2-21 18:22:50 | 显示全部楼层
    好文章,好文章!!!!!!!!!!!!!!!!!!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-7-14 10:15
  • 签到天数: 4 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2016-2-24 09:40:31 | 显示全部楼层
    sellen太给力了,这么好的文章,绝非一般的评测。看来你要用到产品上了。
    回复 支持 反对

    使用道具 举报

    该用户从未签到

     楼主| 发表于 2016-2-24 10:23:11 | 显示全部楼层
    本帖最后由 sellen-314350 于 2016-2-24 10:25 编辑

    谢谢飞鸟哥的夸奖!
    事实上,里面的几个细节还真是我产品用到的。
    时间精力有限,无法继续和大家分享比如环形链的使用,DMA的AD数据采集等等。
    继续等待最为看好的GD32F170和190两个工控专用MCU出来,再抽时间细细和大家分享吧。
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-12-24 21:31 , Processed in 0.180169 second(s), 30 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.