STM32 systick做为系统时间使用(非简单延时)

    基本能搜到的systick 都是作为延时使用的,因为设计需求我更多实用的是系统时间判断。
假如我有个LED 需要每10s 闪一下,并且单片机还需要做其他的工作,用延时工作效率太低了,开个定时器又太浪费了。因此系统时钟就体现出了由为重要的应用场合。只需要检测到系统时间为10s 的倍数就可以做动作了,当然前提保证程序的大体循环能在1s 内完成,这个基本没问题如果一个大体循环1s 内完成不了 那这个程序要么就是大到无法形容,要么就是无止境的运行。

   首先进入while(1) 大循环前初始化systick,进入主体程序就在计数了,计数分毫秒,秒,这样能在特点的时间进入指定的程序中运行。

初始化systick


void TimeCount_Init(void)
{
	
	SysTick->LOAD=72000000/1000;//系统频率为72MH
	SysTick->VAL=0x00;
	SysTick->CTRL=0xFFFFFFFF;
	MilliS=0;
	Second=0;
	Min=0;
} 

当然如果用库函数也行的


void TimeCount_Init(void)
{
	
  if (SysTick_Config (SystemCoreClock / 1000)) 
  {  while (1); }

  MilliS=0;
  Second=0;
  Min=0;
} 

中断函数


void SysTick_Handler(void)
{															 
  MilliS++;
  if(MilliS>=10000) MilliS=0;//此处10000ms 主要方便延时函数使用
  if(MilliS%1000==0) {Second++;}
  if(Second>=60) {Second=0;min++;}
}

如果我们要想5秒去做一次计算某组数据只需要写入秒判断即可
if(Second%5 == 0)
{  需要间隔运行的程序……    }
毫秒也是可以的,换成MilliS 即可,甚至分钟都行。

   好了,那延时怎么办?是的特殊场合我们可能还是可能有需要延时函数的。当然这个也是可以做到的,不过相对运算多一点点。
延时函数如下:


u8 delayms(u16 nCount)
{
  u16 CurTim=MilliS;
  u16 i=0;
  if(nCount<60000)
	 {
		if(CurTim+nCount<10000)  
		  {
			 while(MilliS-CurTim<nCount); return 1;
		  }
		i=nCount-(10000-CurTim);
		while(MilliS<9998);
		while(MilliS>=9998||MilliS<i);   
		return 1;
	 }
  delayS(nCount/1000);
  delayms(nCount%1000);
  //while()
  return 1;
}

u8 delayS(u16 nCount)
{
  u16 CurTim=Second;
  u16 i=0;
  if(nCount<6000)
  	 {
		if(CurTim+nCount<60)
		  {  while(Second-CurTim<nCount);return 1;}
		i=nCount-(60-CurTim);
		while(Second<59);
		while(Second>=59||Second<i);
		return 1;
	 }
  return 0;
}

    这里我做的毫秒延时中实际最长的只能计算60000ms 即60s的延时 再长的延时就会先调用秒延时,运行完后再把剩下的毫秒延时运行掉。所以超出部分将计算越出计数器最大计算值多少来计算i=nCount-(10000-CurTim);而中断中定义的技术最大值10000 原因是一般我们使用延时都不会太长,最长一般也就数秒而已,更多的是毫秒级的,所以这里放宽至10S 的长度,直接等待值到了就推出,所以10S内的延时延时还是非常精确的,但如果大于就会有几个微秒的差距了,中间插入了好几条运算和指令,非精确定时就别用这个定时器了。

 

 

STM32 Comments(1) 2012年10月21日 19:29

STM32 学习笔记_定时器输出PWM

    前面写过定时器TIME的相关原理和使用,PWM其实也是在其基础上补充出来的功能。首先看定时器的框图

