stm32系统时钟详解&&移植

发布者:Xiaochen520最新更新时间:2016-12-28 来源: eefocus关键字:stm32系统时钟详解 手机看文章 扫描二维码
随时随地手机看文章

写作原由:今日接手用stm32f100xx芯片开发的项目,以前用的是stm8s 和stm32f103xx芯片;因为在别人的项目代码的基础上做2次开发,但是发现那个代码main函数中没有对系统时钟的设置的相关函数,一直纳闷,但也没有深究,直至昨日 调试时出现串口收发数据出错,源代码在原项目的板子上串口发送、接收数据正常,同样程序在项目板子上收发的数据不正确, 两块板子芯片一样,串口收发管脚一样,最后发现原来板子外部晶振是8MHZ ,新板子外部晶振是12MHZ;  而在STM32固件库中,默认的外部晶振是8MHZ,由于时钟源不正确,导致波特率不正确,当然收发的数据也不正确了.....我勒个去!都怪自己平时看问题“不求甚解”。

(波特率与时钟源关系公式:IntegerDivider = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate)))

为了深入思考,我提出了一些疑问,通过解答这些问题更深入的了解时钟;

问题一:对系统时钟的设置的相关函数,在main函数中找不到,那在哪里?还是说根本没有对系统时钟进行配置?

问题二:既然用了外部晶振做系统时钟,那为什么没看到启动外部晶振的操作?

问题三:串口的时钟源是什么?串口时钟源跟系统时钟的关系?

问题四:程序中串口时钟源是怎样实现对波特率的关系的?

现附上我找到上面3个问题答案做的一张框架图:希望博友参考此图理解我后面的内容:

 

在进入主题之前我们先了解一些必要的基础知识----stm32系列芯片的种类和型号:

startup_stm32f10x_cl.s 互联型的器件,STM32F105xx,STM32F107xx
startup_stm32f10x_hd.s 大容量的STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_hd_vl.s 大容量的STM32F100xx
startup_stm32f10x_ld.s 小容量的STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_ld_vl.s 小容量的STM32F100xx
startup_stm32f10x_md.s 中容量的STM32F101xx,STM32F102xx,STM32F103xx
startup_stm32f10x_md_vl.s 中容量的STM32F100xx  (我项目中用的是此款芯片 stm32f100CB)
startup_stm32f10x_xl.s FLASH在512K到1024K字节的STM32F101xx,STM32F102xx,STM32F103xx


cl:互联型产品,stm32f105/107系列
vl:超值型产品,stm32f100系列
xl:超高密度产品,stm32f101/103系列
ld:低密度产品,FLASH小于64K
md:中等密度产品,FLASH=64 or 128
hd:高密度产品,FLASH大于128

 

(下图中的startup_stm32f10_ld.s应改为:startup_stm32f10x_md_vl.s)

问题一:对系统时钟的设置的相关函数,在main函数中找不到,那在哪里?还是说根本没有对系统时钟进行配置?

解答:高人指点,我看了一下启动文件startup_stm32f10x_md_vl.s,其中有一段汇编:


; Reset handler routine  

  1. Reset_Handler    PROC  

  2.                  EXPORT  Reset_Handler             [WEAK]  

  3.      IMPORT  __main  

  4.      IMPORT  SystemInit  

  5.                  LDR     R0, =SystemInit  

  6.                  BLX     R0  

  7.                  LDR     R0, =__main  

  8.                  BX      R0  

  9.                  ENDP  

  10.   

其中,

IMPORT __main IMPORT SystemInit      //IMPORT 声明了需要引用C语言中的main函数、systeminit函数,
LDR R0, = systeminint       //把systeminit 函数地址放到r0 寄存器;

BLX   R0    //跳到到R0 寄存器中的地址执行;

所以,在跳到main函数执行前,已经在SystemInit 函数中把系统时钟给设置好了;

 

问题二:既然用了外部晶振做系统时钟,那为什么没看到启动外部晶振的操作?

