这是通过串口通信发送过来的数据,里面包括了故障码,电压,电流,频率等信息,请你用STM32f103系列单片机的串口1读取该数据并解析出电压和电流是多少?
要用STM32F103系列单片机的串口1读取并解析发电机上的逆变器发送的数据,你需要编写代码来完成以下步骤:
- 初始化串口:配置STM32的USART1以正确的波特率和参数读取数据。
- 读取数据:从串口缓冲区读取完整的数据包。
- 解析数据:从数据包中提取电压和电流信息。
假设数据格式如下(每个字节的数据如图所示):
- 起始位:字节0
- 数据长度:字节1
- 故障码、电压、电流、频率等信息从字节2开始
根据图片中的数据:
- 电压信息在字节2和字节3
- 电流信息在字节4和字节5
以下是一个示例代码,用于在STM32F103系列单片机上实现上述步骤:
#include "stm32f10x.h"
#include "usart.h" // 包含USART初始化配置文件
#define DATA_LENGTH 31 // 假设总共要读取31个字节的数据
uint8_t received_data[DATA_LENGTH]; // 接收数据的缓冲区
void USART1_IRQHandler(void) {
static uint8_t i = 0;
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
received_data[i++] = USART_ReceiveData(USART1);
if (i >= DATA_LENGTH) {
i = 0; // 重置接收计数器
}
}
}
void init_USART1(uint32_t baudrate) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置USART1 Tx (PA.09) 作为推挽复用功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1 Rx (PA.10) 作为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// USART参数配置
USART_InitStructure.USART_BaudRate = baudrate;
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_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 配置USART1中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能USART
USART_Cmd(USART1, ENABLE);
}
int main(void) {
uint16_t voltage = 0;
uint16_t current = 0;
// 初始化USART1,波特率设置为9600(根据实际情况调整)
init_USART1(9600);
while (1) {
// 检查数据包是否完整接收
if (received_data[1] == 28) { // 确保接收的数据长度正确
// 解析电压和电流数据
voltage = (received_data[2] << 8) | received_data[3];
current = (received_data[4] << 8) | received_data[5];
// 在这里可以对解析出的电压和电流进行处理,例如显示或记录
// 注意:电压和电流的单位以及是否需要转换取决于发送方的数据格式定义
}
}
}
注意事项:
- 波特率设置:根据实际设备的波特率设置
USART_InitStructure.USART_BaudRate
。 - 数据长度检查:确保接收的数据长度正确。
- 电压和电流解析:根据具体协议,确定数据的字节顺序和单位。
进一步优化:
- 数据验证:增加数据包头和校验码的验证,以确保数据的准确性。
- 错误处理:处理接收过程中可能出现的错误,如丢包或数据损坏。
为了增加数据包头和校验码的验证
,以确保数据的准确性,你可以对接收到的数据包进行更严格的检查和处理。以下是增加数据包头和校验码验证的详细步骤和示例代码。
数据包结构假设
假设你的数据包结构如下:
- 起始位:字节0(例如,固定为0xAA)
- 数据长度:字节1(例如,固定为28)
- 数据内容:字节2到字节29
- 校验码:字节30(例如,使用简单的累加和校验)
校验码计算方法
假设校验码是所有数据字节(包括起始位和数据长度)的累加和的低8位。
示例代码
以下代码展示了如何在STM32F103单片机上实现数据包头和校验码的验证:
#include "stm32f10x.h"
#include "usart.h"
#define DATA_LENGTH 31 // 数据包总长度
uint8_t received_data[DATA_LENGTH]; // 接收数据的缓冲区
// 计算校验码
uint8_t calculate_checksum(uint8_t *data, uint8_t length) {
uint8_t checksum = 0;
for (uint8_t i = 0; i < length; i++) {
checksum += data[i];
}
return checksum;
}
// USART1中断服务程序
void USART1_IRQHandler(void) {
static uint8_t i = 0;
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
received_data[i++] = USART_ReceiveData(USART1);
if (i >= DATA_LENGTH) {
i = 0; // 重置接收计数器
}
}
}
void init_USART1(uint32_t baudrate) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置USART1 Tx (PA.09) 作为推挽复用功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1 Rx (PA.10) 作为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// USART参数配置
USART_InitStructure.USART_BaudRate = baudrate;
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_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 配置USART1中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能USART
USART_Cmd(USART1, ENABLE);
}
int main(void) {
uint16_t voltage = 0;
uint16_t current = 0;
// 初始化USART1,波特率设置为9600(根据实际情况调整)
init_USART1(9600);
while (1) {
// 检查数据包是否完整接收
if (received_data[1] == 28) { // 确保接收的数据长度正确
// 验证数据包头和校验码
if (received_data[0] == 0xAA) { // 验证起始位
uint8_t checksum = calculate_checksum(received_data, DATA_LENGTH - 1);
if (checksum == received_data[30]) { // 验证校验码
// 解析电压和电流数据
voltage = (received_data[2] << 8) | received_data[3];
current = (received_data[4] << 8) | received_data[5];
// 在这里可以对解析出的电压和电流进行处理,例如显示或记录
// 注意:电压和电流的单位以及是否需要转换取决于发送方的数据格式定义
}
}
}
}
}
代码解释
-
校验码计算:
- 函数
calculate_checksum
用于计算接收到的数据包的校验码。
- 函数
-
USART1中断服务程序:
- 在
USART1_IRQHandler
中,当接收到新字节时,将其存入接收缓冲区received_data
中。
- 在
-
主循环:
- 检查接收的数据长度是否正确。
- 验证起始位(假设为0xAA)。
- 计算并验证校验码。
- 解析并处理电压和电流数据。
注意事项
- 波特率设置:根据实际设备的波特率调整
USART_InitStructure.USART_BaudRate
。 - 校验和验证:根据实际应用选择合适的校验算法,如CRC校验等。
- 数据解析:根据实际数据格式调整解析代码。
为了处理接收过程中可能出现的错误
,如丢包或数据损坏,你可以增加一些错误处理机制,包括超时处理、错误计数、数据重发请求等。以下是具体的步骤和代码示例。
增加错误处理机制
- 超时处理:设置接收数据的超时机制,如果在预定时间内没有接收到完整的数据包,则认为接收超时。
- 错误计数:记录接收错误的次数,当错误次数超过一定阈值时,采取相应的措施。
- 数据包头和校验码验证:在接收数据时,严格验证数据包头和校验码,确保数据完整性。
- 丢包重发机制:如果检测到数据包错误,可以请求发送方重新发送数据。
示例代码
以下是一个包含错误处理机制的示例代码:
#include "stm32f10x.h"
#include "usart.h"
#include <stdbool.h>
#include <string.h>
#define DATA_LENGTH 31 // 数据包总长度
#define TIMEOUT_THRESHOLD 1000 // 超时时间阈值
#define ERROR_THRESHOLD 5 // 最大允许错误次数
uint8_t received_data[DATA_LENGTH]; // 接收数据的缓冲区
uint8_t data_index = 0; // 当前接收数据的索引
uint32_t timeout_counter = 0; // 超时计数器
uint8_t error_count = 0; // 错误计数器
// 计算校验码
uint8_t calculate_checksum(uint8_t *data, uint8_t length) {
uint8_t checksum = 0;
for (uint8_t i = 0; i < length; i++) {
checksum += data[i];
}
return checksum;
}
// USART1中断服务程序
void USART1_IRQHandler(void) {
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
uint8_t received_byte = USART_ReceiveData(USART1);
received_data[data_index++] = received_byte;
timeout_counter = 0; // 重置超时计数器
if (data_index >= DATA_LENGTH) {
data_index = 0; // 重置接收计数器
}
}
}
void init_USART1(uint32_t baudrate) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置USART1 Tx (PA.09) 作为推挽复用功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1 Rx (PA.10) 作为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// USART参数配置
USART_InitStructure.USART_BaudRate = baudrate;
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_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 配置USART1中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能USART
USART_Cmd(USART1, ENABLE);
}
int main(void) {
uint16_t voltage = 0;
uint16_t current = 0;
// 初始化USART1,波特率设置为9600(根据实际情况调整)
init_USART1(9600);
while (1) {
// 超时处理
if (timeout_counter++ > TIMEOUT_THRESHOLD) {
timeout_counter = 0;
data_index = 0; // 重置接收索引
error_count++;
if (error_count >= ERROR_THRESHOLD) {
// 错误处理逻辑,例如重启或告警
// 这里简单地重置错误计数器
error_count = 0;
}
}
// 检查数据包是否完整接收
if (data_index == DATA_LENGTH) {
// 验证数据包头和校验码
if (received_data[0] == 0xAA && received_data[1] == 28) { // 验证起始位和长度
uint8_t checksum = calculate_checksum(received_data, DATA_LENGTH - 1);
if (checksum == received_data[30]) { // 验证校验码
// 解析电压和电流数据
voltage = (received_data[2] << 8) | received_data[3];
current = (received_data[4] << 8) | received_data[5];
// 在这里可以对解析出的电压和电流进行处理,例如显示或记录
// 注意:电压和电流的单位以及是否需要转换取决于发送方的数据格式定义
// 数据处理成功,重置错误计数器
error_count = 0;
} else {
// 校验码错误
error_count++;
}
} else {
// 数据包头错误
error_count++;
}
// 重置接收索引
data_index = 0;
}
}
}
代码解释
-
超时处理:
- 通过
timeout_counter
计数器记录接收数据的超时时间。 - 如果在预定时间内(
TIMEOUT_THRESHOLD
)没有接收到完整的数据包,则认为接收超时,重置接收索引并增加错误计数器。
- 通过
-
错误计数:
- 通过
error_count
记录接收错误的次数,当错误次数超过ERROR_THRESHOLD
时,可以采取相应的措施,比如重启或告警。 - 在本示例中,错误计数器简单地重置。
- 通过
-
数据包头和校验码验证:
- 检查接收到的数据包长度是否正确。
- 验证起始位和数据长度。
- 计算校验码并与接收到的校验码比较。
- 如果数据包头或校验码错误,增加错误计数器。
-
丢包重发机制:
- 这个示例中没有具体实现丢包重发机制,但可以通过串口发送请求重新发送数据包的命令来实现。
- 如果检测到数据包错误,可以发送请求给发电机上的逆变器,要求重新发送数据。
注意事项
- 实际应用中,错误处理策略需要根据具体需求进行调整,例如记录错误日志、发送告警信息或重新初始化串口等。
- 校验码计算方法可以根据实际应用进行调整,如使用CRC校验等。
- 丢包重发机制可以通过串口发送指令来实现,需要和逆变器的通信协议相匹配。
为了提高通信的可靠性可以增加握手协议
,确保双方在传输数据之前已经准备好,并且可以用于初始化一些必要的参数。
以下是一个简单的握手协议示例:
- 发送握手请求:STM32单片机发送握手请求到发电机上的逆变器。
- 等待握手响应:STM32单片机等待逆变器的握手响应。
- 验证握手响应:STM32单片机验证握手响应是否正确。如果正确,则继续进行数据传输;否则,重新发送握手请求或进行错误处理。
示例代码
以下是一个示例代码,展示了如何在STM32F103单片机上实现握手协议:
#include "stm32f10x.h"
#include "usart.h"
#include <stdbool.h>
#include <string.h>
#define DATA_LENGTH 31 // 数据包总长度
#define TIMEOUT_THRESHOLD 1000 // 超时时间阈值
#define ERROR_THRESHOLD 5 // 最大允许错误次数
#define HANDSHAKE_REQUEST 0x55 // 握手请求命令
#define HANDSHAKE_RESPONSE 0xAA // 握手响应命令
uint8_t received_data[DATA_LENGTH]; // 接收数据的缓冲区
uint8_t data_index = 0; // 当前接收数据的索引
uint32_t timeout_counter = 0; // 超时计数器
uint8_t error_count = 0; // 错误计数器
// 计算校验码
uint8_t calculate_checksum(uint8_t *data, uint8_t length) {
uint8_t checksum = 0;
for (uint8_t i = 0; i < length; i++) {
checksum += data[i];
}
return checksum;
}
// USART1中断服务程序
void USART1_IRQHandler(void) {
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
uint8_t received_byte = USART_ReceiveData(USART1);
received_data[data_index++] = received_byte;
timeout_counter = 0; // 重置超时计数器
if (data_index >= DATA_LENGTH) {
data_index = 0; // 重置接收计数器
}
}
}
void init_USART1(uint32_t baudrate) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置USART1 Tx (PA.09) 作为推挽复用功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1 Rx (PA.10) 作为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// USART参数配置
USART_InitStructure.USART_BaudRate = baudrate;
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_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 配置USART1中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能USART
USART_Cmd(USART1, ENABLE);
}
bool handshake(void) {
uint32_t handshake_timeout = 0;
// 发送握手请求
USART_SendData(USART1, HANDSHAKE_REQUEST);
// 等待握手响应
while (handshake_timeout++ < TIMEOUT_THRESHOLD) {
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
uint8_t response = USART_ReceiveData(USART1);
if (response == HANDSHAKE_RESPONSE) {
return true; // 握手成功
}
}
}
return false; // 握手失败
}
int main(void) {
uint16_t voltage = 0;
uint16_t current = 0;
// 初始化USART1,波特率设置为9600(根据实际情况调整)
init_USART1(9600);
// 进行握手
while (!handshake()) {
// 握手失败,重新尝试
}
while (1) {
// 超时处理
if (timeout_counter++ > TIMEOUT_THRESHOLD) {
timeout_counter = 0;
data_index = 0; // 重置接收索引
error_count++;
if (error_count >= ERROR_THRESHOLD) {
// 错误处理逻辑,例如重启或告警
// 这里简单地重置错误计数器
error_count = 0;
}
}
// 检查数据包是否完整接收
if (data_index == DATA_LENGTH) {
// 验证数据包头和校验码
if (received_data[0] == 0xAA