Apprentissage de la carte de développement Pandora IOT (bibliothèque HAL) - Expérience 5 Expérience d'interruption externe (Notes d'apprentissage)

Le code de cet article fait référence à la routine atomique ponctuelle

Fonctionnalités expérimentales

Code source de routine : (main.c)

Cette expérience réalise le contrôle de la LED et du BIP via une entrée clé, et le code de fonction principal se trouve dans la fonction de service d'interruption externe.

#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "beep.h"
#include "key.h"
#include "exti.h"

/*********************************************************************************
			  ___   _     _____  _____  _   _  _____  _____  _   __
			 / _ \ | |   |_   _||  ___|| \ | ||_   _||  ___|| | / /
			/ /_\ \| |     | |  | |__  |  \| |  | |  | |__  | |/ /
			|  _  || |     | |  |  __| | . ` |  | |  |  __| |    \
			| | | || |_____| |_ | |___ | |\  |  | |  | |___ | |\  \
			\_| |_/\_____/\___/ \____/ \_| \_/  \_/  \____/ \_| \_/

 *	******************************************************************************
 *	正点原子 Pandora STM32L475 IoT开发板	实验5
 *	外部中断实验	HAL库版本
 *	技术支持:www.openedv.com
 *	淘宝店铺:http://openedv.taobao.com
 *	关注微信公众平台微信号:"正点原子",免费获取STM32资料。
 *	广州市星翼电子科技有限公司
 *	作者:正点原子 @ALIENTEK
 *	******************************************************************************/

int main(void)
{
    
    
    HAL_Init();
    SystemClock_Config();	//初始化系统时钟为80M
    delay_init(80); 		//初始化延时函数    80M系统时钟
    uart_init(115200);		//初始化串口,波特率为115200

    LED_Init();				//初始化LED
    BEEP_Init();			//初始化蜂鸣器
    EXTI_Init();			//初始化外部中断

    while(1)
    {
    
    
        printf("OK\r\n");           //打印OK提示程序运行
        delay_ms(1000);             //每隔1s打印一次
    }
}

Analyse de codes

HAL_Init()

HAL_Init()La définition est la suivante : (voir les notes pour les fonctions spécifiques mises en œuvre)

HAL_StatusTypeDef HAL_Init(void)
{
    
    
  HAL_StatusTypeDef  status = HAL_OK;

  /* 配置 Flash 预取,指令缓存,数据缓存        */
  /* 默认配置为:预存取关闭 指令缓存和数据缓存开启 */     
#if (INSTRUCTION_CACHE_ENABLE == 0)  // Flash开启预存取配置,能加速CPU代码的执行
   __HAL_FLASH_INSTRUCTION_CACHE_DISABLE();
#endif /* INSTRUCTION_CACHE_ENABLE */

#if (DATA_CACHE_ENABLE == 0)
   __HAL_FLASH_DATA_CACHE_DISABLE();
#endif /* DATA_CACHE_ENABLE */ 

#if (PREFETCH_ENABLE != 0)
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif /* PREFETCH_ENABLE */

  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); // 配置 NVIC 优先级分组

  /* Use SysTick as time base source and configure 1ms tick (default clock after Reset is MSI) */
  if (HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) //初始化滴答定时器,时钟节拍设置为 1ms
  {
    
    
    status = HAL_ERROR;
  }
  else
  {
    
    
    /* Init the low level hardware */
    HAL_MspInit(); // 低速的外设初始化,比如 GPIO、中断等的设置(使用 STM32CubeMx 生成代码时会将低速外设初始
                   // 代码当这类函数里,其他情况下可以忽略这个函数
  }

  /* Return function status */
  return status;
}

HAL_InitTick()
fonction d'initialisation de la minuterie de l'horloge

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
    
    
  HAL_StatusTypeDef  status = HAL_OK;

  /*Configure the SysTick to have interrupt in 1ms time basis*/
  if (HAL_SYSTICK_Config(SystemCoreClock/1000UL) != 0U) // 系统时钟/1000,中断周期为 1ms
  {
    
    
    status = HAL_ERROR;
  }
  else
  {
    
    
    /*Configure the SysTick IRQ priority */
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0); // 将滴答定时器的中断优先级设置为最高
  }

  /* Return function status */
  return status;
}

SystemClock_Config()