我们在解答这个问题前,先到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.   

  7.   /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */  

  8. #ifndef STM32F10X_CL  

  9.   RCC->CFGR &= (uint32_t)0xF8FF0000;  

  10. #else  

  11.   RCC->CFGR &= (uint32_t)0xF0FF0000;  

  12. #endif /* STM32F10X_CL */     

  13.     

  14.   /* Reset HSEON, CSSON and PLLON bits */  

  15.   RCC->CR &= (uint32_t)0xFEF6FFFF;  

  16.   

  17.   /* Reset HSEBYP bit */  

  18.   RCC->CR &= (uint32_t)0xFFFBFFFF;  

  19.   

  20.   /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */  

  21.   RCC->CFGR &= (uint32_t)0xFF80FFFF;  

  22.   

  23. #ifdef STM32F10X_CL  

  24.   /* Reset PLL2ON and PLL3ON bits */  

  25.   RCC->CR &= (uint32_t)0xEBFFFFFF;  

  26.   

  27.   /* Disable all interrupts and clear pending bits  */  

  28.   RCC->CIR = 0x00FF0000;  

  29.   

  30.   /* Reset CFGR2 register */  

  31.   RCC->CFGR2 = 0x00000000;  

  32. #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL)   

  33.   /* Disable all interrupts and clear pending bits  */  

  34.   RCC->CIR = 0x009F0000;  

  35.   

  36.   /* Reset CFGR2 register */  

  37.   RCC->CFGR2 = 0x00000000;        

  38. #else  

  39.   /* Disable all interrupts and clear pending bits  */  

  40.   RCC->CIR = 0x009F0000;  

  41. #endif /* STM32F10X_CL */  

  42.       

  43.   /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */  

  44.   /* Configure the Flash Latency cycles and enable prefetch buffer */  

  45.   SetSysClock();  

  46.   

  47. #ifdef STM32F10X_HD  

  48.   #ifdef DATA_IN_ExtSRAM  

  49.     SystemInit_ExtMemCtl();   

  50.   #endif /* DATA_IN_ExtSRAM */  

  51. #endif /* STM32F10X_HD */    

  52.   

  53. }  

我们进入setsysclock()函数,看其中作了哪些操作:
 


  1. /**  

  2.   * @brief  Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.  

  3.   * @param  None  

  4.   * @retval None  

  5.   */  

  6. static void SetSysClock(void)  

  7. {  

  8. #ifdef SYSCLK_FREQ_HSE  

  9.   SetSysClockToHSE();  

  10. #elif defined SYSCLK_FREQ_24MHz  

  11.   SetSysClockTo24();  

  12. #elif defined SYSCLK_FREQ_36MHz  

  13.   SetSysClockTo36();  

  14. #elif defined SYSCLK_FREQ_48MHz  

  15.   SetSysClockTo48();  

  16. #elif defined SYSCLK_FREQ_56MHz  

  17.   SetSysClockTo56();    

  18. #elif defined SYSCLK_FREQ_72MHz  

  19.   SetSysClockTo72();  

  20. #endif  

  21.    

 

我项目中用的是此款芯片 stm32f100CB,内部晶振是24MHZ, 所以应进入执行SetSysClockTo24() 函数:


  1. /**  

  2.   * @brief  Sets System clock frequency to 24MHz and configure HCLK, PCLK2   

  3.   *          and PCLK1 prescalers.  

  4.   * @note   This function should be used only after reset.  

  5.   * @param  None  

  6.   * @retval None  

  7.   */  

  8. static void SetSysClockTo24(void)  

  9. {  

  10.   __IO uint32_t StartUpCounter = 0, HSEStatus = 0;  

  11.     

  12.   /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/      

  13.   /* Enable HSE */      

  14.   RCC->CR |= ((uint32_t)RCC_CR_HSEON); //开启外部晶振  

  15.    

  16.   /* Wait till HSE is ready and if Time out is reached exit */  

  17.   do  

  18.   {  

  19.     HSEStatus = RCC->CR & RCC_CR_HSERDY;  

  20.     StartUpCounter++;    

  21.   } while((HSEStatus == 0) && (StartUpCounter != HSEStartUp_TimeOut));  

  22.   

  23.   if ((RCC->CR & RCC_CR_HSERDY) != RESET)//等待外部晶振起振  

  24.   {  

  25.     HSEStatus = (uint32_t)0x01;  

  26.   }  

  27.   else  

  28.   {  

  29.     HSEStatus = (uint32_t)0x00;  

  30.   }    

  31.   

  32.   if (HSEStatus == (uint32_t)0x01)  

  33.   {  

  34. #if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL   

  35.     /* Enable Prefetch Buffer */  

  36.     FLASH->ACR |= FLASH_ACR_PRFTBE;  

  37.   

  38.     /* Flash 0 wait state */  

  39.     FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);  

  40.     FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;      

  41. #endif  

  42.    

  43.     /* HCLK = SYSCLK */  

  44.     RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;  

  45.         

  46.     /* PCLK2 = HCLK */  

  47.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;  

  48.       

  49.     /* PCLK1 = HCLK */  

  50.     RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;  

  51.       

  52. #ifdef STM32F10X_CL  

  53.     /* Configure PLLs ------------------------------------------------------*/  

  54.     /* PLL configuration: PLLCLK = PREDIV1 * 6 = 24 MHz */   

  55.     RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);  

  56.     RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |   

  57.                             RCC_CFGR_PLLMULL6);   

  58.   

  59.     /* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */  

  60.     /* PREDIV1 configuration: PREDIV1CLK = PLL2 / 10 = 4 MHz */         

  61.     RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |  

  62.                               RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);  

  63.     RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |  

  64.                              RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV10);  

  65.     

  66.     /* Enable PLL2 */  

  67.     RCC->CR |= RCC_CR_PLL2ON;  

  68.     /* Wait till PLL2 is ready */  

  69.     while((RCC->CR & RCC_CR_PLL2RDY) == 0)  

  70.     {  

  71.     }     

  72. #elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL)  

  73.     /*  PLL configuration:  = (HSE / 2) * 6 = 24 MHz */  

  74.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));  

  75.     RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1_Div2 | RCC_CFGR_PLLMULL6);  

  76. #else      

  77.     /*  PLL configuration:  = (HSE / 2) * 6 = 24 MHz */  

  78.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));  

  79.     RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_HSE_Div2 | RCC_CFGR_PLLMULL6);  

  80. #endif /* STM32F10X_CL */  

  81.   

  82.     /* Enable PLL */  

  83.     RCC->CR |= RCC_CR_PLLON;  

  84.   

  85.     /* Wait till PLL is ready */  

  86.     while((RCC->CR & RCC_CR_PLLRDY) == 0)  

  87.     {  

  88.     }  

  89.   

  90.     /* Select PLL as system clock source */  

  91.     RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));  

  92.     RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;      

  93.   

  94.     /* Wait till PLL is used as system clock source */  

  95.     while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)  

  96.     {  

  97.     }  

  98.   }  

  99.   else  

  100.   { /* If HSE fails to start-up, the application will have wrong clock   

  101.          configuration. User can add here some code to deal with this error */  

  102.   }   

  103. }  

  104. #elif defined SYSCLK_FREQ_36MHz  

解析:

void SetSysClockTo24函数中主要做了下面几件事:(不同颜色功能分别对应上面对应颜色代码部分)

1.启动外部晶振作为系统时钟源;

2.等待外部晶振起振;

3.(起振后)对系统时钟源进行分频:(默认外部是HSE--8MHZ晶振)  PLL configuration: = (HSE / 2) * 6 = 24 MHz

4. 配置HCLK、PCLK1、PCLK2 与系统时钟源的关系;

 

问题三:串口的时钟源是什么?串口时钟源跟系统时钟的关系?

回答这个问题前,我们先来看stm32f100芯片手册数据图(第12页):

从图里面我们可看出串口1的时钟源是PCLK1;

上面问题2的代码中我们看到PCLK1是时钟源24MHZ的1分频,即还是24MHZ;

 

问题四:程序中串口时钟源是怎样实现对波特率的关系的?

由问题3我们知道串口时钟源是24mhz,依据公式:IntegerDivider = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate)))

库中自动设置IntegerDivider 存入寄存器中;

 

 

附加: 转自http://blog.sina.com.cn/s/blog_542bad910101g1gu.html

(1)

在KEIL下可以在项目的选项C/C++/PREPROMCESSOR symbols的Define栏里定义,比如STM32F10X_CL

也可以在STM32F10X.H里用宏定义

#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL)
           
   #define STM32F10X_HD     
             
#endif

 

(2)
如果芯片更换,除了做如上所述的更改外,还需以下几步

第一步  system_stm32f10x.c的系统主频率,依实际情况修改

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL)

 #define SYSCLK_FREQ_24MHz  24000000
#else
#define SYSCLK_FREQ_72MHz  72000000
#endif

另外外部时钟在文件:stm32f10x.h 依实际修改

第二步 定时器的参数依系统主时钟做适当修改

第三步 flash地址misc.h中的NVIC_VectTab_Flash 0x08000000 要与KEIL选项target的IROM1的地址一致,如果是IAP程序,依ISP程序占用大小,APP的FLASH地址向后延,比如0X8002000,那么KEIL选项target的IROM1的地址也要就0x8002000,SIZE因为ISP占用了2000,所以就为0x40000-0x2000,即只能填写0X3E000

 

 ISP程序与APP程序连接

 打开 User 选项卡  在 Run User Programs Before Build/Rebuild 中,勾选 Run#1,并在
其中填入
        D:\Keil\ARM\BIN40\fromelf.exe --bin -o ./obj/Project.bin ./obj/Project.axf
              其中,Project.bin 和 Project.axf 要和 Output 选项卡中的 Name  of  Executable  的名字

 

IAP我的总结

1 先FLASH_Unlock();

2 小于或等于128K的STM每页为1k bytes,大于128K的每页为2K BYTES,减去从地址0x8002000占用的0x2000后,算出页数,比如IAP占用8K,则64K的MD的STM32F系列用for(i=0;i<(64-8);i++) FLASH_ErasePage(0x8002000+0x400*i);循环按页擦除FLASH

3 按从外部串口获取到的数据,FLASH_ProgramWord(address,dat);//注意是按4字节方式写入的
 if (*(uint32_t*)address!= dat)//字编程后校验


关键字:stm32系统时钟详解 引用地址:stm32系统时钟详解&&移植

上一篇:18B20温度传感器详解
下一篇:STM32菜鸟成长记录---AD7792遇到的问题

小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多精选电路图
换一换 更多 相关热搜器件
更多每日新闻
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved