欢迎光临
我们一直在努力

学习嵌入式第一天--串口发送及接收

目录

介绍

通信

USART

基本结构

串口硬件电路

串口帧结构

 串口发送数据

串口接收数据

串口常用寄存器

状态寄存器(USART_SR)

重映射

其他

相关代码

通用

串口接收数据

介绍

一种异步通信接口,常用于设备间的数据传输。其核心特点是逐位(bit-by-bit)传输,通过TX(发送)和RX(接收)

通信

通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统

通信协议:制定通信的规则,通信双方按照协议规则进行数据收发

注意:差分信号抗干扰能力强,适合长距离传输

USART

基本结构

时钟频率一般为72MHZ

波特率:每秒钟最多传输多少位

常用的波特率:9600、115200、921600

移位寄存器移位寄存器是数字电路中实现 “并行→串行” 或 “串行→并行” 数据转换,就是在CPU 输出的并行字节,拆解成串行比特流

串口硬件电路

简单双向串口通信有两根通信线(发送端TX和接收端RX)

TX与RX要交叉连接

当只需单向的数据传输时,可以只接一根通信线当电平标准不一致时,需要加电平转换芯片

电平标准

电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

TTL电平:+3.3V或+5V表示1,0V表示0

RS232电平:-3~-15V表示1,+3~+15V表示0

RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)

串口帧结构

流程:空闲时为高电平,在发送数据时先发送低电平(起始位)  ,高电平表示1,低电平表示0并且一个字节被拆分成二进制然后从最低位开始传输,数据位传输完成后,发送 高电平,作为一帧数据的 “结束信号”,告诉接收端 “这一帧数据已发完”(注意:二进制是从低位开始发送,不是从高位)。

其中7/8为检验位

1.奇校验 如果0-6/7有四个1那么校验位D7就变成1 变成奇数

2.偶校验  如果0-6/7有三个1那么校验位D7就变成1 变成偶数

3.无校验

并且数据位可以有8位,和9位,其中8位为默认,因为计算机和 MCU 的基本数据单位是 “字节”(1 字节 = 8 位),8 位可以完整传输一个字节的二进制数据,无需拆分或截断。9 位数据位在多机串口组网场景中非常实用,核心用途是用第 9 位作为 “地址标识位”,区分 “地址帧” 和 “数据帧”

停止位

长度可配置为 0.5 位、1 位、1.5 位或 2 位,常用的是 1 位

 串口发送数据

串口接收数据

串口常用寄存器

状态寄存器
(USART_SR)
数据寄存器(USART_DR)、波特比率寄存器
(USART_BRR)
控制寄存器 1(USART_CR1) 等,其中状态寄存器
(USART_SR)常用到串口发送

状态寄存器(USART_SR)

CTS :用于硬件流控制,当 CTS 引脚为低电平时,该位置 1,表示接收方允许发送数据;高电平时置 0,需停止发送。

TXE:当发送数据寄存器(USART_DR)中的数据被移位寄存器取走后,该位置 1,表示 “发送寄存器已空,可以写入下一个字节”,会自动清0

TC:当一帧数据(包括起始位、数据位、停止位)完全发送完毕后,该位置 1,表示 “当前字节已完整发出”。可以由软件自动清0也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序

RXNE:当接收数据寄存器(USART_DR)中有新数据时置 1,表示 “可以读取接收到的字节”,是接收中断的核心触发源。

ORE:当接收缓冲区已满但又收到新数据时置 1,表示 “数据溢出丢失”,需处理错误(如清空缓冲区、重新通信)  可使用硬件流控制解决

NE:检测到接收数据中有噪声干扰时置 1,用于判断数据是否受电磁干扰影响

FE:检测到接收数据的帧格式错误时置 1(比如停止位不是高电平),表示 “帧同步失败”

PE:当启用奇偶校验时,检测到数据校验不匹配时置 1,表示 “校验错误,数据可能损坏”

重映射

查看引脚定义表可以发现  不仅PA9和PA10有USART的引脚接口,PB6和PB7也有,如果PA9和PA10被占用,就可以使用PB6和PB7引脚使能USART。

相关代码

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);// 使能AFIO模块的时钟
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE); // USART1的重映射开启

其他

同步模式:在全双工的基础上增加CK线,用于传输时钟信号,相当于双向发送信息时每发一个数据进行一下相应时间的更新,确定单位时间从而可以把两个设备同步起来

硬件流控:在标准的基础上增加了CTS和RTS 并交叉接入,核心是接收方通过硬件引脚 “主动告诉” 发送方 ——“我处理完了,你继续发”/“我忙不过来,你先停”,解决 “发送方速率> 接收方处理能力” 导致的数据丢失(也可也是使用DMA转运,将数据存到内存缓冲器中)

相关代码

通用

初始化

//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

//GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz ;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//USART初始化
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 9600;//波特率
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制
USART_InitStruct.USART_Mode = USART_Mode_Tx;//TX发送使能 RX为接收使能
USART_InitStruct.USART_Parity = USART_Parity_No;//无校验 奇、偶、无
USART_InitStruct.USART_StopBits = 1;//停止位
USART_InitStruct.USART_WordLength = 8;//帧传输或帧接收的位数,相当于数据位
USART_Init(USART1, &USART_InitStruct );

开启USART

USART_Cmd(USART1,ENABLE);

串口接收数据

SendByte(发送字符)

void Serial_SendByte(uint16_t Byte){
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待发送数据寄存器为空
USART_SendData(USART1,Byte);//发送数据到串口
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//等待 USART1 串口当前字节完全发送完成
}

SendArray(发送数组)

数组传参时会自动退化为指针,因此Array[i]等价于*(Array+i),故可以访问数组的第i个元素

void Serial_SendArray(uint16_t *Array,uint8_t Length){
for(uint16_t i = 0;i<Length;i++){
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//等待发送数据寄存器为空
USART_SendData(USART1,Array[i]);//通过for循环依次发送数据到串口
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//等待 USART1 串口当前字节完全发送完成
}
}

SendString(发送字符串)

void Serial_SendString(char *String){
for(uint16_t i = 0;i!='\\0';i++){
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
USART_SendData(USART1,String[i]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
}
}



SendNum(发送数字)

发送数字就相当于在电脑上先转成字符然后再依次打印字符串

例如:

        123 就先要拆分成 1  2  3 ,然后使用SendByte逐一发送  

        123   

        百位—-123/10^2%10 = 1  十位  123/10^1%10 = 2  个位123/10^0%10 =3

        依次打印百位、十位、个位

        12345

        万位  12345/10^4%10 =1 千位12345/1^3%10 = 2 百位 12345/10^2%10 = 3

        十位  12345/10^1%10 = 4  个位  12345/10^0%10 = 5

        依次打印万位、千位、百位、十位、个位

//算10的n次方

uint32_t Num_Pow(uint32_t Num,uint16_t Length){
uint32_t result = 1;
for(int16_t i = 0;i<Length;i++){
result*=Num;
}
return result;
}

void Serial_SendNum(uint32_t Num,uint8_t Length){
for(int16_t i = Length;i>=0;i–){
Serial_SendByte(Num/Num_Pow(10,i)%10 + '0');//如果没有+ '0',根据ASCII表发送到串口的数据不会是正确的数据
}
}

如果想使用printf()函数

1.需要先打开魔术棒并且勾选Use MicroLIB

2.对fputc进行重定向到串口(固定流程)

由于printf()函数默认输出到屏幕,但是单片机没有屏幕,所以需要重定向,并且还要添加头文件#include<stdio.h>

int fputc(int ch,FILE *f){
Serial_SendByte((uint16_t)ch);
return ch;
}

接收

在通用配置中,要将RX和TX使能,并且还要配置PA10接收串口的引脚

USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz ;

1.通过NVIC开启中断进行接收

在开始的通用配置中添加中断再添加NVIC的初始化和中断服务函数

//开启中断
USART_ITConfig( USART1,USART_IT_RXNE, ENABLE);

//NVIC分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//·Ö×é
//NVIC初始化
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStruct);

中断服务函数

void USART1_IRQHandler(void){
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) ==SET){
uint32_t Serial_RXData = USART_ReceiveData(USART1);

USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除NXNE标志位,会自动清除,可以不写

}

2.判断RXNE是否为1,如果为1则调用ReceiveData(),读取DR寄存器

主函数

if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) ==SET){
RXData = USART_ReceiveData(USART1);
}

赞(0)
未经允许不得转载:171主机测评 » 学习嵌入式第一天--串口发送及接收
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址