这是高级定时器的框图,通用定时器少了图中DTG寄存器,此寄存器是做为死区设置的,在这里暂且不管。
单单定时器应用而已,是在自动重装载寄存器与计数器所产生的中断中所应用的,而PWM输出是在其计数器后一级加入一比较器后再做输出的。多出的一级寄存器为CCRx 捕获/比较寄存器,与计数器CNT 一样同为16位的寄存器,当CNT的值小于CCRx 中的值时候输出高电平或低电平,当大于的时候反向,当然也可以设置CCRx 的值大于ARR重载寄存器的值,这样就是一个占空比为100% 或0%的输出,失去了PWM的意义。手册上说的由ARR来确定频率,这我认为的是ARR更应该定义为确定最大周期长度,而要设定频率和占空比精度的更关联的是PSC预分频寄存器,预分频寄存器把单个计数时间分得更小,就能获得同样周期的更精细的占空比千分之一或万分之一,PWM的周期也能设定得更短。所以PWM 的频率是由PSC和ARR两个寄存器所决定的,我的建议是首先初始化就设定好一个较小的PSC 值,计数器单个计数周期可达数十微秒级别的,这样便能由重载寄存器更好的控制PWM的 脉冲周期长度,短可达微秒单位,长可达毫秒。

下图为更为直接的PWM 输出关系图

ARR寄存器设定脉冲周期,CRRx设定占空比,CNT计数器最大值为ARR载入。
每组TIM有4组独立通道,图1中有表示的,CCR1、CCR2、CCR3、CCR4,即一组TIM可以输出4组 相同频率不同占空比的脉冲输出。

    PWM的控制寄存器有CCMR1、CCMR2 设定输入输出模式,CCMR1 高8位对应通道2,低8位对应通道1,CCMR2高8位对应通道4,低8位对应通道3。组要控制输出对应电平及计数方式等。
   CR2使能PWM 反向控制的输出使能。
   CCER 输出极性控制以及通道使能。
以上寄存器功能详细请看数据手册。

库函数分析:
    3.0的固件库 与PWM输出相关的设置函数是TIM_OCxInit。在原先TIM定时器的函数设定上再加上次函数的设定即可。3.0函数与2.0函数相比没了 TIM_Channel 这个数据类,转而多了个由TIM_OCInit变成了,TIM_OC1Init、TIM_OC2Init、TIM_OC3Init、TIM_OC4Init四个函数来设定。
    应用就没啥好说的,设一设,然后记得 TIM_CtrlPWMOutputs(TIMx,ENABLE); 就这样输出了。

PS:在做可调的PWM输出 最好以PSC 来改频率 CRRx改占空比。如果把PSC定死了,那么不管要修改频率还是占空比都要把ARR和CCRx 的值都重新算一遍。

 

 

STM32 Comments(0) 2012年10月01日 14:36

STM32 学习笔记_TIME定时器1

     STM32 单片机的定时器的确很强大,参考说明书中就占了一百多页,占参考手册1/4 有多了。
STM32的定时器分了好几个类别,各个类别针对功能作用都不大相同。
分有: 一、高级定时器
       二、通用定时器
       三、基本定时器
       四、看门狗定时器
       五、SysTick定时器

      其中看门狗定时器和SysTick定时器本篇笔记阐述,这里主要记下对平时使用定时器作用的计时计数器的一些自己的理解。
按照参考手册中的定义 高级定时器 通用定时器 基本定时器,这三个定时器成上下级的关系,即基本定时器有的功能通用定时器都有,而且还增加了向下、向上/向下计数器、PWM生成、输出比较、输入捕获等等功能;而高级定时器又包含了通用定时器的所有功能,另外还增加了死区互补输出、刹车信号、加入重复计数器等等。(这里等等功能请参考《STM32参考手册》)
      所以学习STM32 定时器实际就是学习一下高级定时器,然后适当的删减后就是后面的两种定时器了。
假若不涉及输出输入,定时器的最基本用法就是计数定时作用了本篇笔记主要针对这部分的理解所写下的。

高级定时器中一共有20个寄存器:

