STM32基础--SysTick系统定时器

SysTick 简介

SysTick—系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK等于 72M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。
因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。

SysTick 寄存器介绍

SysTick—系统定时器有 4 个寄存器,简要介绍如下。在使用 SysTick 产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。
SysTick寄存器汇总

寄存器名称寄存器描述
CTRLSysTick控制及状态寄存器
LOADSysTick重装载数值寄存器
VALSysTick当前数值寄存器
CALIBSysTick校准数值寄存器

SysTick控制及状态寄存器

位段名称类型复位值描述
16COUNTFLAGR/W0如果在上次读取本寄存器后,SysTick已经计数位0,则该位为1
2CLKSOURCER/W0时钟源选择位,0=AHB/8,1=处理器时钟AHB
1TICKINTR/W01=SysTick倒数计到0时产生SysTick异常请求,0=数到0时无动作。也可以通过读取COUNTFLAG标志位来确定计数器是否递减到0
0ENABLER/W0SysTick定时器的使能位

SysTick重装载数值寄存器

位段名称类型复位值描述
23:0RELOADR/W0当倒数计数置零时,将被重装载的值

SysTick当前数值寄存器

位段名称类型复位值描述
23:0CURRENTR/W0读取时返回当前倒计数的值,写他则使置清零,同时还会清除SysTick控制及状态寄存器中的COUNTFLAG标志

SysTick校准数值寄存器

位段名称类型复位值描述
31NOREFR01=没有外部参考时钟(STCLK 不可用)
0=外部参考时钟可用
30SKEWR11=校准值不是准确的 10ms
0=校准值是准确的 10ms
23:0TENMSR010ms 的时间内倒计数的格数。芯片设计者应该通过 Cortex-M3 的输入信号提供该数值。若该值读回零,则表示无法使用校准功能

SysTick 定时实验

利用 SysTick 产生 1s 的时基,LED 以 1s 的频率闪烁。

硬件设计

SysTick 属于单片机内部的外设,不需要额外的硬件电路,剩下的只需一个 LED 灯即可。

软件设计

我们创建了两个文件:bsp_SysTick.c 和 bsp_ SysTick.h 文件用来存放 SysTick驱动程序及相关宏定义,中断服务函数放在 stm32f10x_it.c 文件中。(别忘了添加.h文件的路径)

编程要点

1、设置重装载寄存器的值
2、清除当前数值寄存器的值
3、配置控制与状态寄存器

代码分析

SysTick 属于内核的外设,有关的寄存器定义和库函数都在内核相关的库文件 core_cm3.h 中。

SysTick 配置库函数

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  /* 不可能的重装载值,超出范围*/
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);          
   // 设置重装载寄存器                       
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; 
  // 设置中断优先级
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  
  // 设置当前数值寄存器
  SysTick->VAL   = 0;  
    // 设置系统定时器的时钟源为 AHBCLK=72M
	// 使能系统定时器中断
	// 使能定时器
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}

用固件库编程的时候我们只需要调用库函数 SysTick_Config() 即可,形参 ticks 用来设置重装载寄存器的值,最大不能超过重装载寄存器的值 224,当重装载寄存器的值递减到 0 的时候产生中断,然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。紧随其后设置好中断优先级,最后配置系统定时器的时钟等于 AHBCLK=72M,使能定时器和定时器中断,这样系统定时器就配置好了,一个库函数搞定。

SysTick_Config() 库函数主要配置了 SysTick 中的三个寄存器:LOAD、VAL 和 CTRL,有关具体的部分看代码注释即可。

配置 SysTick 中断优先级

static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if(IRQn < 0) {
    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
  else {
    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */
}

函数首先先判断形参 IRQn 的大小,如果是小于 0,则表示这个是系统异常,系统异常的优先级由内核外设 SCB 的寄存器 SHPRx 控制,如果大于 0 则是外部中断,外部中断的优先级由内核外设 NVIC 中的 IPx 寄存器控制。

因为 SysTick 属于内核外设,跟普通外设的中断优先级有些区别,并没有抢占优先级和子优先级的说法。在 STM32F103 中,内核外设的中断优先级由内核 SCB 这个外设的寄存器:SHPRx(x=1.2.3)来配置。有关 SHPRx 寄存器的详细描述可参考《Cortex-M3 内核编程手册》4.4.8 章节。

下面我们简单介绍下这个寄存器。

SPRH1-SPRH3 是一个 32 位的寄存器,但是只能通过字节访问,每 8 个字段控制着一个内核外设的中断优先级的配置。在 STM32F103 中,只有位 7:4 这高四位有效,低四位没有用到,所以内核外设的中断优先级可编程为:0~15,只有 16 个可编程优先级,数值越小,优先级越高。如果软件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决定优先级大小,编号越小,优先级越高。

系统异常优先级字段

异常字段寄存器描述
Memory management faultPRI_4SHPR1
Bus faultPRI_5SHPR1
Usage faultPRI_6SHPR1
SVCallPRI_11SHPR2
PendSVPRI_14SHPR3
SysTickPRI_15SHPR3

如果要修改内核外设的优先级,只需要修改下面三个寄存器对应的某个字段即可。

在这里插入图片描述

在系统定时器中,配置优先级为 (1 « __NVIC_PRIO_BITS) - 1),其中宏 __NVIC_PRIO_BITS为 4,那计算结果就等于 15,可以看出系统定时器此时设置的优先级在内核外设中是最低的,如果要修改优先级则修改这个值即可,范围为:0~15。

// 设置系统定时器中断优先级
NVIC_SetPriority (SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1);

但是,问题来了,刚刚我们只是学习了内核的外设的优先级配置。如果我同时使用了 systick 和片上外设呢?而且片上外设也刚好需要使用中断,那 systick 的中断优先级跟外设的中断优先级怎么设置?会不会因为 systick 是内核里面的外设,所以它的中断优先级就一定比内核之外的外设的优先级高?

外设在设置中断优先级的时候,首先要分组,然后设置抢占优先级和子优先级。而 systick 这类内核的外设在配置的时候,只需要配置一个寄存器即可,取值范围为 0~15。既然配置方法不同,那如何区分两者的优先级?下面举例说明。

比如配置一个外设的中断优先级分组为 2,抢占优先级为 1,子优先级也为 1,systick 的优先级为固件库默认配置的 15。当我们比较内核外设和片上外设的中断优先级的时候,我们只需要抓住NVIC 的中断优先级分组不仅对片上外设有效,同样对内核的外设也有效。我们把 systick 的优先级 15 转换成二进制值就是 1111(0b),又因为 NVIC 的优先级分组 2,那么前两位的 11(0b) 就是3,后两位的 11(0b) 也是 3。无论从抢占还是子优先级都比我们设定的外设的优先级低。如果当两个的软件优先级都配置成一样,那么就比较他们在中断向量表中的硬件编号,编号越小,优先级越高。

SysTick 初始化函数(bsp_SysTick.c中)

/**
  * @brief  启动系统滴答定时器 SysTick
  * @param  无
  * @retval 无
  */
void SysTick_Init(void)
{

	if (SysTick_Config(SystemCoreClock / SysTime))	// ST3.5.0库版本
	{ 
		//初始化出错
		while (1);
	}
}

(在bsp_SysTick.h中)

//系统滴答定时器中断时间,分别为1ms,10us,1us
 	/* SystemFrequency / 1000    1ms中断一次
	 * SystemFrequency / 100000	 10us中断一次
	 * SystemFrequency / 1000000 1us中断一次
	 */
	 
#define OMs										0			//修改宏选择使用那个时间
#define TUs										1
 
 
 #if			OMs
				 #define SysTime             1000
 #elif		TUs
 				 #define SysTime             100000
 #else		
				 #define SysTime             1000000
 #endif

SysTick 初始化函数由用户编写,里面调用了 SysTick_Config() 这个固件库函数,通过设置该固件库函数的形参,就决定了系统定时器经过多少时间就产生一次中断。

SysTick 中断时间的计算
SysTick 定时器的计数器是向下递减计数的,计数一次的时间 TDEC=1/CLKAHB,当重装载寄存器中的值 VALUELOAD减到 0 的时候,产生中断,可知中断一次的时间 TINT=VALUELOAD*TDEC=VALUELOAD/CLKAHB,其中 CLKAHB=72MHZ。如果设置 VALUELOAD为 72,那中断一次的时间TINT=72/72M=1us。不过 1us 的中断没啥意义,整个程序的重心都花在进出中断上了,根本没有时间处理其他的任务。

