STM32微控制器串行通信简介及开发环境搭建指南

STM32:STM32串行通信:STM32微控制器简介与开发环境搭建

STM32微控制器概述

STM32系列微控制器介绍

STM32是意法半导体(STMicroelectronics)推出的一系列基于ARM Cortex-M内核的32位微控制器。这一系列的微控制器以其高性能、低功耗和丰富的外设而闻名,广泛应用于各种嵌入式系统中,包括工业控制、汽车电子、消费电子、医疗设备和物联网设备等。

特点

  • 高性能:STM32系列微控制器采用ARM Cortex-M内核,提供从Cortex-M0到Cortex-M7不同级别的性能。
  • 低功耗:STM32具有多种低功耗模式,包括睡眠模式、停止模式和待机模式,适用于电池供电的设备。
  • 丰富的外设:STM32集成了多种外设,如ADC、DAC、定时器、串行通信接口(USART、SPI、I2C)、USB、CAN等,满足不同应用需求。
  • 灵活的时钟系统:STM32的时钟系统支持多种时钟源,包括内部RC振荡器、外部晶振、PLL等,提供灵活的时钟配置。
  • 广泛的存储选项:STM32提供从几KB到几MB的闪存和RAM选项,适应不同复杂度的应用。
  • STM32微控制器架构与特性

    STM32微控制器基于ARM Cortex-M内核,具有以下架构和特性:

    ARM Cortex-M内核

  • Cortex-M0:最低功耗,适用于简单应用。
  • Cortex-M3:平衡性能与功耗,广泛应用于STM32F1系列。
  • Cortex-M4:具有DSP指令和FPU,适用于需要高性能计算的应用。
  • Cortex-M7:最高性能,适用于复杂应用,如音频处理和图像处理。
  • 低功耗模式

    STM32支持多种低功耗模式,包括:

  • 睡眠模式:CPU停止工作,但RAM和外设保持运行。
  • 停止模式:CPU和RAM停止工作,但保留RAM数据,快速唤醒。
  • 待机模式:所有硬件停止工作,仅保留RTC和备份寄存器,最低功耗。
  • 时钟系统

    STM32的时钟系统包括:

  • 内部RC振荡器:用于低速应用,无需外部晶振。
  • 外部晶振:提供更稳定的时钟源。
  • PLL(Phase-Locked Loop):用于倍频或分频,提高时钟灵活性。
  • 存储器

    STM32的存储器包括:

  • 闪存:用于存储程序代码和常量数据。
  • RAM:用于存储运行时数据和变量。
  • SRAM:静态RAM,用于高速数据存储。
  • ROM:部分型号包含ROM,用于存储固件或库。
  • STM32在嵌入式系统中的应用

    STM32微控制器因其高性能、低功耗和丰富的外设,被广泛应用于各种嵌入式系统中:

    工业控制

  • 电机控制:利用STM32的PWM功能和高速ADC进行电机速度和位置的精确控制。
  • 自动化设备:STM32的实时操作系统支持和丰富的外设使其成为自动化设备的理想选择。
  • 汽车电子

  • 车身控制:如车窗、门锁、灯光控制等。
  • 信息娱乐系统:利用STM32的多媒体外设和高速通信接口。
  • 消费电子

  • 智能家居:如智能灯泡、智能插座等,利用STM32的低功耗和无线通信能力。
  • 可穿戴设备:如智能手表、健康监测器等,STM32的小尺寸和低功耗特性非常适合。
  • 医疗设备

  • 便携式设备:如血糖仪、血压计等,STM32的低功耗和高精度ADC是关键。
  • 监测系统:利用STM32的实时处理能力和通信接口进行数据采集和传输。
  • 物联网设备

  • 传感器网关:STM32的低功耗和多种通信接口使其成为传感器数据收集和传输的理想平台。
  • 云连接设备:利用STM32的网络通信能力,如以太网或Wi-Fi模块,实现设备与云的连接。
  • 示例:STM32F103C8T6的GPIO配置

    #include "stm32f1xx_hal.h"
    
    int main(void)
    {
      HAL_Init(); // 初始化HAL库
    
      __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
      __HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
    
      GPIO_InitTypeDef GPIO_InitStruct = {0};
    
      GPIO_InitStruct.Pin = GPIO_PIN_0; // 配置GPIOA0
      GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
      GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
      HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化GPIOA0
    
      while(1)
      {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); // 切换GPIOA0的输出状态
        HAL_Delay(500); // 延时500ms
      }
    }
    

    解释

    上述代码示例展示了如何使用STM32F103C8T6微控制器的GPIO(General Purpose Input/Output)进行简单的LED闪烁。首先,通过HAL_Init()初始化HAL库,然后使能GPIOA和GPIOB的时钟。接着,配置GPIOA0为推挽输出模式,低速,无上拉下拉。最后,在无限循环中,通过HAL_GPIO_TogglePin()函数切换GPIOA0的输出状态,并使用HAL_Delay()函数实现500ms的延时,从而实现LED的闪烁效果。

    通过这个简单的示例,我们可以看到STM32微控制器的GPIO配置和基本操作,这是嵌入式系统开发中非常基础且常用的功能。

    STM32微控制器开发环境搭建与配置

    安装STM32CubeMX与KeilMDK

    STM32CubeMX

  • 下载与安装
  • 访问STMicroelectronics官网下载STM32CubeMX。
  • 运行安装程序,按照提示完成安装。
  • KeilMDK

  • 下载与安装
  • 访问Keil官网下载KeilMDK。
  • 安装过程中,选择STM32支持包。
  • 创建STM32CubeMX项目

    1. 启动STM32CubeMX

    2. 打开STM32CubeMX软件。
    3. 选择微控制器

    4. 在“Device Selection”窗口中,选择你的STM32微控制器型号。
    5. 例如:STM32F103C8T6。
    6. 配置引脚

    7. 在“Pinout & Configuration”窗口中,配置你将要使用的引脚。
    8. 例如:设置USART2的TX和RX引脚。
    9. 生成代码

    10. 点击“Generate Code”按钮,选择“Keil MDK-ARM”作为IDE。
    11. 选择项目保存路径,生成项目代码。

    配置KeilMDK环境

    1. 导入STM32CubeMX生成的项目

    2. 在KeilMDK中,选择“Project” -> “Open Project”,导入STM32CubeMX生成的项目。
    3. 设置调试器

    4. 在“Target”选项卡中,选择“Debug” -> “Settings”。
    5. 选择你的调试器,例如:ST-Link。
    6. 配置链接器

    7. 在“Target”选项卡中,选择“Settings” -> “Target” -> “Linker”。
    8. 确保“Linker settings”正确配置了你的微控制器。
    9. 编译设置

    10. 在“C/C++ Compiler”选项卡中,设置编译选项。
    11. 例如:优化级别、警告级别等。

    STM32CubeMX与KeilMDK的联合使用

  • STM32CubeMX用于硬件配置

  • STM32CubeMX提供图形化界面,方便配置微控制器的硬件资源。
  • 例如:设置时钟、GPIO、USART等。
  • KeilMDK用于代码编写与调试

  • KeilMDK提供强大的代码编辑器和调试工具。
  • 例如:编写C代码、设置断点、查看变量值等。
  • 项目编译与固件下载

    1. 编译项目

    2. 在KeilMDK中,选择“Project” -> “Build All”编译项目。
    3. 编译成功后,生成的固件文件通常为.axf或.bin格式。
    4. 下载固件

    5. 连接调试器到微控制器。
    6. 在KeilMDK中,选择“Project” -> “Download”下载固件到微控制器。
    7. 确保微控制器已上电,且调试器正确连接。

    示例代码:STM32CubeMX生成的USART2配置

    // STM32CubeMX生成的USART2配置代码示例
    #include "stm32f1xx_hal.h"
    
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    static void MX_USART2_UART_Init(void);
    
    int main(void)
    {
        HAL_Init();
        SystemClock_Config();
        MX_GPIO_Init();
        MX_USART2_UART_Init();
    
        // 串行通信初始化
        huart2.Instance = USART2;
        huart2.Init.BaudRate = 9600;
        huart2.Init.WordLength = UART_WORDLENGTH_8B;
        huart2.Init.StopBits = UART_STOPBITS_1;
        huart2.Init.Parity = UART_PARITY_NONE;
        huart2.Init.Mode = UART_MODE_TX_RX;
        huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
        huart2.Init.OverSampling = UART_OVERSAMPLING_16;
        HAL_UART_Init(&huart2);
    
        while (1)
        {
            // 发送数据
            HAL_UART_Transmit(&huart2, (uint8_t *)"Hello STM32!", 13, 1000);
        }
    }
    
    // GPIO配置
    static void MX_GPIO_Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};
    
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_GPIOC_CLK_ENABLE();
        __HAL_RCC_GPIOD_CLK_ENABLE();
    
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, GPIO_PIN_SET);
    }
    
    // USART2配置
    static void MX_USART2_UART_Init(void)
    {
        huart2.Instance = USART2;
        huart2.Init.BaudRate = 9600;
        huart2.Init.WordLength = UART_WORDLENGTH_8B;
        huart2.Init.StopBits = UART_STOPBITS_1;
        huart2.Init.Parity = UART_PARITY_NONE;
        huart2.Init.Mode = UART_MODE_TX_RX;
        huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
        huart2.Init.OverSampling = UART_OVERSAMPLING_16;
        HAL_UART_Init(&huart2);
    }
    

    代码解释

  • 主函数 (main())

  • 初始化HAL库、系统时钟、GPIO和USART2。
  • 进入无限循环,发送“Hello STM32!”字符串。
  • GPIO配置 (MX_GPIO_Init())

  • 配置GPIO引脚,确保所有引脚处于高电平状态。
  • USART2配置 (MX_USART2_UART_Init())

  • 配置USART2的波特率、数据位、停止位、校验位等参数。
  • 通过以上步骤,你可以在STM32微控制器上实现串行通信功能,发送简单的文本信息。这为后续的串行通信开发奠定了基础。

    STM32串行通信基础

    串行通信原理与类型

    串行通信是一种数据传输方式,其中数据位被逐个按顺序传输,通常用于长距离通信或在设备间传输数据时节省线路资源。串行通信有两种主要类型:同步和异步。

    同步串行通信

    同步串行通信中,发送和接收设备通过共享一个时钟信号来同步数据传输。这种通信方式适用于高速数据传输,如SPI(Serial Peripheral Interface)和I2C(Inter-Integrated Circuit)。

    异步串行通信

    异步串行通信中,数据位之间没有共享的时钟信号。数据通过起始位和停止位来界定,常用于低速通信,如UART(Universal Asynchronous Receiver/Transmitter)。

    STM32的USART与UART模块

    STM32微控制器支持多种串行通信协议,其中USART(Universal Synchronous/Asynchronous Receiver/Transmitter)和UART是两种常用的串行通信接口。

    USART

    USART模块支持同步和异步通信,可以配置为UART或SPI模式。它具有数据接收、数据发送、错误检测等功能,适用于多种通信需求。

    UART

    UART是USART在异步模式下的特例,主要用于异步串行通信。它简化了USART的功能,去除了同步通信相关的特性,但保留了基本的异步通信能力。

    配置USART与UART参数

    配置USART或UART模块涉及设置波特率、数据位、停止位、奇偶校验等参数。以下是一个配置STM32的USART模块的示例代码:

    // 包含必要的头文件
    #include "stm32f1xx_hal.h"
    
    // 定义USART的配置结构体
    USART_InitTypeDef USART_InitStruct = {0};
    
    void MX_USART2_UART_Init(void)
    {
        // 配置GPIO
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_USART2_CLK_ENABLE();
        
        // GPIOA2为TX,GPIOA3为RX
        GPIO_InitTypeDef GPIO_InitStruct = {0};
        GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
        // 配置USART2
        USART_InitStruct.BaudRate = 9600;
        USART_InitStruct.WordLength = UART_WORDLENGTH_8B;
        USART_InitStruct.StopBits = UART_STOPBITS_1;
        USART_InitStruct.Parity = UART_PARITY_NONE;
        USART_InitStruct.Mode = UART_MODE_TX_RX;
        USART_InitStruct.HwFlowCtl = UART_HWCONTROL_NONE;
        USART_InitStruct.OverSampling = UART_OVERSAMPLING_16;
        HAL_UART_Init(&huart2);
    }
    

    代码解释

    1. GPIO配置:启用GPIOA和USART2的时钟,配置GPIOA2和GPIOA3为复用推挽输出模式,用于USART2的TX和RX。
    2. USART配置:设置波特率为9600,数据位为8位,停止位为1位,无奇偶校验,通信模式为发送和接收,无硬件流控制,过采样为16倍。

    实现STM32串行通信

    实现STM32的串行通信,需要初始化USART或UART模块,然后使用HAL库提供的函数进行数据的发送和接收。

    发送数据

    使用HAL_UART_Transmit函数发送数据:

    // 发送数据
    void SendData(uint8_t *pData, uint16_t Size)
    {
        HAL_UART_Transmit(&huart2, pData, Size, HAL_MAX_DELAY);
    }
    

    接收数据

    使用HAL_UART_Receive函数接收数据:

    // 接收数据
    void ReceiveData(uint8_t *pData, uint16_t Size)
    {
        HAL_UART_Receive(&huart2, pData, Size, HAL_MAX_DELAY);
    }
    

    通信示例

    以下是一个简单的STM32串行通信示例,发送和接收一个字符串:

    // 主函数
    int main(void)
    {
        // 初始化HAL库
        HAL_Init();
        
        // 配置系统时钟
        SystemClock_Config();
        
        // 初始化USART2
        MX_USART2_UART_Init();
        
        // 发送字符串
        char data[] = "Hello, STM32!";
        SendData((uint8_t *)data, strlen(data));
        
        // 接收字符串
        char receivedData[20];
        ReceiveData((uint8_t *)receivedData, sizeof(receivedData));
        
        // 无限循环
        while (1)
        {
            // 可以在此处添加更多的通信逻辑
        }
    }
    

    代码解释

    1. 初始化:调用HAL_Init初始化HAL库,SystemClock_Config配置系统时钟。
    2. 发送数据:使用SendData函数发送字符串“Hello, STM32!”。
    3. 接收数据:使用ReceiveData函数接收最多20字节的数据。
    4. 无限循环:在主循环中可以添加更多的通信逻辑,如接收数据后的处理。

    通过以上步骤,可以实现STM32微控制器的基本串行通信功能。在实际应用中,可能还需要处理中断、错误检测等更复杂的通信场景。

    串行通信实例与调试

    编写串行通信代码

    在STM32微控制器上实现串行通信,通常使用USART(通用同步/异步收发器)模块。以下是一个使用STM32 HAL库进行串行通信的代码示例:

    // 导入必要的库
    #include "stm32f1xx_hal.h"
    
    // 定义USART的实例
    USART_HandleTypeDef huart1;
    
    // 初始化USART1
    void MX_USART1_UART_Init(void)
    {
        huart1.Instance = USART1;
        huart1.Init.BaudRate = 9600;
        huart1.Init.WordLength = UART_WORDLENGTH_8B;
        huart1.Init.StopBits = UART_STOPBITS_1;
        huart1.Init.Parity = UART_PARITY_NONE;
        huart1.Init.Mode = UART_MODE_TX_RX;
        huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
        huart1.Init.OverSampling = UART_OVERSAMPLING_16;
        HAL_UART_Init(&huart1);
    }
    
    // 发送数据
    void send_data(uint8_t data)
    {
        HAL_UART_Transmit(&huart1, &data, 1, 1000);
    }
    
    // 接收数据
    void receive_data(uint8_t *data)
    {
        HAL_UART_Receive(&huart1, data, 1, 1000);
    }
    
    // 主函数
    int main(void)
    {
        // 初始化HAL库
        HAL_Init();
        // 配置时钟
        __HAL_RCC_USART1_CLK_ENABLE();
        // 初始化USART1
        MX_USART1_UART_Init();
        // 无限循环
        while (1)
        {
            uint8_t tx_data = 'A';
            send_data(tx_data);
            HAL_Delay(1000);
        }
    }
    

    代码解释

  • 初始化USART1:设置波特率、字长、停止位、校验位等参数。
  • 发送数据:使用HAL_UART_Transmit函数发送一个字节的数据。
  • 接收数据:使用HAL_UART_Receive函数接收一个字节的数据。
  • 使用串口调试助手

    串口调试助手是用于STM32串行通信调试的工具,它可以帮助你发送和接收数据,检查通信是否正常。以下是如何使用串口调试助手的步骤:

    1. 连接STM32与PC:使用USB转串口线将STM32的USART引脚与PC的串口连接。
    2. 打开串口调试助手:设置正确的串口号、波特率等参数。
    3. 发送数据:在助手的发送窗口输入数据,点击发送。
    4. 接收数据:在助手的接收窗口查看STM32发送的数据。

    STM32串行通信故障排查

    当STM32的串行通信出现问题时,以下是一些常见的故障排查步骤:

    1. 检查硬件连接:确保USART引脚正确连接,电源和地线连接无误。
    2. 检查配置参数:波特率、数据位、停止位、校验位等是否与PC端设置一致。
    3. 使用示波器检查信号:检查USART的TX和RX引脚上的信号是否正常。
    4. 检查代码逻辑:确保发送和接收函数的逻辑正确,没有死循环或错误的中断处理。

    优化串行通信性能

    为了提高STM32的串行通信性能,可以采取以下策略:

    1. 提高波特率:在硬件允许的范围内,提高波特率可以增加数据传输速度。
    2. 使用DMA传输:DMA(直接内存访问)可以减少CPU的负担,提高数据传输效率。
    3. 优化中断处理:减少中断处理时间,避免不必要的延时,可以提高通信的实时性。

    DMA传输示例

    // 导入必要的库
    #include "stm32f1xx_hal.h"
    
    // 定义USART和DMA的实例
    USART_HandleTypeDef huart1;
    DMA_HandleTypeDef hdma_usart1_rx;
    DMA_HandleTypeDef hdma_usart1_tx;
    
    // 初始化USART1和DMA
    void MX_USART1_UART_Init(void)
    {
        // 初始化USART1
        huart1.Instance = USART1;
        huart1.Init.BaudRate = 9600;
        huart1.Init.WordLength = UART_WORDLENGTH_8B;
        huart1.Init.StopBits = UART_STOPBITS_1;
        huart1.Init.Parity = UART_PARITY_NONE;
        huart1.Init.Mode = UART_MODE_TX_RX;
        huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
        huart1.Init.OverSampling = UART_OVERSAMPLING_16;
        HAL_UART_Init(&huart1);
    
        // 初始化DMA
        hdma_usart1_rx.Instance = DMA1_Channel2;
        hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_usart1_rx.Init.Mode = DMA_NORMAL;
        hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
        HAL_DMA_Init(&hdma_usart1_rx);
    
        hdma_usart1_tx.Instance = DMA1_Channel3;
        hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_usart1_tx.Init.Mode = DMA_NORMAL;
        hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
        HAL_DMA_Init(&hdma_usart1_tx);
    
        // 配置USART的DMA
        __HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx);
        __HAL_LINKDMA(&huart1, hdmatx, hdma_usart1_tx);
    }
    
    // 主函数
    int main(void)
    {
        // 初始化HAL库
        HAL_Init();
        // 配置时钟
        __HAL_RCC_USART1_CLK_ENABLE();
        // 初始化USART1和DMA
        MX_USART1_UART_Init();
        // 开启USART的DMA接收和发送
        HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE);
        HAL_UART_Transmit_DMA(&huart1, tx_buffer, BUFFER_SIZE);
        // 无限循环
        while (1)
        {
            // 在这里可以处理其他任务
        }
    }
    

    代码解释

  • 初始化DMA:配置DMA的传输方向、数据对齐方式、优先级等参数。
  • 配置USART的DMA:使用__HAL_LINKDMA函数将USART与DMA关联。
  • 开启DMA接收和发送:使用HAL_UART_Receive_DMAHAL_UART_Transmit_DMA函数启动DMA传输。
  • 通过以上步骤,你可以有效地在STM32上实现串行通信,调试通信问题,并优化通信性能。

    高级STM32串行通信技术

    DMA在串行通信中的应用

    原理

    DMA(Direct Memory Access,直接内存访问)是一种硬件技术,允许数据在内存和外设之间直接传输,而无需CPU的干预。在STM32中,DMA可以显著提高串行通信的效率,尤其是在大量数据传输时,可以避免CPU因数据处理而过度占用,从而实现更流畅的通信过程。

    内容

    配置DMA

    在STM32中使用DMA进行串行通信,首先需要配置DMA控制器。以下是一个使用DMA进行串行数据发送的例子:

    // 配置DMA通道
    void DMA_Configuration(void)
    {
        DMA_InitTypeDef DMA_InitStructure;
    
        // 使能DMA时钟
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
        // DMA通道配置
        DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; // USART数据寄存器地址
        DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Tx_Buffer; // 发送缓冲区地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; // 内存到外设
        DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; // 缓冲区大小
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不增加
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址增加
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据大小为字节
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据大小为字节
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // DMA模式为普通
        DMA_InitStructure.DMA_Priority = DMA_Priority_High; // DMA优先级为高
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 不启用内存到内存的传输
        DMA_Init(DMA1_Channel1, &DMA_InitStructure); // 初始化DMA通道1
    
        // 使能DMA通道1的传输完成中断
        DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    
        // 使能DMA通道1
        DMA_Cmd(DMA1_Channel1, ENABLE);
    }
    
    启动DMA传输

    启动DMA传输后,STM32将自动从内存中读取数据并发送到USART,无需CPU的进一步干预。

    // 启动DMA传输
    void Start_DMA_Transmission(void)
    {
        // 使能USART的DMA发送
        USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    }
    

    实现

    在实际应用中,DMA可以与USART的中断结合使用,以实现更复杂的通信协议,如在数据发送完成后自动接收响应数据。

    中断处理与串行通信

    原理

    中断是STM32处理外部事件的一种机制,当串行通信中发生特定事件(如数据接收完成)时,STM32会暂停当前执行的程序,转而执行中断服务程序,处理这些事件。中断处理可以提高系统的响应速度和实时性。

    内容

    配置USART中断

    配置USART中断,需要设置USART的中断使能,并在NVIC中配置中断优先级。

    // 配置USART中断
    void USART_Configuration(void)
    {
        USART_InitTypeDef USART_InitStructure;
    
        // 使能USART时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    
        // USART配置
        USART_InitStructure.USART_BaudRate = 9600; // 波特率
        USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 数据位长度
        USART_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位
        USART_InitStructure.USART_Parity = USART_Parity_No; // 无校验
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控制
        USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 同时使能发送和接收
        USART_Init(USART1, &USART_InitStructure); // 初始化USART1
    
        // 使能USART1的接收中断
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
        // 配置中断优先级
        NVIC_InitTypeDef NVIC_InitStructure;
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // USART1中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子优先级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
        NVIC_Init(&NVIC_InitStructure);
    }
    
    中断服务程序

    中断服务程序用于处理USART中断事件,如接收数据或发送数据。

    // USART1中断服务程序
    void USART1_IRQHandler(void)
    {
        if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
        {
            // 读取接收到的数据
            uint8_t data = USART_ReceiveData(USART1);
    
            // 处理接收到的数据
            // ...
    
            // 清除接收中断标志
            USART_ClearITPendingBit(USART1, USART_IT_RXNE);
        }
    }
    

    实现多路串行通信

    原理

    STM32支持多个USART/UART接口,可以同时与多个设备进行串行通信。通过合理配置,可以实现多路串行通信,提高系统的通信能力。

    内容

    配置多个USART

    配置多个USART接口,需要为每个接口分别配置时钟、引脚和USART参数。

    // 配置USART1和USART2
    void USART1_Configuration(void)
    {
        // 配置USART1
        // ...
    }
    
    void USART2_Configuration(void)
    {
        // 配置USART2
        // ...
    }
    
    同步通信

    在多路串行通信中,可能需要同步处理多个USART的通信,例如,等待所有USART的数据接收完成。

    // 等待所有USART的数据接收完成
    void WaitForAllUSART(void)
    {
        while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET ||
               USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET)
        {
            // 等待
        }
    }
    

    STM32串行通信与实时操作系统结合

    原理

    实时操作系统(RTOS)可以提供任务调度、中断管理、时间管理等功能,与STM32的串行通信结合,可以实现更复杂的通信任务,如在多个任务之间共享串行通信资源。

    内容

    创建RTOS任务

    在STM32上使用RTOS,首先需要创建RTOS任务,每个任务可以独立运行,处理不同的通信任务。

    // 创建RTOS任务
    void vTask1(void *pvParameters)
    {
        for(;;)
        {
            // 任务1的代码
            // ...
            vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1秒
        }
    }
    
    void vTask2(void *pvParameters)
    {
        for(;;)
        {
            // 任务2的代码
            // ...
            vTaskDelay(pdMS_TO_TICKS(500)); // 延迟0.5秒
        }
    }
    
    任务间通信

    使用RTOS的任务间通信机制,如信号量、消息队列等,可以在多个任务之间共享串行通信资源,实现更复杂的通信任务。

    // 创建信号量
    SemaphoreHandle_t xSemaphore = xSemaphoreCreateBinary();
    
    // 任务1发送数据
    void vTask1(void *pvParameters)
    {
        for(;;)
        {
            // 发送数据
            // ...
    
            // 释放信号量
            xSemaphoreGive(xSemaphore);
    
            vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1秒
        }
    }
    
    // 任务2接收数据
    void vTask2(void *pvParameters)
    {
        for(;;)
        {
            // 等待信号量
            xSemaphoreTake(xSemaphore, portMAX_DELAY);
    
            // 接收数据
            // ...
    
            vTaskDelay(pdMS_TO_TICKS(500)); // 延迟0.5秒
        }
    }
    

    通过上述配置和实现,可以充分利用STM32的硬件资源,实现高效、稳定的串行通信,同时结合RTOS,可以实现更复杂的通信任务和系统功能。

    作者:kkchenjj

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32微控制器串行通信简介及开发环境搭建指南

    发表回复