原创 STM32学习笔记(5)--勉勉强强看懂一行程序

2010-7-8 19:33 5201 4 10 分类: MCU/ 嵌入式

继续学习中,先把开发板自带一个例子做了些精简,以免看得吓人。。。。


<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


6c431aa1-208e-40da-ba2c-d37ba186b313.jpg


就是这个,让PORTD上接的4LED分别点亮。


开始研究代码


int main(void)

 Init_All_Periph();


......


看到这一行,开始跟踪,于是又看到了下面的内容


void Init_All_Periph(void)
{
 RCC_Configuration(); 


......


继续跟踪


void RCC_Configuration(void)
{
 SystemInit(); 


......


这行代码在system_stm<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />32f10x.c中找到了。


void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;


  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CL
  RCC->CFGR &= (uint32_t)0xF8FF0000;
#else
  RCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */  
 
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;


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


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


#ifndef STM32F10X_CL
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000;
#else
  /* Reset PLL2ON and PLL3ON bits */
  RCC->CR &= (uint32_t)0xEBFFFFFF;


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


  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000;
#endif /* STM32F10X_CL */
   
  /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers */
  /* Configure the Flash Latency cycles and enable prefetch buffer */
  SetSysClock();


}


       这一长串的又是什么,如何来用呢?看来,偷懒是不成的了,只能回过头去研究STM32的时钟构成了。


       相当的复杂。


       系统的时钟可以有3个来源:内部时钟HSI,外部时钟HSE,或者PLL(锁相环模块)的输出。它们由RCC_CFGR寄存器中的SW来选择。


SW10):系统时钟切换
      
由软件置’1’或清’0’来选择系统时钟源。        在从停止或待机模式中返回时或直接或间接作为系统时钟的HSE出现故障时,由硬件强制选择HSI作为系统时钟(如果时钟安全系统已经启动)
00
HSI作为系统时钟;
01
HSE作为系统时钟;
10
PLL输出作为系统时钟;
11
:不可用。


////////////////////////////////////////////////////////////////////


PLL的输出直接送到USB模块,经过适当的分频后得到48M的频率供USB模块使用。


系统时钟的一路被直接送到I2S模块;另一路经过AHB分频后送出,送往各个系统,其中直接送往SDIFMSCAHB总线;8分频后作为系统定时器时钟;经过APB1分频分别控制PLK1、定时器TIM2~TIM7;经过APB2分频分别控制PLK2、定时器TIM1~TIM8、再经分频控制ADC


由此可知,STM32F10x芯片的时钟比之于51AVRPIC8位机要复杂复多,因此,我们立足于对着芯片手册来解读程序,力求知道这些程序代码如何使用,为何这么样使用,如果自己要改,可以修改哪些部分,以便自己使用时可以得心应手。


单步执行,看一看哪些代码被执行了。


 /* Reset the RCC clock configuration to the default reset state(for debug purpose) */


  /* Set HSION bit */


 RCC->CR |= (uint32_t)0x00000001;



 


点击看大图


这是RCC_CR寄存器,由图可见,HSION是其bit 0位。


       HSION:内部高速时钟使能


       由软件置’1或清零。


       当从待机和停止模式返回或用作系统时钟的外部4-25MHz时钟发生故障时,该位由硬件置’1来启动内部8MHzRC振荡器。当内部8MHz时钟被直接或间接地用作或被选择将要作为系统时钟时,该位不能被清零。


       0:内部8MHz时钟关闭;


       1:内部8MHz时钟开启。


///////////////////////////////////////////////////////////////////////


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


#ifndef STM32F10X_CL


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



点击看大图


这是RCC_CFGR寄存器


该行程序清零了MC0[2:0]这三位,和ADCPRE[1:0],ppre2[2:0]PPRE12:0],HPRE30,SWS10]和SW10]这16位。


/*


MCO 微控制器时钟输出,由软件置’1或清零。


0xx:没有时钟输出;


100:系统时钟(SYSCLK)输出;


101:内部8MHzRC振荡器时钟输出;


110:外部4-25MHz振荡器时钟输出;


111PLL时钟2分频后输出。


*/


/* Reset HSEON, CSSON and PLLON bits */


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


清零了PLLON,HSEBYP,HSERDY3位。


 /* Reset HSEBYP bit */


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


清零了HSEBYP ///???为什么不一次写??


       HSEBYP:外部高速时钟旁路,在调试模式下由软件置’1或清零来旁路外部晶体振荡器。只有在外部4-25MHz振荡器关闭的情况下,才能写入该位。


0:外部4-25MHz振荡器没有旁路;


1:外部4-25MHz外部晶体振荡器被旁路。


所以要先清HSEON位,再清该位。


 


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


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


清零了:USBPREPLLMULPLLXTPRPLLSRC7


 /* Disable all interrupts and clear pending bits  */


  RCC->CIR = 0x009F0000;


////这个暂不解读


SetSysClock();


跟踪进入该函数,可见一连串的条件编译:



3e989fdb-6bf3-4271-b6c0-88fb9ced6ded.jpg


单步运行,执行的是:


#elif defined SYSCLK_FREQ_72MHz


  SetSysClockTo72();


为何执行该行呢,找到SYSCLK_PREQ_**的相关定义,如下图所示。



72e657f0-45f3-45c9-acf2-b2cde617fd65.gif


       这样就得到了我们所要的一个结论:如果要更改系统工作频率,只需要在这里更改就可以了。


    可以继续跟踪进入这个函数来观察如何将工作频率设定为72MHz的。


static void SetSysClockTo72(void)


{


  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;


 


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


  /* Enable HSE */   


  RCC->CR |= ((uint32_t)RCC_CR_HSEON);


 //开启HSE


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


  do


  {


    HSEStatus = RCC->CR & RCC_CR_HSERDY;


    StartUpCounter++; 


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


//等待HSE确实可用,这有个标志,即RCC_CR寄存器中的HSERDY位(bit 17),这个等待不会无限长,有个超时策略,即每循环一次计数器加1,如果计数的次数超过HSEStartUp_TimeOut,就退出循环,而这个HSEStartUp_TimeOutstm32f10x.h中定义,


#define HSEStartUp_TimeOut   ((uint16_t)0x0500) /*!< Time out for HSE start up */


///////////////////////////////////////////////////////////////////////////////////////////////


  if ((RCC->CR & RCC_CR_HSERDY) != RESET)


  {


    HSEStatus = (uint32_t)0x01;


  }


  else


  {


    HSEStatus = (uint32_t)0x00;


  } 


///再次判断HSERDY标志位,并据此给HSEStatus变量赋值。


  if (HSEStatus == (uint32_t)0x01)


  {


    /* Enable Prefetch Buffer */


    FLASH->ACR |= FLASH_ACR_PRFTBE;


 


    /* Flash 2 wait state */


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


    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;   


 


 


    /* HCLK = SYSCLK */


    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;


   //找到定义: #define  RCC_CFGR_HPRE_DIV1                  ((uint32_t)0x00000000)        /*!< SYSCLK not divided */ 


 


    /* PCLK2 = HCLK */


    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;


  //找到定义:#define  RCC_CFGR_PPRE2_DIV1                 ((uint32_t)0x00000000)        /*!< HCLK not divided */


 


    /* PCLK1 = HCLK */


    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;


//找到定义:#define  RCC_CFGR_PPRE1_DIV2                 ((uint32_t)0x00000400)        /*!< HCLK divided by 2 */


 


#ifdef STM32F10X_CL


    ……


  #else   


    /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */


    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |


                                        RCC_CFGR_PLLMULL));


    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);


#endif /* STM32F10X_CL */


//以上是设定PLL的倍频系数为9,也就是说,这个72M是在外部晶振为8M时得到的。


    /* Enable PLL */


    RCC->CR |= RCC_CR_PLLON;


 


    /* Wait till PLL is ready */


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


    {


    }


        /* Select PLL as system clock source */


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


    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;   


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


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


    {


    }


  }


  else


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


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


    /* Go to infinite loop */


    while (1)


    {


    }


  }


}


