Pandora IOT開発ボード学習(HALライブラリ)-実験5外部割り込み実験(学習ノート)

この記事のコードは、時間厳守のアトミックルーチンを参照しています

実験的特徴

ルーチンのソースコード:(main.c)

この実験では、キー入力によるLEDとBEEPの制御を実現し、主な機能コードは外部割り込みサービス機能にあります。

#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打印一次
    }
}

コード分​​析

HAL_Init()

HAL_Init()定義は次のとおりです。(実装されている特定の機能については、注を参照してください)

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()
ティックタイマークロックティック初期化関数

__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()関数の定義は次のとおりです。(関数の具体的な実装については、コメントを参照してください。参照のみ)

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()

ティックタイマーはHAL_Init()で初期化されており、次の関数が実際にfac_us値を割り当てます(現在、オペレーティングシステムは関与しておらず、他のコードは当面調査されません)。

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()

外部割り込み割り込み初期化機能、4キーの外部割り込み機能を設定します。

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

割り込みサービス関連機能

4つのキーがそれぞれ処理されEXTI9_5_IRQHandler()EXTI15_10_IRQHandler()2つの割り込み機能があります。

/**
 * @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()GPIOの汎用割り込み処理関数です。この関数は、最初に割り込みライン番号を取得し、次に割り込みをクリアし、最後に割り込みコールバック関数を呼び出します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()HALライブラリには定義がありますが、他のシステム割り込みサービス関数と同様に、関数を書き直す限り、書き直した関数はプログラムの実行時に実行され、GPIO外部割り込みコールバック関数は弱い定義です。はルーチンで定義されます次のように、機能も非常に単純です。さまざまなキーを押してさまざまな機能を実行します。

/**
 * @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()ここで実行されているのはdelay_us()、遅延がdelay_us()ティックタイマーによって実現されることです。上記でdelay_init()はfac_usを80に設定しており、ティックタイマーが80回(システムクロックは80MHz)カウントするのに10〜6秒かかります。これは1usです。

/**
 * @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;			//时间超过/等于要延迟的时间,则退出.
        }
    }
}

LEDビープ操作機能

LEDとブザーの制御機能はマクロ機能でありHAL_GPIO_WritePin()HAL_GPIO_TogglePin()それぞれと2つのライブラリ機能を使用します。

#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)

おすすめ

転載: blog.csdn.net/weixin_43772810/article/details/125313225