จากบทความก่อนหน้านี้ได้ทดลองเขียนโปรแกรมเพื่อศึกษาองค์ประกอบของไฟล์ต่าง ๆ ที่ต้องใช้งานซึ่งจะพบว่ามีรายละเอียดและขั้นตอนเยอะพอสมควร แต่เป็นพื้นฐานสำคัญสำหรับผู้ต้องการศึกษาการเขียนโปรแกรมควบคุม Cortex-M0 ผ่านชิพ STM32F030F4P6 (ภาพที่ 1), Cortex-M3 ด้วย STM32F103C (ภาพที่ 32) และ Cortex-M4 ด้วย STM32F401CCU6 (ภาพที่ 27) อย่างจริงจัง และมีประโยชน์ต่อการศึกษาโครงสร้างการเขียนโปรแกรมด้วยเครื่องมือเขียนโปรแกรมอย่าง STM32CubeIDE (ภาพที่ 2) ซึ่งเป็นเครื่องมือหลักที่จะใช้ในบทความชุดนี้ เนื่องจากเป็นการรวมชุดพัฒนาสำหรับ ARM ของบริษัท ST แบบครบครันทั้ง CubeMX สำหรับออกแบบการใช้งานชิพ เครื่องมือชุดคอมไพล์เลอร์ เครื่องมือดีบักโปรแกรมผ่าน ST-Link ลงชิพ และชุดแก้ไขโค้ดอยู่ในตัวเดียว แถมรองรับการใช้งานทั้งระบบปฏิบัติการ Windows, Linux และ macOS
เริ่มต้นสร้างโครงงาน
จากภาพที่ 2 ให้เลือกรายการ “Create a New STM32 project” ดังภาพที่ 3 หรือเข้าเมนู File เลือก New และเลือกรายการย่อย STM32 Project ระบบจะทำการเชื่อมต่ออินเทอร์เน็ตเพื่อดาวน์โหลดข้อมูลสำหรับเลือกไมโครคอนโทรลเลอร์สำหรับใช้งานในโครงงานดังภาพที่ 4
จากภาพที่ 4 ในช่อง Part Number ให้ใส่ STM32F030F4 จะแสดงรายละเอียดของชิพ ให้เลือกรายการชิพ หลังจากนั้นคลิกปุ่ม Next>> เพื่อสร้างโครงงานดังภาพที่ 5
จากภาพที่ 5 ให้ตั้งชื่อโครงงาน (ทีมเราตั้งชื่อไว้เป็น helloSTM32Core0) กำหนดที่เก็บโครงงาน เลือกภาษา (เลือก C ไว้) ตั้งประเภทไบนารีปลายทางเป็น Executable และเลือกประโครงงานเป็น STM32Cube สุดท้ายคลิกที่ปุ่ม Next > จะเข้าสู่ขั้นตอนถัดไปตามภาพที่ 6
จากหน้าจอในภาพที่ 6 ให้เลือกรุ่นของเฟิร์มแวร์ที่ต้องการ โดยในที่นี้เลือก V1.11.2 และบันทึกไฟล์ที่ดาวน์โหลดเอาไว้ใน Location ที่กำหนด เมื่อเรียบร้อยคลิกที่ปุ่ม Finish ระบบจะดาวน์โหลดไฟล์ที่เกี่ยวข้องตามขั้นตอนในภาพที่ 7 และ 8 หลังจากนั้นจะสร้างไฟล์ตามโครงสร้างในภาพที่ 9 หลังจากที่ผ่านขั้นตอนการกำหนดค่าความถี่สัญญาณนาฬิกาในหัวข้อถัดไป
ปรับแต่งการใช้งานขา
เมื่อทุกอย่างเรียบร้อยจะมีกำหนดหน้าที่ขาใช้งานของชิพดังภาพที่ 10 หรือถ้าต้องการปรับแก้ให้เข้าไปที่ส่วนของ Project Explorer แล้วดับเบิลคลิกที่ไฟล์นามสกุล ioc
ขั้นตอนถัดไปนี้เป็นการกำหนดหน้าที่ให้กับขาของไมโครคอนโทรลเลอร์ โดยแบ่งเป็น 3 ขา คือ ขาสำหรับนำเข้าสัญญาณนาฬิกาภายนอกจากคริสตัล 8MHz บนบอร์ด ขาที่ 2 เป็นขาสำหรับกำหนดหน้าที่ให้กับขาส่งสัญญาณนาฬิาออกไปให้คริสตัล และขา PA4 สำหรับเป็น GPIO_Output ซึ่งเป็นขาที่เชื่อมต่อกับหลอด LED บนบอร์ด STM32F030F4P6
ในภาพที่ 11 ให้คลิกที่ขา PF0 หลังจากนั้นจะปรากฏรายการหน้าที่ของขาให้เลือก โดยให้เลือกเป็น RCC_OSC_IN หรือกรณีที่ต้องการยกเลิกให้คลิกที่รายการ Reset_State ผลลัพธ์หลังจากเลือก RCC_OSC_IN เป็นดังภาพที่ 12
จากภาพที่ 12 ให้คลิกเลือกที่ขา PF1 เพื่อกำหนดหน้าที่เป็น RCC_OSC_OUT จะได้ผลลัพธ์ดังภาพที่ 13
จากภาพที่ 13 ให้คลิกที่ขา PA4 หลังจากนั้นกำหนดหน้าที่เป็น GPIO_Output
ถ้ากำหนดหน้าที่ถูกต้องจะแสดงหน้าจอของชิพดังภาพที่ 14
ตั้งค่าสัญญาณนาฬิกา
เนื่องจากบอร์ดมีคริสตัลภายนอกให้ ทำให้สามารถสร้างความถี่จากการคูณ 6 (ตัวเลือก X6) และคลิกเลือก PLLCLK เพื่อเปิดการทำงานของ css และจะได้ความถี่ในการทำงานเป็น 48MHz ดังภาพที่ 16 โดยให้คลิกที่แถบ Clock Configuration ดังภาพที่ 15
เมื่อเรียบร้อยให้บันทึกด้วยปุ่ม ดังภาพที่ 17 หรือเข้าเมนู File เลือก Save All จะมีการดาวน์โหลดไฟล์เฟิร์มแวร์และสร้างไฟล์ต้นฉบับให้ใช้งานดังภาพที่ 18 และได้ผลลัพธ์รายการไฟล์ดังภาพที่ 19
ไฟล์โปรแกรม
เมื่อสร้างไฟล์เรียบร้อยจะสามารถเขียนคำสั่งการทำงานได้ในไฟล์ main.c ที่อยู่ในโฟลเดอร์ Core/Src ดังภาพที่ 19
เมื่อดับเบิลคลิกที่ชื่อไฟล์ main.c ในภาพที่ 19 จะปรากฏหน้าต่างสำหรับแก่ไขโค้ดดังภาพที่ 20 และ 21 โดยในภาพที่ 20 แสดงให้เห็นการกำหนดหน้าที่ของขาจากหน้าจอกำหนดหน้าที่ของขาจากหัวข้อก่อนหน้านี้ มีการเปิดการทำงานของ GPIOF (สำหรับเชื่อมต่อคริสตัลภายนอก) และ GPIOA ซึ่งขา PA4 เป็นส่วนหนึ่งของ GPIO กลุ่ม A และได้กำหนดให้ขา 4 หรือ GPIO_PIN_4 มีค่าสถานะของของเป็น GPIO_PIN_RESET หรือค่า 0 ถ้าต้องการส่ง 1 ให้ใส่ตัวเลข 1 หรือใช้ GPIO_PIN_SET
ส่วนคำสั่ง GPIO_InitStruct เป็นการกำหนดการทำงานของขา ซึ่งจะเห็นว่ากำหนดให้ GPIO_PIN_4 เป็น GPIO_MODE_OUTPUT_PP ไม่ใช้ Pull Up และความเร็วเป็น GPIO_SPEED_FREQ_LOW หลังจากนั้นทำการกำหนดค่าที่ตั้งไว้ให้ไมโครคอนโทรลเลอร์ทำตามที่กำหนดด้วยคำสั่ง HAL_GPIO_Init( ) ดังภาพที่ 19
ในภาพที่ 21 เป็นฟังก์ชัน main() จะพบว่ามีการสร้าง comment เพื่อให้ผู้เขียนโปรกรมอ่านทำความเข้าใจและระบุตำแหน่งของการเขียนโค้ดเอาไว้ค่อนข้างละเอียด โดยใจความหลักของขั้นตอนที่ทำของ main() คือ มีการเรียก HAL_Init( ) หลังจากนั้นเรียก SystemClock_Config() แล้วกำหนดค่าของ GPIO ตามฟังก์ชัน MX_GPIO_Init() สุดท้ายมีส่วนของวนรอบไม่รู้จบด้วย while (1) { } สำหรับให้เขียนโค้ดที่ต้องการเหมือนในฟังก์ชัน loop( ) ของ Arduino
คำสั่งสำหรับส่งสถานะ 0 หรือ 1 ให้ขา PA4 ซึ่งมีชื่อเรียกว่า GPIO_PIN_4 ซึ่งเชื่อมต่อกับวงจรหลอด LED บนบอร์ดมีรูปแบบการใช้งานดังนี้ โดยสถานะเป็น 1 หรือ 0 หรือใช้ค่าคงที่ GPIO_PIN_SET และ GPIO_PIN_RESET แทนตัวเลข ส่วนพอร์ตเป็นชื่อเรียกของกลุ่มขาที่ใช้งาน จึงใช้ชื่อพอร์ต GPIOA
HAL_GPIO_WritePin( พอร์ต, ชื่อขา, สถานะ )
กรณีที่สั่งพร้อมกันหลายขาสามารถใช้เครื่องหมาย | เช่น ต้องการส่งบิต 1 ให้กับ PA2, PA3 และ PA4 เขียนดังนี้
HAL_GPIO_WritePin( GPIOA, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4, GPIO_PIN_SET );
คำสั่งสำหรับหน่วงเวลาในหลักมิลลิวินาทีคือ
HAL_Delay( จำนวนมิลลิวินาทีที่ต้องการหน่วงเวลา )
โค้ดโปรแกรมของ main.c เป็นดังนี้
#include "main.h"
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 0);
HAL_Delay( 500 );
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, 1);
HAL_Delay( 500 );
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
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();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
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 : 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) { }
}
คอมไพล์
เมื่อเขียนโค้ดใน main.c เสร็จให้เลือกประเภทของโครงงานที่ต้องการสร้างเป็น Release ด้วยการคลิกที่ปุ่มดังภาพที่ 22 และจะพบหน้าต่างดังภาพที่ 23 ให้เลือก Configuration เป็น Release แล้วคลิกปุ่ม Set Active ค่า Status ที่ชื่อ Active จะเปลี่ยนจากที่อยู่หลัง Debug จะมาอยู่ที่แถวของ Release สุดท้ายคลิกที่ OK เพื่อตกลงการตั้งค่า
เมื่อตั้งค่าเป็น Release เป็นที่เรียบร้อย (ไม่เลือกเป็น Debug เพื่อลดปริมาณโค้ดและทีมงานเราไม่มีเครื่องมือสำหรับดีบักโปรแกรมด้วยเช่นกันครับ) ให้คลิกที่ไอคอนตามภาพที่ 24 แล้วรอการคอมไพล์ ถ้าไม่พิมพ์ผิดพลาดจะแสดงผลลัพธ์ในช่อง Console ดังภาพที่ 25
เมื่อคอมไพล์ผ่านสำเร็จจะปรากฏโฟลเดอร์ Release ใน Project Explorer ดังภาพที่ 26
เมื่อได้ helloSTM32Core0.bin หรือ helloSTM32Core0.elf ให้ใช้โปรแกรม STM32CubeProgrammer ตามบทความก่อนหน้านี้อัพเข้าสู่ชิพ หลังจากนั้นเปลี่ยนโหมดบนบอร์ดและรีเซ็ตเพื่อรันโปรแกรมจะพบว่าหลอดแอลอีดีบทบอร์ดติดดับสลับกันไป
STM32F401CCU6
กรณีที่ใช้ Cortex-M4 ชิพ STM32F401CCU6 หรือ Black Pill ในภาพที่ 27 ซึ่งมีการต่อคริสตัลภายนอกที่ขา PC14 และ PC15 และปรับตั้งความเร็วสูงสุดของการทำงานอยู่ที่ 84MHz นั้นมีขั้นตอนการสร้างโครงงานเหมือน Cortex-M0 แต่เลือกประเภทของไมโครคอนโทรลเลอร์เป็นดังภาพที่ 28
กำหนดหน้าที่ของขา
ให้กำหนดหน้าที่ของขา PC13, PC14 และ pc15 เป็น GPIO_Output, RCC_OSC32_ON และ RCC_OSC32_OUT ตามลำดับ เหมือนดังในภาพที่ 29 ซึ่งวิธีการเลือกส่วนของขา PC14 และ PC15 ให้เข้าไปส่วนของ System Core และคลิกช่อง RCC และตั้งค่า High Speed Clock (HSE) และ Low Speed Clock (LSE) เป็น Crystal/Ceramic Resonator เมื่อกำหนด 2 ค่านี้ทำให้ขา PH0, PH1, PC14 และ PC15 ถูกเลือก
กำหนดสัญญาณนาฬิกา
หลังจากกำหนดหน้าที่ของขาเสร็จให้กำหนดเรื่องของการทำงานของสัญญาณนาฬิา โดยตั้งค่าตามภาพที่ 30 ด้วยการเลือกเปิด HSE ตั้งค่า /M เป็น /25 ตัวคูณ *N เป็น X168 และหาร /P เป็น /2 พร้อมทั้งเลือก PLLCLK กำหนด AHB Prescaler เป็น /1 จะได้ HCLK เป็น 84MHz
main.c
โค้ดโปรแกรมใน main.c เป็นดังนี้
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* 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 */
/* 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 */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);
HAL_Delay( 500 );
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1);
HAL_Delay( 500 );
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
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.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13 */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
คอมไพล์โปรแกรม
จากโค้ดเมื่อทำการคอมไพล์โปรแกรมโดยไม่พบข้อผิดจะเป็นดังภาพที่ 31 หลังจากได้ไฟล์ให้ดำเนินการอัพโหลดลงชิพด้วยโปรแกรม STM32CubeProgrammer เพื่อทดสอบการทำงาน ถ้าไม่มีความผิดพลาดจะพบว่าหลอด LED ที่ต่อกับขา PC13 นั้นติดดับสลับกันทุก 250msec
STM32F103C8
สำหรับ Cortex-M3 ซึ่งในที่นี้ใช้ไมโครคอนโทรลเลอร์ STM32F103C8 หรือ Blue Pill ดังภาพที่ 32 มีการจัดวางโครงสร้างขาเหมือนกับบอร์ด Black Pill
การสร้างโครงงานให้เลือกชิพเป็น STM32F103C8 ดังภาพที่ 33 หลังจากนั้นกำหนดชื่อโครงงานตามภาพที่ 34 และติดตั้งเฟิร์มแวร์ดังภาพที่ 35 ตามลำดับ
ในขั้นตอนของการกำหนดหน้าที่ของขาให้เลือกตามภาพที่ 36 และในขั้นตอนของการกำหนดสัญญาณนาฬิกาให้เลือกตามภาพที่ 37 หลังจากนั้น Save All เพื่อสร้างโค้ดต้นแบบ
จากโค้ดต้นแบบให้แก้ไข main.c เป็นดังนี้ หลังจากนั้นเลือกการคอมไพล์เป็น Release และสั่งคอมไพล์จะได้รายงานผลของการใช้หน่วยความจำดังภาพที่ 36 พร้อมผลของการคอมไพล์ดังภาพที่ 37
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2021 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* 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 */
/* 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 */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 0);
HAL_Delay( 500 );
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, 1);
HAL_Delay( 500 );
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13 */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
เมื่ออัพโหลด ep02f103.bin เข้าชิพและเปลี่ยนโหมดเป็นโหมดทำงานจะได้ผลการทำงานเป็นดังภาพที่ 38 และ39
สรุป
จากบทความนี้จะพบว่า เครื่องมือการเขียนโปรแกรมของ STM32 นั้นมีให้ใช้งานครอบคลุมตั้งแต่การสร้างโครงงาน การกำหนดหน้าที่ของขา การตั้งค่าสัญญาณนาฬิกา การแก้ไขโค้ด การคอมไพล์ และการอัพโหลดเข้าบอร์ด ซึ่งช่วยซ่อนการเขียนส่วนต่าง ๆ จากบทความในตอนแรกได้มาก แต่อย่างไรก็ดีถ้าผู้อ่านเข้าใจบทความก่อนหน้านี้จะมองภาพรวมของการบริหารจัดการโค้ดโปรแกรมได้ดีขึ้น และสามารถเข้าไปแก้ไขส่วนของตารางอินเทอร์รัพต์หรือส่วนเริ่มต้นทำงานของโปรแกรมได้ถึงระดับล่างของการเขียนโปรแกรม ส่วนบทความในตอนที่ 2 นี้ผู้อ่านได้ผ่านขั้นตอนการใช้เครื่องมือ พร้อมทั้งได้เรียนรู้การตั้งค่า GPIO การกำหนดค่าของขา และการหน่วงเวลากันแล้ว สุดท้ายขอให้สนุกกับการเขียนโปรแกรมครับ
ท่านใดต้องการพูดคุยสามารถคอมเมนท์ไว้ได้เลยครับ
(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-07-17, 2021-07-18, 2021-10-24