STM32的软件开发

软件开发模式

编程模式

  • 嵌入式系统的软件开发,可以分为两种模式:

  • 前后台系统

    又称裸机编程,整个程序框架由一个死循环和若干中断服务程序组成

    后台系统指一个无限循环,循环中调用各个相关函数完成所需操作;前台系统指各个中断服务函数,用于处理系统的异步事件和实时性要求较高的任务

    比如一个发挥定时器功能的程序,后台系统中不断刷新数码管进行时间的显示,前台系统则是定时器的中断,每当达到定时值时进入中断修改显示的时间

    这种模式结构简洁,开发人员能全盘掌控程序运行流程,因此适合控制流程简单,任务固定的中小型嵌入式系统

  • 嵌入式操作系统

    当项目更加复杂,外围电路更多,各个任务频繁交互时,前后台系统模式的开发难度就大大提升了,这时就要使用嵌入式操作系统

    嵌入式操作系统EOS是工作在嵌入式处理器上的系统软件,它具备通用操作系统的基本功能,负责嵌入式系统的软硬件资源分配、任务调度、同步机制、中断处理等

    在基于EOS进行开发时,就不必全盘掌握整个程序的运行,而是将程序分解为各个任务进行单独的编写与调试,由操作系统进行任务的调度,这样的模式简化了应用程序的设计过程,缩短开发时间,更好地保证系统的实时性和可靠性

程序开发方式

  • 开发微控制器上的程序,同样可以分为两种方式:

  • 寄存器方式

    也就是在我的STM8教程中使用的方式;从MCU的底层学起,掌握每一个寄存器的含义,亲自编写底层驱动程序,这样的方式可以全面了解、掌握MCU,写出效率较高的程序

    但是显然这种方式学习难度大,开发效率低,最为重要的是:若更换了新的MCU,那么就得按新的MUC重新开发全部程序

  • 库函数方式

    将在本教程中使用的方式;使用芯片厂家封装的库函数进行编程,开发与移植更为方便

    常接触的库函数有两类:

    • STD库:即标准外设库,基于寄存器操作的封装,它允许开发者通过函数调用直接与硬件寄存器交互,从而实现对单片机的控制,也就类似与在STM8笔记中我们所写的那些函数,因此没有可移植性

    • HAL库:全称Hardware Abstraction Layer,硬件抽象层,是ST公司为了方便STM32之间的移植而开发的库,它通过更高级的封装减少了不同型号STM32之间的函数差异,提高了代码的通用性和移植性,CubeMX所生成的工程文件中就使用了HAL库,但是执行效率、占用空间均表现较差

    • LL库:全称Low Layer,比HAL库更贴近底层,执行效率更高,可以说兼顾了STD的效率与HAL的可移植性,问题在于LL库功能不如HAL强大,只提供了一些基础的功能


开发环境搭建

开发所用软件

  • STM32CubeMX

    图形化的软件配置软件,使用它我们就可以极其简单地配置STM32和生成初始化代码,与一行一行亲自写代码配置寄存器来初始化相比大大降低了使用难度

  • Keil MDK-ARK

    用于ARM的集成开发环境,集成了程序编写、编译、下载调试、仿真等的功能

STM32CubeMX的使用

  1. 软件配置

    在安装前,要确定自己的电脑有JAVA环境,即安装jre

    在安装完成之后,还要安装MCU固件包才能对相应型号的STM32进行开发:在菜单栏点击Help -> Manage embedded software packages 打开对应窗口后找到需要的芯片系列勾选所需版本后点击安装(蓝桥杯要用的是STM32G4,可以点击From Local选中赛点资源包中的固件包进行本地安装)

  2. 新建工程
    1. 菜单栏File -> New Project 或者 在启动的I need to窗口中点击ACCESS TO MCU SELECTOR

    2. 进入New Project from a MCU/MPU窗口后在Part Number中输入单片机型号

      (对蓝桥杯开发板,选择STM32G431RB,然后在MCUs/MPUs List中进一步选择对应型号(STM32G431RBTx,注意是封装为LQFP64的版本)

    3. 单击右上角Start Project

  3. 配置RCC

    RCC,即Reset and Clock Control,复位和时钟晶振

    首先在左侧System Core一栏选中RCC,在右侧弹出High Speed Clock(HSE)一栏中配置HSE为晶振Crystal/Ceramic Resonator或者旁路模式BYPASS

  4. 配置时钟

    在窗口上方的标签页中切换到Clock Configuration,进入时钟的系统结构图,在此可以配置各时钟参数

    蓝桥杯开发板程序题目要求运行在80MHz下,对应的时钟设置是:

    • Input frequency:改为24

    • PLL Source Mux:选择HSE

    • System Clock Mux:SYSCLK选择为PLLCLK(Enable CSS)

      对HSE进行锁相环倍频:PLLM:/3;PLL*N:×20;/R:/2

    • 完成如上修改后会得到系统时钟SYSCLK:80,外设时钟不额外分频(ABH:/1)得到HCLK:80

  5. 工程管理

    切换到Project Manager标签页,填写项目名称,项目存放路径

    另外,需要选择Application Structure为Basic

    由于我们使用keil MDK-ARM作为IDE编写程序,所以在Toolchain/IDE中选择MDK-ARM,并选择版本为V5

  6. 代码生成

    在配置完成后,可以生成初始化代码,先在Project Manager一页左侧栏切换到Code Generator,选择Copy only the necessary library files,并勾选Generate peripheral initialization as a pair of .c/.h files per peripheral,这些配置作用是只复制需要的.c/.h文件,并把每个外设作为单独的.c/.h文件

    最后点标签页上方的GENERATE CODE按钮

    生成完成在提示窗口中选择Open Project自动跳转至Keil打开工程

Keil的使用

  1. 软件配置

    由于版本问题,在编译时会出现Build aborted的错误,这需要进行一些额外配置:点击工具栏的魔法棒按键打开Options for Target,在ARM Compiler一栏切换为Use default compiler version 6,或者自行安装使用旧版本的compiler version 5

    此外,还需要选择调试器:

    使用DAP-link(蓝桥杯开发板所使用):

    1. 在打开的Options for Target窗口切换到Debug标签页,选择Use CMSIS-DAP Debugger并点击Settings
    2. 在弹出的CMSIS-DAP Cortex-M Target Driver Setup窗口中把Por选项选择为SW模式,再切换到Flash Download标签页勾选Reset and Run,最后点OK

    如果使用ST-link:

    1. 先安装ST-Link的驱动,文件位于Keil_v5\ARM\STLink\USBDriver中,dpinst_amd64.exe适用于64位操作系统;安装完成后会在电脑设备管理器的通用串行总线设备在看到ST-Link Debug
    2. 特别注意:使用ST-Link需要在CubeMX中System Core一栏下SYS选项把Debug调试接口配置为Serial Wire
    3. 根据类似上述的步骤将Keil调试器选择为ST-Link
  2. 程序编译

    在窗口上方的工具栏有一个Build按钮(或者快捷键F7),按下即可编译当前程序

  3. 程序烧录

    编译完成后就可以把程序下载到单片机中了,使用USB线把电脑和单片机上的USB下载口连接(DAP)或者使用ST-Link跳线连接,然后单击工具栏的Load按钮进行Download(或者快捷键F8)

    烧录完成后会在Output栏显示Verify OK

开发过程

程序编写

  • CubeMX的作用

    简单来说,STM32CubeMX会生成包含各项初始化配置的代码文件,在Keil打开后我们可以直接在此基础上编写程序,不必再亲自配置各项参数或者编写用于初始化函数

  • CubeMX的代码分区

    CubeMX生成的工程中会给代码文件分区,在MDK-ARM中编写程序时我们要把代码写在相应的分区中

    分区除了让代码更有条理方便阅读外,其作用是防止CubeMX再生成的代码覆盖用户编写的内容,比如说在Keil编写代码的途中在CubeMX中对初始化配置作一些修改,这时Keil会提示更新代码,若是我们写的代码没有放在各个USER CODE区域之间,就可能被新生成的代码覆盖

    生成的main.c具体如下

    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    Includes:为要包含的头文件
    /* USER CODE END Includes */
    
    /* Private typedef -----------------------------------------------------------*/
    /* USER CODE BEGIN PTD */
    PTD:添加typedef以自定义变量
    /* USER CODE END PTD */
    
    /* Private define ------------------------------------------------------------*/
    /* USER CODE BEGIN PD */
    PD:添加define宏定义
    /* USER CODE END PD */
    
    /* Private macro -------------------------------------------------------------*/
    /* USER CODE BEGIN PM */
    PM:添加宏
    /* USER CODE END PM */
    
    /* Private variables ---------------------------------------------------------*/
    
    /* USER CODE BEGIN PV */
    PV:自定义全局变量
    /* USER CODE END PV */
    
    /* Private function prototypes -----------------------------------------------*/
    void SystemClock_Config(void);
    /* USER CODE BEGIN PFP */
    PFP:自定义函数声明
    /* USER CODE END PFP */
    
    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    USER CODE 0:进入main之前的代码
    /* USER CODE END 0 */
    
    /**
      * @brief  The application entry point.
      * @retval int
      */
    int main(void)
    {
    
      /* USER CODE BEGIN 1 */
    USER CODE 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 SysInit:用户编写系统初始化函数的区域
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      /* USER CODE BEGIN 2 */
    USER CODE 2:最常用的区域,此处已经进入主函数并且经过各项初始化配置
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    USER CODE 3:存放需要循环执行的代码,即后台系统
        /* USER CODE BEGIN 3 */
      }
      /* USER CODE END 3 */
    }
    
      /* USER CODE BEGIN 4 */
    USER CODE 4:自定义函数体
      /* USER CODE END 4 */
    

程序调试

  • 常规调试方法

    在Keil中,单击工具栏的Debug按钮进入调试模式,在工具栏可以看到一排按钮:复位、全速运行、停止运行、跟踪调试(程序单步运行、遇到函数调用时会跳到子函数内部)、跳出函数(当用户不想再执行函数内剩余代码时跳出函数)、单步运行(与跟踪调试相反,把调用子函数当作一条语句来执行)

  • 断点调试

    与一般编程类似,执行到断点处时停止

  • 观察窗口

    用于查看变量的当前值,从而掌握程序的运行状态

    在源码调试窗口中选中需要观察的变量(对应局部变量必须在局部变量所在的函数才能观察到),右键它选则Add '变量名' to,并在二级菜单中选中使用的观察窗口Watch1或2,变量值默认显示为十六进制

  • 外设查看窗口

    用于查看与片内外设相关的寄存器的当前值,从而掌握外设的状态

    片内外设分为两大类,查看方式不同:

    • Cortex-M的固有外设:如嵌套向量中断控制器NVIC、系统节拍定时器Systick等

      点击工具栏外设查看窗口System Viewer Windows,在弹出菜单的Core Peripherals选项中打开二级菜单,选中要查看的外设

    • 目标芯片的片内外设:如GPIO、定时器和串口

      步骤类似,但在System Viewer Windows弹出菜单的下方即可看到各个外设选项

posted on 2025-05-06 09:19  无术师  阅读(147)  评论(0)    收藏  举报