查看: 31288|回复: 12

[7月赛] 【麒麟传说】麒麟座开发板初探+库的移植+时钟树的配置

  [复制链接]

2

主题

6

帖子

19

积分

新手上路

Rank: 1

积分
19
发表于 2017-7-31 16:48:37 | 显示全部楼层 |阅读模式
    刚接触到OneNET开放平台和麒麟开发板的时候内心自然是一阵欢喜,因为有了这样的平台支持,就能够将自己的想法快速的实现了。    闲言少叙接下来上点实用的东西,就像标题所说的“麒麟座开发板初探+库的移植+时钟树的配置”,我会按照这个步奏给大家分享一下我这一段时间对麒麟开发板使用过程遇到的一些问题和问题解决之后的一些个人见解。
    声明:以下纯属个人见解,如有不对之处还请各位大佬指正,如因按照本人的操作步奏对你和你的财产造成的损失,本人将概不负责,亦不承担任何法律责任,特此声明!
  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     对于麒麟座开发板的使用这里就不在介绍,因为自己也是刚接触,肯定没有历程上讲解的好,大家可以去官网上下载相应的历程,里面有相应额操作手册,能够帮助快速连上云端。
     下面就分享一下自己在学习过程中遇到的问题,在刚接触到麒麟开发板的时候,很多人应该跟我一样先把板子打开,下载历程挨个测试功能是否能够使用,但是测试之后会发现,板子中使用的代码风格与自己经常使用的代码不是一样的,像我这种“强迫症”的肯定受不了啊,于是乎我就萌生了一种想法,为何不把麒麟开发板的代码库文件换成自己经常使用的库呢,再有着这种想法之后就开始着手将程序移植到自己的库文件上,本想着移植不就是将相应的源文件和头文件添加进来就行了,后来才发现自己太天真了,刚移植一个串口的程序,就一直调试不通,无论怎样调试怎样配置波特率都是输出的乱码,怎么办??????
     这时我想起了万能的度娘,于是就上百度“渡”了一下,后来看到原来是时钟树的“锅”!由于以前使用的板子大都是外置8MHz晶振的,但是麒麟开发板是12MHz这就造成了在移植程序的时候遇到的各种乱码!
下面不是一条分割线
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
废话不多说直接召唤神龙---------时钟树

STM32时钟树

STM32时钟树

在开始说时钟树之前先说一下STM32的启动文件,因为一会要用到它。
先解释一下启动代码中常用的一些汇编指令
以下代码全部来至于历程:“OneNET_Demo_M6311_EDP_SHT20”
书写形式为  : 指令+解释
EQU+ 给数字常量取一个符号名,相当于 C 语言中的 define
  1. Stack_Size      EQU     0x00000400
复制代码
声明堆栈的大小
AREA+汇编一个新的代码段或者数据段
  1. AREA    RESET, DATA, READONLY
  2.                 EXPORT  __Vectors
  3.                 EXPORT  __Vectors_End
  4.                 EXPORT  __Vectors_Size
复制代码
声明一个,只读的,名为RESET的数据段
SPACE+分配内存空间
  1. Heap_Size       EQU     0x00000200

  2.                 AREA    HEAP, NOINIT, READWRITE, ALIGN=3
  3. __heap_base
  4. Heap_Mem        SPACE   Heap_Size
  5. __heap_limit

  6.                 PRESERVE8
  7.                 THUMB
复制代码
分配大小为Heap_Size的内存空间
PRESERVE8+当前文件堆栈需按照 8 字节对齐
EXPORT+声明一个标号具有全局属性,可被外部的文件使用
DCD+以字为单位分配内存,要求 4 字节对齐,并要求初始化这些内存
PROC+定义子程序,与 ENDP 成对使用,表示子程序结束
WEAK+弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不出错。
IMPORT+声明标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似
B+跳转到一个标号
ALIGN+编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即数
  1. Heap_Size       EQU     0x00000200

  2.                 AREA    HEAP, NOINIT, READWRITE, ALIGN=3
  3. __heap_base
  4. Heap_Mem        SPACE   Heap_Size
  5. __heap_limit

  6.                 PRESERVE8
  7.                 THUMB
