一.STM32串口通信硬件及通信协议
1.串口协议
-STM32F103串口通信协议
STM32F103微控制器的串口通信协议,该协议使用UART串口通信进行数据传输,并定义了数据帧格式、通信流程和错误处理等内容。
- 通信流程
初始化串口通信参数,包括波特率、数据位、校验位和停止位等。
发送端准备数据,将数据按照数据帧格式组织,并发送给接收端。
接收端接收数据,检查起始位、校验位和停止位的正确性。
如果数据校验正确,接收端将数据提取出来进行处理;如果数据校验错误,接收端发送错误消息给发送端。
发送端和接收端通过该协议进行连续的数据传输。
- 数据帧格式
起始位:逻辑低电平表示起始位,用于标识数据帧的开始。
数据位:8位数据位用于传输实际的数据。
奇偶校验位:用于检测数据传输过程中的错误,可以选择奇校验、偶校验或无校验。
停止位:逻辑高电平表示停止位,用于标识数据帧的结束。
- 错误处理
在数据传输过程中,可能会发生以下错误情况:
起始位错误:接收端未正确检测到起始位,导致数据解析错误。
校验位错误:接收端在校验数据时发现错误,表明数据传输过程中发生了错误。
停止位错误:接收端未正确检测到停止位,导致数据解析错误。
在出现错误时,接收端应向发送端发送错误消息,以便重新发送数据或采取其他纠错措施。
示例代码
以下是使用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库配置和使用串口通信功能
- RS-232标准
RS-232(Recommended Standard 232)是一种常用的串行通信标准,用于在计算机和外部设备之间进行数据传输。它定义了电气特性、信号传输方式和接口连接等方面的规范。
- 电气特性
RS-232标准规定了通信线路的电气特性,包括信号电平、波特率和时钟频率等。根据RS-232标准,信号电平分为正负两种,分别表示逻辑1和逻辑0。通常,正电平表示逻辑0,负电平表示逻辑1。波特率是指数据传输的速率,通常以每秒传输的位数(bps)表示。时钟频率则是指传输中使用的时钟信号频率。
- 信号传输方式
RS-232标准使用异步传输方式,即每个数据字节之间没有固定的时间间隔。数据传输以数据帧为单位,每个数据帧包括起始位、数据位、校验位和停止位。起始位用于标识数据帧的开始,停止位用于标识数据帧的结束。数据位用于传输实际的数据,校验位用于检测数据传输中的错误。
2.RS232电平与TTL电平的区别
RS232电平和TTL电平是两种常见的串行通信电平标准,它们在电气特性、电平范围和使用环境等方面存在一些区别。
-RS232电平
RS232:
电气特性:RS232电平使用正负电平表示逻辑1和逻辑0,通常正电平表示逻辑0,负电平表示逻辑1。根据RS232标准,信号电平范围在-15V至+15V之间。
电平范围:RS232电平的电压范围较大,可以在较长距离上进行可靠的数据传输。
使用环境:由于RS232电平的电压范围较大,它主要用于长距离和工业环境中的通信应用。
TTL电平:
电气特性:TTL电平使用0V和5V(或3.3V)的电平表示逻辑1和逻辑0。通常0V表示逻辑0,5V(或3.3V)表示逻辑1。
电平范围:TTL电平的电压范围较小,一般在0V至5V(或3.3V)之间。
使用环境:由于TTL电平的电压范围较小,它主要用于短距离和低功耗的通信应用,如嵌入式系统和电子设备之间的通信。
由此可见,RS232和TTL电平在电气特性、电平范围和使用环境方面存在明显的区别。根据具体的应用需求,我们可以选择适合的电平标准来进行串行通信。需要注意的是,在使用不同电平标准时,需要进行电平转换以确保正常的数据传输。
二.基于HAL库中断方式进行串口通信
1.STM32CubeMX配置
STM32CubeMX选择单片机(此处以STM32F103C8T6型号单片机为例)
设置RCC,使用外部高速时钟
设置SYS
设置USART,设置NVIC
- 创建代码
在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)
示例1
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);
};
实验结果:
三.基于HAL库实现DMA串口通信
STM32CubeMX配置
选择和上面相同的单片机,创建工程
配置RCC
配置USART1
添加两个通信信道
程序1
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模式接收
程序2
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 */
实验结果
四.实验总结
通过本次实验,我学会了选择合适的串口通信协议,即根据应用需求选择合适的串口通信协议,如UART、USART、SPI、I2C等,每种协议都有其特点和适用场景,需要根据具体情况进行选择,复习了一些基础操作,如配置串口通信参数:在实验中,需要正确配置串口的波特率、数据位、停止位、校验位等通信参数、中断或DMA配置:根据实验需求,选择中断方式或DMA方式进行数据的接收和发送、数据处理和拆包:在接收数据时,根据通信协议和数据格式进行正确的数据处理和拆包。根据实际应用需求,可以使用缓冲区或队列来存储接收到的数据,以便进行后续的处理和解析。
总体来说,基于中断/DMA方式的串口通信实验需要根据具体应用需求选择合适的通信协议和方式,并正确配置参数和处理数据。通过合理的配置和优化,可以提高数据传输的效率和可靠性,满足实际应用需求。