[TH] Bare Metal Cortex-M Ep.3

จากบทความก่อนหน้านี้ได้ทดลองควบคุมการนำออกสัญญาณดิจิทัลด้วยการขับวงจรแอลอีดีที่เชื่อมต่อกับบอร์ดไมโครคอนโทรลเลอร์ STM32 ทั้งแบบ Cortex-M0, Cortex-M3 และ Cortex-M4 ในบทความนี้กล่าวถึงการใช้งานขาเพื่อนำเข้าสัญญาณดิจิทัล และใช้ตัวอย่างการต่อวงจรสวิตช์เพื่อควบคุมการติดหรือดับของหลอดแอลอีดีดังภาพที่ 1

ภาพที่ 1 ภาพตัวอย่างของการทดลองใน Ep3

คำสั่งตั้งค่า

ตั้งแต่บทความก่อนหน้านี้เราได้ใช้งาน GPIO กันไปแล้วเพื่อทำการสั่งให้หลอดแอลอีดีอีสว่างและดับซึ่งต้องไปตั้งค่าเปิดการใช้งานขาในฟังก์ชัน MX_GPIO_Init() อันเป็นฟังก์ชันที่โปรแกรม STM32CubeIDE/STM32CubeMX สร้างให้ใช้งานเพื่อใส่ชุดคำสั่งสำหรับจัดการกับ GPIO ตามที่ตั้งค่าไว้ หรือเขียนโค้ดเพิ่มเติม ซึ่งคำสั่งสำหรับเปิดการทำงานของ GPIO คือ

HAL_GPIO_Init( กลุ่มของพอร์ต, &ตัวแปรตั้งค่า )

ส่วนคำสั่งสำหรับเปิดการเชื่อมสัญญาณนาฬิกาเข้ากับพอร์ตเป็นดังนี้

__HAL_RCC_GPIOF_CLK_ENABLE()

__HAL_RCC_GPIOA_CLK_ENABLE()

กำหนดหน้าที่ของ GPIO

ขั้นตอนของการกำหนดหน้าที่ขา คือ

  1. เปิดการใช้กลุ่มของ GPIO
  2. กำหนดให้ขาที่ต้องการใช้มีสถานะเริ่มต้น
  3. สร้างตัวแปรประเภท GPIO_InitTypeDef เพื่อใช้สำหรับเก็บการตั้งค่า
  4. กำหนดการตั้งค่าในคุณสมบัติ Pin, Mide และ Pull หรือ Speed ให้แก่ตัวแปรในข้อ 1
  5. เรียกใช้คำสั่งตั้งค่าด้วยตัวแปรที่ผ่านการกำหนดค่าแล้วในข้อ 2

ซึ่งรหัสคำสั่งที่ได้จะออกมาเป็นดังนี้

  __HAL_RCC_ชื่อกลุ่มGPIO_CLK_ENABLE();

  HAL_GPIO_WritePin( ชื่อกลุ่มGPIO, ชื่อขา, GPIO_PIN_RESET );

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  GPIO_InitStruct.Pin = ชื่อขา;
  GPIO_InitStruct.Mode = โหมดของขา;
  GPIO_InitStruct.Pull = สถานะการมีวงจรพูลอัพ;
  GPIO.InitStruct.Spedd = ความเร็วของการทำงาน;
  HAL_GPIO_Init( ชื่อกลุ่มGPIO, &GPIO_InitStruct );

คำสั่งอ่านข้อมูลดิจิทัล

การอ่านค่าข้อมูลดิจิทัลจากขานำเข้าสัญญาณจะต้องกำหนดให้ขานั้นมีการทำงานเป็น GPIO_Input ในขั้นตอนของการตั้งค่าขา และใช้คำสั่งสำหรับนำเข้าสัญญาณดิจิทัลดังนี้

ค่าที่ได้รับ = HAL_GPIO_ReadPin( ชื่อกลุ่มGPIO, ชื่อขาที่ต้องการอ่านค่า )

ตัวอย่างโปรแกรม

การต่อวงจรทดสอบ

ตัวอย่างโปรแกรมสำหรับทดสอบการนำเข้าสัญญาณดิจิทัลจากขาของบอร์ดไมโครคอนโทรลเลอร์ STM32 เป็นดังภาพที่ 2 และ 3 โดยต่อขานำเข้าสัญญาณ (สายเส้นเหลืองจากภาพที่ 2) เข้ากับขา PA3 ของ STM32F030F4P6 (ดังภาพที่ 3)

ภาพที่ 2 วงจรสวิตช์
ภาพที่ 3 บอร์ด STM32F030F4P6

STM32F030F4P6