复制代码
ALIGN=3 说明是以2^3对齐,也就是8字节对齐
END+到达文件的末尾,文件结束
IF,ELSE,ENDIF+汇编条件分支语句,跟 C 语言的类似
LDR+从存储器中加载字到一个寄存器中
BL+跳转到由寄存器/标号给出的地址,并把跳转前的下条指令地址保存到 LR
BLX+跳转到由寄存器给出的地址,并根据寄存器的 LSE 确定处理器的状态,还要把跳转前的下条指令地址保存到 LR
BX+跳转到由寄存器/标号给出的地址,不用返回
了解完上面的基本的汇编语句大家可以把启动文件理解的差不多了,接下来就在汇编中找到今天的主角,大家找到复位服务函数,也就是启动代码的146~155行大家会看到这样几行代码:
  1. ; Reset handler
  2. Reset_Handler   PROC
  3.                 EXPORT  Reset_Handler             [WEAK]
  4.                 IMPORT  __main
  5.                 IMPORT  SystemInit
  6.                 LDR     R0, =SystemInit
  7.                 BLX     R0               
  8.                 LDR     R0, =__main
  9.                 BX      R0
  10.                 ENDP
复制代码
这几行代码就是今天的主角,也就是汇编中的复位服务函数,有连个函数名字大家肯定很熟悉那就是__main和SystemInit是不是很眼熟!对就是主函数还有系统初始化函数,可以说没有这两个函数的调用那STM32板子是不会运行的。
在这里大家点击鼠标左键,选择“Go To Definition Of 'SystemInit”,进去之后会看到系统初始化函数的真面目。
  1. void SystemInit (void)
  2. {
  3.   /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  4.   /* Set HSION bit */
  5.   RCC->CR |= (uint32_t)0x00000001;

  6.   /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
  7. #ifndef STM32F10X_CL
  8.   RCC->CFGR &= (uint32_t)0xF8FF0000;
  9. #else
  10.   RCC->CFGR &= (uint32_t)0xF0FF0000;
  11. #endif /* STM32F10X_CL */   
  12.   
  13.   /* Reset HSEON, CSSON and PLLON bits */
  14.   RCC->CR &= (uint32_t)0xFEF6FFFF;

  15.   /* Reset HSEBYP bit */
  16.   RCC->CR &= (uint32_t)0xFFFBFFFF;

  17.   /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  18.   RCC->CFGR &= (uint32_t)0xFF80FFFF;

  19. #ifdef STM32F10X_CL
  20.   /* Reset PLL2ON and PLL3ON bits */
  21.   RCC->CR &= (uint32_t)0xEBFFFFFF;

  22.   /* Disable all interrupts and clear pending bits  */
  23.   RCC->CIR = 0x00FF0000;

  24.   /* Reset CFGR2 register */
  25.   RCC->CFGR2 = 0x00000000;
  26. #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  27.   /* Disable all interrupts and clear pending bits  */
  28.   RCC->CIR = 0x009F0000;

  29.   /* Reset CFGR2 register */
  30.   RCC->CFGR2 = 0x00000000;   
  31. #else
  32.   /* Disable all interrupts and clear pending bits  */
  33.   RCC->CIR = 0x009F0000;
  34. #endif /* STM32F10X_CL */
  35.    
  36. #if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)
  37.   #ifdef DATA_IN_ExtSRAM
  38.     SystemInit_ExtMemCtl();
  39.   #endif /* DATA_IN_ExtSRAM */
  40. #endif

  41.   /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  42.   /* Configure the Flash Latency cycles and enable prefetch buffer */
  43.   SetSysClock();         //+++++++++++++++++++++++我绝对不会告诉你这里是重点++++++++++++++++++++++++++++++++++++++++++++//

  44. #ifdef VECT_TAB_SRAM
  45.   SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
  46. #else
  47.   SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
  48. #endif
  49. }
复制代码
上面的一些代码主要是说打开中断,配置寄存器,这些对我们移植程序没有影响就不多介绍,感兴趣的同学可以去参考《STM32中文参考手册_V10》主要关注的就是函数SetSysClock();
大家再次的“Go To Definition Of 'SetSysClock()”,会看到这样一个函数:
  1. static void SetSysClock(void)
  2. {
  3. #ifdef SYSCLK_FREQ_HSE
  4.   SetSysClockToHSE();
  5. #elif defined SYSCLK_FREQ_24MHz
  6.   SetSysClockTo24();
  7. #elif defined SYSCLK_FREQ_36MHz
  8.   SetSysClockTo36();
  9. #elif defined SYSCLK_FREQ_48MHz
  10.   SetSysClockTo48();
  11. #elif defined SYSCLK_FREQ_56MHz
  12.   SetSysClockTo56();  
  13. #elif defined SYSCLK_FREQ_72MHz
  14.   SetSysClockTo72();
  15. #endif

  16. /* If none of the define above is enabled, the HSI is used as System clock
  17.     source (default after reset) */
  18. }
复制代码
选择任意一个#define 后面的进行查找,以“SYSCLK_FREQ_24MHz”为例:
2.png
双击搜索结果


3.png
来到了
  1. #if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
  2. /* #define SYSCLK_FREQ_HSE    HSE_VALUE */
  3. #define SYSCLK_FREQ_24MHz  24000000
  4. #else
  5. /* #define SYSCLK_FREQ_HSE    HSE_VALUE */
  6. /* #define SYSCLK_FREQ_24MHz  24000000 */
  7. /* #define SYSCLK_FREQ_36MHz  36000000 */
  8. /* #define SYSCLK_FREQ_48MHz  48000000 */
  9. /* #define SYSCLK_FREQ_56MHz  56000000 */
  10. #define SYSCLK_FREQ_72MHz  72000000
  11. #endif
复制代码
因为启动框中填写的是STM32F10X_HD, USE_STDPERIPH_DRIVER,也就是大容量产品如下图所示:
4.png
所以上面的宏编译会编译下面的代码,也就是编译#define SYSCLK_FREQ_72MHz 72000000 这也就说明我们的板子的系统是72MHz的。
当然大家要是想降低自己的系统时钟的频率,在这里可以更改成自己想要的时钟频率,接下来会与大家分享如何更改系统的时钟以及更换外置晶振。
更改自己外部晶振只需要三步就可以了
第一步:
5.png
第二步:将8MHz更改为12MHz
6.png
第三步:找到下面的函数 更改的地方我已在代码中声明
  1. static void SetSysClockTo72(void)
  2. {
  3.   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  4.   
  5.   /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/   
  6.   /* Enable HSE */   
  7.   RCC->CR |= ((uint32_t)RCC_CR_HSEON);

  8.   /* Wait till HSE is ready and if Time out is reached exit */
  9.   do
  10.   {
  11.     HSEStatus = RCC->CR & RCC_CR_HSERDY;
  12.     StartUpCounter++;  
  13.   } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  14.   if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  15.   {
  16.     HSEStatus = (uint32_t)0x01;
  17.   }
  18.   else
  19.   {
  20.     HSEStatus = (uint32_t)0x00;
  21.   }  

  22.   if (HSEStatus == (uint32_t)0x01)
  23.   {
  24.     /* Enable Prefetch Buffer */
  25.     FLASH->ACR |= FLASH_ACR_PRFTBE;

  26.     /* Flash 2 wait state */
  27.     FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
  28.     FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;   


  29.     /* HCLK = SYSCLK */
  30.     RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  31.       
  32.     /* PCLK2 = HCLK */
  33.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
  34.    
  35.     /* PCLK1 = HCLK */
  36.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;

  37. #ifdef STM32F10X_CL
  38.     /* Configure PLLs ------------------------------------------------------*/
  39.     /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
  40.     /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */
  41.         
  42.     RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |
  43.                               RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
  44.     RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |
  45.                              RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);
  46.   
  47.     /* Enable PLL2 */
  48.     RCC->CR |= RCC_CR_PLL2ON;
  49.     /* Wait till PLL2 is ready */
  50.     while((RCC->CR & RCC_CR_PLL2RDY) == 0)
  51.     {
  52.     }
  53.    
  54.    
  55.     /* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */
  56.     RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
  57.     RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
  58.                             RCC_CFGR_PLLMULL9);
