协议 UARST & 数据发送与接收

磕伴 / 2023-08-31 / 原文

STM32具有的协议

 UASRT是通用异步/同步收发器,UART是通用异步收发器

 串口空闲状态时高电平,开始传输数据时,第一个数据为固定的低电平; 数据;最后为高电平的停止位

 奇偶校验:通过+1或者不变,使数据中1的个数为奇数或者偶数

CRC校验

 UASRT外设自动完成电平的高低反转(低位先行),虽有时钟线(为了兼容),但主要还是用的异步通信

 配置好USART电路,只需要读写对应的寄存器就可以实现自动发送和接收

硬件流控制:多一根线来显示接受状态(准备好接受就置低电平)

实现逻辑:

发送,写一个数据到TDR中(以二进制保存),硬件检测到后,就会检查当前的移位寄存器是不是有数据在转运,没有的话就立即上,此时会置一个标志位TXE(empty)[发送寄存器空]为1,判断后就可以在TDR写入下一个数据,循环

接收,数据先到接受移位寄存器中,接受一个周期完毕后,转运到RDR,此时置标志位RXNE(RX Not Empty), 判断后就可以从RDR读取数据

 

流控引脚nRTS  请求发送,是输出脚,告诉别人我当前能不能接收   ;nCTS清除发送,是输入脚(接受别人nRTS的信号),能不能发送。 n表示低电平有效

流控逻辑,外部TX接我的RX,我的nRTS输出一个能不能接收的反馈信号,接外部的nCTS; 我能接收的时候RTS低电平,对方接受信号就可以一直发。反之亦然。

 

发送寄存器每移位一次。同步时钟电平就跳变个周期,(时钟功能一般不用)

唤醒单元:实现挂载多设备,通过给串口分配地址,用到哪个设备就把它的地址写到唤醒单元里

分频,支持小数点后4位,然后除以16,得到发送器时钟和接收器时钟

代码提示:CTRL+ALT+空格

单数据

发送:发送单字节,字符串,数组,数字(字符型)

void Serial_Init(void)
{    // 开启USART和GPIO的RCC时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//初始化TX引脚是USART外设控制的输出脚,复用推挽
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
        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_Init(USART1, &USART_InitStructure);

        USART_Cmd(USART1, ENABLE);
}


void Serial_SendByte(uint8_t Byte)
{
    USART_SendData(USART1, Byte);//写DR寄存器
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//等待TDR数据转移到移位寄存器(对其写操作会自动使其清零)
}
View Code

打印换行“hello \r \n”  ,POW()函数数字分解

printf设置

 对输出进行重定向(从输出屏幕重定向到电脑)

#include <stdio.h>

int fputc(int ch, FILE *f) //是printf的底层
{
    Serial_SendByte(ch);
    return ch;
}

printf("hello,%d\r\n",9);

//多串口使用printf
char String[100]
sprintf(String,"hello,%d\r\n",9);
Serial_SendString(String) //使用其他串口发送

//可变参数
void Serial_Printf(char *format, ...)
{
    char String[100];
    va_list arg;
    va_start(arg, format);
    vsprintf(String, format, arg); // 数据源地址,格式化字符串,参数表
    va_end(arg);
    Serial_SendString(String);
}

打印汉字(编译器设置)

Serial_Printf(“你好呀”),串口工具也使用UTF-8显示才行
方法2:GBK模式,在keil选择Chinese GB2312(Simplified)),串口就直接可以融合中文 (记得关闭.c文件重新打开刷新字体)

发送+接收
查询:

 

中断:

void Serial_Init(void)
{    // 开启USART和GPIO的RCC时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    //发送
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//初始化TX引脚是USART外设控制的输出脚,复用推挽
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    //接收
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //初始化RX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    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_Init(USART1, &USART_InitStructure);
    //串口接收可以用查询(不用下面的代码【比较简单】)和中断(使用下面代码)
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
    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);
    
    USART_Cmd(USART1, ENABLE);
}

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

uint8_t Serial_GetRxFlag(void)
{
    if (Serial_RxFlag == 1)
    {
        Serial_RxFlag = 0;
        return 1;
    }
    return 0;
}

uint8_t Serial_GetRxData(void)
{
    return Serial_RxData;
}

void USART1_IRQHandler(void)
{
    if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
    {
        Serial_RxData = USART_ReceiveData(USART1);// 接收数据,读DR寄存器
        Serial_RxFlag = 1;
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}
View Code

 

串口助手是用visual studio软件,winform模板写的

 输入数据处理:以波特率的频率连续采样一帧的数据,采样要在正中间,如何实现?:以波特率的16倍进采样,

比如起始位,在下降沿后的3 5 7 次进行一批采样,8 9 10(正中间)再进行一次采样,两批采样要求每三位里面至少2个0,若有噪声则会置标志位NE(Noise Error)

 数据包的接收,利用串口进行人机交互

 

如何避免数据包含包头包尾呢?:增加包头尾的长度,严格限定数据长度或范围吗,也可以只要包头不要尾(兼容差)

接受数据包

code

通过状态机判断,可将数据部分存储到数组中读出(包头和包尾为条件判断使用)

 跟接收HEX的区别是:char Serial_RxPacket[100];

状态机,在进中断时判断处于哪个状态,从而执行不同的逻辑