TIMx_CR1、TIMx_CR2、TIMx_SMCR、TIMx_DIER、TIMx_SR、TIMx_EGR、TIMx_CCMR1、TIMx_CCMR2、
TIMx_CCER、TIMx_CNT、TIMx_PSC、TIMx_ARR、TIMx_RCR、TIMx_CCR1、
TIMx_CCR2、TIMx_CCR3、
TIMx_CCR4、TIMx_BDTR、TIMx_DCR、TIMx_DMAR
好吧一堆寄存器光看都看到眼花缭乱了,当然不是所有寄存器都涉及到才能让定时器工作的,例如最基本的定时功能所涉及的只有几个与时基功能相关的寄存器,
TIMx_CNT(计数器寄存器)、TIMx_PSC(预分频器寄存器)、TIMx_ARR(自动装载寄存器)、TIMx_RCR(重复次数寄存器)。参考手册中有那么 衣服定时器的框图。这几个寄存器的关系如图所示的:

CK_PSC这根时钟线上的时钟源的选择,即给定时器计数计时的时钟源的输入方式,有四种方式,分别是内部时钟,外部时钟模式1,外部时钟模式2,内部触发。这部分日后再说,这里暂且使用最常用的内部时钟方式,既是当内部时钟为72MHz 的内部时钟源。
如图所示的,时钟源首先进入预分频器,然后再进入预先装入自动重装载寄存器的计数器中,当计数器溢出时产生一次中断和一次事件更新。除了多了一个PSC,其他的基本和51单片机很相似,初次看参考手册中的功能描述中出现了好多次“
更新事件(UEV)”。这究竟是怎么的一样东西呢? 在这里有个新概念叫“影子寄存器”,在上图中,可以看到PSC、ARR、REP(重复计数器中的低八位)这三个寄存器框框下都有个黑影,每次这三个寄存器就是影子寄存器,如果看到参考手册全图中还可以看到另外还有几个框框下也有阴影部分的,这几个寄存器也是影子寄存器。何谓影子寄存器呢,例如PSC寄存器可以理解为有两个,一个是用户可以访问到的寄存器,可读可写,另一部分就是客户访问不到的但其装载值和实际寄存器是密切关联的,当程序在运行中改写PSC 这时候影子寄存器的作用就体现了,因为立刻写入的值可能会大于或小于目前正在运行的寄存器中的数值,而真实在运行时候的正是这个影子寄存器中的值,而程序写入的是可访问的寄存器,只有当产生一个更新事件的时候影子寄存器才会读入访问寄存器中的值,这样就可以防止突然修改而产生的非正常中断或不会中断等异常问题。当然在控制器CR1中控制这个影子寄存器是否起作用,不起作用的话就是立即写入这个数值到寄存器中。下面两幅是参考手册中的相关时序图:

 


