BÀI 03: LẬP TRÌNH NGẮT NGOÀI VỚI STM32F1

posted in: Vi điều khiển | 0

1. Ngắt là gì?

Ngắt (Interrupts) hiểu theo nghĩa đơn giản là các sự kiện ngẫu nhiên làm gián đoạn quá trình của một sự kiện đang xảy ra. Để có thể dễ hiểu khái niệm mới này ta cùng đưa ra một ví dụ trong thực tế như sau:

Ví dụ: Trong giờ làm việc tại công ty, bạn đang làm những công việc hàng ngày thì bất ngờ nhận được nhiệm vụ “đi gặp khách hàng”. Sự kiện “đi gặp khách hàng” sẽ được ưu tiên làm trước, sau đó mới quay lại công việc thường ngày. Trong lập trình thì “đi gặp khách hàng” chính là chương trình ngắt, còn công việc hàng ngày là chương trình chính trong hàm main.

Các ngắt đều có 1 trị số priority ( Sự ư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 sảy ra ngắt chồng nhau.

Cũng trong ví dụ trên, trong lúc đang gặp khách hàng mà bạn lại nhận được tin “nhà cháy”, khi đó ta phải xét đến priority. Nếu sự kiện “nhà cháy” có mức độ ưu tiên cao hơn thì bạn phải về nhà dập lửa trước, khi đó sự kiện “gặp khách hàng” sẽ được thực hiện sau.

Trong STM32 việc quản lý các ngắt đó được điều khiển bằng bộ NVIC tạm dịch là bộ điều khiển ngắt lồng nhau.

2. Bộ điều khiển ngắt lồng nhau NVIC trong STM32

NVIC (Nested Vector Interrupt Controller) là khối quản lý ngắt trên các dòng vi điều khiển dựa trên lõi ARM Cortex. Khi có một sự kiện và sự kiện đó được cấu hình ngắt, thì tất cả ngắt sẽ được đưa vào khối NVIC, và dựa trên nhiều yếu tố mà NVIC sẽ quyết định xem ngắt nào sẽ được thực thi để đưa CPU vào Interrupt Service Routine. كيف تلعب لعبة البوكر

Các khối ngắt cơ bản của hệ thống bao gồm một số ngắt ở các địa chỉ đầu tiên đã đươc định nghĩa, ví dụ thì ta xem Interrupt Vector Table được lấy ra trong tài liệu của STM32F1:

Địa chỉ của ngắt ở cột cuối (Address), là địa chỉ lưu trữ giá trị – mà giá trị này là địa chỉ bắt đầu của chương trình phục vụ ngắt (interrupt service routine) của loại ngắt tương ứng, hay còn gọi là interrupt function pointer, các interrupt function pointers này có địa chỉ nằm ở Interrupt Vector Table được sắp xếp lên vùng nhớ trong file startup.

Hệ thống ngắt trên vi điều khiển ARM Cortex bao gồm các loại ngắt: ngắt hệ thống (exception), ngắt GPIO và ngắt phần mềm. قواعد لعب البوكر Ví dụ:

+ Ngắt hệ thống: Khi có sự truy cập vào vùng nhớ không được cho phép, thì chương trình sẽ tạo ra ngắt MemManage_Handler.

+ Ngắt GPIO: 1 chân GPIO PA3 có sự chuyển tiếp từ mức thấp lên cao tạo ra ngắt.

+ Ngắt phần mềm: Khi timer 1 bị tràn tạo ra ngắt.

Sau khi NVIC đã quyết định được loại ngắt cần được thực hiện: NVIC sẽ lưu lại trạng thái của hệ thống (lưu lại thanh ghi của vi điều khiển) tại thời điểm chuẩn bị chuyển ngắt, sau đó buộc vi điều khiển thực thi ngắt bằng cách tra bảng vector ngắt, khi đã thực thi xong tất cả cả ngắt thì phục hồi trạng thái và tiếp tục thực thi chương trình chính.

NVIC bao gồm:

  • 68 ngắt (chưa kể 16 ngắt hệ thống)
  • 16 ngắt ưu tiên
  • Độ trễ thấp
  • Có khả năng quản lí năng lượng cho vector ngắt

3.Ngắt ngoài STM32F103C8T6

External interrupt (EXTI) hay 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,…




Ngắt ngoài STM32 được mô tả như sau:

Các tham số (Main Features):

  • 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
  • Tín hiệu phải có độ rộng xung thấp hơn chu kì xung nhịp của APB2 (vì APB2 cấp xung cho GPIO)

Sơ đồ khối của các khối điều khiển ngắt ngoài:

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

STM32F103C8T6 có 16 line ngắt, từ line EXTI0 đến line EXTI15.  Line0 sẽ chung cho tất cả chân Px0 ở tất cả các Port, với x là tên của Port A, B…Các line tiếp theo sẽ tương tự như vậy. كيف يلعب البوكر

Các Line ngắt của chip STM32F103 được phân bố vào các vector ngắt như sau:

3. Lập trình ngắt ngoài với thư viện chuẩn của ST

Trong bài này chúng ta sẽ cấu hình 2 nút nhấn gồm các chân PB5, PB6 sau đó phân biệt các sự kiện đó trong ngắt rồi điều khiển bật tắt led trên chân PC13.

  • Chương trình cấu hình GPIO
void LED_init(void){
	
  GPIO_InitTypeDef  GPIO_InitStructure; 		
	
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	    		
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;			  //LED0-->PC.13 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 	 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 
  GPIO_Init(GPIOC, &GPIO_InitStructure);			     			 
}
  • Chương trình cấu hình ngắt

void EXTIx_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, EXTI_Trigger_t trigger) {
	GPIO_InitTypeDef GPIO_InitStruct;
	EXTI_InitTypeDef EXTI_InitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	uint32_t gpio_clock;
	uint8_t pinsource, portsource, irqchannel;
		/* Enable clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
	/* Get proper settings */
	if (GPIOx == GPIOA) {
		gpio_clock = RCC_APB2Periph_GPIOA;
		portsource = GPIO_PortSourceGPIOA;
	} else if (GPIOx == GPIOB) {
		gpio_clock = RCC_APB2Periph_GPIOB;
		portsource = GPIO_PortSourceGPIOB;
	} else if (GPIOx == GPIOC) {
		gpio_clock = RCC_APB2Periph_GPIOC;
		portsource = GPIO_PortSourceGPIOC;
	} else if (GPIOx == GPIOD) {
		gpio_clock = RCC_APB2Periph_GPIOD;
		portsource = GPIO_PortSourceGPIOD;
	} else if (GPIOx == GPIOE) {
		gpio_clock = RCC_APB2Periph_GPIOE;
		portsource = GPIO_PortSourceGPIOE;
	} else if (GPIOx == GPIOF) {
		gpio_clock = RCC_APB2Periph_GPIOF;
		portsource = GPIO_PortSourceGPIOF;
	} else if (GPIOx == GPIOG) {
		gpio_clock = RCC_APB2Periph_GPIOG;
		portsource = GPIO_PortSourceGPIOG;
	} 
	switch (GPIO_Pin) {
		case GPIO_Pin_0:
			pinsource = GPIO_PinSource0;
			irqchannel = EXTI0_IRQn;
			break;
		case GPIO_Pin_1:
			pinsource = GPIO_PinSource1;
			irqchannel = EXTI1_IRQn;
			break;
		case GPIO_Pin_2:
			pinsource = GPIO_PinSource2;
			irqchannel = EXTI2_IRQn;
			break;
		case GPIO_Pin_3:
			pinsource = GPIO_PinSource3;
			irqchannel = EXTI3_IRQn;
			break;
		case GPIO_Pin_4:
			pinsource = GPIO_PinSource4;
			irqchannel = EXTI4_IRQn;
			break;
		case GPIO_Pin_5:
			pinsource = GPIO_PinSource5;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_6:
			pinsource = GPIO_PinSource6;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_7:
			pinsource = GPIO_PinSource7;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_8:
			pinsource = GPIO_PinSource8;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_9:
			pinsource = GPIO_PinSource9;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_10:
			pinsource = GPIO_PinSource10;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_11:
			pinsource = GPIO_PinSource11;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_12:
			pinsource = GPIO_PinSource12;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_13:
			pinsource = GPIO_PinSource13;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_14:
			pinsource = GPIO_PinSource14;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_15:
			pinsource = GPIO_PinSource15;
			irqchannel = EXTI15_10_IRQn;
			break;
		default:
			break;
	}
	
	/* Enable clock for GPIO */
	RCC_APB2PeriphClockCmd(gpio_clock, ENABLE);
	
	/* Set pin as input */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	if (trigger == Trigger_Falling) {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	} else if (trigger == Trigger_Rising) {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	} else {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	}
	
	/* Initialize pin */
	GPIO_Init(GPIOx, &GPIO_InitStruct);
	
	/* Connect proper GPIO */
	GPIO_EXTILineConfig(portsource, pinsource);
	
	/* Enable EXTI */
	EXTI_InitStruct.EXTI_Line = GPIO_Pin;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = (EXTITrigger_TypeDef)trigger;
	EXTI_Init(&EXTI_InitStruct);

	/* Add to NVIC */
	NVIC_InitStruct.NVIC_IRQChannel = irqchannel;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = EXTI_PRIORITY;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = pinsource;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	switch (GPIO_Pin) {
		case GPIO_Pin_0:
			pinsource = GPIO_PinSource0;
			irqchannel = EXTI0_IRQn;
			break;
		case GPIO_Pin_1:
			pinsource = GPIO_PinSource1;
			irqchannel = EXTI1_IRQn;
			break;
		case GPIO_Pin_2:
			pinsource = GPIO_PinSource2;
			irqchannel = EXTI2_IRQn;
			break;
		case GPIO_Pin_3:
			pinsource = GPIO_PinSource3;
			irqchannel = EXTI3_IRQn;
			break;
		case GPIO_Pin_4:
			pinsource = GPIO_PinSource4;
			irqchannel = EXTI4_IRQn;
			break;
		case GPIO_Pin_5:
			pinsource = GPIO_PinSource5;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_6:
			pinsource = GPIO_PinSource6;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_7:
			pinsource = GPIO_PinSource7;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_8:
			pinsource = GPIO_PinSource8;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_9:
			pinsource = GPIO_PinSource9;
			irqchannel = EXTI9_5_IRQn;
			break;
		case GPIO_Pin_10:
			pinsource = GPIO_PinSource10;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_11:
			pinsource = GPIO_PinSource11;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_12:
			pinsource = GPIO_PinSource12;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_13:
			pinsource = GPIO_PinSource13;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_14:
			pinsource = GPIO_PinSource14;
			irqchannel = EXTI15_10_IRQn;
			break;
		case GPIO_Pin_15:
			pinsource = GPIO_PinSource15;
			irqchannel = EXTI15_10_IRQn;
			break;
		default:
			break;
	}
	
	/* Enable clock for GPIO */
	RCC_APB2PeriphClockCmd(gpio_clock, ENABLE);
	
	/* Set pin as input */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	if (trigger == Trigger_Falling) {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	} else if (trigger == Trigger_Rising) {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
	} else {
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	}
	
	/* Initialize pin */
	GPIO_Init(GPIOx, &GPIO_InitStruct);
	
	/* Connect proper GPIO */
	GPIO_EXTILineConfig(portsource, pinsource);
	
	/* Enable EXTI */
	EXTI_InitStruct.EXTI_Line = GPIO_Pin;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStruct.EXTI_Trigger = (EXTITrigger_TypeDef)trigger;
	EXTI_Init(&EXTI_InitStruct);

	/* Add to NVIC */
	NVIC_InitStruct.NVIC_IRQChannel = irqchannel;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = EXTI_PRIORITY;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = pinsource;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
}
  • Chương trình phục vụ ngắt