SysTick_Config(SystemCoreClock / 100000)

SysTick_Config()的形我们配置为 SystemCoreClock / 100000=72M/100000=720,从刚刚分析我们知道这个形参的值最终是写到重装载寄存器 LOAD 中的,从而可知我们现在把 SysTick 定时器中断一次的时间 TINT=720/72M=10us。

SysTick 定时时间的计算
当设置好中断时间 TINT后,我们可以设置一个变量 t,用来记录进入中断的次数,那么变量 t 乘以中断的时间 TINT就可以计算出需要定时的时间。

SysTick 定时函数(bsp_SysTick.c)
现在我们定义一个微秒级别的延时函数,形参为 nTime,当用这个形参乘以中断时间 TINT就得出我们需要的延时时间,其中 TINT我们已经设置好为 10us。

/**
* @briefus 延时程序,10us 为一个单位
* @param
*@arg nTime: Delay_us( 1 ) 则实现的延时为 1 * 10us = 10us
* @retval无
*/
void Delay_us(__IO u32 nTime)
{
TimingDelay = nTime;
while (TimingDelay != 0);
}

函数 Delay_us() 中我们等待 TimingDelay 为 0,当 TimingDelay 为 0 的时候表示延时时间到。变量TimingDelay 在中断函数中递减,即 SysTick 每进一次中断即 10us 的时间 TimingDelay 递减一次。
SysTick 中断服务函数(stm32f10x_it.c中)

void SysTick_Handler(void)
{
TimingDelay_Decrement();
}

中断复位函数调用了另外一个函数 TimingDelay_Decrement(),原型如下:(bsp_SysTick.c中)

/**
  * @brief  获取节拍程序
  * @param  无
  * @retval 无
  * @attention  在 SysTick 中断函数 SysTick_Handler()调用
  */
void TimingDelay_Decrement(void)
{
	if (TimingDelay != 0x00)
	{ 
		TimingDelay--;
	}
}

别忘记定义全局变量TimingDelay

TimingDelay 的值等于延时函数中传进去的 nTime 的值,比如 nTime=100000,则延时的时间等于100000*10us=1s。

测试函数(bsp_SysTick.c中)

/**
  * @brief  使用中断的方法,滴答定时器测试函数
  * @param  无
  * @retval 无
  */
void TimingDelay_Interrupt_Test(void)
{
	/* LED 端口初始化 */
	LED_GPIO_Config();
	
	/* 配置SysTick 为10us中断一次 */
	SysTick_Init();
		for(;;)
	{

		LED_RED;
	    Delay_10us(100000);    	// 100000 * 10us = 1000ms


		LED_GREEN;
	    Delay_10us(100000);		// 100000 * 10us = 1000ms


	
		LED_BLUE;
	    Delay_10us(100000);		// 100000 * 10us = 1000ms

	}     
}

初始化了 LED 和 SysTick,然后在一个 while 循环中以 1s 的频率让 LED 闪烁。

另外一种更简洁的定时编程

上面的实验,我们是使用了中断,而且经过多个函数的调用,还使用了全局变量,理解起来挺费劲的,其实还有另外一种更简洁的写法。我们知道,systick 的 counter 从 reload 值往下递减到 0 的时候,CTRL 寄存器的位 16:countflag 会置 1,且读取该位的值可清 0,所有我们可以使用软件查询的方法来实现延时。(在bsp_SysTick.c中)

// couter 减1的时间 等于 1/systick_clk
// 当counter 从 reload 的值减小到0的时候,为一个循环,如果开启了中断则执行中断服务程序,
// 同时 CTRL 的 countflag 位会置1
// 这一个循环的时间为 reload * (1/systick_clk)

/**
  * @brief  直接查询CTRL寄存器,进行us级延时
  * @param  us为延时多少us
  * @retval 无
  */
void SysTick_Delay_Us( __IO uint32_t us)
{
	uint32_t i;
	SysTick_Config(SystemCoreClock/1000000);
	
	for(i=0;i<us;i++)
	{
		// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1	
		while( !((SysTick->CTRL)&(1<<16)) );
	}
	// 关闭SysTick定时器
	SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}