回头再说一下“
更新事件(UEV)” ,当计数器溢出的时候产生一次UEV事件,另外还可以在事件寄存器TMx_EGR中的UG位软件写入产生一次事件更新,当UEV事件来临的时候所有影子寄存器均载入寄存器中的值,从而实现所有带影子寄存器的更新,而不启用影子寄存器的情况下只能实现,写那个寄存器更新那个寄存器而,这可能造成相关联的寄存器产生冲突矛盾,建议还是开启此功能,在下一个溢出周期后产生事件更新。

       (既然说到了影子寄存器也说点自己的猜测,了解了点STM32单片机的都知道几乎所有寄存器都是32位的,唯独TIM寄存器是16位的,是的如果是32位的计数器我们可能还能做更宽广的定时作用。但我们也还是发现即使加入了影子寄存器而整体的寄存器地址依然保持是连续的,这我猜测一种可能性寄存器本身其实还是32位的,但高位提供了影子寄存器的载入功能,所以依然能保持地址连续性,只要设定了高位禁止访问即可。官方资料和搜索中均未有任何确认说法,纯粹本人猜测未得到官方任何证实)

     另外高级定时器中还有RCR重复次数寄存器这个,也是比较简单的事件更新(UEV) 都是在RCR为0的情况下产生计数器溢出而产生的,当RCR中不为0的时候计数器溢出只会使得重复次数寄存器递减而不会产生UEV,这样就可以使得定时器的定时情况得以延长,而相当于有16位的分频器,16位的计数器,再加入16位的重复次数,一共48位的计数定时器。详细看参考手册,这个很好理解。

    基本的基时单元就是上面提及的这几个,下面看看3.0库是如何实习的基本使用。

 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
 
       
       TIM_DeInit(TIM2);                                           //重新将Timer设置为缺省值
       
       TIM_InternalClockConfig(TIM2);                              //采用内部时钟给TIM2提供时钟源      
       TIM_TimeBaseStructure.TIM_Prescaler = 36000 - 1;            //预分频系数为36000-1,这样计数器时钟为72MHz/36000 = 2kHz       
       TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //设置时钟分割      
       TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置计数器模式为向上计数模式       
       TIM_TimeBaseStructure.TIM_Period = 2000 - 1;           //设置计数溢出大小,每计2000个数就产生一个更新事件
       TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);         //将配置应用到TIM2中
       TIM_ClearFlag(TIM2, TIM_FLAG_Update);                  //清除溢出中断标志  
       
       TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);                //开启TIM2的中断

以上是一个最基本的定时器配置的代码,载自网上被转载无数次的地方……
中断函数自己按照需求写,这里不多说。
在库中的初始化函数和初始化数据类型有3类,TIM_TimeBaseInitTypeDef、TIM_OCInitTypeDef、TIM_ICInitTypeDef

与基时参数相关的数据类型是TIM_TimeBaseInitTypeDef

 typedef struct
 {
   uint16_t TIM_Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
                                        This parameter can be a number between 0x0000 and 0xFFFF */
 
   uint16_t TIM_CounterMode;       /*!< Specifies the counter mode.
                                        This parameter can be a value of @ref TIM_Counter_Mode */
 
   uint16_t TIM_Period;            /*!< Specifies the period value to be loaded into the active
                                        Auto-Reload Register at the next update event.
                                        This parameter must be a number between 0x0000 and 0xFFFF.  */ 
 
   uint16_t TIM_ClockDivision;     /*!< Specifies the clock division.
                                       This parameter can be a value of @ref TIM_Clock_Division_CKD */
 
   uint8_t TIM_RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
                                        reaches zero, an update event is generated and counting restarts
                                        from the RCR value (N).
                                        This means in PWM mode that (N+1) corresponds to:
                                           - the number of PWM periods in edge-aligned mode
                                           - the number of half PWM period in center-aligned mode
                                       This parameter must be a number between 0x00 and 0xFF. 
                                        @note This parameter is valid only for TIM1 and TIM8. */
 } TIM_TimeBaseInitTypeDef; 
以上是从库stm32f10x_tim.h中 截取的代码,整体的数据结构可以中这段注释中得知,不懂E文的要么翻字典要么翻库函数中文翻译
版本(当然这个是2.0的库,有部分会和3.0后的版本很不相同),这部分的数据类型还是很一样的,不多说。 
接着就是TIM_TimeBaseInit()这个函数了,在stm32f10x_tim.c的224行中
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
{
  uint16_t tmpcr1 = 0;

  /* Check the parameters */
  assert_param(IS_TIM_ALL_PERIPH(TIMx)); 
  assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
  assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));

  tmpcr1 = TIMx->CR1;  

  if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
     (TIMx == TIM4) || (TIMx == TIM5)) 
  {
    /* Select the Counter Mode */
    tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
  }
 
  if((TIMx != TIM6) && (TIMx != TIM7))
  {
    /* Set the clock division */
    tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
  }

  TIMx->CR1 = tmpcr1;

  /* Set the Autoreload value */
  TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
 
  /* Set the Prescaler value */
  TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
    
  if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17))  
  {
    /* Set the Repetition Counter value */
    TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
  }

  /* Generate an update event to reload the Prescaler and the Repetition counter
     values immediately */
  TIMx->EGR = TIM_PSCReloadMode_Immediate;           
}

