0x0A UART

本文的大部分内容来自B站up主 江协科技, 此文只供本人学习记录用途, 侵删

一、通信协议

  • 通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统
  • 通信协议:制定通信的规则,通信双方按照协议规则进行数据收发
  • 双工: 全双工表示能同收同发, 半双工表示同一时刻只能收或者发, 单工代表只能收或者发,(固定方向)
  • 时钟: 同步就是有根时钟线控制发送和接收的时机, 异步就是双方约定好收发的时机(如波特率), 无时钟
  • 电平: 单端需要共地才能接收信号, 差分通过差分引脚的电压差来传输信号

image-20231102091545477

二、UART通信协议

电路: 典中典, 共电源, 共地, TX接RX, RX接TX

image-20231102091425390

时序:

  • 波特率:串口通信的速率
  • 起始位:标志一个数据帧的开始,固定为低电平
  • 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
  • 校验位:用于数据验证,根据数据位计算得来(奇校验和偶校验)
  • 停止位:用于数据帧间隔,固定为高电平

image-20231102092540707

image-20231102092818799

image-20231102092844392

简单说下奇校验和偶校验, 奇偶校验的本质就是为了确保数据中1的个数为奇数或者偶数, 在校验位补1或者0,例如下面这个偶校验时序,发送0x55也就是:

1 0 1 0 1 0 1 0

发现有4个1 也就是偶数, 于是校验位补0,使得时序中的1的个数是偶数

如果此时是奇检验, 就会校验位补1,使得时序中的1的个数是奇数

image-20231102093020863

三、UASRT外设

  • USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器
  • 其实同步模式就是多了个时钟, 用的很少, 本章不涉及
  • USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
  • 自带波特率发生器,最高达4.5Mbits/s
  • 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)可选校验位(无校验/奇校验/偶校验)
  • 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
  • STM32F103C8T6 USART资源: USART1、 USART2、 USART3

工作流程:

(TDR和RDR在物理上是两个寄存器,但在软件上是同一个DR)

发送时:

把数据写入DR, 如果发送移位寄存器没有数据在移位, 就会把DR的数据放到发送移位寄存器中, 准备发送,此时将TXE置1, 此时我们就可以写入下个数据了, 注意此时数据还没发送完毕, 只不过是从TDR放到了发送移位寄存器, 然后在发送控制器的驱动下, 一位一位的往外发

接收时:

数据进来, 在接收控制器的驱动下, 数据一位一位进入接收移位寄存器, 当接收移位寄存器接收了8个位, 这一个字节的数据会一下子被放进RDR中, 此时将RXNE置1,我们就可以读取数据了

发送和接收剔除帧头帧尾这个工作已经被硬件完成, 我们只需要配置好, 然后关注数据就好了,(51震怒)

image-20231102094151514

四、常用配置代码

为了使用printf的库函数stdio, 在魔术棒中勾选 Use MicroLIB

使用微库,将以更精简短小的C 库替代标准C 库,减小代码大小。MicroLib 是默认C 库的备选库。它主要用于内存有限的嵌入式应用程序中。这些应用程序不在操作系统中运行。

image-20231102104255999

用中断接收的配置代码:

//C文件
#include "stm32f10x.h"                  // Device header
#include "usart.h"
void UART_Init(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启时钟
	
	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(USART1_TX)复位推挽输出
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure); //PA10(USART1_RX)浮空输入
	
	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;
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;
    /*
   		USART_BaudRate=9600; 波特率9600
   		USART_HardwareFlowControl_None 硬件流控制失能
   		USART_Mode=USART_Mode_Tx |USART_Mode_Rx; 发送使能,接收使能
   		USART_Parity=USART_Parity_No; 校验失能(无校验)
   		USART_StopBits=USART_StopBits_1; 一位停止位
   		USART_WordLength=USART_WordLength_8b; 字长8Bit
    */
	USART_Init(USART1,&USART_InitStructure);
    
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//USART中断配置
    
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStructure);//NVIC中断
	
	USART_Cmd(USART1, ENABLE);//使能USART
}
//发送字节
void UART_SendByte(uint8_t data){
	USART_SendData(USART1,data);
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);//等待TXE置1
}
//发送数组
void UART_SendArray(uint8_t* Arr,uint8_t Length){
	for(uint8_t i=0;i<Length;i++)
        UART_SendByte(Arr[i]);
}
//发送字符串
void UART_SendString(uint8_t* Str){
	while(*Str){
		UART_SendByte(*Str);
		Str++;
	}
}
//平方运算
uint32_t UART_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)Result *= X;
	return Result;
}
//发送数字
void UART_SendNum(uint32_t Num,uint8_t Length){
	uint8_t temp;
	for(int8_t i=(int8_t)Length-1;i>=0;i--){
		temp=Num/UART_Pow(10,i)%10;
		UART_SendByte(temp);
	}
}
//printf重定向, printf的底层是fputc 把它重定向到串口上
int fputc(int ch,FILE *f){
	UART_SendByte(ch);
	return ch;
}

uint8_t UART_Flag; //接收完成标志位
uint8_t UART_Data; //接收到的数据
//中断服务函数
void USART1_IRQHandler(void){
    //如果RXNE为1
	if(USART_GetITStatus(USART1, USART_IT_RXNE)==SET){
		UART_Flag=1;
        //接收数据
		UART_Data=USART_ReceiveData(USART1);
        //清除标志位
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}


//H文件
#ifndef __USART_H__
#define __USART_H__

extern uint8_t UART_Flag;
extern uint8_t UART_Data;
void UART_Init(void);
void UART_SendByte(uint8_t data);
void UART_SendArray(uint8_t* Arr,uint8_t Length);
void UART_SendString(uint8_t* Str);
void UART_SendNum(uint32_t Num,uint8_t Length);
#include <stdio.h>

#endif

假设我们有这样一种数据包格式, 包长度可变, 以@开头,以\r\n结尾

image-20231102113037714

基于以上代码, 我们用状态机的思维来编写接收数据包

//发送数据包 直接SendString
void UART_SendStringPacket(uint8_t * Str){
	UART_SendString(Str);
}
//接收的数据包, 在h中extern出去
char UART_ReturnStr[100];
//接收状态
typedef enum{
	UART_Rxstate_WaitingStart, //等待开始
	UART_Rxstate_Receiving, //接收中
	UART_Rxstate_WaitingOver, //等待结束
}UART_Rxstate;
UART_Rxstate s=UART_Rxstate_WaitingStart;//接收状态实例化
//中断服务函数, 用于接收数据包到ReturnStr
void USART1_IRQHandler(void){
	static uint8_t UART_Count=0;
	if(USART_GetITStatus(USART1, USART_IT_RXNE)==SET){
		UART_Data=USART_ReceiveData(USART1);//接收一个字节
        //状态为等待开始
		if(s==UART_Rxstate_WaitingStart){
			if(UART_Data=='@'){
                //如果该字节为@,进入接收状态
				s=UART_Rxstate_Receiving;
			}
		}
        //状态为正在接收
		else if(s==UART_Rxstate_Receiving){
			if(UART_Data=='\r')
                //如果该字节为\r',进入等待结束状态
				s=UART_Rxstate_WaitingOver;
			else{
                //正常接收字节
				UART_ReturnStr[UART_Count]=UART_Data;
				UART_Count++;
			}
		}
        //状态为等待结束
		else if(s==UART_Rxstate_WaitingOver){
            //结束接收,Flag置1 
			if(UART_Data=='\n'){
				s=UART_Rxstate_WaitingStart;
				UART_ReturnStr[UART_Count]='\0';
				UART_Count=0;
				UART_Flag=1;
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值