هدف از این آموزش نشان دادن چگونگی استفاده ازPlatformIO IDEدرVS Codeبرای ایجاد، اجرا و اشکال زدایی یک پروژه نمونه چشمک زن با فریموورکSTM32Cubeبرای بردSTM32 Nucleo-F401REاست.
نیازمندی ها
افزونهPlatformIOبرایVS Codeدانلود و نصب شده باشد
درایورهایST-LINKبرای ابزار اشکال زدایی نصب شده باشد
برد توسعهNucleo-F401RE
راه اندازی پروژه
در مرحله اول، ما باید با استفاده از صفحه اصلیPlatformIOیک پروژه جدید ایجاد کنیم (برای باز کردن این صفحه آیکونHomeرا در نوار ابزار کلیک کنید)

در مرحله بعدی، بایدST Nucleo-F401REرا به عنوان یک برد توسعه،STM32Cubeبه عنوان یک فریموورک و در نهایت مسیری برای مکان پروژه انتخاب کنیم (یا از حالت پیش فرض استفاده کنید)

پردازش پروژه ممکن است مدتی طول بکشد (PlatformIOتمام بسته های مورد نیاز را دانلود و نصب خواهد کرد) و پس از این مراحل، ما یک پروژه کاملاً پیکربندی شده داریم که آماده تهیه کد با فریموورکSTM32Cubeاست.
افزودن کد به پروژه تولید شده
بریم کد اصلی را به پروژه اضافه کنیم. اولا، ما دو فایل اصلیmain.cوmain.hرا در پوشهsrc_dirایجاد می کنیم. بر رویsrcکلیک راست کنید

کد زیر را بهmain.hاضافه کنید:
main.h
#ifndef MAIN_H
#define MAIN_H
#include "stm32f4xx_hal.h"
#define LED_PIN GPIO_PIN_5
#define LED_GPIO_PORT GPIOA
#define LED_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#endif // MAIN_H
و کد زیر را به main.c اضافه کنید:
main.c
#include "main.h"
void LED_Init();
int main(void) {
HAL_Init();
LED_Init();
while (1)
{
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_PIN);
HAL_Delay(1000);
}
}
void LED_Init() {
LED_GPIO_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
}
void SysTick_Handler(void) {
HAL_IncTick();
}
بعد از این مرحله ، یک پروژه اولیه چشمک زن ایجاد کردیم که آماده کامپایل و آپلود است.
کامپایل و آپلود
اکنون می توانیم پروژه را بیلد کنیم. برای کامپایل کردن می توانیم از گزینه های زیر استفاده کنیم:
- گزینهBuildرا در منویProject Tasks
- دکمهBuildرویPlatformIO Toolbar
- با استفاده از دستورView: Command Palette> PlatformIO: Build
- با استفاده از منوTask:Task... > PlatformIO: Buildیا از طریق کلیدهای میانبرcmd-alt-b/ctrl-alt-b

اگر همه چیز خوب پیش رفت ، باید نتیجه موفقیت آمیز را در پنجره ترمینال مشاهده کنیم:

برای آپلود فریموورک در برد می توانیم از گزینه های زیر استفاده کنیم:
- گزینه آپلود در منوی Project Tasks
- دکمه آپلود در PlatformIO Toolbar
- با استفاده از دستورView: Command Palette> PlatformIO
- با استفاده از منوی Task: Task... > PlatformIO: Upload یا از طریق کلیدهای میانبر cmd-alt-u/ctrl-alt-u

بعد از آپلود موفقیت آمیز ، چراغ سبزLED2باید چشمک بزند.
اشکال زدایی
برای شروع اشکال زدایی می توانید از گزینهStart debuggingدر منوی دسترسی سریعPlatformIO، یا از منوی بالا یا دکمه کلیدF5استفاده کنید.

لازم است مدتی صبر کنیم تا زمانی کهPlatformIOدر حال اینیشیال اشکال زدایی است و وقتی اولین خط بعد از تابعmainبرجسته شد، آماده رفع اشکال می شویم.

