Lập trình ngắt EXTI với STM32

1.Ngắt (Interrupts) là gì?

Giả sử bạn là 1 con MCU. Bạn đang làm việc (trong while(1)) thì có 1 cuộc điện thoại gọi tới, bạn đi bắt điện thoại(cuộc gọi ưu tiên hơn công việc đang làm), sau khi nghe điện thoại xong quay lại bàn làm việc tiếp. Thì sự kiện đi bắt điện thoại ngắt ngang công việc bạn đang làm có thể hiểu nôm na là sự kiên ngắt ( Interrupts)

Các ngắt đều có 1 trị số priority ( Mức độ ưu tiên) cho phép người lập trình có thể ưu tiên xử lý ngắt nào trước nếu xảy ra nhiều sự kiện ngắt cùng lúc.

Trong STM32 việc quản lý các ngắt đó được điều khiển bằng bộ NVIC. Thông số NVIC:

Bao gồm 68 ngắt ( chưa gồm 16 ngắt hệ thống)

Lập trình 16 mức ưu tiên ngắt (4 bit được sử dụng)

Interrupt Vector Table được lấy ra trong tài liệu của STM32F1:

Bảng mức độ ưu tiên ngắt NVIC:

Có hai loại ưu tiên ngắt khác nhau trên MCU STM32F103C8T6 đó là Preemption Priorities và Sub Priorities:

– Mặc định thì ngắt nào có Preemtion Priority cao hơn thì sẽ được thực hiện trước.

– Khi nào 2 ngắt có cùng một mức Preemption Priority thì ngắt nào có Sub Priority cao hơn thì ngắt đó được thực hiện trước.

– Còn trường hợp 2 ngắt có cùng mức Preemption và Sub Priority luôn thì ngắt nào đến trước được thực hiện trướ

2.EXTI là gì?

EXTI còn gọi là ngắt ngoài. Là 1 sự kiện ngắt xảy ra khi có tín hiệu can thiệp từ bên ngoài, từ phần cứng, người sử dụng hay ngoại vi,…

Kích hoạt độc lập trên mỗi dòng ngắt (Line Interrupts)

Truy cập đến từng Bit trong mỗi dòng ngắt

Tạo ra tối đa 20 sự kiện/ngắt

Các line ngắt ngoài được tổ chức như sau

Bảng phân bố vector ngắt:

3.Lập trình

Chúng ta sẽ lập trình ngắt nút nhấn PC13 để chớp tắt led trên PD2. Khi nhấn nút thì chân PD2 sẽ đảo trạng thái led

Sơ đồ nguyên lý:

Phần tạo project các bạn tham khảo lại bài 2 phần GPIO (cách tạo project, cấu hình mạch nạp, xung clock) tại đây

Bây giờ mình tiến hành cấu hình để dùng EXTI

(1) Chuột phải vào PC13 chọn GPIO_EXTI13

(2) Trong tab NVIC enable interrupt

Cấu hình GPIO với các bước

(1) Chọn GPIO

(2) Chuột phải vào chân PD2 chọn GPIO_Output

(3) PC13 chọn Pull-up

Sau đó Generation để sinh code.

Mở file stm32f1xx_it.c trong thư mục Core/Src.

Vì mình dùng chân PC13 nên thuộc line EXTI15_10, các chân khác thì tìm đến line tương ứng. Các bạn tìm đến đoạn code bên dưới :

void EXTI15_10_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI15_10_IRQn 0 */

  /* USER CODE END EXTI15_10_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
  /* USER CODE BEGIN EXTI15_10_IRQn 1 */

  /* USER CODE END EXTI15_10_IRQn 1 */
}

Sau đó ctrl +click trái để trỏ tới hàm:

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);
  }
}

và tìm tới hàm Hàm xử lý ngắt

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

Từ khóa __weak có tác dụng nếu một hàm cùng tên được gọi mạnh hơn thì hàm này sẽ không được gọi. Giờ chúng ta sẽ copy và chỉnh sửa như bên dưới cho chương trình chính:

/* USER CODE BEGIN PV */
volatile int flag_blink=0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);
  if(GPIO_Pin==GPIO_PIN_13){
	  flag_blink=1;
  }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
	  if(flag_blink==1){
		  HAL_Delay(250);
		  flag_blink=0;
		  HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_2);
	  }
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}


Một số hàm liên quan:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin): Khi có sự kiện ngắt nút nhấn EXTI thì hàm này sẽ được gọi. Hàm HAL_GPIO_EXTI_Callback được tạo sẵn khi sử dụng EXTI. GPIO_Pin đối số chính là biến để kiểm tra xem chân nào đang được ngắt.

Source code đính kèm: Github

Icons made by Freepik from www.flaticon.com