至此,我们可以归纳几条:


(1)       时钟源有3


(2)       开机时默认是HSI起作用,可以配置为所要求的任意一个时钟


(3)       配置时必须按一定的顺序来打开或都关闭一些位,并且各时钟起作用有一定的时间,因此要利用芯片内部的标志位来判断是否可以执行下一步。


(4)       如果外部时钟、PLL输出失效,系统可以自动回复到HSI(开启时钟安全系统)


(5)       HSI的频率准确度可以达到+/- 1%,如果有必要时,还可以用程序来调整这个频率,可调的范围大致在200KHz左右。


最后让我们来感受一下劳动的果实吧--试着改改频率看有何反应。


为查看更改后的效果,先记录更改前的数据。将调试切换到仿真,在第一条:


Delay(0xAFFFF);


指令执行前后,分别记录下Status和Sec


Status:2507      3606995


Sec:0.00022749   0.05028982


将振荡频率更改为36MHz,即


...


 #define SYSCLK_FREQ_36MHz  36000000   //去掉该行的注释
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
/*#define SYSCLK_FREQ_72MHz  72000000*/  //将该行加上注释


再次运行,结果如下:


Status:2506      3606994


Sec:0.00008478   0.10036276


    基本上是延时时间长了一倍。改成硬件仿真,将代码写入板子,可以看到LED闪烁的频率明显变慢了。

文章评论6条评论)

登录后参与讨论

用户377235 2012-12-13 22:41

不错的初学者笔记,让人程序快速入门,谢谢!!!

teach51_109853927 2012-3-19 21:53

呵呵,这里部分内容是跟踪到库里面去了,,,自己还是用的库函数。用库函数也要知其所以然,因此就跟踪进去看看咯。

用户414980 2012-2-28 20:06

楼主好像没有用ST库函数中关于时钟RCC的函数,而是自己编写的底层驱动?

用户1190637 2010-8-2 12:27

厉害了

teach51_109853927 2010-7-13 09:42

用插入图片的方法,从本地硬盘上传的。

用户220339 2010-7-13 00:55

你这图片怎么贴上去的?
相关推荐阅读
teach51_109853927 2015-10-25 22:37
带“锁定输出”的数控稳压电源(二)
3.控制程序编写        硬件电路设计完成后,就要编写软件了。软件基本上可以分为键盘驱动,PWM生成,功能实现等部分。 3.1 键盘处理程序        STC单片机的引脚可以...
teach51_109853927 2014-03-02 12:17
带“锁定输出”的数控稳压电源(一)
本文是探索“开源培训”思想的作品。 所谓“开源培训”是指在某个社区,各志愿者提供有一定实用价值和教学价值的作品,同时详细说明制作过程,并提供制作所需的各类资源,帮助入门者快速找到适合自己的项目...
teach51_109853927 2014-02-22 21:15
《单片机项目教程》、《单片机项目教程C语言版》电子课件
《单片机项目教程》电子课件 http://pan.baidu.com/s/1kT2Yb6f 《单片机项目教程C语言版》电子课件 http://pan.baidu.com/s/1gd5O...
teach51_109853927 2014-02-22 21:05
《单片机应用与接口技术》相关资源
单片机应用与接口技术    机械工业出版社出版,江苏省教育厅推荐教材 《单片机应用与接口技术》课件 http://pan.baidu.com/s/1c0kpzUg 《单片机应用与接...
teach51_109853927 2014-02-22 15:25
便携式无线抢答器
本产品专门为开展活动时需要用到抢答器的场合设计,已制作成品,读者可以参考作为练手之用。 如图1所示左边为信号接收装置(主持人用),右边两个为发射装置(比赛选手用)。都只需要使用干电池供电即可。...
teach51_109853927 2014-02-22 15:22
Modbus通讯协议
Modbus协议最初由Modicon公司开发出来,在1979年末该公司成为施耐德自动化(Schneider Automation)部门的一部分,现在Modbus已经是工业领域全球最流行的协议。此协...
我要评论
6
4
关闭 站长推荐上一条 /2 下一条