SystemClock_Config()La définition de la fonction est la suivante : (Pour l'implémentation spécifique de la fonction, voir les commentaires, à titre indicatif uniquement)

void SystemClock_Config(void)
{
    
    
    HAL_StatusTypeDef	ret = HAL_OK;

    RCC_OscInitTypeDef RCC_OscInitStruct; // 定义振荡器初始化结构体变量
    RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义时钟初始化结构体变量

    __HAL_RCC_PWR_CLK_ENABLE(); // 使能电源控制时钟

    /*Initializes the CPU, AHB and APB busses clocks*/
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 将 HSE(外部高速时钟)作为时钟源
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;  // 开启 HSE
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 开启 PLL(锁相环)
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 将 HSE 作为 PLL 的时钟源
    RCC_OscInitStruct.PLL.PLLM = 1; // PLL-VCO 输入时钟分频系数,1 表示 2 分频(8 / 2 = 4M,本开发板外部晶振频率为 8MHz)
    RCC_OscInitStruct.PLL.PLLN = 20; // PLL-VCO 输出时钟倍频系数,4 * 20 = 80M,即输出时钟频率为 80MHz
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI 时钟的分频系数
    RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC1, RNG 和 USB 的时钟分频系数
    RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 主系统时钟的分频系数

    ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); //初始化时钟配置

    if(ret != HAL_OK)	while(1);

    /*Initializes the CPU, AHB and APB busses clocks*/
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                  | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; // 将所有时钟同时进行配置
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 将 PLL 作为系统时钟源
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB 不分频
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1 不分频
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2 不分频


    ret	= HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); // 配置时钟初始结构体变量,
    //使用 Flash 延迟4,等待状态(延迟)的数量需要根据CPU时钟(HCLK)的频率和内部电压范围来选择,具体怎么
    //选需要参考芯片手册

    if(ret != HAL_OK)	while(1);

    /*Configure the main internal regulator output voltage*/
    ret = HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); //内部寄存器输出电压配置
    // 下面是 HAL_PWREx_ControlVoltageScaling() 函数说明的部分内容:
    //PWR_REGULATOR_VOLTAGE_SCALE1 Regulator voltage output range 1 mode, typical output voltage
    // at 1.2 V, system frequency up to 80 MHz.

    if(ret != HAL_OK)	while(1);
}

delay_init()

La minuterie de tick a HAL_Init()été initialisée dans , et la fonction suivante fac_usattribue en fait une valeur (actuellement n'impliquant pas le système d'exploitation, et d'autres codes ne seront pas étudiés pour le moment).

static u32 fac_us = 0;							//us延时倍乘数
/**
 * @brief	初始化延迟函数,SYSTICK的时钟固定为AHB时钟
 *
 * @param   SYSCLK	系统时钟频率
 *
 * @return  void
 */
void delay_init(u8 SYSCLK)
{
    
    
#if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
    u32 reload;
#endif
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);//SysTick频率为HCLK
    fac_us = SYSCLK;						//不论是否使用OS,fac_us都需要使用

#if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
    reload = SYSCLK;					  //每秒钟的计数次数 单位为K
    reload *= 1000000 / delay_ostickspersec;	//根据delay_ostickspersec设定溢出时间
    //reload为24位寄存器,最大值:16777216,在80M下,约209.7ms左右
    fac_ms = 1000 / delay_ostickspersec;		//代表OS可以延时的最少单位
    SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
    SysTick->LOAD = reload; 					//每1/OS_TICKS_PER_SEC秒中断一次
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
#else
#endif
}

EXTI_Init()

Fonction d'initialisation d'interruption d'interruption externe, configurez la fonction d'interruption externe de 4 touches.

/**
 * @brief	外部中断初始化函数
 *
 * @param   void
 *
 * @return  void
 */
void EXTI_Init(void)
{
    
    
	/*
		KEY0 	- PD10
		KEY1 	- PD9
		KEY2 	- PD8
		WK_UP 	- PC13
	*/
    GPIO_InitTypeDef GPIO_Initure;
    
    __HAL_RCC_GPIOC_CLK_ENABLE();               //开启GPIOC时钟
    __HAL_RCC_GPIOD_CLK_ENABLE();               //开启GPIOD时钟
    
    GPIO_Initure.Pin=GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 ;	//PD8.9.10
    GPIO_Initure.Mode=GPIO_MODE_IT_FALLING;     //下降沿触发GPIO_MODE_IT_FALLING
    GPIO_Initure.Pull=GPIO_PULLUP;
    HAL_GPIO_Init(GPIOD,&GPIO_Initure);
	
	GPIO_Initure.Pin=GPIO_PIN_13;                //PC13
    GPIO_Initure.Mode=GPIO_MODE_IT_RISING;      //上升沿触发
    GPIO_Initure.Pull=GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIOC,&GPIO_Initure);
    
    //中断线8,9
    HAL_NVIC_SetPriority(EXTI9_5_IRQn,2,0);       //抢占优先级为2,子优先级为0
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);             //使能中断线0

    //中断线10,13
    HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,1);   //抢占优先级为2,子优先级为1
    HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);         //使能中断线13  
}

Fonctions liées au service d'interruption

Quatre touches sont gérées dans EXTI9_5_IRQHandler()et EXTI15_10_IRQHandler()deux fonctions d'interruption respectivement,