หลังจากการต่อวงจรในภาพที่ 2 และ 3 เสร็จ ขั้นตอนต่อไปคือการเขียนโปรแกรม โดยเริ่มต้นจากการสร้างโครงงานแบบ STM32 Project ดังภาพที่ 4 และให้ดำเนินการตั้งค่าขาการทำงานโดยให้ PA4 เป็น GPIO_Output และ PA3 เป็น GPIO_Input ดังภาพที่ 5

ภาพที่ 4 เมนู STM32 Project
ภาพที่ 5 การตั้งค่าขาของ STM32F040F4P6

เมื่อจัดการเรื่องการตั้งค่าขาและสัญญาณนาฬิกาจะได้ไฟล์ต่าง ๆ ที่โปรแกรม STM32CubeMX สร้างให้ และให้แก้ไข main.c ในฟังก์ชัน main ดังต่อไปนี้

#include "main.h"

void SystemClock_Config(void);
static void MX_GPIO_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  int pa3Value=0;
  while (1)
  {
	  pa3Value = HAL_GPIO_ReadPin( GPIOA, GPIO_PIN_3 );
	  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, pa3Value);
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
  RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;

  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);

  /*Configure GPIO pin : PA3 */
  GPIO_InitStruct.Pin = GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PA4 */
  GPIO_InitStruct.Pin = GPIO_PIN_4;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

หลังจากนั้นให้ตั้งค่าการคอมไพล์เป็นแบบ Release (อธิบายในบทความก่อนหน้านี้) และคอมไพล์จะได้ผลลัพธ์ดังภาพที่ 6 และ 7

ภาพที่ 6 ผลลัพธ์จากการคอมไพล์
ภาพที่ 7 ขนาดไฟล์และการใช้หน่วยความจำของโปรแกรม

ผลลัพธ์การทำงาน

สุดท้ายอัพโหลดโปรแกรมเข้าบอร์ดไมโครคอนโทรลเลอร์ และเมื่อเปลี่ยนโหมดเป็นโหมดทำงาน ผลการทำงานจะเป็นดังภาพที่ 8 คือ ถ้าไม่กดสวิตช์หลอดแอลอีดีที่ต่อกับ PA4 จะดับ และเมื่อกดสวิตช์จะทำให้หลอดสว่างดังภาพที่ 9

ภาพที่ 8 ตัวอย่างภาพผลลัพธ์เมื่อยังไม่กดสวิตช์
ภาพที่ 9 ผลลัพธ์เมื่อมีการกดสวิตช์

สำหรับบอร์ดคอนโทรลเลอร์ Cortex-M3 และ Cortex-M4 ให้เลือกขาที่เป็นขา GPIO_Output และ GPIO_Input หลังจากนั้นเปลี่ยนแปลงชื่อขาและกลุ่มของขาให้ถูกต้องการทำงานจะเหมือนกันกับการใช้ Cortex-M0

สรุป

จากบทความนี้จะได้ว่า การเขียนโปรแกรมเพื่อใช้งานกับ Cortex-M0/M3 หรือ M4 นั้นมีหลักการสั่งงานที่เหมือนกัน แต่ในไมโครคอนโทรลเลอร์แต่ละรุ่นมีวงจรภายในที่แจกจ่างเพิ่มเติมหรือตัดออกไม่เท่ากัน โดยสรุปแล้ว ขั้นตอนการพัฒนาโปรแกรมประกอบด้วย

  1. สร้างโครงงานสำหรับไมโครคอนโทรลเลอร์ที่ต้องการใช้งาน
  2. กำหนดบทบาทหน้าที่ของขาใช้งาน ดังนี้
    1. ขาสำหรับเชื่อมต่อกับวงจรสร้างสัญญาณนาฬิกา
    2. กำหนดขานำออกสัญญาณ
    3. กำหนดขานำเข้าสัญญาณ
      1. นำเข้าสัญญาณดิจิทัล
      2. นำเข้าสัญญาณแอนาล็อก
  3. ตั้งค่าสัญญาณนาฬิกา
  4. ให้โปรแกรมสร้างรหัสคำสั่งต้นแบบ
  5. แก้ไขโปรแกรมเพื่อให้ทำงานที่ต้องการ
  6. คอมไพล์
  7. อัพโหลดเข้าบอร์ด
  8. ทดสอบการทำงาน ถ้าผิดพลาดให้ทบทวนการตั้งค่าในขั้นตอน 2 และ 3 หรือตรวจสอบแก้ไขโปรแกรมและคอมไพล์ อัพโหลดและทดสอบซ้ำ

ดังนั้น ในบทความชุดนี้จะกล่าวถึงส่วนของการเขียนโปรแกรมร่วมกัน และจะแยกการใช้งานพิเศษออกเป็นบทความเป็นกรณีศึกษา สุดท้ายนี้ขอให้สนุกกับการเขียนโปรแกรมครับ

(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-07-24, 2021-07-26, 2021-10-29