1、systick是内置于stm mcu的一个低级定时器,俗称滴答定时器,使用较简单。
SysTick定时器
SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号: 15)。在以前,大多操
作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。例如,
为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期
的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时
器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问
它的寄存器,以维持操作系统“心跳”的节律。
Cortex‐M3处理器内部包含了一个简单的定时器。因为所有的CM3芯片都带有这个定时
器,软件在不同 CM3器件间的移植工作得以化简。该定时器的时钟源可以是内部时钟(FCLK,
CM3上的自由运行时钟),或者是外部时钟(CM3处理器上的STCLK信号)。不过, STCLK的
具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能会大不相同,你需要检视
芯片的器件手册来决定选择什么作为时钟源。
SysTick定时器能产生中断, CM3为它专门开出一个异常类型,并且在向量表中有它的一
席之地。它使操作系统和其它系统软件在CM3器件间的移植变得简单多了,因为在所有CM3
产品间对其处理都是相同的。
有4个寄存器控制SysTick定时器,如表8.9至表8.12所示。
133
Cortex‐M3 权威指南 初稿 第 8 章
134
表8.9 SysTick控制及状态寄存器(地址:0xE000_E010)
位段 | 名称 | 类型 | 复位值 | 描述 |
16 | COUNTFLAG | R | 0 | 如果在上次读取本寄存器后, SysTick 已经数到了 0,则该位为 1。如果读取该位,该位将自动清零 |
2 | CLKSOURCE | R/W | 0 | 0=外部时钟源(STCLK) 1=内核时钟(FCLK) |
1 | TICKINT | R/W | 0 | 1=SysTick 倒数到 0 时产生 SysTick 异常请求 0=数到 0 时无动作 |
0 | ENABLE | R/W | 0 | SysTick 定时器的使能位 |
表8.10 SysTick重装载数值寄存器(地址:0xE000_E014)
位段 | 名称 | 类型 | 复位值 | 描述 |
23:0 | RELOAD | R/W | 0 | 当倒数至零时,将被重装载的值 |
表8.11 SysTick当前数值寄存器(地址:0xE000_E018)
位段 | 名称 | 类型 | 复位值 | 描述 |
23:0 | CURRENT | R/Wc | 0 | 读取时返回当前倒计数的值,写它则使之清零, 同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志 |
表8.10 SysTick校准数值寄存器(地址:0xE000_E01C)
位段 | 名称 | 类型 | 复位值 | 描述 |
31 | NOREF | R | ‐ | 1=没有外部参考时钟(STCLK 不可用) 0=外部参考时钟可用 |
30 | SKEW | R | ‐ | 1=校准值不是准确的 10ms 0=校准值是准确的 10ms |
23:0 | TENMS | R/W | 0 | 10ms 的时间内倒计数的格数。芯片设计者应该通 过 Cortex‐M3 的输入信号提供该数值。若该值读 回零,则表示无法使用校准功能 |
校准值寄存器提供了这样一个解决方案:它使系统即使在不同的CM3产品上运行,也能
产生恒定的SysTick中断频率。最简单的作法就是:直接把TENMS的值写入重装载寄存器,这
样一来,只要没突破系统极限,就能做到每10ms来一次 SysTick异常。如果需要其它的SysTick
异常周期,则可以根据TENMS的值加以比例计算。只不过,在少数情况下, CM3芯片可能无
法准确地提供TENMS的值(如, CM3的校准输入信号被拉低),所以为保险起见,最好在使
用TENMS前检查器件的参考手册。
SysTick定时器除了能服务于操作系统之外,还能用于其它目的:如作为一个闹铃,用于
测量时间等。要注意的是,当处理器在调试期间被喊停(halt)时,则SysTick定时器亦将暂
停运作。
要想使用该定时器,利用上一个教程建的工程,上一节流水灯的延时用的是软件延时,并不精准,这次改用sysytick。
①、声明一个全局变量
u32 TimeDelay=0;
②、自定义一个延时函数
void Delay_ms(u32 nTime)
{
TimeDelay=nTime;
while(TimeDelay!=0);
}
③、在stm32f10x_it.c文件中写systick处理函数,因为是外部变量(在主函数中定义因而对stm32f10x_it.c相当于外部)所以需要外部引用
extern u32 TimeDelay ;
在最后加上(注:可以写在主函数中,但不建议)
④、最后在主函数中调用systick配置函数
SysTick_Config(SystemCoreClock/1000); //1ms
因为该处理器为速度为每秒计算72m 次,而SystemCoreClock为系统时钟
所以用该时钟后间隔1ms,最后在用到延时的地方直接使用
Delay_ms(1000); //延时1s
即可;
2、LCD
比赛一般提供驱动函数,把图中三个文件加到自己建的工程中即可使用
在图示四个位置更改延时函数使用上面的systick,同样外部引用
最后主函数中便可以调用
#include "stm32f10x.h"
#include "lcd.h"
u32 TimingDelay = 0;
void Delay_Ms(u32 nTime);
//Main Body
int main(void)
{
STM3210B_LCD_Init();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
SysTick_Config(SystemCoreClock/1000);
LCD_DrawLine(120,0,320,Horizontal);
LCD_DrawLine(0,160,240,Vertical);
Delay_Ms(1000);
LCD_Clear(Blue);
LCD_DrawRect(70,210,100,100);
Delay_Ms(1000);
LCD_Clear(Blue);
LCD_DrawCircle(120,160,50);
Delay_Ms(1000);
LCD_Clear(Blue);
LCD_DisplayStringLine(Line4 ," Hello,world. ");
Delay_Ms(1000);
LCD_SetBackColor(White);
LCD_DisplayStringLine(Line0," ");
LCD_SetBackColor(Black);
LCD_DisplayStringLine(Line1," ");
LCD_SetBackColor(Grey);
LCD_DisplayStringLine(Line2," ");
LCD_SetBackColor(Blue);
LCD_DisplayStringLine(Line3," ");
LCD_SetBackColor(Blue2);
LCD_DisplayStringLine(Line4," ");
LCD_SetBackColor(Red);
LCD_DisplayStringLine(Line5," ");
LCD_SetBackColor(Magenta);
LCD_DisplayStringLine(Line6," ");
LCD_SetBackColor(Green);
LCD_DisplayStringLine(Line7," ");
LCD_SetBackColor(Cyan);
LCD_DisplayStringLine(Line8," ");
LCD_SetBackColor(Yellow);
LCD_DisplayStringLine(Line9," ");
while(1);
}
//
void Delay_Ms(u32 nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
3、外部中断
①、若想用到外部中断首先把相应的exti和misc文件包含进去
②、 这次程序的主要功能是实现按键中断,因此先看按键,由CT117E的电路图可知KEY1-KEY4分别对应PA0、PA8、PB1、PB2,本次实验用到key1和key2。首先是引脚的初始化,和led的类似不过mode为GPIO_Mode_IN_FLOATING;(也可以是GPIO_Mode_IPU),注意要开启引脚的复用功能。
③、接着是exti的初始化:
STM32 的每个 IO 都可以作为外部中断
的中断输入口,这点也是 STM32 的强大之处。 STM32F103 的中断控制器支持 19 个外部中断/
事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。 STM32F103 的
19 个外部中断为:
线 0~15:对应外部 IO 口的输入中断。
线 16:连接到 PVD 输出。
线 17:连接到 RTC 闹钟事件。
线 18:连接到 USB 唤醒事件
在库函数中,配置 GPIO 与中断线的映射关系的函数 GPIO_EXTILineConfig()来实现的:
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
该函数将 GPIO 端口与中断线映射起来,使用范例是:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
将中断线 2 与 GPIOE 映射起来,那么很显然是 GPIOE.2 与 EXTI2 中断线连接了。 设置好中断
线映射之后, 那么到底来自这个 IO 口的中断是通过什么方式触发的呢? 接下来我们就要设置
该中断线上中断的初始化参数了。
中断线上中断的初始化是通过函数 EXTI_Init()实现的。 EXTI_Init()函数的定义是:
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
下面我们用一个使用范例来说明这个函数的使用:
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据 EXTI_InitStruct 中指定的
//参数初始化外设 EXTI 寄存器
上面的例子设置中断线 4 上的中断为下降沿触发。 STM32 的外设的初始化都是通过结构体来设
置初始值的,这里就不罗嗦结构体初始化的过程了。
④、NVIC
NVIC,中文名嵌套中断向量控制器,是 Cortex-M3 系列控制器内部独有集成单元,
与 CPU 结合紧密,降低中断延迟时间并且能更加高效处理后续中断。举个例子,比如火车站买票,
那些火车站的规章制度就是 NVIC,规定学生和军人有比一般人更高优先级,它们则给你单独安排
个窗口,同学与同学之间也有区别,那就是你也得排队,也就是你的组别(抢断优先级)和你的排
队序号(响应优先级)决定你何时能买到票。
抢断优先级,顾名思义,能再别人中断是抢占别人中断,实现中断嵌套。响应优先
级则只能排队,不能抢在前面插别人的对,即不能嵌套。
STM32 中指定优先级的寄存器为 4 位,其定义如下:
第 0 组:所有 4 位用于指定响应优先级
第 1 组:最高 1 位用于指定抢占式优先级,最低 3 位用于指定响应优先级
第 2 组:最高 2 位用于指定抢占式优先级,最低 2 位用于指定响应优先级
第 3 组:最高 3 位用于指定抢占式优先级,最低 1 位用于指定响应优先级
第 4 组:所有 4 位用于指定抢占式优先级
以上定义也称作中断优先级分组,相关内容在 STM32 固件库的 misc.h 文件中有详细定义。
初始化:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //使能按键外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //中断优先级分组初始化
STM32 的 IO 口外部中断函数只有 6 个,
分别为:
EXPORT EXTI0_IRQHandler
EXPORT EXTI1_IRQHandler
EXPORT EXTI2_IRQHandler
EXPORT EXTI3_IRQHandler
EXPORT EXTI4_IRQHandler
EXPORT EXTI9_5_IRQHandler
EXPORT EXTI15_10_IRQHandler
中断线 0-4 每个中断线对应一个中断函数, 中断线 5-9 共用中断函数 EXTI9_5_IRQHandler,中
断线 10-15 共用中断函数 EXTI15_10_IRQHandler。 在编写中断服务函数的时候会经常使用到两
个函数, 第一个函数是判断某个中断线上的中断是否发生(标志位是否置位):
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
这个函数一般使用在中断服务函数的开头判断中断是否发生。 另一个函数是清除某个中断线上
的中断标志位:
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
这个函数一般应用在中断服务函数结束之前, 清除中断标志位。
常用的中断服务函数格式为:
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生
{
中断逻辑…
EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中断标志位
}
}
在这里需要说明一下,固件库还提供了两个函数用来判断外部中断状态以及清除外部状态
标志位的函数 EXTI_GetFlagStatus 和 EXTI_ClearFlag,他们的作用和前面两个函数的作用类似。
只是在 EXTI_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而
EXTI_GetFlagStatus 直接用来判断状态标志位。
然后在stm32f10x_it.c中写相应的中断服务函数(注意,函数名必须是上文中提到的不可自定义)
这里我们主要想实现按键功能,因此主函数自定义了一个 变量u8 EXTI_Status=0;之后通过判断EXTI_Status的值来判断按下的按键。
附上主函数的完整代码
#include "stm32f10x.h"
#include "lcd.h"
u32 TimingDelay = 0;
u8 EXTI_Status=0;
void Delay_Ms(u32 nTime);
void EXTI_Config(void);
//Main Body
int main(void)
{
STM3210B_LCD_Init();
EXTI_Config();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
SysTick_Config(SystemCoreClock/1000);
LCD_DisplayStringLine(Line1," KEY_EXTI TEST ");
LCD_DisplayStringLine(Line3," PRESS THE BUTTON ");
LCD_SetBackColor(White);
LCD_SetTextColor(Blue);
while(1)
{
switch(EXTI_Status)
{
case 1:
LCD_DisplayStringLine(Line5," BUTTON1 PRESSED ");
break;
case 2:
LCD_DisplayStringLine(Line5," BUTTON2 PRESSED ");
break;
}
}
}
void EXTI_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource8);
EXTI_InitStructure.EXTI_Line = EXTI_Line8;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void Delay_Ms(u32 nTime)
{
TimingDelay = nTime;
while(TimingDelay != 0);
}
stm32f10x_it.c代码
/**
******************************************************************************
* @file Project/STM32F10x_StdPeriph_Template/stm32f10x_it.c
* @author MCD Application Team
* @version V3.5.0
* @date 08-April-2011
* @brief Main Interrupt Service Routines.
* This file provides template for all exceptions handler and
* peripherals interrupt service routine.
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>© COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
extern u32 TimingDelay;
extern u8 EXTI_Status;
/** @addtogroup STM32F10x_StdPeriph_Template
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/******************************************************************************/
/* Cortex-M3 Processor Exceptions Handlers */
/******************************************************************************/
/**
* @brief This function handles NMI exception.
* @param None
* @retval None
*/
void NMI_Handler(void)
{
}
/**
* @brief This function handles Hard Fault exception.
* @param None
* @retval None
*/
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Memory Manage exception.
* @param None
* @retval None
*/
void MemManage_Handler(void)
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Bus Fault exception.
* @param None
* @retval None
*/
void BusFault_Handler(void)
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Usage Fault exception.
* @param None
* @retval None
*/
void UsageFault_Handler(void)
{
/* Go to infinite loop when Usage Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles SVCall exception.
* @param None
* @retval None
*/
void SVC_Handler(void)
{
}
/**
* @brief This function handles Debug Monitor exception.
* @param None
* @retval None
*/
void DebugMon_Handler(void)
{
}
/**
* @brief This function handles PendSVC exception.
* @param None
* @retval None
*/
void PendSV_Handler(void)
{
}
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
TimingDelay--;
}
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
EXTI_Status=1;
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line8) != RESET)
{
EXTI_Status=2;
EXTI_ClearITPendingBit(EXTI_Line8);
}
}
/******************************************************************************/
/* STM32F10x Peripherals Interrupt Handlers */
/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */
/* available peripheral interrupt handler's name please refer to the startup */
/* file (startup_stm32f10x_xx.s). */
/******************************************************************************/
/**
* @brief This function handles PPP interrupt request.
* @param None
* @retval None
*/
/*void PPP_IRQHandler(void)
{
}*/
/**
* @}
*/
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/