/**
 * @brief	EXTI9_5中断服务函数
 *
 * @param   void
 *
 * @return  void
 */
void EXTI9_5_IRQHandler(void)
{
    
    
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);//调用中断处理公用函数
	
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);//调用中断处理公用函数
}

/**
 * @brief	EXTI15_10中断服务函数
 *
 * @param   void
 *
 * @return  void
 */
void EXTI15_10_IRQHandler(void)
{
    
    
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);//调用中断处理公用函数
	
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);//调用中断处理公用函数
}

HAL_GPIO_EXTI_IRQHandler()Il s'agit de la fonction générale de traitement des interruptions du GPIO. Cette fonction obtient d'abord le numéro de ligne d'interruption, puis efface l'interruption et enfin appelle la fonction de rappel d'interruption HAL_GPIO_EXTI_Callback().

/**
  * @brief  Handle EXTI interrupt request.
  * @param  GPIO_Pin Specifies the port pin connected to corresponding EXTI line.
  * @retval None
  */
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
    
    
  /* EXTI line interrupt detected */
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    
    
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

HAL_GPIO_EXTI_Callback()Il existe une définition dans la bibliothèque HAL, mais c'est une définition faible, tout comme les autres fonctions de service d'interruption système, tant que nous réécrivons la fonction, la fonction que nous réécrivons s'exécutera lors de l'exécution du programme et la fonction de rappel d'interruption externe GPIO est défini dans la routine Comme suit, la fonction est également très simple, appuyez sur différentes touches pour exécuter différentes fonctions.

/**
 * @brief	中断服务程序中需要做的事情,在HAL库中所有的外部中断服务函数都会调用此函数
 *
 * @param   GPIO_Pin	中断引脚号
 *
 * @return  void
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    
    
    delay_ms(50);      //消抖
    switch(GPIO_Pin)
	{
    
    
		case  GPIO_PIN_13:		//控制蜂鸣器状态翻转
			if( WK_UP == 1)
				BEEP_TogglePin;
			break;

		case  GPIO_PIN_8:		//控制LED_B灯状态翻转
			if( KEY2 == 0)
				LED_B_TogglePin;
			break;

		case  GPIO_PIN_9:		//控制LED_G灯状态翻转
			if( KEY1 == 0)
			  LED_G_TogglePin;
			break;

		case  GPIO_PIN_10:		//控制LED_R灯状态翻转
			if( KEY0 == 0)
				LED_R_TogglePin;
			break;
		default:
			break;
	}
}

delay_ms()

delay_ms()Ce qui fonctionne ici, c'est delay_us()que le retard est delay_us()réalisé via une minuterie. Ce qui précède delay_init()a défini fac_us sur 80, et il faut 10 à 6 secondes pour que le chronomètre compte 80 fois (l'horloge système est de 80 MHz), soit 1 us.

/**
 * @brief	延时毫秒(ms)函数
 *
 * @param   nms		需要延时多少毫秒
 *
 * @return  void
 */
void delay_ms(u16 nms)
{
    
    
    u32 i;

    for(i = 0; i < nms; i++) delay_us(1000);
}

/**
 * @brief	延时微秒(us)函数
 *
 * @remark	nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
 *
 * @param   nus		需要延时多少微秒
 *
 * @return  void
 */
void delay_us(u32 nus)
{
    
    
    u32 ticks;
    u32 told, tnow, tcnt = 0;
    u32 reload = SysTick->LOAD;				//LOAD的值
    ticks = nus * fac_us; 					//需要的节拍数
    told = SysTick->VAL;        			//刚进入时的计数器值

    while(1)
    {
    
    
        tnow = SysTick->VAL;

        if(tnow != told)
        {
    
    
            if(tnow < told)tcnt += told - tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
            else tcnt += reload - tnow + told;
			
            told = tnow;
            if(tcnt >= ticks)break;			//时间超过/等于要延迟的时间,则退出.
        }
    }
}

Fonction de fonctionnement LED BEEP

Les fonctions de contrôle de la LED et du buzzer sont des fonctions macro, qui HAL_GPIO_WritePin()utilisent HAL_GPIO_TogglePin()respectivement deux fonctions de bibliothèque.

#define LED_R(n)			(n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_7,GPIO_PIN_RESET))
#define LED_R_TogglePin		HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_7)

#define LED_G(n)			(n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_8,GPIO_PIN_RESET))
#define LED_G_TogglePin     HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_8)

#define LED_B(n)			(n?HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOE,GPIO_PIN_9,GPIO_PIN_RESET))
#define LED_B_TogglePin     HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_9)

#define BEEP(n)	(n?HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET))
#define BEEP_TogglePin	HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_2)

Je suppose que tu aimes

Origine blog.csdn.net/weixin_43772810/article/details/125313225
conseillé
Classement