可以看3.0后的函数里把所有的TIMx都加入一个函数里面做判断了,不需要和2.0的区分TIM1和TIM 两类函数,比较其基本操作都一样无非就是多了一个两个寄存器而已。
程序中可以看到这一段:

  if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
     (TIMx == TIM4) || (TIMx == TIM5))
  {
    /* Select the Counter Mode */​
    tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
  }
if((TIMx != TIM6) && (TIMx != TIM7))
{     
    /* Set the clock division */     
    tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));     
    tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;   
}
TIMx->CR1 = tmpcr1;
    高级定时器和通用定时器拥有向上计数、向下技术、向上/向下模式,三种,而基本计时器只有向上计数一种,因此TIM6和TIM7木有的设置计数模式,tmpcr1首先装入了控制寄存器CR1d 值,然后把其中DIR(位4)和CMS(位6:5)清除,然后或运算上载入数据结构中的值,这里代码再跳转到stm32f10x_tim.h中第362行
#define TIM_CounterMode_Up                 ((uint16_t)0x0000)
#define TIM_CounterMode_Down               ((uint16_t)0x0010)
#define TIM_CounterMode_CenterAligned1     ((uint16_t)0x0020)
#define TIM_CounterMode_CenterAligned2     ((uint16_t)0x0040)
#define TIM_CounterMode_CenterAligned3     ((uint16_t)0x0060) 定义的是TIM_CounterMode的几种情况,即控制计数器向上
技术或向下计数,或是向上向下交替计数,当设置的是交替计数的情况DIR位为只读。
TIM_ClockDivision为时钟分配因子,其中有 0、2x、4x ,在AHB低时钟时为提高定时精度而实用的倍频器,位于CR1寄存器中的位9:8。这段程序便配置好了CR1寄存器,然后再看下面的程序。 载入了PSC寄存器和ARR寄存器值,如果是高级定时器还有RCR寄存器值,在这一步便基本配置得差不多了,但还是发现函数最后还有一句 TIMx->EGR = TIM_PSCReloadMode_Immediate; 这里在事件寄存器中做了一次软件的事件更新触发 使得其UG位置1。
#define TIM_PSCReloadMode_Immediate        ((uint16_t)0x0001)
 
基本定时器的工作原理便如本文所述,如有错误非常感谢您的指出。

 

STM32 Comments(4) 2012年7月22日 10:17

STM32 学习笔记 中断优先级理解

    Cortex-M3的中断嵌套可真让我理解得云里雾里的。一般的中断优先级就算了,还搞个什么的亚优先级。查找资料中查找到这个亚优先级还有好多别名'副优先级'、'响应优先级'……

     不过从'响应优先级' 和'抢占优先级' 这两个就可以看出这两种优先级的区别。

      抢占优先级就是假如当前情况是在运行着某个中断程序的情况先,触发了一个中断信号,而且比当前的中断等级要高,那么当前的中断程序会被挂起,直接跳到高抢占优先级的中断程序去。一般说法就是:具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。

    响应优先级就是来一个中断运行一个中断程序,如果两个中断信号来到,并且抢占优先级相同,那么判断响应优先级高的先运行,结束后再运行优先级低的。而这运行中断程序当中再来同抢占优先级,不同响应优先级,是不会打断当前运行的程序,也只会等到当前中断程序运行完后再运行。即这两个中断没有任何嵌套关系。

 

STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下: 
第0组:所有4位用于指定响应优先级 
第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级 
第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级 
第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级 
第4组:所有4位用于指定抢占式优先级 
可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种: 
NVIC_PriorityGroup_0 => 选择第0组 
NVIC_PriorityGroup_1 => 选择第1组 
NVIC_PriorityGroup_2 => 选择第2组 
NVIC_PriorityGroup_3 => 选择第3组 
NVIC_PriorityGroup_4 => 选择第4组 

 