void EXTI9_5_IRQHandler(void)
{
  delay_ms(20);	 
  if(B5 == 1)
  {
  GPIO_SetBits(GPIOC, GPIO_Pin_13);
  }
  if(B6 == 1)
  {
  GPIO_ResetBits(GPIOC, GPIO_Pin_13); 
  }
// clear flag	 EXTI_ClearITPendingBit(EXTI_Line5|EXTI_Line6|EXTI_Line7|EXTI_Line8|EXTI_Line9);  
}		

Khi trình phục vụ ngắt được gọi, ta tiến hành phân luồng để phân biệt ngắt trên chân PB5 và PB6 bằng các câu lệnh If để bật tắt Led trên chân PC13, cuối hàm ta phải xóa cờ ngắt để sự kiện ngắt tiếp theo được phát hiện.

  • Chương trình trong hàm main
int main(void)
 {	
      // Cau hinh GPIO led trên chân PC13
         LED_init();
      // Cau hinh ngat xung canh len tren chan PB6
	 EXTIx_Init(GPIOB, GPIO_Pin_6, Trigger_Rising);
      // Cau hinh ngat xung canh len tren chan PB5
	 EXTIx_Init(GPIOB, GPIO_Pin_5, Trigger_Rising);
	 delay_init();
  while(1)
	{
	}
 }

Link code mẫu

Trả lời

Email của bạn sẽ không được hiển thị công khai.