• 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;
    }
    

    参考和下一步阅读

  • 相关阅读:
    史上最全常用正则表达式大全
    JAVA中不要用e.printStackTrace()
    JAVA中不要用e.printStackTrace()
    为什么尽量不用e.printStackTrace
    java返回集合为null还是空集合以及空集合的三种写法
    IntelliJ IDEA使用技巧——常用快捷键Mac篇
    [php] 使用laravel-modules模块化开发
    [php] 解决laravel: production.ERROR: No application encryption key has been specified
    [gitlab] 解决:remote: Ask a project Owner or Maintainer to create a default branch:
    [composer] composer 安装配置laravel 8
  • 原文地址:https://www.cnblogs.com/milton/p/15260805.html
Copyright © 2020-2023  润新知