Chinaunix首页 | 论坛 | 博客
  • 博客访问: 224815
  • 博文数量: 52
  • 博客积分: 15
  • 博客等级: 民兵
  • 技术积分: 390
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-06 09:31
文章分类

全部博文(52)

文章存档

2015年(1)

2014年(44)

2013年(7)

我的朋友

分类: 嵌入式

2014-05-30 09:15:40

Systick 是stm32上一个用处很多的内设,所有基于arm-cortex m3 的芯片都有这个定时器,所以考虑到平台的可移植性时,可以多使用Systick。
 
Systick 是一个24位数据宽度的倒计数定时器,其计数范围只能到 1677215(2^24),当计数到0时会从RELOAD寄存器 中自动重装定时初值。只要不把SysTick的控制及状态寄存器中的使能位清除,计数器就不会停止。
 
SysTick 可以产生中断、设置中断优先级,有专门的中断处理函数SysTick_Handler().库函数作为ST公司自己的推出的框架, 在中断这方面做了更为细致的处理。
库函数包含的头文件是 stm32f10x.h  这个文件中 列出了完整的stm32中断向量表(截取部分):
01 /******  Cortex-M3 Processor Exceptions Numbers ***************************************************/
02   NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                             */
03   MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M3 Memory Management Interrupt              */
04   BusFault_IRQn               = -11,    /*!< 5 Cortex-M3 Bus Fault Interrupt                      */
05   UsageFault_IRQn             = -10,    /*!< 6 Cortex-M3 Usage Fault Interrupt                    */
06   SVCall_IRQn                 = -5,     /*!< 11 Cortex-M3 SV Call Interrupt                       */
07   DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M3 Debug Monitor Interrupt                 */
08   PendSV_IRQn                 = -2,     /*!< 14 Cortex-M3 Pend SV Interrupt                       */
09   SysTick_IRQn                = -1,     /*!< 15 Cortex-M3 System Tick Interrupt                   */
10  
11 /******  STM32 specific Interrupt Numbers *********************************************************/
12   WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                            */
13   PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt            */
14   TAMPER_IRQn                 = 2,      /*!< Tamper Interrupt                                     */
15   RTC_IRQn                    = 3,      /*!< RTC global Interrupt                                 */
16   FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt                               */
17   RCC_IRQn                    = 5,      /*!< RCC global Interrupt                                 */
18   EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                 */
19   EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt                                 */
20   EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt                                 */
21   EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt                                 */
22   EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt                                 */
23   DMA1_Channel1_IRQn          = 11,     /*!< DMA1 Channel 1 global Interrupt                      */
24   DMA1_Channel2_IRQn          = 12,     /*!< DMA1 Channel 2 global Interrupt                      */
25   DMA1_Channel3_IRQn          = 13,     /*!< DMA1 Channel 3 global Interrupt                      */
26   DMA1_Channel4_IRQn          = 14,     /*!< DMA1 Channel 4 global Interrupt                      */
27   DMA1_Channel5_IRQn          = 15,     /*!< DMA1 Channel 5 global Interrupt                      */
28   DMA1_Channel6_IRQn          = 16,     /*!< DMA1 Channel 6 global Interrupt                      */
29   DMA1_Channel7_IRQn          = 17,     /*!< DMA1 Channel 7 global Interrupt                      */
其中就包含了对系统级中断的处理,有SysTick的中断定义。
在 库函数的Cmsis\core_cm3.h文件中的中断配置函数也区分了系统中断 和 其他中断的区分处理 :
1 static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
2 {
3   if(IRQn < 0) {
4     SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
5   else {
6     NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */
7 }
其中 SysTick_Config()函数也在这个文件中:
01 static __INLINE uint32_t SysTick_Config(uint32_t ticks)
02 {
03   if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
04                                                                 
05   SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
06   NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
07   SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
08   SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
09                    SysTick_CTRL_TICKINT_Msk   |
10                    SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
11   return (0);                                                  /* Function successful */
12 }
所以库函数中可以将SysTick作为一个中断操作。
但是直接操作寄存器,包含的头文件是 stm32f10x_lib.h ,中断向量表在stm32f10x_nvic.h 中(截取部分):
01 /* IRQ Channels --------------------------------------------------------------*/
02 #define WWDG_IRQChannel              ((u8)0x00)  /* Window WatchDog Interrupt */
03 #define PVD_IRQChannel               ((u8)0x01)  /* PVD through EXTI Line detection Interrupt */
04 #define TAMPER_IRQChannel            ((u8)0x02)  /* Tamper Interrupt */
05 #define RTC_IRQChannel               ((u8)0x03)  /* RTC global Interrupt */
06 #define FLASH_IRQChannel             ((u8)0x04)  /* FLASH global Interrupt */
07 #define RCC_IRQChannel               ((u8)0x05)  /* RCC global Interrupt */
08 #define EXTI0_IRQChannel             ((u8)0x06)  /* EXTI Line0 Interrupt */
09 #define EXTI1_IRQChannel             ((u8)0x07)  /* EXTI Line1 Interrupt */
10 #define EXTI2_IRQChannel             ((u8)0x08)  /* EXTI Line2 Interrupt */
11 #define EXTI3_IRQChannel             ((u8)0x09)  /* EXTI Line3 Interrupt */
12 #define EXTI4_IRQChannel             ((u8)0x0A)  /* EXTI Line4 Interrupt */
并没有对systick做定义,所以直接操作寄存器,需要通过查询标志位的方式,查看Systick的状态。
 
本例将实现用Systick 产生一个100ms的定时器,让GPIO_Pin_5管脚led灯闪烁。


 
操作寄存器
在stm32f10x_map.h中,包含了对SysTick的结构体定义:
typedef struct
{
  vu32 CTRL;
  vu32 LOAD;
  vu32 VAL;
  vuc32 CALIB;
} SysTick_TypeDef;
手册上对SysTick的介绍并不详细,各个寄存器的各位定义如下:
systick.png
systick的时钟来自外部时钟,经倍频器后再8分频作为时钟信号。
 
代码如下:    (sys.h 代码参照 )
01 #include
02 #include "system.h"    
03  
04 //LED  按键端口定义
05 #define LED0 PAout(4)// PA4
06  
07  
08 void Gpio_Init(void);//初始化        
09  
10 void SysTick_Delay(u32 time);
11  
12 int main(void)
13 {  
14     u32 temp;            
15     Rcc_Init(9); //系统时钟设置
16     Gpio_Init();             //初始化与LED连接的硬件接口
17  
18     SysTick_Delay(100000);
19  
20     while(1)
21     {  
22         do
23         {
24             temp=SysTick->CTRL;
25         }while(temp&0x01&&!(temp&(1<<16)));  //查询COUNTFLAG标志位,等待时间到达
26            
27         LED0 = !LED0;
28     }
29 }
30  
31 void SysTick_Delay(u32 us)
32 {      
33               
34     u8 us_radix=72/8;//us延时倍乘数  SYSTICk的时钟固定为HCLK时钟的1/8,这里使用系统时钟72MHz
35     SysTick->CTRL&=0xfffffffb;//bit2清空,选择外部时钟  HCLK/8
36     SysTick->LOAD=us*us_radix; //时间加载            
37     SysTick->VAL=0x00;        //清空计数器
38     SysTick->CTRL=0x01;      //开始倒数      
39      
40     //SysTick->CTRL=0x00;       //关闭计数器
41     //SysTick->VAL =0X00;       //清空计数器 
42 }
43  
44  
45 void Gpio_Init(void)
46 {
47     RCC->APB2ENR|=1<<2;    //使能PORTA时钟     
48           
49     GPIOA->CRL&=0XFFF0FFFF;
50     GPIOA->CRL|=0X00030000;//PA4 推挽输出    
51     GPIOA->ODR|=1<<4;      //PA4 输出高
52        
53 }
 
库函数操作
 
在旧版本的库函数中,有较多的可配置内容。但是在固件库V3.5中,对Systick寄存器的只有一个函数: 
 
  SysTick_Config(uint32_t ticks);    //注意这是一个24位计数器,超出24位则返回配置错误,返回1
 
该函数设置了自动重载入计数器(LOAD)的值,SysTick IRQ的优先级,复位了计数器(VAL)的值,开始计数并打开SysTick IRQ中断。SysTick时钟默认使用系统时钟(72MHz)。  

在标准外设库中移除了SysTick的驱动,因此用户必须调用CMSIS定义的函数。 驱动已经包含在了Cmsis文件夹中;

但是查看源代码可以知道,在标准外设库(Libraries/src)中有一个misc.c文件,其中提供了一个修改SysTick默认时钟的函数:

01 /**
02   * @brief  Configures the SysTick clock source.
03   * @param  SysTick_CLKSource: specifies the SysTick clock source.
04   *   This parameter can be one of the following values:
05   *     @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.
06   *     @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.
07   * @retval None
08   */
09 void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
10 {
11   /* Check the parameters */
12   assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
13   if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
14   {
15     SysTick->CTRL |= SysTick_CLKSource_HCLK;
16   }
17   else
18   {
19     SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
20   }
21 }

可以选择systick 的时钟是否是HCLK(AHB)时钟 或者是HCLK的八分频时钟。

库函数操作代码如下:



main.c:

01 #include "stm32f10x.h"
02  
03 void RCC_Configuration(void);
04 void GPIO_Configuration(void);
05  
06 int main(void)
07 {
08  
09     RCC_Configuration();
10     GPIO_Configuration();
11  
12     if(SysTick_Config(1*7200000))    //配置错误返回1,max 16777216   默认72Mhz 时钟 ,100ms延时
13     {                          
14         GPIO_SetBits(GPIOA , GPIO_Pin_4);   //错误处理                             
15     }
16     while(1);
17      
18 }
19  
20  
21    
22 void GPIO_Configuration(void)
23 {
24     GPIO_InitTypeDef GPIO_InitStructure;
25  
26     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;
27     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
28     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;           
29     GPIO_Init(GPIOA , &GPIO_InitStructure);
30 }
31  
32  
33 void RCC_Configuration(void)
34 {
35     /* 定义枚举类型变量 HSEStartUpStatus */
36     ErrorStatus HSEStartUpStatus;
37  
38     /* 复位系统时钟设置*/
39     RCC_DeInit();
40     /* 开启HSE*/
41     RCC_HSEConfig(RCC_HSE_ON);
42     /* 等待HSE起振并稳定*/
43     HSEStartUpStatus = RCC_WaitForHSEStartUp();
44     /* 判断HSE起是否振成功,是则进入if()内部 */
45     if(HSEStartUpStatus == SUCCESS)
46     {
47         /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
48         RCC_HCLKConfig(RCC_SYSCLK_Div1);
49         /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
50         RCC_PCLK2Config(RCC_HCLK_Div1);
51         /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
52         RCC_PCLK1Config(RCC_HCLK_Div2);
53         /* 设置FLASH延时周期数为2 */
54         //FLASH_SetLatency(FLASH_Latency_2);
55         /* 使能FLASH预取缓存 */
56         //  FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
57         /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
58         RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
59         /* 使能PLL */
60         RCC_PLLCmd(ENABLE);
61         /* 等待PLL输出稳定 */
62         while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
63         /* 选择SYSCLK时钟源为PLL */
64         RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
65         /* 等待PLL成为SYSCLK时钟源 */
66         while(RCC_GetSYSCLKSource() != 0x08);
67     }
68     /* 打开APB2总线上的GPIOA时钟*/
69     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
70  
71      
72 }
 stm3210x_it.c  //中断处理函数


1 #include "stm32f10x_it.h"
2  
3 void SysTick_Handler(void)
4 {
5     GPIO_WriteBit(GPIOA , GPIO_Pin_5,(BitAction)(1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5)));  //翻转GPIO_Pin_5的电平

}   

阅读(1499) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~