STM32是一款基于ARM Cortex-M处理器的32位微控制器系列,具有丰富的外设和强大的性能,广泛应用于嵌入式系统开发。串口通信是嵌入式系统中常用的通信方式之一,可以实现与外部设备的数据交互。
本实验旨在介绍如何在STM32微控制器上使用中断和DMA(直接内存访问)方式进行串口通信。中断方式通过使能串口接收中断,当接收到数据时,通过中断服务函数进行数据处理。DMA方式则通过配置DMA通道,实现数据的直接传输,减轻CPU的负担,提高通信效率。
在本实验中,我将使用STM32的内部串口模块和相关外设,通过中断和DMA方式实现串口通信。首先,我将介绍STM32串口模块的配置和初始化。然后,我们将详细讲解中断方式和DMA方式的实现步骤和原理,并给出相应的代码示例。最后,我们将进行实验验证,通过发送和接收数据,检验通信功能的正确性和稳定性。
通过完成本实验,够掌握STM32中断和DMA方式的串口通信原理和实现方法,为后续的嵌入式系统开发奠定基础。同时,也将深入了解STM32的外设配置和使用,提升对嵌入式系统的理解和实践能力。
STM32F103微控制器的串口通信协议,该协议使用UART串口通信进行数据传输,并定义了数据帧格式、通信流程和错误处理等内容。
数据帧采用以下格式进行传输:
起始位 | 数据位 | 奇偶校验位 | 停止位 |
---|---|---|---|
1位 | 8位 | 1位 | 1位 |
在数据传输过程中,可能会发生以下错误情况:
以下是使用STM32Cube HAL库配置STM32F103串口通信的示例代码:
// 初始化串口通信
void UART_Init()
{
huart.Instance = USART1;
huart.Init.BaudRate = 9600;
huart.Init.WordLength = UART_WORDLENGTH_8B;
huart.Init.StopBits = UART_STOPBITS_1;
huart.Init.Parity = UART_PARITY_NONE;
huart.Init.Mode = UART_MODE_TX_RX;
huart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart);
}
// 发送数据
void UART_SendData(uint8_t* data, uint16_t length)
{
HAL_UART_Transmit(&huart, data, length, HAL_MAX_DELAY);
}
// 接收数据
void UART_ReceiveData(uint8_t* data, uint16_t length)
{
HAL_UART_Receive(&huart, data, length, HAL_MAX_DELAY);
}
以上代码演示了如何使用STM32Cube HAL库配置和使用串口通信功能。你可以根据具体的需求进行修改和扩展。
以上就是基于STM32F103的串口通信协议的介绍,通过该协议可以实现可靠的数据传输和错误处理。你可以根据实际应用需求进行相应的调整和扩展。
RS-232(Recommended Standard 232)是一种常用的串行通信标准,用于在计算机和外部设备之间进行数据传输。它定义了电气特性、信号传输方式和接口连接等方面的规范。
RS-232标准规定了通信线路的电气特性,包括信号电平、波特率和时钟频率等。根据RS-232标准,信号电平分为正负两种,分别表示逻辑1和逻辑0。通常,正电平表示逻辑0,负电平表示逻辑1。波特率是指数据传输的速率,通常以每秒传输的位数(bps)表示。时钟频率则是指传输中使用的时钟信号频率。
RS-232标准使用异步传输方式,即每个数据字节之间没有固定的时间间隔。数据传输以数据帧为单位,每个数据帧包括起始位、数据位、校验位和停止位。起始位用于标识数据帧的开始,停止位用于标识数据帧的结束。数据位用于传输实际的数据,校验位用于检测数据传输中的错误。
RS-232标准规定了连接计算机和外部设备的接口连接方式和引脚定义。一般来说,RS-232标准使用9针或25针的D型插座连接器。其中,9针的连接器常用于较新的设备,而25针的连接器则常用于较旧的设备。接口连接的引脚定义包括数据线、控制线和地线等。
9线连接示意图:
由于其广泛的应用和成熟的技术,RS-232标准在许多领域中仍然被广泛使用。它常用于计算机与打印机、调制解调器、终端设备和工业自动化设备等外部设备之间的通信。尽管现在有许多新的通信标准和接口出现,但RS-232标准仍然在许多传统应用和设备中发挥着重要作用。
以上就是RS-232标准的介绍,该标准定义了串行通信的电气特性、信号传输方式和接口连接等方面的规范。通过了解RS-232标准,我们可以更好地理解和应用串行通信技术。
RS232电平和TTL电平是两种常见的串行通信电平标准,它们在电气特性、电平范围和使用环境等方面存在一些区别。
特性 | RS232电平 | TTL电平 |
---|---|---|
电气特性 | 正负电平 | 0V和5V(或3.3V) |
电平范围 | -15V至+15V | 0V至5V(或3.3V) |
适用环境 | 长距离和工业环境 | 短距离和低功耗环境 |
RS232和TTL电平在电气特性、电平范围和使用环境方面存在明显的区别。根据具体的应用需求,我们可以选择适合的电平标准来进行串行通信。需要注意的是,在使用不同电平标准时,需要进行电平转换以确保正常的数据传输。
STM32CubeMX选择单片机(此处以STM32F103C8T6型号单片机为例)
设置RCC,使用外部高速时钟
设置SYS
设置USART(波特率,校验位等参数)
设置NVIC,使USART使能
创建代码
在main函数前面定义全局变量
char c;//指令 0:停止 1:开始
char message[]="hello Windows\n";//输出信息
char tips[]="CommandError\n";//提示1
char tips1[]="Start.....\n";//提示2
char tips2[]="Stop......\n";//提示3
int flag=0;//标志 0:停止发送 1.开始发送
在main函数中设置接收中断
函数:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
函数功能
该函数用于通过中断方式接收一定长度的数据。当接收到指定长度的数据或发生错误时,会触发相应的中断回调函数。
参数
huart
: UART_HandleTypeDef类型的指针,表示UART控制器的句柄。pData
: uint8_t类型的指针,表示接收数据的缓冲区指针。Size
: uint16_t类型的参数,表示要接收的数据的长度。说明
该函数会启动UART接收,并启用接收完成中断。当接收到指定长度的数据时,会触发UART_RX_CPLT_Callback()
回调函数,用户可以在回调函数中对接收到的数据进行处理。
需要注意的是,为了使能接收完成中断,需要提前在中断优先级组配置中设置相应的中断优先级,以便正确处理中断。
示例
uint8_t rxBuffer[10];
UART_HandleTypeDef huart;
// 初始化UART句柄和其他相关配置
// 启动接收10个字节的数据
HAL_UART_Receive_IT(&huart, rxBuffer, 10);
// 接收完成中断回调函数
void UART_RX_CPLT_Callback(UART_HandleTypeDef *huart)
{
// 在这里处理接收到的数据
}
在while循环中添加一下代码
if(flag==1)
{
//发送信息
HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF);
//延时
HAL_Delay(1000);
};
解决方法
添加以下头文件即可
#include
在main函数下面添加以下代码:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//当输入的指令为0时,发送提示并改变flag
if(c=='0')
{
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF);
}
//当输入的指令为1时,发送提示并改变flag
else if(c=='1')
{
flag=1;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF);
}
//当输入不存在指令时,发送提示并改变flag
else
{
flag=0;
HAL_UART_Transmit(&huart1, (uint8_t *)&tips, strlen(tips),0xFFFF);
}
//重新设置中断
HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);
}
Direct Memory Access(DMA)是一种计算机系统中用于数据传输的技术。它允许数据在外设和内存之间直接进行传输,而无需CPU的干预。DMA技术可以提高数据传输的效率,减轻CPU的负担。
DMA的工作原理
DMA的工作原理如下:
DMA的优势
DMA技术相比于CPU直接处理数据传输具有以下优势:
DMA的应用领域
DMA技术广泛应用于各种领域,包括但不限于以下方面:
DMA的应用示例
下表是一些常见的DMA应用示例:
应用示例 | 描述 |
---|---|
音频播放 | 使用DMA从内存中读取音频数据,并传输给音频编解码器进行播放。 |
图像采集 | 使用DMA从图像传感器中读取图像数据,并传输到内存或显示器进行处理或显示。 |
存储器备份 | 使用DMA从一个存储设备读取数据,并将数据直接传输到另一个存储设备进行备份。 |
网络数据传输 | 使用DMA从网络接口读取数据包,并将数据包传输到内存进行处理或发送。 |
以上就是对Direct Memory Access(DMA)的简介。DMA技术可以提高数据传输的效率,减轻CPU的负担,广泛应用于各个领域。
选择和上面相同的单片机,创建工程
此处省略
配置RCC,开启外部高速时钟
配置USART1,选择异步通信
其余参数默认即可
添加两个通信信道
然后生成代码即可!
代码解释
HAL_UART_Transmit_DMA()
函数原型**
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
函数说明**
使用DMA传输UART数据,将指定长度的数据从pData缓冲区通过DMA传输到UART外设寄存器。
参数说明
返回值
uint8_t tx_buffer[] = "Hello World";
HAL_StatusTypeDef state;
state = HAL_UART_Transmit_DMA(&huart2, (uint8_t*)tx_buffer, strlen(tx_buffer));
if(state != HAL_OK) {
// error handling
}
首先定义一个字符串缓冲区,然后调用HAL_UART_Transmit_DMA函数通过DMA将缓冲区数据发送到UART外设。
HAL_UART_Receive_DMA():串口DMA模式接收
函数原型
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
函数说明
使用DMA从UART外设接收指定长度的数据,并存储到pData缓冲区。
参数说明
返回值
uint8_t rx_buffer[100];
HAL_StatusTypeDef state;
state = HAL_UART_Receive_DMA(&huart2, rx_buffer, 100);
if(state != HAL_OK) {
// error handling
}
// 在DMA完成后,rx_buffer中存储了接收到的数据
通过HAL_UART_Receive_DMA函数启动DMA,从UART外设接收100字节数据到rx_buffer缓冲区。
实验任务
STM32系统给上位机(win10)连续发送“hello windows!”;当上位机给stm32发送字符“stop”后,stm32暂停发送“hello windows!”;发送一个字符“start”后,stm32继续发送;
编写代码的思路
代码框架
uint8_t tx_buf[] = "hello windows!";
uint8_t rx_buf;
uint8_t is_sending = 1;
void UART_Send(void) {
if(is_sending == 1) {
HAL_UART_Transmit(&huart, tx_buf, sizeof(tx_buf), 1000);
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if(rx_buf == 'stop')
is_sending = 0;
else if(rx_buf == 'start')
is_sending = 1;
}
int main(void) {
while(1) {
UART_Send();
UART_Receive();
}
}
通过HAL库完成UART发送和接收,实现PC与STM32的双向通信控制。
最终完成后main函数中的代码:
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
void SystemClock_Config(void);
uint8_t flag=1;
uint8_t rx_buf[6];//接收串口数据存放的数组
int strEqual(char rcData[6],char rcData2[6])
{
for(uint8_t i = 0 ; i < 6 ; i++){
if (rcData[i] != rcData2[i]) return 0;
}
return 1;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//当输入的指令为“stop!"时,发送提示并改变flag=0
if(strEqual(rx_buf,"stop!"))
{
flag=0;
}
//当输入的指令为"start"时,发送提示并改变flag=1
else if(strEqual(rx_buf,"start"))
{
flag=1;
}
HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);
}
int main(void)
{
HAL_Init();
uint8_t message[] = "hello windows!\n"; //定义数据发送数组
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);//设置DMA接收到的数据存放在rx_buf中
while (1)
{
if(flag==1)
{
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)message, sizeof(message));
HAL_Delay(600);
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
参考文章:
链接: https://blog.csdn.net/weixin_63019977/article/details/133749827?spm=1001.2014.3001.5502
得到的波形
由图可知时序状态正确,波特率实际为0.983689Hz
在进行STM32基于中断/DMA方式的串口通信实验时,我得出了以下几点总结和经验:
总体而言,基于中断/DMA方式的串口通信实验需要根据具体应用需求选择合适的通信协议和方式,并正确配置参数和处理数据。通过合理的配置和优化,可以提高数据传输的效率和可靠性,满足实际应用需求。