|
本帖最后由 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具备很大的超频性能。
整篇方案以实验纪事方式进行,语言表达追求诙谐轻松,思路导向追求散漫飞翔,值得初学者细读学之。
三、方案结构框图:
四、设计应用描述:
1、强大的120MHz时钟的选择:
我的任务是移植嵌入式商业实时操作系统,因为在QQ群里面对120MHz这么高的频率产生疑问,被几位大牛好好取笑了一大番,因此我决心努力学习下这个功能,因此在这里要特别体现出GD32F207Z的强大超频性质。
好,闲话少说,我们先来选择下时钟,论坛很多例子,都是利用了GD32F207Z内置的8MHz的HSI来驱动,是个在要求精度不是特别苛刻情况下非常经济节约的一个方法。我看到GD给的demo板子,很大方的外挂了一个25MHz的晶振,所以我想考虑下用HSE来做驱动时钟……好,先探索下最高频率120MHz如何得到先,从官方手册里面,我们看一下以下的时钟图,如图所示,红色部分的路线,不可能得到我们需要的结果,绿色部分的可以也,如图暂且先找到3个简单的组合线路,我的例子是选择了最下面一条线路,虽然复杂了那么一点点,在此以学习为主啊!
时钟设置的代码如下:- void RCC_Configuration120MHz(void){
- TypeState HSEStartUpStatus;
- /* Enable HSE */
- RCC_HSEConfig(RCC_HSE_ON);
-
- /* Wait till HSE ready */
- HSEStartUpStatus = RCC_WaitForHSEStartUp();
- if(HSEStartUpStatus == SUCCESS){
- /* CK_AHB = (CK_SYS / 1) = CK_SYS */
- RCC_AHBConfig(RCC_SYSCLK_DIV1);
-
- /* CK_APB2 = (CK_AHB / 1) = CK_SYS */
- RCC_APB2Config(RCC_APB2AHB_DIV1);
-
- /* CK_APB1 = (CK_AHB / 2) = CK_SYS / 2 */
- RCC_APB1Config(RCC_APB1AHB_DIV2);
-
- /* CK_ADC = (CK_APB2 / 12) = CK_SYS / 12 */
- RCC_ADCCLKConfig(RCC_ADCCLK_APB2_DIV12);
-
- /* CK_PLL2 = (HSE / 5) * 8 = 40 MHz */
- RCC_PREDV2Config(RCC_PREDIV2_DIV5);
- RCC_PLL2Config(RCC_PLL2MUL_8);
- /* Enable PLL2 */
- RCC_PLL2_Enable(ENABLE);
- /* Wait till PLL2 is ready */
- while (RCC_GetBitState(RCC_FLAG_PLL2STB) == RESET){ }
-
- /* CK_PLL = (CK_PLL2 / 5) * 9 = 72 MHz */
- /* CK_PLL = (CK_PLL2 / 8) * 24 = 120 MHz */
- RCC_PREDV1Config(RCC_PREDIV1_SOURCE_PLL2, RCC_PREDIV1_DIV8);
- RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_24);
-
- /* Enable PLL */
- RCC_PLL_Enable(ENABLE);
- /* Wait till PLL is ready */
- while(RCC_GetBitState(RCC_FLAG_PLLSTB) == RESET){ }
-
- /* Select PLL as system clock source */
- /* so CK_SYS = CK_PLL = 120 MHz */
- RCC_CK_SYSConfig(RCC_SYSCLKSOURCE_PLLCLK);
-
- /* Wait till PLL is used as system clock source */
- while(RCC_GetCK_SYSSource() != 0x08){ }
- }
- }
复制代码 紧接着,需要设置ThreadX的初始化文件tx_initialize_low_level.s,代码如下:- SYSTEM_CLOCK EQU 120000000
复制代码 该文件其他部分基本不用修改了!
所以说我个人感觉ThreadX是最好移植的一个高性能嵌入式实时操作系统了,各位看官同意不?
到这里或许有看官提出了:能否和电脑CPU一样,加上水冷,超频到200MHz用?
我的建议是:不用问,试试看就知道了,呵呵……
2、是时候做个跑马灯了,120MHz行否?:
我就把一步步过程,都抓图上贴,其实也是展示个过程,教导新手而已。
前面我们已经把系统时钟设置为120MHz了,其实里面还多了这几行代码,我们先保留,跳过去看后面先:- /* CK_APB2 = (CK_AHB / 1) = CK_SYS */
- RCC_APB2Config(RCC_APB2AHB_DIV1);
-
- /* CK_APB1 = (CK_AHB / 2) = CK_SYS / 2 */
- RCC_APB1Config(RCC_APB1AHB_DIV2);
-
- /* CK_ADC = (CK_APB2 / 12) = CK_SYS / 12 */
- RCC_ADCCLKConfig(RCC_ADCCLK_APB2_DIV12);
复制代码 首先看看硬件图,发现demo板子的3个LED和2个按钮均是连接到GPIOD,如图:
再从官方手册GD32F20x系统架构图中,找到GPIOD属于APB2的外围设备,如图:
因此要使用GPIOD,先要配置CK_APB2时钟,我们直接配备成CK_SYS(也就是120MHz咯),也就是前面多的第一句代码。
然后另外新建立一个void SetupHardware(void)函数,如下:- void SetupHardware(void){
- GPIO_InitPara GPIO_InitStructure;
- /* Enable APB2 Clock */
- RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOD, ENABLE);
- /* Configure led mode */
- GPIO_InitStructure.GPIO_Pin = BSP_LED_RED | BSP_LED_GREEN | BSP_LED_YELLOW;
- GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
- GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_50MHZ;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- }
复制代码 写到这里,发现GD的函数格式和ST确实不同,不过不用担心,外围驱动的文件分类其实是一样的。
比如说:
ST定义格式是:- GPIO_InitTypeDef GPIO_InitStructure;
复制代码 GD定义格式变了,是:- GPIO_InitPara GPIO_InitStructure;
复制代码 其实都是在他们各自的外设头文件里面定义的,ST是stm32fxxx_gpio.h,GD是gd32f20x_gpio.h,我抓了个比较图如下:
到这里,估计妈妈再也不用担心各位在移植ST代码到GD时碰到麻烦了。
另外,记得在编译之前,在IAR工程里面增加驱动文件gd32f20x_gpio.c,否则GPIO_Init函数会报错。
就是下图红框框这个文件了!
(其他3个驱动,先不用管,我的代码写了很长,但是这个帖子现在才更新,你懂的)
到这里,我们编写个简单main文件,就能轻松点亮LED了:- /* 点亮led灯 */
- GPIO_SetBits(GPIOD,BSP_LED_RED);
- GPIO_SetBits(GPIOD,BSP_LED_GREEN);
- 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外部函数是下面内容提到的,先略过):
修改main.c文件:- #include "tx_api.h"
- /* 定义函数声明 --------------------------------------------------------------*/
- extern void SetupHardware(void);
- /* Define the ThreadX object control blocks... */
- TX_EVENT_FLAGS_GROUP event_flags_1;
- TX_EVENT_FLAGS_GROUP event_flags_2;
- TX_QUEUE queue_1;
- TX_QUEUE queue_2;
- TX_QUEUE queue_3;
- TX_SEMAPHORE semaphore_1;
- TX_SEMAPHORE semaphore_2;
- TX_TIMER onesec_timer;
- TX_THREAD thread_1;
- TX_THREAD thread_2;
- TX_THREAD thread_3;
- TX_THREAD thread_4;
- TX_THREAD thread_5;
- TX_THREAD thread_6;
- TX_THREAD thread_7;
- TX_THREAD thread_8;
- /* Define the counters used in the demo application... */
- ULONG thread_1_counter;
- ULONG thread_2_counter;
- ULONG thread_3_counter;
- ULONG thread_4_counter;
- ULONG thread_5_counter;
- ULONG thread_6_counter;
- ULONG thread_7_counter;
- ULONG thread_8_counter;
- /* Define thread prototypes. */
- void thread_1_entry(ULONG thread_input);
- void thread_2_entry(ULONG thread_input);
- void thread_3_entry(ULONG thread_input);
- void thread_4_entry(ULONG thread_input);
- void thread_5_entry(ULONG thread_input);
- void thread_6_entry(ULONG thread_input);
- void thread_7_entry(ULONG thread_input);
- void thread_8_entry(ULONG thread_input);
- void onesec_timer_entry(ULONG);
- /* Define main entry point. */
- int main(){
- /* 初始化硬件BSP,包括时钟、IO模式、串口等等等等. */
- SetupHardware();
- /* Enter the ThreadX kernel. */
- tx_kernel_enter();
- }
复制代码 新增ThreadX初始化定义函数:- void tx_application_define(void *first_unused_memory){
-
- CHAR *pointer;
-
- /* Pickup first free address. */
- pointer = (CHAR *) first_unused_memory;
-
- /* Create the event flags groups */
- tx_event_flags_create(&event_flags_1, "event flags 1");
- tx_event_flags_create(&event_flags_2, "event flags 2");
-
- /* Create the message queues*/
- tx_queue_create(&queue_1, "queue 1", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));
- pointer = pointer + (DEMO_QUEUE_SIZE*sizeof(ULONG));
- tx_queue_create(&queue_2, "queue 2", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));
- pointer = pointer + (DEMO_QUEUE_SIZE*sizeof(ULONG));
- tx_queue_create(&queue_3, "queue 3", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));
- pointer = pointer + (DEMO_QUEUE_SIZE*sizeof(ULONG));
-
- /* Create the semaphores */
- tx_semaphore_create(&semaphore_1, "semaphore 1", 0);
- tx_semaphore_create(&semaphore_2, "semaphore 2", 0);
-
- /* Create threads*/
- tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, pointer, DEMO_STACK_SIZE, 10, 10, TX_NO_TIME_SLICE, TX_AUTO_START);
- pointer = pointer + DEMO_STACK_SIZE;
- tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, pointer, DEMO_STACK_SIZE, 9, 9, TX_NO_TIME_SLICE, TX_AUTO_START);
- pointer = pointer + DEMO_STACK_SIZE;
- tx_thread_create(&thread_3, "thread 3", thread_3_entry, 3, pointer, DEMO_STACK_SIZE, 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
- pointer = pointer + DEMO_STACK_SIZE;
- tx_thread_create(&thread_4, "thread 4", thread_4_entry, 4, pointer, DEMO_STACK_SIZE, 7, 7, TX_NO_TIME_SLICE, TX_AUTO_START);
- pointer = pointer + DEMO_STACK_SIZE;
- tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, pointer, DEMO_STACK_SIZE, 6, 6, TX_NO_TIME_SLICE, TX_AUTO_START);
- pointer = pointer + DEMO_STACK_SIZE;
- tx_thread_create(&thread_6, "thread 6", thread_6_entry, 6, pointer, DEMO_STACK_SIZE, 5, 5, TX_NO_TIME_SLICE, TX_AUTO_START);
- pointer = pointer + DEMO_STACK_SIZE;
- tx_thread_create(&thread_7, "thread 7", thread_7_entry, 7, pointer, DEMO_STACK_SIZE, 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);
- pointer = pointer + DEMO_STACK_SIZE;
- tx_thread_create(&thread_8, "thread 8", thread_8_entry, 8, pointer, DEMO_STACK_SIZE, 3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);
- pointer = pointer + DEMO_STACK_SIZE;
-
- /* 建立《一秒钟定时器线程》:初始化1秒钟后启动,间隔1秒 */
- tx_timer_create (&onesec_timer, "1s_timer", onesec_timer_entry, 0x00, 100, 100, TX_AUTO_ACTIVATE);
- }
复制代码 这两段代码,含有了嵌入式操作系统的各个主要部件和函数。其数量之多,达到变态级别,呵呵。
如有不明白之处,建议先自行购买相应书籍学习先。
在这里极力推荐本群飞鸟兄弟的大作:《嵌入式实时操作系统原理与最佳实践》。
该书以飞鸟内核(就是本次活动的配套系统Colibri_GD32F207啊)作为例子,对嵌入式操作系统进行了非常全面的诠释,非常适合新手入门!
链接是:http://item.jd.com/11545659.html
各位可以看到,上面代码最后一行,我们建立了一个《一秒钟定时器线程》,在这里就让小红板秒闪黄色的LED吧。
也就是一句代码就能切换了:- GPIOD->DOR ^= BSP_LED_YELLOW;
复制代码 抓图如下:
到这里,编译下载后,效果良好。
至于其他8个线程,它们事实上非常简单,第一个线程传递个信息给第二个,第二个又传递给第三个……以此类推。
我原本意思是做一个虐待GD32F207的代码(测试板嘛,一般都是被虐待的遍体鳞伤,才能证明真实是耐C):
按下按钮后,发送一个信号出去,里面8个线程分别通过板子的几个串口发送和回传数据,甚至其他接口也全部纳入,信号做一个级数增加,1变2,2变4……
看看我们的主角啥时候会自杀?
爽吧,呵呵。
时间太紧迫,先做个简单的内部传递先吧。
4、外部中断的引入及自定义中断处理函数
小红板板载了两个按钮,其原理图在前面展示过,分别连接到GPIOD_PIN_14和GPIOD_PIN_15。
官方附带的样板代码,把这两个按钮作为外部中断输入,分别控制了剩下的两个LED灯状态。
我们也来仿制下:- /* Configure BSP_BTN_KEYs */
- GPIO_InitStructure.GPIO_Pin = BSP_BTN_KEY1 |BSP_BTN_KEY2;
- GPIO_InitStructure.GPIO_Mode = GPIO_MODE_IN_FLOATING;//DUE TO PCB HAVE UPLINKED,HERE CAN FLOATING
- GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_50MHZ;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- /* Connect EXTI Line to pin */
- // GPIO_EXTILineConfig(GPIO_PORT_SOURCE_GPIOD, BSP_KEY1_EXTI_PIN_SOURCE | BSP_KEY2_EXTI_PIN_SOURCE);//DO NOT USE THIS
- GPIO_EXTILineConfig(GPIO_PORT_SOURCE_GPIOD, BSP_KEY1_EXTI_PIN_SOURCE);
- GPIO_EXTILineConfig(GPIO_PORT_SOURCE_GPIOD, BSP_KEY2_EXTI_PIN_SOURCE);
-
- /* Configure EXTI line */
- EXTI_DeInit(&EXTI_InitStructure);
- EXTI_InitStructure.EXTI_LINE = BSP_KEY1_EXTI_LINE | BSP_KEY2_EXTI_LINE;
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//TWO BUTTONS,SAME EXIT,ANY INPUT STATES
- EXTI_InitStructure.EXTI_LINEEnable = ENABLE;
- EXTI_Init(&EXTI_InitStructure);
- /* Enable and set EXTI Line Interrupt */
- NVIC_InitStructure.NVIC_IRQ = BSP_KEYS_EXTI_IRQChannel;
- NVIC_InitStructure.NVIC_IRQPreemptPriority = 0x0F;
- NVIC_InitStructure.NVIC_IRQSubPriority = 0x0F;
- NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
复制代码 以上代码,和样本程序基本一样的,唯一不同的地方是:
在按钮中断的触发状态定义里:
样本代码是监控了下降沿触发(提示:PCB板子是做了上拉设计):- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling
复制代码 我的代码是监控了上升沿和下降沿两个触发:- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
复制代码 新增加一个IT.C文件,导入到app目录,新增一个中断处理函数:- void IrqHandler_BTN(void){
- static ULONG xTimeKeyTriggerRising1, xTimeKeyTriggerFalling1;
- static ULONG xTimeKeyTriggerRising2, xTimeKeyTriggerFalling2;
- ULONG xTimeKeyBetweenInterrupts1, xTimeKeyBetweenInterrupts2;
-
- if(EXTI_GetIntBitState(BSP_KEY1_EXTI_LINE) != RESET){
- if(GPIO_ReadInputBit(GPIOD, BSP_BTN_KEY1) == 0){
- xTimeKeyTriggerFalling1 = tx_time_get();
- xTimeKeyTriggerRising1 = NULL;
- }
- else if(GPIO_ReadInputBit(GPIOD, BSP_BTN_KEY1) == 1){
- if(xTimeKeyTriggerFalling1 != NULL)
- xTimeKeyTriggerRising1 = tx_time_get();
- }
- if(xTimeKeyTriggerRising1 != NULL && xTimeKeyTriggerFalling1 != NULL){
- xTimeKeyBetweenInterrupts1 = xTimeKeyTriggerRising1 - xTimeKeyTriggerFalling1;
- if(xTimeKeyBetweenInterrupts1 > btnMIN_TIME_BETWEEN_010_PUSH_MS){
- xTimeKeyTriggerRising1 = NULL;
- xTimeKeyTriggerFalling1 = NULL;
- GPIOD->DOR ^= BSP_LED_RED;
- }
- }
- /* Clear the EXTI line pending bit */
- EXTI_ClearIntBitState(BSP_KEY1_EXTI_LINE);
- }
- if(EXTI_GetIntBitState(BSP_KEY2_EXTI_LINE) != RESET){
- if(GPIO_ReadInputBit(GPIOD, BSP_BTN_KEY2) == 0){
- xTimeKeyTriggerFalling2 = tx_time_get();
- xTimeKeyTriggerRising2 = NULL;
- }
- else if(GPIO_ReadInputBit(GPIOD, BSP_BTN_KEY2) == 1){
- if(xTimeKeyTriggerFalling2 != NULL)
- xTimeKeyTriggerRising2 = tx_time_get();
- }
- if(xTimeKeyTriggerRising2 != NULL && xTimeKeyTriggerFalling2 != NULL){
- xTimeKeyBetweenInterrupts2 = xTimeKeyTriggerRising2 - xTimeKeyTriggerFalling2;
- if(xTimeKeyBetweenInterrupts2 > btnMIN_TIME_BETWEEN_010_PUSH_MS){
- xTimeKeyTriggerRising2 = NULL;
- xTimeKeyTriggerFalling2 = NULL;
- GPIOD->DOR ^= BSP_LED_GREEN;
- }
- }
- /* Clear the EXTI line pending bit */
- EXTI_ClearIntBitState(BSP_KEY2_EXTI_LINE);
- }
- }
复制代码 需要特别指出的是,两个按钮共用了一个入口EXTI15_10_IRQn,我们需要在中断服务程序里面分别判断并不同处理。
其中KEY1是切换红色灯,KEY2是切换绿色灯。
到这里有经验的群友会立马发现:为啥我前面要监控按钮的上升沿和下降沿了。
好处就是可以在中断里面直接抗干扰滤波,甚至在里面嵌入更多功能,干净快捷!
5、串口的实用用法
各位看官,时间剩下两天了,看来今天要终稿。
我看到在小红板的样本代码里面,飞鸟提供了一个简易的串口交互例子,非常值得新手参考学习。
我这里仅点滴提一下,飞鸟例子里面,存储串口交互数据的是一个静态缓冲区:- static char _buffer[256];
复制代码 但如果交互数据比较多,交互非常频繁,为了节约单片机里面非常珍贵的RAM,那必须推荐另外一个更实用和巧妙地方法——环形队列,我在网上找到一张示意图如下:
至于如何使用,我在这里提供一个非常适合新手的办法——购买飞鸟的大作,然后就可以名正言顺地要求他提供相应售后服务了嘛!
这是真的,我以前使用另外一款免费RTOS——FreeRTOS时,碰到一个问题,发邮件给他的作者,结果鬼佬很直接地说——你得先买我的正版书籍,看一周后,如果还是不懂,我一定答疑!
于是我就直接用信用卡刷了美金,购买了他的两本电子书,最后问题还真的就这么自己解决了。
这个经验必须得推广。实在是不建议有些群友啥问题都张口要,同学们必须学会自己探索啊,呵呵。
6、大话GD32F207ZE的超频
首先声明:
1.在任何量产的电子产品中应用超频技术,会引来额外的风险!
2.本节内容包含的试验方法,并非全部科学准确,仅适合在有限条件下的一种主观测试。
3.本节语言风格散漫飞翔,追求大话演绎效果第一。
好了,念完经书后,我们马上进入话题了。
首先回忆一下我们当年在电脑上是如何超频的吧——大都是进入主板的BIOS,然后把CPU的倍频从1改到1.2,内存倍频从1改到1.2等等等等……
当然了,有些智能主板事先已经提供了几种模式——比如“稳定”、“平常”、“专家”。
只要选择了“专家”模式,整个电脑跑起来那就自动打了鸡血一样活蹦乱跳,你也就自然也变成“超频专家”一个!
在和同学们交流时候,你的声音特别大和兴奋,是吧?
好,先冷静下。各位看官是否还记得,我在本文第一节《强大的120MHz时钟的选择》里面提到,在计算120MHz(官方推荐的最高核心频率)时,提到了3种不同的时钟计算路径。既然我们要超频嘛,还得从这个时钟着手——还是选择最后那条路径,然后还是通过PLL.倍频寻找最大值吧,计算结果如下:- 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初始化文件的频率)- /* CK_AHB = (CK_SYS / 1) = CK_SYS */
- RCC_AHBConfig(RCC_SYSCLK_DIV1);
-
- /* CK_APB2 = (CK_AHB / 1) = CK_SYS */
- RCC_APB2Config(RCC_APB2AHB_DIV1);
-
- /* CK_PLL2 = (HSE / 5) * 8 = 40 MHz */
- RCC_PREDV2Config(RCC_PREDIV2_DIV5);
- RCC_PLL2Config(RCC_PLL2MUL_8);
- /* Enable PLL2 */
- RCC_PLL2_Enable(ENABLE);
- /* Wait till PLL2 is ready */
- while (RCC_GetBitState(RCC_FLAG_PLL2STB) == RESET){ }
-
- /* CK_PLL = (CK_PLL2 / 5) * 15 = 120 MHz */
- /* CK_PLL = (CK_PLL2 / 5) * 20 = 160 MHz */
- /* CK_PLL = (CK_PLL2 / 5) * 24 = 192 MHz */
- /* CK_PLL = (CK_PLL2 / 5) * 25 = 200 MHz */
- RCC_PREDV1Config(RCC_PREDIV1_SOURCE_PLL2, RCC_PREDIV1_DIV5);
- RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_15); //120MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_20); //160MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_24); //192MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_25); //200MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_26); //208MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_27); //216MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_28); //224MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_29); //232MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_30); //240MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_31); //248MHz
- // RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_32); //256MHz
复制代码 上面的代码中,CK_PLL2事先得到是40MHz(注意:我并不是一开始就跑峰值哦),因此下面两句代码,就能算出核心频率是官方推荐的120MHz(即40 / 5 * 15 = 120):- RCC_PREDV1Config(RCC_PREDIV1_SOURCE_PLL2, RCC_PREDIV1_DIV5);
- RCC_PLLConfig(RCC_PLLSOURCE_PREDIV1, RCC_PLLMUL_15); //120MHz
复制代码 眼尖的朋友马上发现,只要修改RCC_PLLConfig函数的第二个参数,就能得到不同的超频效果了,是吧?
于是我就有了以下的虐待笔记:- 120MHz,运行良好
- 128MHz,运行良好
- 136MHz,运行良好
- 144MHz,运行良好
- 152MHz,运行良好
- 160MHz,运行良好
- 168MHz,运行良好
- 176MHz,运行良好
- 184MHz,运行良好
- 192MHz,运行良好
- 200MHz,运行出现异常,5次重启,1次成功
- 208MHz,运行异常
- 。。。
复制代码 写到这里,有必要解释下我前面念过的3句经文了,其中第一句的意思是:
电脑的超频,和GD32F207ZE不同,电脑的CPU,纯粹就是一个运算单元,它外面还需要配备内存啊南桥啊北桥啊啥的。
电脑的超频是否成功,要看CPU所配备的内存啊南北桥啊啥的是否跟得上它快速的频率。
有过电脑超频经验的会明白,为啥我们为了让内存跟得上,有时候还得增加它的供电电压,就是这个道理。
而GD32呢,它不仅内置了RAM,也内置了AHB桥还有各种外设的桥,比如我这里跑马灯用到的APB2桥等等。
我们在这里单纯超了核心频率,但是RAM和各种外设频率会出现跟不上的情况。
我在调试时就发现,在超频至200MHz时,事实上内核仍然在跑动的,按钮中断也表现正常,但是LED始终无法点亮。
是否外设频率APB2太高跟不上呢?我于是又调整了外设的分频器:- RCC_APB2Config(RCC_APB2AHB_DIV2);
复制代码 这算下来,外设APB2也就100MHz,连官方的最高120MHz都达不到。
但事实上下载后,LED仍然无法正常工作。
所以说单片机由于内置了太多的功能,它的超频看来要比电脑CPU更难理解和操作。
在经过多次倍频和多次分频后,时钟波形肯定出现了严重变形。
我这个例子还仅仅是使用了GPIO一个外设,如果你还用到串口啊、I2C啊I2S啊,估计能否保证正常的时序都是个问题。
最终的结论就是:
GD32F207ZE在使用GPIO的情况下,能够稳定超频至192MHz,是官方标称频率的1.6倍,非常可观的结果。
当然了,如果有哪位朋友有更稳定和精密的25MHz的晶振,可以替换下小红板本身这个,或许能够取得更佳的结果了。
看到这里估计有看官提出来了,都是听哥哥你在吹牛,能否发个证明出来看看?
这个我考虑过了,我看过GD32F207的时钟图,下面这个确实可以把核心频率直接通过IO输出来,直接用示波器采集一下,更科学更有说服力啊!但是由于时间关系,先在此搁置咯。
五、总结、建议(也要加分):
从2月14日至今天2月21日,大概花了一周多的时间(大部分都是晚上),啰里啰唆的,简简单单做了几个试验。
全文至此,算是告一段落了。
总结和建议如下:- 1、GD32F207系列,是一个高频率、稳定可用的芯片,值得在产品中投入使用。
- 2、从推广上,建议兆易继续加大推广力度,大肆发放物美价廉的核心demo板子,加大飞鸟内核的附带推广。
- 3、从应用上,建议照顾微小企业,推出TSSOP,QFN32,LQFP48等小封装芯片,降低加工和返修难度。
- 4、从定位上,建议把130定位在STM8价格,估计能够占据大幅安防市场的空间,当然了,如果配备了摄像头和LAN接口的20X系列,也降价冲击这块市场,估计想不用都很难。
- 5、从体验上,建议尽快把GD内核模型,纳入到IAR或KEIL等等IDE的数据库中去,大大降低推广应用的入门难度。(另外一个方法是,大肆提供这两个编程平台的demo代码,也是个好办法)
- 6、从发展上,建议下一个版本(GD32F40X系列)的测试板,必须增加一块高清液晶屏幕,理由见下图。
- 7、从品牌上,建议推出一个板载正版JLINK OB(仅支持GD内核)的纪念版测试板,大大提高GD的品牌效应,估计人人都喊好!
复制代码
六、作品实物图+视频(加分):
七、方案代码+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)
|
|