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 Core
和Full MCU package
的對應關系在README.md的表格中, 現在最新的是v2.6.7, 對應的完整庫版本為v1.26.2 - 這個庫與Windows Keil5 MDK下使用的標准外設庫
Standard peripherals library
不兼容, 后者已經不再更新, 最高版本到1.8.0
- 這個庫只是
- STM32Cube完整庫的倉庫
- Github地址是 https://github.com/STMicroelectronics/STM32CubeF4
- 上面的framework庫, 在這個倉庫里的路徑是 tree/master/Drivers/CMSIS/Device/ST/STM32F4xx, 可以對比兩邊的文件, 是一樣的
- 完整庫比較大, 壓縮包接近300MB
綜上, STM32F4的CMSIS環境下是沒有可用的標准外設庫(Standard peripherals library)的, 嘗試過用1.8.0的標准外設庫, 編譯有不少錯誤, 所以用這個開發的話
- 要么自己修訂標准外設庫, 使其兼容v2.6.x的CMSIS核心庫, 這個需要對各外設的變量和結構體很熟, 我這樣剛入門的估計是搞不定
- 要么不用標准外設庫, 純使用核心庫變量開發. 這個難度也不小, 類似於回到8051開發的模式了, 每寫一步都要查寄存器手冊.
- 要么直接用STM32Cube的完整外設庫. 不過既然都用了STM32Cube的庫了, 為什么不直接選framework時就選STM32Cube呢?
Framework:stm32cube + board:STM32F401CC
創建項目
這個方式相對比較簡單了, 默認方式創建就可以
- 打開PlatformIO:Home, 如果沒裝 Platform:ST STM32 的, 先在 Platform 里裝一下
- 點擊 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;
}
參考和下一步閱讀
- https://docs.platformio.org/en/latest/frameworks/stm32cube.html
- https://docs.platformio.org/en/latest/tutorials/ststm32/stm32cube_debugging_unit_testing.html#tutorial-stm32cube-debugging-unit-testing
- STM32 HAL庫的介紹 https://blog.csdn.net/xuzhexing/article/details/90137754
- HAL庫的nRF24L01操作 https://github.com/pstolarz/NRF_HAL/blob/master/src/nrf_hal.cpp
- 同上 https://github.com/leech001/NRF24L01/blob/master/src/nrf24l01.c
- 同上 https://github.com/eos1d3/NRF24L01
- 同上 https://github.com/J20RC/STM32_RC_Transmitter/tree/master/software/Config