می توانیم با استفاده از دکمه های کنترل، کد را پیمایش کنیم،breakpointها را تنظیم کنیم، رجیستر های پریفرال ها را مشاهده کنیم، متغیرهایی را به پنجرهWatchاضافه کنیم:

نوشتن تست های واحد
خب حالا بریم تست هایی را با استفاده از ویژگی تست واحدPIOبنویسیم که می تواند به ما کمک کند تا کد را مستقیماً روی برد آزمایش کنیم. موتورPIO Unit Testingبه طور پیش فرض تنها از سه فریموورک پشتیبانی می کند:Arduino،ESP-IDF،Mbedو از آنجا که تصمیم گرفتیم ازSTM32Cubeاستفاده کنیم، برای چاپ نتایج آزمایش باید یکtest_transportسفارشی را پیاده سازی کنیم و آن شرایط را درplatformio.ini(فایل پیکربندی پروژه) مشخص کنیم:
[env:nucleo_f401re]
platform = ststm32
board = nucleo_f401re
framework = stm32cube
test_transport = custom
همچنین، باید یک پوشهtestجدید ایجاد کنیم که تست ها و پیاده سازیtest_transportدر آن قرار گیرد:

ما ازUSART2در بردST Nucleo-F401REاستفاده خواهیم کرد زیرا مستقیماً به رابط اشکال زداییSTLinkمتصل است و در فریموورک می تواند به عنوان یک پورت کام مجازی قابل مشاهده باشد ، بنابراین ما به مبدلUSB-UARTاضافی احتیاج نداریم. برای پیاده سازیtest_transportسفارشی، باید دو فایلunittest_transport.hوunittest_transport.cایجاد کنیم و آنها را در پوشهrootدر پروژه خود درtest_dirقرار دهیم. در این فایل ها باید چهار تابع زیر را پیاده سازی کنیم:
void unittest_uart_begin();
void unittest_uart_putchar(char c);
void unittest_uart_flush();
void unittest_uart_end();
اجرای unittest_transport.h:
#ifndef UNITEST_TRANSPORT_H
#define UNITEST_TRANSPORT_H
#ifdef __cplusplus
extern "C" {
#endif
void unittest_uart_begin();
void unittest_uart_putchar(char c);
void unittest_uart_flush();
void unittest_uart_end();
#ifdef __cplusplus
}
#endif
#endif // UNITEST_TRANSPORT_H
اجرای unittest_transport.c:
#include "unittest_transport.h"
#include "stm32f4xx_hal.h"
#define USARTx USART2
#define USARTx_CLK_ENABLE() __HAL_RCC_USART2_CLK_ENABLE()
#define USARTx_CLK_DISABLE() __HAL_RCC_USART2_CLK_DISABLE()
#define USARTx_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USARTx_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USARTx_RX_GPIO_CLK_DISABLE() __HAL_RCC_GPIOA_CLK_DISABLE()
#define USARTx_TX_GPIO_CLK_DISABLE() __HAL_RCC_GPIOA_CLK_DISABLE()
#define USARTx_FORCE_RESET() __HAL_RCC_USART2_FORCE_RESET()
#define USARTx_RELEASE_RESET() __HAL_RCC_USART2_RELEASE_RESET()
#define USARTx_TX_PIN GPIO_PIN_2
#define USARTx_TX_GPIO_PORT GPIOA
#define USARTx_TX_AF GPIO_AF7_USART2
#define USARTx_RX_PIN GPIO_PIN_3
#define USARTx_RX_GPIO_PORT GPIOA
#define USARTx_RX_AF GPIO_AF7_USART2
static UART_HandleTypeDef UartHandle;
void unittest_uart_begin()
{
GPIO_InitTypeDef GPIO_InitStruct;
USARTx_TX_GPIO_CLK_ENABLE();
USARTx_RX_GPIO_CLK_ENABLE();
USARTx_CLK_ENABLE();
GPIO_InitStruct.Pin = USARTx_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
GPIO_InitStruct.Alternate = USARTx_TX_AF;
HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = USARTx_RX_PIN;
GPIO_InitStruct.Alternate = USARTx_RX_AF;
HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);
UartHandle.Instance = USARTx;
UartHandle.Init.BaudRate = 115200;
UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
if(HAL_UART_Init(&UartHandle) != HAL_OK) {
while(1){}
}
}
void unittest_uart_putchar(char c)
{
HAL_UART_Transmit(&UartHandle, (uint8_t*)(&c), 1, 1000);
}
void unittest_uart_flush(){}
void unittest_uart_end() {
USARTx_CLK_DISABLE();
USARTx_RX_GPIO_CLK_DISABLE();
USARTx_TX_GPIO_CLK_DISABLE();
}
حال باید موارد آزمایش را اضافه کنیم. آزمایشات را می توان به یک پروندهCواحد اضافه کرد که ممکن است شامل چندین آزمایش باشد. اول از همه، ما باید سه تابع پیش فرض را اضافه کنیم:setUp،tearDownوmain
ازsetUpوtearDownبرای اولیه و نهایی کردن شرایط آزمایش استفاده می شود. اجرای این توابع برای آزمونهای در حال اجرا ضروری نیست اما اگر قبل از انجام آزمایش نیاز به تنظیم برخی متغیرها دارید ، از تابعsetUpاستفاده می کنید و در صورتclean upکردن متغیرها از عملکردtearDownاستفاده می کنید. در مثال ما از این توابع استفاده خواهیم کرد تا بر این اساسLEDرا اولویت بندی وdeinitializeکنیم.
بریم یک فایل جدیدtest_main.c را به پوشه testاضافه کنیم. تست های زیر برای روتین چشمک زن در این فایل اجرا خواهد شد:
- test_led_builtin_pin_numberتضمین می کند کهLED_PINاز مقدار صحیح برخوردار باشد
- توابع تست_led_state_highتست هایHAL_GPIO_WritePinوHAL_GPIO_ReadPinبا مقدارGPIO_PIN_SET
- توابع تست_led_state_lowتست هایHAL_GPIO_WritePinوHAL_GPIO_ReadPinبا مقدارGPIO_PIN_RESET
تأخیر 2 ثانیه ای لازم است زیرا برد از تنظیم مجدد نرم افزار پشتیبانی نمی کند از طریق Serial.DTR/RTS
#include "../src/main.h"
#include <unity.h>
void setUp(void) {
LED_GPIO_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
}
void tearDown(void) {
HAL_GPIO_DeInit(LED_GPIO_PORT, LED_PIN);
}
void test_led_builtin_pin_number(void) {
TEST_ASSERT_EQUAL(GPIO_PIN_5, LED_PIN);
}
void test_led_state_high(void) {
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, GPIO_PIN_SET);
TEST_ASSERT_EQUAL(GPIO_PIN_SET, HAL_GPIO_ReadPin(LED_GPIO_PORT, LED_PIN));
}
void test_led_state_low(void) {
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_PIN, GPIO_PIN_RESET);
TEST_ASSERT_EQUAL(GPIO_PIN_RESET, HAL_GPIO_ReadPin(LED_GPIO_PORT, LED_PIN));
}
int main() {
HAL_Init(); // initialize the HAL library
HAL_Delay(2000); // service delay
UNITY_BEGIN();
RUN_TEST(test_led_builtin_pin_number);
for (unsigned int i = 0; i < 5; i++)
{
RUN_TEST(test_led_state_high);
HAL_Delay(500);
RUN_TEST(test_led_state_low);
HAL_Delay(500);
}
UNITY_END(); // stop unit testing
while(1){}
}
void SysTick_Handler(void) {
HAL_IncTick();
}
حالا آماده آپلود تست ها در برد هستیم. برای این کار می توان از
گزینهTestاز منویProject Tasks
Tasks: Run Task... > PlatformIO
گزینهTestاز منوی بالا یا دکمهTestدرPlatformIO Toolbarاستفاده کرد

پس از پردازش باید گزارش مفصلی در مورد نتایج آزمایش مشاهده کنیم:

همانطور که از گزارش می بینیم، تمام تست های با موفقیت انجام شد!
اکنون یک الگوی مناسب و معقول داریم که می توانیم برای پروژه های پیچیده تر بعدی خود بهبود ببخشیم.
موفق باشید.