Internet of Things | Keystroke Experiment---Learn I/O input and interrupt programming|Format of function description|How to use CMSIS delay|Read the implementation and analysis of key capture code through external interrupt-Study Notes (14)

Implementation and analysis of key capture code through external interrupts

1 Code flow analysis
2 Code implementation
Analysis of library function HAL_Init(void):

HAL_StatusTypeDef HAL_Init(void)
{
  /* Configure Flash prefetch, Instruction cache, Data cache */
#if (INSTRUCTION_CACHE_ENABLE != 0U) //0U表示无符号整型 0 , 1U 表示无符号整型1 ~0U就是对无符号数0取反。
  __HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
#endif /* INSTRUCTION_CACHE_ENABLE  允许指令缓存*/

#if (DATA_CACHE_ENABLE != 0U)
  __HAL_FLASH_DATA_CACHE_ENABLE();
#endif /* DATA_CACHE_ENABLE */

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

  /* Set Interrupt Group Priority 中断优先级分组*/
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
  HAL_InitTick(TICK_INT_PRIORITY); //初始化系统时钟

  /* Init the low level hardware 初始化底层硬件(堆栈指针)*/
  HAL_MspInit();  //使用HAL_Delay延时

  /* Return function status */
  return HAL_OK;
}

Tip1: Format of function description

/****************
*函数名:main
*函数的描述:通过中断实现按键的捕获
*输入参教:
*输出参数:
*返回值:
*图数作者:
*创建时间:
*更改说明:
*****************/

Tip2: How to use CMSIS delay

HAL_Delay() system delay steps:
Its implementation steps are as follows:
1. Use variables to obtain the value of the system clock source counter
2. Obtain the parameter value of the delay time
3. Compare the two sizes, if the value of the clock counter is greater than the value to be implemented The delayed value will be trapped in the loop; otherwise, the loop will be jumped out and the delay will be completed.

/**
    * @brief此函数提供最小延迟(以毫秒为单位)对变量递增。
    * @note在默认实现中,SysTick计时器是基准时间的来源。
    *它用于在固定的时间间隔生成中断,其中uwTick是递增的。
    这个函数被声明为__weak,在其他情况下会被覆盖实现在用户文件。
    *@param Delay指定延迟时间长度,单位为毫秒。
    *@retval无
  */
__weak void HAL_Delay(uint32_t Delay)
{
  uint32_t tickstart = HAL_GetTick(); //获得起始时钟
  uint32_t wait = Delay;

  /* Add a freq to guarantee minimum wait */
  if (wait < HAL_MAX_DELAY)   //#define HAL_MAX_DELAY      0xFFFFFFFFU=1111 1111 1111 1111 1111 1111 1111 1111
  {

    //  HAL_TICK_FREQ_1KHZ         = 1U,
    //  HAL_TICK_FREQ_DEFAULT      = HAL_TICK_FREQ_1KHZ

    wait += (uint32_t)(uwTickFreq);  //作用是给wait加1。HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT;  /* 1KHz */

  }

  while((HAL_GetTick() - tickstart) < wait) //当前时钟-起始时钟的值小于wait(delay)就重复操作,直到计时结束
  {
  }
}

GetTick function prototype

/**
 调用这个函数是为了增加一个全局变量“uwTick”用作申请时基。
在默认实现中,这个变量每1ms增加一次在SysTick ISR。
这个函数被声明为__weak,在其他情况下会被覆盖实现在用户文件。
* @retval无
  */
__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;
}

/**
* @brief提供以毫秒为单位的tick值。
这个函数被声明为__weak,在其他情况下会被覆盖实现在用户文件。
@retval tick value
  */
__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}

As for the bare metal level, there are no other hidden things. If you analyze it yourself, you can still sort out the details and timing relationships of the program's operation. Principles to follow:
1. Try not to use dead-end delays in the main program. 2. The query frequency of each subprogram (can also be called a task) should be greater than the running time of the main program. For example: ad sampling, sampling once every 100ms, then the main program must be executed within 100ms.
The waiting delay can be at the us level. Where timing is high, timers are used for large delays.

stm32f407_intr_handle.c analysis

Interrupt handling function: void EXTI4_IRQHandler

According to the description of handler in startup_stm32f407xx.s, write the corresponding breakpoint processing function:

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

Execute function: HAL_GPIO_EXTI_IRQHandler(KEY0_PIN), call void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) in stm32f4xx_hal_gpio.c, the function is defined as:

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  //#define __HAL_GPIO_EXTI_GET_IT(__EXTI_LINE__) (EXTI->PR & (__EXTI_LINE__))
  //__EXTI_LINE__ specifies the EXTI line flag to check.
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) // RESET = 0U(stm32f4xx.h),表明检测到了中断
  {
    /**
  * @brief  Clears the EXTI's line pending bits.
  * @param  __EXTI_LINE__ specifies the EXTI lines to clear.
  *          This parameter can be any combination of GPIO_PIN_x where x can be (0..15)
  * @retval None
  */
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);

    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

The callback function in HAL_GPIO_EXTI_Callback is not explicitly implemented in HAL (weak declaration), and you need to construct the implementation function yourself.
The prototype is:

/**
  * @brief  EXTI line detection callbacks.
  * @param  GPIO_Pin Specifies the pins connected EXTI line
  * @retval None
  */
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

The callback function is rewritten in key.c:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == KEY0_PIN) // KEY0_PIN被按下
	{
		Led_Ctrl(LED0_PIN_ROW, LED0_PIN, LED_ON); //执行点灯操作
  }
}

uint16_t Detect_key(uint16_t key_pin) is not used in this section.

Debugging process

Set up interrupts at the interrupt handling function and uwTick self-increasing operation function for debugging:

void EXTI4_IRQHandler(void)
{
	HAL_GPIO_EXTI_IRQHandler(KEY0_PIN);
}


void SysTick_Handler(void)
{
		HAL_IncTick(); //uwTick自加操作 uwTick += uwTickFreq;

}

Insert image description here
Insert image description here

Software simulation debugging

Use software simulation to enter debugging and stop at the LED0_Init() function under the main function:

An error occurred during operation: *** error 65: access violation at 0x40023830: no 'write' permission
because the corresponding cpu was not specified:
Solution:

  • 1 Create a new cpu.ini and write:
map 0x40000000,0x400FFFFF read write
  • 2 Add cpu.ini to debug:
    Insert image description here

Comparison of two codes

Interrupt mode has smaller CPU occupancy rate, shorter waiting time, and reduced CPU occupancy rate.

after class homework:

1: Preview the description of the USART controller in the Booking Manual (Chapter 30)
2: Implement a program through interrupts, requiring that after pressing KEY1: let LED1 flash at intervals of 0.5S and continue for 5S and then turn off, implement the code and board debugging

Guess you like

Origin blog.csdn.net/Medlar_CN/article/details/132098270