/**
  * @brief  直接查询CTRL寄存器,进行ms级延时
  * @param  ms为延时多少ms
  * @retval 无
  */
void SysTick_Delay_Ms( __IO uint32_t ms)
{
	uint32_t i;	
	SysTick_Config(SystemCoreClock/1000);
	
	for(i=0;i<ms;i++)
	{
		// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1
		// 当置1时,读取该位会清0
		while( !((SysTick->CTRL)&(1<<16)) );
	}
	// 关闭SysTick定时器
	SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}



/**
  * @brief  不使用中断的方法,轮循,滴答定时器测试函数
  * @param  无
  * @retval 无
  */
void TimingDelay_Test(void)
{
	/* LED 端口初始化 */
	LED_GPIO_Config();
	
	/* 配置SysTick 为10us中断一次 */
	SysTick_Init();
		for(;;)
	{

		LED_RED;
	    SysTick_Delay_Ms(100);    	// 100ms


		LED_GREEN;
	    SysTick_Delay_Ms(100);		//  100ms


	
		LED_BLUE;
	    SysTick_Delay_Ms(100);		//  100ms

	}     
}

注:轮循的方法虽然便于理解,但是相当于浪费CPU部分性能,在延时的时候CPU被占用,而且中断还开着,还不断查询寄存器,极大的浪费CPU性能。(其实我感觉有点脱裤子放屁)(别想着修改为S级别,因为那个初始化中断的时间会给的特别大,然后就内存溢出了GG)

代码

bsp_SysTick.c

/**
 * *****************************************************************************
 * @file        bsp_SysTick.c
 * @brief     	系统滴答定时器
 * @author       (六千里)
 * @date        2024-03-15
 * @copyright   无
 * *****************************************************************************
 */
#include "bsp_SysTick.h"

/**
 * *****************************************************************************
 * 使用的全局变量
 * *****************************************************************************
 */
 __IO uint32_t TimingDelay;
 
 
 
/**
  * @brief  启动系统滴答定时器 SysTick
  * @param  无
  * @retval 无
  */
void SysTick_Init(void)
{

	if (SysTick_Config(SystemCoreClock / SysTime))	// ST3.5.0库版本
	{ 
		//初始化出错
		while (1);
	}
}

/**
  * @brief   us延时程序,10us为一个单位
  * @param  
  *		@arg nTime: Delay_us( 1 ) 则实现的延时为 1 * 10us = 10us
  * @retval  无
  */
void Delay_10us(__IO u32 nTime)
{ 
	TimingDelay = nTime;	

	// 使能滴答定时器  
	SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;

	while(TimingDelay != 0);
}

/**
  * @brief  获取节拍程序
  * @param  无
  * @retval 无
  * @attention  在 SysTick 中断函数 SysTick_Handler()调用
  */
void TimingDelay_Decrement(void)
{
	if (TimingDelay != 0x00)
	{ 
		TimingDelay--;
	}
}

/**
  * @brief  使用中断的方法,滴答定时器测试函数
  * @param  无
  * @retval 无
  */
void TimingDelay_Interrupt_Test(void)
{
	/* LED 端口初始化 */
	LED_GPIO_Config();
	
	/* 配置SysTick 为10us中断一次 */
	SysTick_Init();
		for(;;)
	{

		LED_RED;
	    Delay_10us(100000);    	// 100000 * 10us = 1000ms


		LED_GREEN;
	    Delay_10us(100000);		// 100000 * 10us = 1000ms


	
		LED_BLUE;
	    Delay_10us(100000);		// 100000 * 10us = 1000ms

	}     
}


// couter 减1的时间 等于 1/systick_clk
// 当counter 从 reload 的值减小到0的时候,为一个循环,如果开启了中断则执行中断服务程序,
// 同时 CTRL 的 countflag 位会置1
// 这一个循环的时间为 reload * (1/systick_clk)

/**
  * @brief  直接查询CTRL寄存器,进行us级延时
  * @param  us为延时多少us
  * @retval 无
  */
void SysTick_Delay_Us( __IO uint32_t us)
{
	uint32_t i;
	SysTick_Config(SystemCoreClock/1000000);
	
	for(i=0;i<us;i++)
	{
		// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1	
		while( !((SysTick->CTRL)&(1<<16)) );
	}
	// 关闭SysTick定时器
	SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}

