工作中常用COM调试,有时也会用到UDP通讯去调试板卡,所以就有了编写一个上位机,去方便工作的中调试工作。

打开2个串中口,可以对发。

用UDP发送HEX、ASC也可以。

一、串口
在嵌入式开发与物联网应用中,串口通信(UART)几乎是每位开发者绕不开的基础模块。它不仅应用广泛,成本低、可靠性高,更是MCU之间、MCU与外设之间互相"交流"的最常见方式。

一些常用的串口通信的设备

1、什么是串口通信?
串口通信(Serial Communication)是一种按位顺序发送数据的通信方式。相比并口通信一次传输多个位,串口只需要几根数据线,因此更适合远距离、成本敏感的设备通信。
2、UART通信的核心参数
UART 通信不需要时钟线,但通信双方必须波特率一致,并协商数据格式:
波特率:单位时间内传输的比特数(如 9600 bps)
数据位:通常为8位(可设为5~9位)
停止位:通常为1位或2位
奇偶校验位:用于错误检测(可设为无、奇、偶)
串口的数据帧格式:
起始位(1位) + 数据位(8位) + 停止位(1位)
3,串口控制器寄存器解析(以STM32为例)
下述就是串口控制器的一个结构框图,下面我也会将主要的寄存器进行一个讲解

4、串口控制器引脚功能详解(TX、RX)
1). TX(Transmit):发送引脚
定义:用于发送串口数据的引脚。
本质:TX 是一个输出引脚(GPIO 输出模式),由 MCU 主动驱动电平输出数据。
应用:将 MCU 的数据通过串口协议发送出去,连接到接收方的 RX 引脚。
注意事项:
在发送数据前需检查是否完成上一次发送(如:检查 USART_SR 的 TXE 位或 TC 位)。
TX 空闲不代表发送完成,需分辨 TXE(发送数据寄存器为空) 与 TC(发送完成)。
2). RX(Receive):接收引脚
定义:用于接收串口数据的引脚。
本质:RX 是一个输入引脚(GPIO 输入模式),接收外部设备发来的数据。
应用:用于接收其他设备通过串口发送过来的数据,接入对方的 TX 引脚。
注意事项:
在读取数据前,必须确认数据已接收完成(如:USART_SR 的 RXNE 位为 1)。
读接收寄存器(如:USART_DR)后,RXNE 自动清除,表示数据已被读取
状态寄存器(USART_SR)
TC(位6):发送完成标志
RXNE(位5):接收数据可读标志
数据寄存器(USART_DR)
读写同一寄存器,写为发送,读为接收
波特率寄存器(USART_BRR)
设置通信速率,如 9600bps
计算公式(16倍过采样):USARTDIV = fCK / (16 × 波特率)
控制寄存器1(USART_CR1)
UE:串口使能
TE:发送使能
RE:接收使能
M:字长设置(默认8位)
控制寄存器2(USART_CR2)
STOP:停止位配置(1位、2位)
c
/初始化USART1
void init(u32 baud)
{
//初始化GPIO
//打开GPIOB时钟
RCC->AHB1ENR |= (1<<0);
//配置IO口模式
GPIOA->MODER &= ~(3<<18);
GPIOA->MODER &= ~(3<<20);
GPIOA->MODER |= (2<<18);
GPIOA->MODER |= (2<<20);
//配置IO口输出类型
GPIOA->OTYPER &= ~(1<<9);
//配置IO口输出速度
GPIOA->OTYPER &= (3<<18);
//配置GPIO端口上下拉
GPIOA->PUPDR &= (3<<18);
GPIOA->PUPDR &= (3<<20);
//配置端口复位
GPIOA->AFR[1] &= ~(0xf << 4);
GPIOA->AFR[1] &= ~(0xf << 8);
GPIOA->AFR[1] |= (7<<4);
GPIOA->AFR[1] |= (7<<8);
// 配置串口初始化
//打开串口时钟
RCC->APB2ENR |= (1<<4);
//初始化波特率
USART1->BRR = 84000000 / baud;
//初始化串口控制寄存器CR1
USART1->CR1 &= ~(1<<15); // //初始化过采样模式
USART1->CR1 |= (1<<12); //初始化字长
USART1->CR1 |= (1<<3); //发送器使能
USART1->CR1 |= (1<<2); //接收器使能
//初始化串口控制寄存器CR2
USART1->CR2 &= (3<<12); //CR2中的停止位
//一般这个在控制寄存器CR1的13位,使能USART,放在串口初始化末尾
USART1->CR1 |= (1<<13);
}
串口发送一个字节函数
void send_Uart(u8 data)
{
while(!(USART1->SR & (1<< 6)));
USART1->DR = data;
}
串口接收一个字节函数
u8 rece_Uart(void)
{
u8 datab ;
while(!(USART1->SR & (1<< 5)));
datab = USART1->DR;
return datab;
}
二、UDP 通信示例代码
UDP 是一种无连接的传输层协议,适用于实时性要求高但允许丢包的场景,如视频直播和在线游戏。
UDP协议是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
既然有了保证可靠传输的TCP协议,为什么还要非可靠传输的UDP协议呢?主要的原因有两个。一是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP高。二是在许多应用中并不需要保证严格的传输可靠性,比如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。

UDP编程的服务器端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、循环接收数据,用函数recvfrom();
5、关闭网络连接;
UDP编程的客户端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置对方的IP地址和端口等属性;
5、发送数据,用函数sendto();
6、关闭网络连接;
c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
int main() {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("Failed to create socket");
return -1;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345); // 目标端口
inet_pton(AF_INET, "192.168.1.100", &server_addr.sin_addr); // 目标IP
const char *msg = "Hello, UDP!";
if (sendto(sock, msg, strlen(msg), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("Send failed");
close(sock);
return -1;
}
printf("Message sent successfully\n");
close(sock);
return 0;
}