引言
在嵌入式系统中,串口通信是一种常见且重要的通信方式,广泛应用于各种嵌入式设备和系统中。串口通信通过串行传输数据,能够在不同设备之间可靠地传输数据,具有简单、稳定、成本低廉等优点,因此在许多嵌入式应用中得到了广泛应用。
重要性和应用场景
-
设备互联: 串口通信可以实现不同设备之间的数据交换和通信,如传感器与控制器之间的数据传输、嵌入式设备与外部设备的连接等。
-
调试和监控: 通过串口通信,可以实现对嵌入式系统的调试和监控,方便开发人员进行系统调试、数据采集和监测等工作。
-
远程控制: 串口通信还可以实现对嵌入式系统的远程控制,通过串口发送控制指令,实现对远程设备的控制和操作,如智能家居控制、远程数据采集等。
本文内容概述
本文将重点讨论STM32F407微控制器中的串口通信模块(USART)的配置和使用。主要内容包括:
-
STM32F407的USART模块简介:介绍USART模块的功能和特点,以及在嵌入式系统中的应用场景。
-
硬件连接和配置:详细说明如何将USART模块连接到外部设备,并配置串口的时钟、工作模式、中断和DMA等参数。
-
开发涉及工具:列举开发串口通信所需的工具,并简要介绍它们的作用。
-
配置串口:讲解如何配置串口的I/O、参数属性、中断等。
-
封装串口配置库文件:分步骤创建头文件和源文件,以便封装串口配置为可重用的库文件。
-
功能实现:根据需求,实现串口收发数据的功能,并展示相关代码和实现细节。
STM32F429的USART模块简介
STM32F407微控制器中的USART(Universal Synchronous Asynchronous Receiver Transmitter)模块是一种功能强大的串口通信模块,具有以下特点和功能:
1. 通用性
USART模块是一种通用的串口通信模块,支持异步串口通信(UART)和同步串口通信(SPI、I2S等),可以满足不同通信需求。
2. 通信协议支持
USART模块支持多种通信协议,包括标准的异步串口通信协议(UART)、同步串口通信协议(SPI、I2S)以及LIN(Local Interconnect Network)等。
3. 外设支持
USART模块可以与多种外设进行通信,如传感器、GPS模块、蓝牙模块、WiFi模块、GSM模块等,实现数据的收发和控制指令的交互。
4. DMA支持
USART模块支持DMA(Direct Memory Access)控制器,可以通过DMA实现高效地数据传输,减少CPU的负载,提高系统性能。
5. 中断支持
USART模块支持中断机制,可以通过中断来处理接收和发送数据,提高系统的实时性和响应性。
6. 高可靠性
USART模块具有数据校验功能,可以通过校验位、奇偶校验和停止位等方式保证数据的可靠性和完整性。
7. 多功能性
USART模块具有丰富的功能特性,如自动波特率检测、多主机通信支持、多帧格式支持等,可以满足复杂通信场景的需求。
硬件连接和配置步骤
1. 引脚连接
将STM32F407微控制器的USART模块的引脚连接到外部设备上:
- TX引脚连接至外部设备的RX引脚,用于发送数据。
- RX引脚连接至外部设备的TX引脚,用于接收数据。
- 如需使用硬件流控制功能,可连接CTS和RTS引脚。
2. 时钟配置
配置USART模块所需的时钟源和时钟频率:
- 使用RCC(Reset and Clock Control)模块配置USART的时钟源和时钟分频系数,以满足通信波特率要求。
3. 工作模式配置
配置USART模块的工作模式和参数:
- 设置USART模块的工作模式,包括异步模式(UART)或同步模式(SPI、I2S)。
- 配置数据位长度、停止位数、奇偶校验位等参数。
4. 中断配置
配置USART模块的中断使能和中断优先级:
- 通过NVIC(Nested Vectored Interrupt Controller)模块使能USART接收和发送中断,并设置中断优先级。
5. DMA配置(可选)
配置USART模块的DMA支持:
- 使用DMA控制器配置USART的接收和发送DMA通道,实现数据的高效传输。
6. 初始化USART模块
最后,根据配置的参数初始化USART模块,启动USART通信功能。
基本的串口收发数据流程
初始化串口
- 配置串口的工作模式、波特率、数据位长度、停止位数、奇偶校验位等参数。
- 启动串口功能,使能发送和接收功能。
发送数据
- 准备要发送的数据,可以是单个字节或数据包。
- 将数据写入串口发送缓冲区。
- 等待发送完成,可以通过轮询或中断方式检查发送完成状态。
接收数据
- 监听串口接收缓冲区,等待接收到数据。
- 一旦接收到数据,将数据从接收缓冲区读取出来。
- 对接收到的数据进行处理,例如解析数据包或执行相应操作。
// 初始化串口
init_uart();
while (1) {
// 发送数据
uint8_t data_to_send = generate_data();
send_data(data_to_send);
// 接收数据
if (uart_has_data()) {
uint8_t received_data = read_received_data();
process_received_data(received_data);
}
}
在实际应用中,可以根据具体的需求和硬件平台选择合适的API函数或库来实现串口的初始化、发送和接收功能。
开发涉及工具
-
开发板:
- 开发板是用于开发和测试嵌入式系统的硬件平台,通常包含了处理器、内存、各种接口以及调试功能。
- 它提供了一个环境,使得开发者可以在上面运行和调试他们的程序。
-
串口调试助手:
- 串口调试助手是一种软件工具,用于监视和调试串口通信。
- 它可以显示发送和接收的数据,支持实时监视、数据记录、发送指令等功能,帮助开发者验证串口通信的正确性。
-
逻辑分析仪:
- 逻辑分析仪是一种测试工具,用于捕获和分析数字信号。
- 它可以帮助开发者观察串口通信的信号波形、时序关系,用于调试通信问题和优化性能。
-
示波器:
- 示波器是一种用于显示电信号波形的仪器,可用于调试串口通信中的信号质量和时序问题。
- 它可以帮助开发者观察串口信号的波形、幅度、频率等特性,识别和解决通信中的问题。
-
开发工具链:
- 包括编译器、调试器、下载工具等,用于编写、编译、调试和下载嵌入式软件到目标设备。
- 这些工具支持开发者编写和调试串口通信相关的程序,确保程序的正确性和稳定性。
配置串口
包含头文件和定义常量
#include "stm32F4xx.h"
#include "stm32f4xx_conf.h"
#include "stdio.h"
#include "led.h"
#include "delay.h"
#include "key.h"
初始化串口硬件
int main(void)
{
GPIO_InitTypeDef gpio_info;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
LED_init();
key1_init();
key2_init();
Delay_init();
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH,ENABLE);
gpio_info.GPIO_Mode = GPIO_Mode_AF;
gpio_info.GPIO_OType = GPIO_OType_PP;
gpio_info.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
gpio_info.GPIO_PuPd = GPIO_PuPd_UP;
gpio_info.GPIO_Speed = GPIO_Low_Speed;
GPIO_Init(GPIOA,&gpio_info);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART1,ENABLE);
}
}
这段代码是用于初始化STM32的USART1串口,并配置为115200波特率,无硬件流控,无奇偶校验,1个停止位,8位数据位的通信模式。让我逐步解释:
-
GPIO初始化:
- 初始化了GPIOH,配置为复用功能(AF)。
- 设置了引脚9和引脚10为GPIO的AF功能。
-
时钟使能:
- 使能了GPIOA的时钟,因为USART1的引脚连接到GPIOA。
-
GPIO引脚复用配置:
- 使用
GPIO_PinAFConfig()
函数将引脚9和引脚10配置为USART1的复用功能。
- 使用
-
USART时钟使能:
- 使能了USART1的时钟。
-
USART初始化:
- 配置了USART1的参数,包括波特率、硬件流控、模式(接收和发送)、奇偶校验、停止位和数据位长度。
-
USART中断配置:
- 使能了USART1的接收中断(USART_IT_RXNE),这表示当接收缓冲区非空时将产生中断。
- 配置了USART1的中断优先级和使能中断。
-
使能USART:
- 最后,使能了USART1,启动串口通信。
封装串口配置库文件
第一步:创建头文件
首先,我们需要创建一个头文件,其中包含函数声明和必要的宏定义。创建一个名为 uart_config.h
的文件,并在其中添加以下内容:
#ifndef UART_CONFIG_H
#define UART_CONFIG_H
#include <stdint.h>
// 函数声明
void init_uart();
void set_uart_params();
void init_uart_interrupt();
void uart_receive_isr();
#endif /* UART_CONFIG_H */
第二步:创建源文件
接下来,我们创建一个源文件,命名为 uart_config.c
,并实现头文件中声明的函数。这里以上提供的代码为基础,稍作修改后进行封装。假设我们把代码放在 Src
文件夹下。
#include "uart_config.h"
#include "stm32f4xx.h" // 假设你在使用STM32F4系列的微控制器
void init_uart() {
GPIO_InitTypeDef gpio_info;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 初始化串口硬件
// 这里省略具体实现,根据具体硬件平台完成初始化操作
// 设置串口参数
set_uart_params();
}
void set_uart_params() {
// 设置波特率等参数
// 省略具体设置,根据需求来设置
}
void init_uart_interrupt() {
// 配置串口接收中断使能
// 根据硬件手册和需求来配置相应的寄存器
// 注册串口接收中断服务函数
// 这一步根据具体的编程环境和操作系统来完成
}
void uart_receive_isr() {
// 在这里处理串口接收中断
// 省略具体实现
}
第三步:使用库文件
#include "uart_config.h"
int main() {
// 初始化串口
init_uart();
// 以下为主程序逻辑
// 可以在这里编写串口通信相关的代码
return 0;
}
功能实现
1. 创建工程
首先,在STM32CubeIDE中创建一个新的STM32工程,并选择对应的芯片型号。
2. 配置串口
使用CubeMX配置串口。在Pinout & Configuration选项卡中配置USART1,并启用其收发功能。确保选择正确的引脚,并设置波特率为115200。
3. 生成代码
生成代码并打开工程。
4. 编写代码
在main.c
文件中编写串口收发数据的代码:
#include "main.h"
#include "stm32f4xx_hal.h"
UART_HandleTypeDef huart1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
uint8_t tx_data[] = "Hello, World!\n";
uint8_t rx_data[20];
while (1)
{
HAL_UART_Transmit(&huart1, tx_data, sizeof(tx_data), HAL_MAX_DELAY);
HAL_UART_Receive(&huart1, rx_data, sizeof(rx_data), HAL_MAX_DELAY);
HAL_Delay(1000);
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 50;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 2;
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_3) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
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;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
5. 编译并下载到STM32芯片
编译代码,并将程序下载到STM32芯片中。
6. 测试
连接串口调试助手,你应该能够看到串口发送 "Hello, World!" 字符串,并且能够收到芯片回传的数据。
实际应用案例
场景描述:
1. 系统硬件组成:
- STM32微控制器:负责控制整个系统的操作,包括串口通信和数据处理。
- 温度传感器:用于测量环境温度,并将数据通过串口发送给STM32微控制器。
- 上位机:通过串口连接到STM32微控制器,接收并处理传感器发送的温度数据,并进行可视化展示。
2. 系统工作流程:
- STM32微控制器初始化并配置串口通信功能。
- 温度传感器定期测量环境温度,并将数据通过串口发送给STM32微控制器。
- STM32微控制器接收传感器发送的温度数据,并进行必要的处理(如单位转换、数据滤波等)。
- 处理后的数据通过串口发送给上位机。
- 上位机接收并解析串口数据,将温度数据显示在界面上,可能还会进行进一步的分析和处理。
实际应用案例:
在实际场景中,我们可以将这个智能温度监控系统应用于以下场景之一:
1. 室内温度监测:
在办公室、实验室或生产车间等场所安装多个温度传感器,通过STM32微控制器将数据发送到上位机,实时监测和记录室内温度变化,以确保环境舒适度和生产安全。
2. 冷链物流监控:
在冷藏货车、冷库或冷链仓库中安装温度传感器,通过STM32微控制器将温度数据传输到上位机,实时监测货物的温度变化,确保货物处于适宜的温度范围内,防止货物腐坏或变质。
3. 智能农业:
在农田、温室或养殖场中安装温度传感器,通过STM32微控制器将温度数据发送到上位机,帮助农民监测环境温度,合理调节种植或养殖条件,提高农作物或动物的产量和品质。
4. 温度控制系统:
与空调、加热器或温室控制系统集成,实现自动调节温度的功能。STM32微控制器通过串口接收温度传感器的数据,并根据设定的温度范围控制相应的设备,实现环境温度的自动调节。
通过以上应用案例,展示了串口通信在嵌入式系统中与传感器、外部设备的数据交换的实际应用。
总结
串口通信在嵌入式开发中具有重要的地位和价值。通过串口通信,嵌入式系统可以与外部设备(如传感器、上位机等)进行可靠的数据交换和通信,实现数据采集、控制和监测等功能。本文介绍了一个智能温度监控系统的设计和应用案例,强调了串口通信在实时数据传输和系统集成中的关键作用。
学习嵌入式开发中串口通信的意义在于:
-
实时数据传输: 串口通信可以实现实时数据传输,将传感器采集到的数据快速、可靠地传输到上位机或其他设备进行处理和分析。
-
系统集成: 通过串口通信,嵌入式系统可以与各种外部设备进行连接和通信,实现系统功能的扩展和灵活性的增强。
-
应用广泛: 串口通信在各种领域和行业中都有广泛的应用,如工业控制、物联网、医疗设备等,学习串口通信能够为未来的职业发展打下坚实的基础。
-
解决实际问题: 通过串口通信,可以设计和实现各种实用的嵌入式系统,解决现实生活中的各种问题,如环境监测、智能控制、数据采集等。
因此,学习嵌入式开发中的串口通信是非常有价值的,能够为个人技能提升和职业发展带来重要的收益。