STM32f407 网络接收 fpga 的 bin 文件并更新到 fpga series7(2)
简介
实验 2:在单片机搭建好 tcp 服务器后,编写传送文件的上位机。
整体实现
- 利用qt的tcpsocket简单封装
- 每次发送512字节,这样小的tcp包就不会自动分包。保证每一个512tcp包都有帧头。
- 每次发送完,等待服务器返回一个包,用来判断是否正确写入fpga
代码
tcpclient.h
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <QObject>
#include <QString>
#include <QTcpSocket>
#include <QHostAddress>
class TcpClient : public QObject {
Q_OBJECT
public:
explicit TcpClient(QObject *parent = nullptr);
Q_INVOKABLE bool connect_server(QString ip, int port);
Q_INVOKABLE void close_client();
Q_INVOKABLE bool send_buffer(char *buf, size_t len);
Q_INVOKABLE bool recv_buffer(char *pdata, uint32_t len);
private:
QTcpSocket *socket_;
};
#endif // TCPCLIENT_H
tcpclient.c
#include "tcpclient.h"
#include <QAbstractSocket>
TcpClient::TcpClient(QObject *parent) {
socket_ = new QTcpSocket(this);
}
/// @brief 连接服务器
/// @param ip
/// @param port
/// @return 成功返回true,超时返回false
bool TcpClient::connect_server(QString ip, int port) {
socket_->connectToHost(QHostAddress(ip), port);
if (socket_->waitForConnected(3000)) {
return true;
} else {
return false;
}
}
void TcpClient::close_client() { socket_->close(); }
/// @brief 发送buffer
/// @param buf
/// @param len
/// @return 成功返回true,超时返回false
bool TcpClient::send_buffer(char *buf, size_t len) {
socket_->write(buf, len);
if (socket_->waitForBytesWritten(3000)) {
return true;
} else {
return false;
}
socket_->flush();
}
/// @brief 接收buffer
/// @param pdata
/// @param len
/// @return 成功返回true,超时返回false
bool TcpClient::recv_buffer(char *pdata, uint32_t len) {
if(!socket_->waitForReadyRead(10000)){
return false;
}
socket_->read(pdata, len);
return true;
}
main.c
// 文件: main.cpp
// 时间: 2024-08-21
// 来自: ccj
// 描述: 把fpga程序发送给STM32服务器
// 1. 自定义帧结构 512字节,其中24字节帧头。剩下都是数据
// 2. 发送一个512字节的帧,要接收一个24字节的帧,里面有判断是否写入fpga成功。
// 3. 整体流程
// 3.1 发送24字节的开始命令帧
// 3.2 发送512字节的数据帧
// 3.1 发送24字节的结束命令帧
#include <stdint.h>
#include <time.h>
#include <thread>
#include <QFile>
#include <QString>
#include <QFileInfo>
#include "tcpclient.h"
#define qdebug() qDebug() << "[" << __FILE__ << ":" << __LINE__ << "]"
// 帧信息
#define kbuffer_len 512
#define kheader_size 24
#define kdata_len (kbuffer_len - kheader_size)
#define kmagic 0xaa5555aa
#define kmagic1 0x55aaaa55
// 发送帧头type字段定义
#define ksend_type_start 0xa
#define ksend_type_write 0xb
#define ksend_type_end 0xc
// 接收帧头offset定义
#define krecv_offset_success 0
#define krecv_offset_failed 1
/// @brief 帧头
struct tcp_package_header {
uint32_t magic;
uint32_t type;
uint32_t data_offset;
uint32_t data_len;
uint32_t order;
uint32_t magic1;
};
// 发送和接收缓冲区
char send_buffer[kbuffer_len];
char recv_buffer[kheader_size];
struct tcp_package_header *send_header = (struct tcp_package_header *)send_buffer;
struct tcp_package_header *recv_header = (struct tcp_package_header *)recv_buffer;
char *pdata = send_buffer + kheader_size;
// 接收校验
// 发送一个tcp包,就接收一个tcp包做校验
#define recv_verify() \
do { \
ret = client.recv_buffer(recv_buffer, kheader_size); \
if (!ret) { \
qdebug() << "recv buffer failed."; \
return 1; \
} \
switch (recv_header->data_offset) { \
case krecv_offset_success: break; \
case krecv_offset_failed: qdebug() << "write to fpga failed."; return 1; \
default: break; \
} \
} while (0)
int main(int argc, char *argv[]) {
// 0 读取命令行参数
if (argc != 4) {
qdebug() << "Usage: tcpclient {ip} {port} {file_full_path}";
return 1;
}
QString server_ip = QString::fromLocal8Bit(argv[1]);
int server_port = std::stoi(argv[2]);
QString file_full_path = QString::fromLocal8Bit(argv[3]);
// QString server_ip = "192.168.11.33";
// int server_port = 7;
// QString file_full_path = "qf_pciefmc_top.bin";
// 1 判断参数是否有效
// 1.1 判断是否可以连接服务器
TcpClient client;
bool ret = client.connect_server(server_ip, server_port);
if (!ret) {
qdebug() << "connect server failed.";
return 1;
}
// 1.2 判断文件是否存在
QFile file(file_full_path);
if (!file.open(QIODevice::ReadOnly)) {
qdebug() << "Cannot open file for reading:" << file.errorString();
return 1;
}
clock_t start, end;
start = clock();
// 2 发送文件到服务器
// 2.1 发送开始帧
std::memset(send_header, 0, sizeof(struct tcp_package_header));
send_header->magic = kmagic;
send_header->data_offset = kheader_size;
send_header->magic1 = kmagic1;
send_header->type = ksend_type_start;
ret = client.send_buffer(send_buffer, kheader_size);
if (!ret) {
qdebug() << "send buffer failed.";
return 1;
}
recv_verify();
// 2.2 循环文件发送每一个帧
QFileInfo fileinfo(file_full_path);
uint64_t total_file_size = fileinfo.size();
uint64_t total_read_size = 0;
uint64_t read_buf_size = 0;
int order = 1;
while (total_read_size < total_file_size) {
// 2.2.1 读取一个帧
read_buf_size = file.read(pdata, kdata_len);
if (read_buf_size == -1) {
qdebug() << "read file error." << file.errorString();
return 1;
}
// 2.2.2 发送帧
send_header->type = ksend_type_write;
send_header->data_len = read_buf_size;
send_header->order = order;
ret = client.send_buffer(send_buffer, kbuffer_len);
if (!ret) {
qdebug() << "send buffer failed.";
return 1;
}
recv_verify();
// 2.2.3 继续循环
total_read_size += read_buf_size;
order++;
// 打印发送进度
if (order % 5 == 0)
qdebug() << "total: " << total_file_size << ", send: " << total_read_size
<< ", process: " << (double)total_read_size / total_file_size * 100 << "%";
}
// 2.3 发送结束帧
send_header->type = ksend_type_end;
ret = client.send_buffer(send_buffer, kheader_size);
if (!ret) {
qdebug() << "send buffer failed.";
return 1;
}
recv_verify();
end = clock();
double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
qdebug() << "update success. cost time: " << cpu_time_used << "s.";
}