PlatformIO下的STM32F4xx項目配置


Framework:CMSIS + Board:STM32F4xx

  • cmsis核心庫的安裝路徑是 /home/[your user]/.platformio/packages/framework-cmsis
    • 對應了STM32Cube完整庫 Drivers/CMSIS/ 路徑下的內容
    • 多了一個Driver目錄
  • stm32f4-framework庫文件安裝路徑是 /home/[your user]/.platformio/packages/framework-cmsis-stm32f4
    • 這個庫只是STM32Cube MCU Full Package 的核心定義部分, 在Github上的倉庫地址: https://github.com/STMicroelectronics/cmsis_device_f4
    • 這個庫有自己的版本號v2.6.x, 容易與完整庫的版本號混淆, 其與CMSIS CoreFull MCU package的對應關系在README.md的表格中, 現在最新的是v2.6.7, 對應的完整庫版本為v1.26.2
    • 這個庫與Windows Keil5 MDK下使用的標准外設庫Standard peripherals library不兼容, 后者已經不再更新, 最高版本到1.8.0
  • STM32Cube完整庫的倉庫

綜上, STM32F4的CMSIS環境下是沒有可用的標准外設庫(Standard peripherals library)的, 嘗試過用1.8.0的標准外設庫, 編譯有不少錯誤, 所以用這個開發的話

  1. 要么自己修訂標准外設庫, 使其兼容v2.6.x的CMSIS核心庫, 這個需要對各外設的變量和結構體很熟, 我這樣剛入門的估計是搞不定
  2. 要么不用標准外設庫, 純使用核心庫變量開發. 這個難度也不小, 類似於回到8051開發的模式了, 每寫一步都要查寄存器手冊.
  3. 要么直接用STM32Cube的完整外設庫. 不過既然都用了STM32Cube的庫了, 為什么不直接選framework時就選STM32Cube呢?

Framework:stm32cube + board:STM32F401CC

創建項目

這個方式相對比較簡單了, 默認方式創建就可以

  1. 打開PlatformIO:Home, 如果沒裝 Platform:ST STM32 的, 先在 Platform 里裝一下
  2. 點擊 New Project,
    • 填入項目名稱,
    • 在 Board 中輸入 stm32f401 在過濾結果的列表中, 選擇 stm32f401cc, 如果是常見的 black pill, 可以直接在底下找到對應的專門的board
    • 選擇 Framework 為 STM32Cube
    • 如果不希望建在默認目錄的話, 取消 Use Default Location 勾選, 指定位置, PlatformIO 會創建項目目錄

項目的目錄和文件

platformio.ini

首先是 platformio.ini, 這個文件的內容如下, 一個項目里可以有多個env, 可以指定一個默認的, 在使用快捷鍵時, 會執行默認env的編譯和寫入.

[env:stm32f401cc]
platform = ststm32
board = genericSTM32F401CC
framework = stm32cube
upload_protocol = stlink
board_build.stm32cube.custom_config_header = yes 

參數說明:

  • upload_protocol 用於指定不同的寫入方式
  • board_build.stm32cube.custom_config_header = yes 禁止platformio自動生成stm32f4xx_hal_conf.h. 默認情況下, 在項目啟動或編譯時, platformio會自動在庫文件目錄, 基於stm32f4xx_hal_conf_template.h復制創建stm32f4xx_hal_conf.h, 而HAL項目中這個配置文件一般是和main.c一起放在用戶代碼目錄下的, 如果按默認的方式, 編譯時會優先使用庫目錄下的文件, 用戶自己配置的就失效了. 加上這個選項可以禁止platformio自己生成這個文件.

.vscode/c_cpp_properties.json

這個文件很重要, 因為任何 include 的錯誤, 都可以在這里檢查. 這個文件是 PlatformIO 自動生成的, 所以不需要去改它, 如果發現你修改了platformio.ini 后它沒有更新, 重新打開 VS Code 就可以了.

測試

閃燈例子, 在src目錄下創建 main.c, 內容如下

#include "stm32f4xx_hal.h"

#define LED_PIN                        GPIO_PIN_13  // 指定PIN
#define LED_GPIO_PORT                  GPIOC        // 指定IO
#define LED_GPIO_CLK_ENABLE()          __HAL_RCC_GPIOC_CLK_ENABLE() // 指定啟用時鍾的IO

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

編譯, 使用stlink連接stm32f401的開發板, 然后寫入

STM32F401CCxx 可用的 SystemClock_Config()

配置不正確的系統時鍾會導致系統卡死, 下面兩個方法是測試可用的

/**
* Generated by STM32Cube 1.26.x
* *) Enable HSE(25MHz), No LSE(32.768KHz)
* *) No PLL, No prescaler, HSE->SYSCLK->PHBPrescaler=1->AHB,APB1,APB2...
*/
void SystemClock_Config_HSE_25MHz(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  //RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // Make APB2 12.5MHz

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

/**
* Generated by STM32Cube 1.26.x
* *) Enable HSE(25MHz), No LSE(32.768KHz)
* *) Enable PLL
* *) HSE->PLL->/25->*168->/2->PPLCLK->SYSCLK(84MHz)->APB1 /2-> APB1
*/
void SystemClock_Config_HSE_84MHz(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
  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();
  }
  
  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();
  }
}

用UART輸出printf的宏定義

因為sdcc和keil5 mdk下的編譯工具不一樣, 所以這部分的定義也不一樣, 可以用下面的宏語句統一處理

#if defined(__GNUC__)
int _write(int fd, char *ptr, int len)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, 0xFFFF);
    return len;
}
#elif defined (__ICCARM__)
size_t __write(int handle, const unsigned char * buffer, size_t size)
{
    HAL_UART_Transmit(&huart1, (uint8_t *) buffer, size, HAL_MAX_DELAY);
    return size;
}
#elif defined (__CC_ARM)
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
}
#endif

或者

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
  return ch;
}

參考和下一步閱讀


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM