当前位置: 首页 > news >正文

网站关键词优化培训jeecg 3.7 网站开发

网站关键词优化培训,jeecg 3.7 网站开发,wordpress集成dplayer,wordpress .htaccess 301重定向按我目前接触下来的感受,通信一般都是比较常用的,在设计一个项目的时候一般用到的开发板都不止一个,但是最后要将功能联合起来,那么就需要通过通信了, 而其中比较方便的就是串口通信,简单好用。 文章目录什…

按我目前接触下来的感受,通信一般都是比较常用的,在设计一个项目的时候一般用到的开发板都不止一个,但是最后要将功能联合起来,那么就需要通过通信了, 而其中比较方便的就是串口通信,简单好用。

请添加图片描述

文章目录

  • 什么是通信?
    • 通信接口
      • 双工
      • 时钟
      • 电平
      • 设备
  • 串口通信
    • 串口通信概述
    • 串口通信核心工作原理
      • 1. 物理层基础
        • TX 和 RX需要反接
      • 2. 串口参数及时序
  • STM32的 USART 外设
    • 简介
    • 硬件连接
    • 串口发送
      • 完整的串口发送代码
    • 串口接收
      • 中断服务配置
      • 获取接收内容
    • 进阶 —— 传输字符串
  • 尾声
  • 感谢大伙观看,别忘了三连支持一下
  • 大家也可以关注一下我的其它专栏,同样精彩喔~
  • 下期见咯~

什么是通信?

在硬件领域中,通信就是将一个设备的数据传送到另一个设备,扩展硬件系统。

根据不同的通信规则衍生出了很多种通信协议,根据这些通信协议,通信双方按照协议规则进行数据收发。

通信接口

在stm32f103c8t6这个最小系统板中,实现了很多种通信协议,如下表所示:

名称引脚双工时钟电平设备
USARTTX、RX全双工异步单端点对点
I2CSCL、SDA半双工同步单端多设备
SPISCLK、MOSI、MISO、CS全双工同步单端多设备
CANCAN_H、CAN_L半双工异步差分多设备
USBDP、DM半双工异步差分点对点

双工

全双工,就是指通信双方能够同时进行双向通信。一般全双工的都会有两根通信线,发送和接收互不影响

半双工,这种方式一般只有一根数据线。

单工, 例如说把 USART 的RX去掉,那它就不能实现数据的接收,那它就是一个单工的。

时钟

同步, 同步时钟能够通过时钟线来进行采样。
异步, 通过特定的采样频率来进行采样,并且需要设置帧头帧尾来进行数据的对齐。

电平

单端,引脚的高低电平是相对于GND而言的,所以通信双方需要进行共地才能进行通信。

双端, 是靠两个差分引脚的电压差来进行信号传输的。一般双端通信具有比较好的抗干扰特性传输速度

设备

点对点,就是两个设备一对一通信。

多设备, 一个设备同时对多个设备进行传输,需要一个寻址的过程,来确定接收对象。

串口通信

串口通信概述

串口通信(Serial Communication)是嵌入式系统中最常用的通信方式之一,其核心思想是通过逐位传输实现设备间的数据交互。在STM32微控制器中,UART(Universal Asynchronous Receiver/Transmitter)模块承担了异步串行通信的核心功能,具有全双工、异步、高可靠性等特点,广泛应用于传感器通信、模块调试、设备控制等场景。

串口通信核心工作原理

1. 物理层基础

电平标准:常见的有TTL电平(3.3V / 5V)和RS-232电平(±12V)

连接方式:采用三线制(TX发送、RX接收、GND地线)实现全双工通信

TX 和 RX需要反接

2. 串口参数及时序

波特率: 串口通信的速率

起始位: 标志一个数据帧的开始,固定为低电平

数据位: 数据帧的有效载荷,1为高电平,0为低电平,低位先行

校验位: 用于数据验证,根据数据位计算得来

停止位: 用于数据帧间隔,固定为高电平
在这里插入图片描述
在这里插入图片描述

再额外说一下校验位,它与数据有关.

例如说选择奇校验数据位加上校验位的 1 数量为奇数

数据位:10001110 —— 1有4个,是偶数 ——那么校验位为 1

数据位:10001100 —— 1有3个,是奇数 ——那么校验位为 0

偶校验则反之。

STM32的 USART 外设

前面所说的都是 USART协议 ,然后下面来详细说一下在stm32中的 USART外设 ,来看看在stm32中怎么去实现和使用USART来进行通讯。

简介

USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里。

自带波特率发生器,最高达4.5Mbits/s

可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)

可选校验位(无校验/奇校验/偶校验)

支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN

STM32F103C8T6 USART资源: USART1、 USART2、 USART3

在这里插入图片描述

硬件连接

这里就以 STM32 与 PC通信为例。

需要使用的硬件设备有: stm32f103c8t6最小系统板,一台电脑, st-link, USB转串口,几根杜邦线。

stm32usb转串口
PA9(TX)RX
PA10(RX)TX

也就是stm32的 USART1 的两个引脚连到 usb转串口上,记得要反接。

串口发送

  1. 使能外设时钟:开启 USART 和对应 GPIO 的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  1. 配置 GPIO 引脚:将 TX 引脚设置为复用推挽输出
	GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);	
  1. 初始化 USART 参数:设置波特率、数据位、停止位等
	/*USART初始化*/USART_InitTypeDef USART_InitStructure;					//定义结构体变量USART_InitStructure.USART_BaudRate = 9600;				//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要USART_InitStructure.USART_Mode = USART_Mode_Tx;			//模式,选择为发送模式USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位USART_Init(USART1, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART1
  1. 实现发送功能:通过查询或中断方式发送数据
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}

因为stm库函数实现了UART发送一个字符,我们直接调用就能直接实现需求,只需要等到标志位变回SET,即可。

然后我们要发送数组只需要套一层循环即可。

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for (i = 0; i < Length; i ++)		//遍历数组{Serial_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据}
}

发送字符数组:

void Serial_SendString(char *String)
{uint8_t i;for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止{Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据}
}

完整的串口发送代码

serial.c

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>/*** 函    数:串口初始化* 参    数:无* 返 回 值:无*/
void Serial_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA9引脚初始化为复用推挽输出/*USART初始化*/USART_InitTypeDef USART_InitStructure;					//定义结构体变量USART_InitStructure.USART_BaudRate = 9600;				//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要USART_InitStructure.USART_Mode = USART_Mode_Tx;			//模式,选择为发送模式USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位USART_Init(USART1, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART1/*USART使能*/USART_Cmd(USART1, ENABLE);								//使能USART1,串口开始运行
}/*** 函    数:串口发送一个字节* 参    数:Byte 要发送的一个字节* 返 回 值:无*/
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}/*** 函    数:串口发送一个数组* 参    数:Array 要发送数组的首地址* 参    数:Length 要发送数组的长度* 返 回 值:无*/
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for (i = 0; i < Length; i ++)		//遍历数组{Serial_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:串口发送一个字符串* 参    数:String 要发送字符串的首地址* 返 回 值:无*/
void Serial_SendString(char *String)
{uint8_t i;for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止{Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:次方函数(内部使用)* 返 回 值:返回值等于X的Y次方*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{uint32_t Result = 1;	//设置结果初值为1while (Y --)			//执行Y次{Result *= X;		//将X累乘到结果}return Result;
}/*** 函    数:串口发送数字* 参    数:Number 要发送的数字,范围:0~4294967295* 参    数:Length 要发送数字的长度,范围:0~10* 返 回 值:无*/
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i ++)		//根据数字长度遍历数字的每一位{Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');	//依次调用Serial_SendByte发送每位数字}
}/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量argva_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中va_end(arg);					//结束变量argSerial_SendString(String);		//串口发送字符数组(字符串)
}

serial.h

#ifndef __SERIAL_H
#define __SERIAL_H#include <stdio.h>void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);#endif

串口接收

当外部设备通过串口发送 1 个字节到 STM32 时,数据会被存入 USART 的接收数据寄存器(RDR),此时 RXNE 标志自动置 1。
若提前配置了 “允许 RXNE 中断”,则 RXNE=1 时会触发中断请求,CPU 会跳转到对应的中断服务函数执行。
在中断服务函数中,需读取 RDR 中的数据(读取后 RXNE 标志会自动清零,或手动清零),否则会持续触发中断。

中断服务配置

void Serial_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA9引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA10引脚初始化为上拉输入/*USART初始化*/USART_InitTypeDef USART_InitStructure;					//定义结构体变量USART_InitStructure.USART_BaudRate = 9600;				//波特率USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	//模式,发送模式和接收模式均选择USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位USART_Init(USART1, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART1/*中断输出配置*/USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//选择配置NVIC的USART1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设/*USART使能*/USART_Cmd(USART1, ENABLE);								//使能USART1,串口开始运行
}

在原本发送的初始化中添加NVIC配置,也就是配置中断。

下面以 USART1 为例,注意选择需要使用的中断服务函数命名

void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断{Serial_RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量Serial_RxFlag = 1;										//置接收标志位变量为1USART_ClearITPendingBit(USART1, USART_IT_RXNE);			//清除USART1的RXNE标志位//读取数据寄存器会自动清除此标志位//如果已经读取了数据寄存器,也可以不执行此代码}
}

这时候我们的接收内容就存储到了Serial_RxData 里了。

获取接收内容

下面我们只需要在主函数中读取Serial_RxData 的内容。

在Serial.c里写两个函数,分别返回Serial_RxData 和Serial_RxFlag 的值 。

uint8_t Serial_GetRxFlag(void)
{if (Serial_RxFlag == 1)			//如果标志位为1{Serial_RxFlag = 0;return 1;					//则返回1,并自动清零标志位}return 0;						//如果标志位为0,则返回0
}
uint8_t Serial_GetRxData(void)
{return Serial_RxData;			//返回接收的数据变量
}

然后再main.c里面判断 Serial_RxFlag ,为 1 既是读取到了新的内容,再将 Serial_RxData 的值传输出来即可。

	while (1){if (Serial_GetRxFlag() == 1)			//检查串口接收数据的标志位{RxData = Serial_GetRxData();		//获取串口接收的数据Serial_SendByte(RxData);			//串口将收到的数据回传回去,用于测试OLED_ShowHexNum(1, 8, RxData, 2);	//显示串口接收的数据}}

进阶 —— 传输字符串

传输字符串和单个字符的差别就在于数量,我们只需要 判定字符串头尾,然后将字符串内容都保存下来,然后传递给主函数即可。

首先,修改中断服务函数。

void USART1_IRQHandler(void)
{static uint8_t RxState = 0;		//定义表示当前状态机状态的静态变量static uint8_t pRxPacket = 0;	//定义表示当前接收数据位置的静态变量if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)	//判断是否是USART1的接收事件触发的中断{uint8_t RxData = USART_ReceiveData(USART1);			//读取数据寄存器,存放在接收的数据变量/*使用状态机的思路,依次处理数据包的不同部分*//*当前状态为0,接收数据包包头*/if (RxState == 0){if (RxData == '@' && Serial_RxFlag == 0)		//如果数据确实是包头,并且上一个数据包已处理完毕{RxState = 1;			//置下一个状态pRxPacket = 0;			//数据包的位置归零}}/*当前状态为1,接收数据包数据,同时判断是否接收到了第一个包尾*/else if (RxState == 1){if (RxData == '\r')			//如果收到第一个包尾{RxState = 2;			//置下一个状态}else						//接收到了正常的数据{Serial_RxPacket[pRxPacket] = RxData;		//将数据存入数据包数组的指定位置pRxPacket ++;			//数据包的位置自增}}/*当前状态为2,接收数据包第二个包尾*/else if (RxState == 2){if (RxData == '\n')			//如果收到第二个包尾{RxState = 0;			//状态归0Serial_RxPacket[pRxPacket] = '\0';			//将收到的字符数据包添加一个字符串结束标志Serial_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);		//清除标志位}
}

然后在 Serail.h 中加入 ——

extern char Serial_RxPacket[];
extern uint8_t Serial_RxFlag;

就是共同使用同一个变量。

同样,也要在Serial.c 中加入变量声明 ——

char Serial_RxPacket[100];				//定义接收数据包数组,数据包格式"@MSG\r\n"
uint8_t Serial_RxFlag;					//定义接收数据包标志位

尾声

OK,串口的大致内容就是这些,如果有问题,可以私信 或者 评论,我会尽我所能帮助大家,需要源代码也是同样可以 私信 或者 评论。

感谢大伙观看,别忘了三连支持一下

大家也可以关注一下我的其它专栏,同样精彩喔~

下期见咯~

请添加图片描述

http://www.dtcms.com/a/410727.html

相关文章:

  • 右键菜单增强工具,自定义系统功能
  • 图像分类入门:从数据到模型的深度学习核心知识解析
  • 攻防世界-Web-PHP2
  • Windows系统Web UI自动化测试学习系列3--浏览器驱动下载使用
  • 00-为什么要系统学习正则表达式?
  • 湖北网站建设检修金融股票类app网站开发
  • C++ 序列容器深度解析:vector、deque 与 list
  • 提供企业网站建设上海公司注册一网通办
  • 高效的技术支持提升用户体验
  • 满山红网站建设做家装的网站有什么
  • 建设部网站社保联网小程序注册平台
  • Mysql中GROUP_CONCAT分组聚合函数的使用以及示例
  • 2025无人机林业行业场景解决方案
  • 化肥网站模板青岛建设集团 招聘信息网站
  • 【在Ubuntu 24.04.2 LTS上安装Qt 6.9.2】
  • 家居企业网站建设渠道百度如何推广广告
  • 《MLB美职棒》运动员体质特征·棒球1号位
  • AI 应用和工业软件
  • 网站备案空壳网站制作找
  • 洛谷 P3388:【模板】割点(割顶)← Tarjan 算法
  • DeepSeek“问道”-第二章:问算法 —— 阴与阳如何在我内部舞蹈?
  • 重学JS-009 --- JavaScript算法与数据结构(九)Javascript 方法
  • Python项目中ModuleNotFoundError与FileNotFoundError的深度解决指南(附实战案例)
  • LeetCode:61.分割回文串
  • 坑: console.log,对象引用机制
  • 网站模板找超速云建站学校网站建设是什么意思
  • 做购物网站的业务微信公众号开发网站开发
  • Matlab通过GUI实现点云的均值滤波(附最简版)
  • 应用部署(后端)
  • 手机网站吧怎样做一个app平台