/**
  * @brief  直接查询CTRL寄存器,进行ms级延时
  * @param  ms为延时多少ms
  * @retval 无
  */
void SysTick_Delay_Ms( __IO uint32_t ms)
{
	uint32_t i;	
	SysTick_Config(SystemCoreClock/1000);
	
	for(i=0;i<ms;i++)
	{
		// 当计数器的值减小到0的时候,CRTL寄存器的位16会置1
		// 当置1时,读取该位会清0
		while( !((SysTick->CTRL)&(1<<16)) );
	}
	// 关闭SysTick定时器
	SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}



/**
  * @brief  不使用中断的方法,轮循,滴答定时器测试函数
  * @param  无
  * @retval 无
  */
void TimingDelay_Test(void)
{
	/* LED 端口初始化 */
	LED_GPIO_Config();
	
	/* 配置SysTick 为10us中断一次 */
	SysTick_Init();
		for(;;)
	{

		LED_RED;
	    SysTick_Delay_Ms(100);    	// 100ms


		LED_GREEN;
	    SysTick_Delay_Ms(100);		//  100ms


	
		LED_BLUE;
	    SysTick_Delay_Ms(100);		//  100ms

	}     
}

bsp_SysTick.h

#ifndef __BSP_SYSTICK_H
#define __BSP_SYSTICK_H
/**
 * *****************************************************************************
 * 包含的头文件
 * *****************************************************************************
 */
 #include "stm32f10x.h"
 
 
 /**
 * *****************************************************************************
 * 宏定义
 * *****************************************************************************
 */
 
 
 
//系统滴答定时器中断时间,分别为1ms,10us,1us
 	/* SystemFrequency / 1000    1ms中断一次
	 * SystemFrequency / 100000	 10us中断一次
	 * SystemFrequency / 1000000 1us中断一次
	 */
	 
#define OMs										0			//修改宏选择使用那个时间
#define TUs										1
 
 
 #if			OMs
				 #define SysTime             1000
 #elif		TUs
 				 #define SysTime             100000
 #else		
				 #define SysTime             1000000
 #endif		


 
 
/**
 * *****************************************************************************
 * .c文件中包含的函数
 * *****************************************************************************
 */
 void SysTick_Init(void);
 void Delay_10us(__IO u32 nTime);
 void TimingDelay_Decrement(void);
 void TimingDelay_Interrupt_Test(void);
 void TimingDelay_Test(void);
 void SysTick_Delay_Ms( __IO uint32_t ms);
 void SysTick_Delay_Us( __IO uint32_t us);
#endif /*__BSP_SYSTICK_H*/

stm32f10x_conf,h

/**
  ******************************************************************************
  * @file    Project/STM32F10x_StdPeriph_Template/stm32f10x_conf.h 
  * @author  MCD Application Team
  * @version V3.5.0
  * @date    08-April-2011
  * @brief   Library configuration file.
  ******************************************************************************
  * @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>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F10x_CONF_H
#define __STM32F10x_CONF_H

/* Includes ------------------------------------------------------------------*/
/* Uncomment/Comment the line below to enable/disable peripheral header file inclusion */
#include "stm32f10x_adc.h"
#include "stm32f10x_bkp.h"
#include "stm32f10x_can.h"
#include "stm32f10x_cec.h"
#include "stm32f10x_crc.h"
#include "stm32f10x_dac.h"
#include "stm32f10x_dbgmcu.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_flash.h"
#include "stm32f10x_fsmc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_i2c.h"
#include "stm32f10x_iwdg.h"
#include "stm32f10x_pwr.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_rtc.h"
#include "stm32f10x_sdio.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_wwdg.h"
#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */
/**
 *自己书写文件的头文件
 **/
#include "bsp_led.h"
#include "bsp_key.h"
#include "bsp_led_bitbang.h"
#include "bsp_clkconfig.h"
#include "bsp_mcooutput.h"
#include "bsp_exti.h"
#include "bsp_SysTick.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Uncomment the line below to expanse the "assert_param" macro in the 
   Standard Peripheral Library drivers code */
/* #define USE_FULL_ASSERT    1 */

/* Exported macro ------------------------------------------------------------*/
#ifdef  USE_FULL_ASSERT