一个简单的例子

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 
//USART1 中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 指定抢占式优先级别1 
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定响应优先级别0 
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
NVIC_Init(&NVIC_InitStructure); 
// USART2 中断
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 指定抢占式优先级别0 
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 指定响应优先级别1 
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
NVIC_Init(&NVIC_InitStructure); 

 

这样串口2的优先级就比串口1要高,优先处理串口2的数据,当然这样写程序是有问题的,串口被打断意味这数据传输必定会出现失败的事情,这些就考虑整体程序时候要考虑的了,毕竟这就是个例子看看而已

STM32 Comments(3) 2012年5月30日 14:49

STM32 学习感悟

      在学习stm32 之前,编程风格可以说是非常烂的,大量的使用全局变量,函数即没返回也没输入,注释混乱,命名无根据。在它的固件库中有着许多规范的命名,调用等方式,已经很多C语言巧妙的使用。

      毕竟自己做了款学习板所以程序也要亲力亲为了,思前想后的,最后决定不能像市场的货那样,一款一个单,一个模块一个程序,实在是只能让人知道怎么读读数据而已。要为我后面的设计打基础,必须要有像样点的小规模级别上的程序才能训练我,最后还决定,把模块程序一个一个的集合到一起,形成一个大集合,像操作系统一样,给一个命令就做那事情,不需要,要那个功能就烦人的又烧一次程序。

      关于功能实现先与状态机模式实现,并行的情况后面深入了之后必须要再做一次研究,以超级终端为上位机做控制,当然没有超级终端的win7 可以用SecureCRT 这个软件,同样的终端功能,设置好串口相应设置后即可做通讯。单片机与其的通讯只能采用字符的形式出现,不支持直接看8位数据。

现在构造了两级菜单,总体感觉还行,但对这控制模式还说不清,毕竟自己设计的很难说缺点,查错误。

主级控制:

第二级控制菜单1:

第二级控制菜单1:

在非循环型工作中也并非要做到一个大循环体中,如主页和时钟选择,只要等待接收缓存发生变化后检查缓存值便可以做先关动作了。

 int main(void)
{ ………………// 省略初始化数据 和寄存器
     while (1)
     { switch(Sys_State)
	{
     /*------进入时钟选择-----------*/
	case '1':   
	  TempState='\0';
	  Receive_buffer='\0';
	  RCCUart_Display();
	  while(Receive_buffer!='0')
	    {  
	      if(TempState!='\0')
		{ 
		  RCC_ConfigControl(TempState);RCCUart_Display();Receive_buffer='\0';
		}
    /*---------动作后再做缓存检查----------------------*/
	      TempState=Receive_buffer;
	    }
	  break;

	  /*************************************************************/
	case '2':
	  TempState='0';
	  LedState='1';
	  Receive_buffer='1';
	  //  LEDUart_Display(LedState);
	  while(Receive_buffer!='0')
	    {
	      LedState=Receive_buffer; 		
      
	      if(TempState!=LedState)
		{TempState=LedState;LEDUart_Display(LedState);}	//数据变化,每次循环变化一次,实现跑马灯移动      

	      LED_Display(LedState,&LedData);
	      GPIO_Write(GPIOE,LedData);
	      delay_MS(1000);
	    }
	  break;

	  /*************************************************************/
	case '3':

	  break;

	  /*************************************************************/	  
	default :
	  
	  MAIN_Display();
	  while(Receive_buffer=='0')
	    {}
	  
	  break;
	}

	Sys_State=Receive_buffer;	  
    }

 

两个菜单均以状态机模式循环,并检查到退出控制命令符才得以跳出至上一级菜单。如果要做多级,还真有点复杂,判断太多了,得抽个时间看看操作系统的思路

 

 

 

STM32 Comments(1) 2012年4月02日 00:29