复制代码
到这里你就彻底的将自己时钟更换成了12MHz的了,接下来闲聊一下时钟的配置。
将上面的代码负责一份:
  1. static void SetSysClockTo72(void)
  2. {
  3.   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  4.   
  5.   /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/   
  6.   /* Enable HSE */
复制代码
下面着重对:
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;

/* PCLK1 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;进行说明;






第一句:
/* HCLK = SYSCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;     //若想/HCLK = SYSCLK 如图:
6.png
所以这句话:RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; 的意思就是说设置  AHB  Prescaler的预分频值为1,只有 AHB  Prescaler的预分频值为1的时候才能够达到条件HCLK = SYSCLK
第二句:
/* PCLK2 = HCLK */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
结合时钟树图:
7.png
若想使/* PCLK2 = HCLK */就必须需设置APB2预分频值为1,也就是RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;只有这样才能够使/* PCLK2 = HCLK */


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


同理第三句:
/* PCLK1 = HCLK */  //注意   这是官方给的,你的程序里也应该是这样,这里的注释应该是错的,应该改成:/* PCLK1 = HCLK/2 */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;进行说明;
结合时钟树
8.png
可以看出这句代码的意思是设置APB1的预分频因子为2,也就是在系统时钟频率设置为72MHz的时候PLCK1的时钟频率设置为36MHz。






以上见解纯属个人见解,有不对之处还请各位大佬指正。











[attach]4063[/a OneNET_Demo_M6311_EDP_SHT20.zip (4.35 MB, 下载次数: 2913)
5.png
回复

举报

0

主题

85

帖子

246

积分

中级会员

Rank: 3Rank: 3

积分
246
发表于 2017-8-23 15:16:58 | 显示全部楼层
LZ说出了很实际的东西,时钟很关键,你太棒了!

2

主题

6

帖子

19

积分

新手上路

Rank: 1

积分
19
 楼主| 发表于 2017-8-22 10:28:33 | 显示全部楼层
李冰洋 发表于 2017-8-10 00:34
麒麟座移植到stm32f103指南者,烧上官网那个上传二进制文件的例程,帖子中第一步和第二步这两个地方都和您 ...

使用的是标准库吗?还有波特率设置对吗?

0

主题

6

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 2017-8-17 14:25:56 | 显示全部楼层
还漏了一步哦~

0

主题

1

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2017-8-10 00:34:18 | 显示全部楼层
麒麟座移植到stm32f103指南者,烧上官网那个上传二进制文件的例程,帖子中第一步和第二步这两个地方都和您说的一样(第一步改过了,第二步不知道为什么我原本就是12M),然而收到的为什么还是乱码

0

主题

1

帖子

2

积分

新手上路

Rank: 1

积分
2
发表于 2017-8-4 21:42:42 | 显示全部楼层
大牛回来求带啊

0

主题

1

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2017-8-1 18:20:33 | 显示全部楼层
过来膜拜一下大牛,千人计划团队走出来帅哥

2

主题

6

帖子

19

积分

新手上路

Rank: 1

积分
19
 楼主| 发表于 2017-8-1 14:13:46 | 显示全部楼层

在哪?求抱大腿

2

主题

6

帖子

19

积分

新手上路

Rank: 1

积分
19
 楼主| 发表于 2017-8-1 10:28:43 | 显示全部楼层

2

主题

6

帖子

19

积分

新手上路

Rank: 1

积分
19
 楼主| 发表于 2017-8-1 10:27:51 | 显示全部楼层
NUPT-CY 发表于 2017-7-31 16:54
向大佬低头。ID头像好评!

没有大佬,只有新手,共同学习

1

主题

5

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 2017-8-1 09:03:10 | 显示全部楼层

3

主题

6

帖子

14

积分

新手上路

Rank: 1

积分
14
发表于 2017-7-31 16:54:55 | 显示全部楼层
向大佬低头。ID头像好评!

3

主题

7

帖子

22

积分

新手上路

Rank: 1

积分
22
发表于 2017-8-1 10:12:48 | 显示全部楼层
大佬!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表