/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param  expr: If expr is false, it calls assert_failed function which reports 
  *         the name of the source file and the source line number of the call 
  *         that failed. If expr is true, it returns no value.
  * @retval None
  */
  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
  void assert_failed(uint8_t* file, uint32_t line);
#else
  #define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */

#endif /* __STM32F10x_CONF_H */

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

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>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"

/** @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  系统滴答定时器中断函数,10us中断一次
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
	TimingDelay_Decrement();	
}



/**
  * @brief  按键1中断处理函数
  * @param  None
  * @retval None
  */
void KEY1_IRQHandler(void)
{
	//确保是否产生了 EXTI Line 中断
	if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) == SET){
		//再次检查信号是否来自KEY1,上升沿
		if(GPIO_ReadInputDataBit(KEY1_INT_GPIO_PORT,KEY1_INT_GPIO_PIN)){
			// LED1 取反
			LED1_TOGGLE;
			//清除中断标志
			EXTI_ClearFlag(KEY1_INT_EXTI_LINE);
		}
	}
}
/**
  * @brief  按键2中断处理函数
  * @param  None
  * @retval None
  */
void KEY2_IRQHandler(void)
{
	//确保是否产生了 EXTI Line 中断
	if(EXTI_GetITStatus(KEY2_INT_EXTI_LINE) == SET){
		//再次检查信号是否来自KEY2,下降沿
		if(GPIO_ReadInputDataBit(KEY2_INT_GPIO_PORT,KEY2_INT_GPIO_PIN) == RESET){
			// LED2 取反
			LED2_TOGGLE;
			//清除中断标志位
			EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);
		}

	}
}



/******************************************************************************/
/*                 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****/

main.c

/**
 * *****************************************************************************
 * @file        main.c
 * @brief       主函数
 * @author       (六千里)
 * @date        2024-03-10
 * @copyright   无
 * *****************************************************************************
 */
#include "stm32f10x.h"  

int main(void)	
{
	// 来到这里的时候,系统的时钟已经被配置成72M。
	#if 0
		TimingDelay_Interrupt_Test();
	#else
		TimingDelay_Test();
	#endif
}
  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是可以实现你所描述的功能的主函数代码,需要注意的是,因为你使用了多个头文件并且连接了多个模块,所以需要在main函数中进行初始化设置。 ```c #include "stm32f4xx.h" #include "led.h" #include "key.h" #include "usart1.h" #include "stdio.h" #include "systick.h" #include "beep.h" #include "timer.h" #include "adc.h" #include "ds18b20.h" #define MOTOR_PORT GPIOA #define MOTOR_PIN GPIO_Pin_6 void motor_init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = MOTOR_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(MOTOR_PORT, &GPIO_InitStructure); } void motor_start(void) { GPIO_SetBits(MOTOR_PORT, MOTOR_PIN); } void motor_stop(void) { GPIO_ResetBits(MOTOR_PORT, MOTOR_PIN); } void delay_ms(uint32_t ms) { uint32_t i; while (ms--) { i = 10000; while (i--) { __NOP(); } } } int main(void) { uint16_t adc_value = 0; uint8_t ds18b20_temp[2]; float temperature = 0.0; /* 初始化LED */ LED_Init(); /* 初始化按键 */ KEY_Init(); /* 初始化串口1 */ USART1_Init(115200); /* 初始化SysTick */ SysTick_Init(); /* 初始化蜂鸣器 */ BEEP_Init(); /* 初始化定时器 */ TIM3_Int_Init(9999, 8399); /* 初始化ADC */ ADC1_Init(); /* 初始化DS18B20 */ DS18B20_Init(); /* 初始化电机 */ motor_init(); while (1) { /* 读取温度值 */ DS18B20_ReadTemp(ds18b20_temp); temperature = ((float)ds18b20_temp[1] * 256 + ds18b20_temp[0]) / 16.0; printf("Temperature: %.1f°C\n", temperature); /* 显示温度值 */ LED_Show_Num(temperature * 10); /* 检测雨水 */ adc_value = ADC1_Get_Sample(ADC_Channel_12); if (adc_value > 1000) { motor_start(); printf("Rain detected!\n"); } else { motor_stop(); } /* 延时 */ delay_ms(500); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值