STM32之串口

1. 通信协议

• 假如,如果有两个芯片要通信该怎么办,这时候就需要用到串口了。

• 串口,其实就是一种通信接口,是可以用来传输数据的,如图:

• 这里的关键是两个引脚要交叉接线,从而可以实现双向通信。Tx --- Rx Rx --- Tx。

1.1 通信协议-》其实就是通信的格式

• 如图:

• 这是如果,串口不工作的时候(空闲状态下),总线上是表现出高电压状态。

• 如果突然出现了低电压,就说明 数据传输 的开始。

• 后8位或者9位表示的数据位。

• 如果启动了校验,数据位后面会紧接着校验位。

• 最后停止位,表示一帧数据传输的结束,停止位表现的是 高电压。

1.2 而这里有三处地方可以配置的

• 数据位的长度可以是8位,也可以是9位。

• 可以有校验位,也可以没有检验位。

• 停止位的长度也可以配置的。

• 因此,停止位是1位的情况下,可以得出四种不同的配置方法,如图:

1.3 例子

• 如图

• 串口传输的是二进制数据,这里会把27转换为二进制0001 1011,并且是低位先传输。

• 发送字符串,首先会把每个字符的ASCII码找出来,然后在转化对应的二进制,然后再传输。

1.3.1 检验位,也分为奇校验和偶校验

• 奇校验:要求数据位中有奇数个1。

• 偶校验:要求数据位中有偶数个1。

• 例子,这里发送这一帧数据用的是,奇校验,发送85,转换为二进制之后数为0101 0101,这里有4个1,所以为了达到奇校验,所以校验位写1。

• 在发送的时候把校验位发送给接收方,接收方得到校验位之后,会重新计算一帧数据有多少个1,看和校验位是不是相等,如果不相等,就说发送过程出现错误。

• 如图:

2. USART(其实就是串口)

2.1 基本用法

• 串口的框图:

• 假如需要发送数字100发出去,首先会把100变成二进制数,然后放到发送数据寄存器中,然后通过Tx引脚发送出去。

• 假如需要接收88,首先会从Rx引脚那里接收一组二进制数,然后把二进制数存放到接收数据寄存器中,然后转变为十进制88。

2.2 移位寄存器和串并转换

2.2.1 串行 和 并行 的概念

• 串行通信:数据按位顺序 一次传输一个bit,即一次只传输一个bit的数据。

• 并行通信:多个bit同时传输,即一次可以传输多个bit的数据。

• 如图,发送数据寄存器里面保存待发送的数据,然后发送数据寄存器通过 并行传输 给发送移位寄存器,之后发送数据寄存器会被清除。还有一个接收移位寄存器,接收传输进来的数据,然后再并行传输给接收数据寄存器。

2.2.1.1 发送移位寄存器并到串的意思

• 并:发送数据寄存器里面的数据 并行传输 给发送移位寄存器。

• 串:待发送的数据到了发送移位寄存器里面,之后会通过Tx引脚,一个数据一个数据进行发送。如图所示: • 如上图例子:数据一位一位地向外发送,并且数据也会一位一位在移位寄存器里面向前移动,直到移动完为止。

2.2.1.2 接收移位寄存器串到并的意思

• 如图:

• 串:Rx引脚接收到的输入数据,需要一位一位向前移动,在接收移位寄存器中。直到接收完一帧数据之后,才停止移动。

• 并:在接收完一帧数据,并且数据停止移动的时候,接收移位寄存器的值 通过 并行传输 发送给接收数据寄存器。并且并行传输完之后,会清空接收移位寄存器,准备下次接收。

2.2.2 数据帧格式的转换方法

• 如图,是通过这一部分控制电路配置的。

2.2.3 波特率的设置方法

• 如图

• 波特率:每秒钟最多传输多少位数据,这其中发送位越多,传输速度就越快。

• 如何转换的,如图

• 这里的分频器是对时钟信号进行除法操作。

• 得出的结果就是寄存器里面保存的值。

• 上述是算出一个比特率的步骤。

2.3 函数接口

• 如图

2.4 USART模块的引脚

• 如图:

• 一共有5个引脚,但是主要是用TX和RX引脚。

2.4.1引脚分布表

• 可以通过查数据手册,如图:

2.4.2 重映射表

• 如图(部分):

• 重映射,可以理解为"备用"。

2.4.3 IO模式配置表

• 如图:

• 这里的RX全双工,建议使用的上拉输入:

• 如果使用的是浮空,在RX引脚断了的情况下,可能会遭到外界的电磁干扰。

• 为什么不用下拉,这是因为串口总线电平的空闲的时候是高电压,如果用低电压,这可能导致总线认为这是一个开始信号。

2.5 标志位

2.5.1 TxE标志位

• TxE标志位是发送数据寄存器空的意思。

• 判断发送数据寄存器是否有数据。

2.5.2 TC标志位

• TC标志位是发送完成,就是两个寄存器都为空了,发送数据寄存器和发送移位寄存器为空。

3. 编程接口

3.1 USART_Cmd:使能

3.2 USART_GetFlagStatus:获得标志位的状态,从而可以知道串口的状态

3.3 USART_SendData:发送数据

3.4 实践

• 示例代码

cpp 复制代码
#include "stm32f10x.h"
#include "stdio.h"
#include "delay.h"

void My_Usart_SendDatas(USART_TypeDef* USARTx,uint8_t * data,uint16_t size);
void My_Usart_Init();

int main(void)
{
    Delay_Init();
    
    My_Usart_Init();

    //uint8_t datas[5] = {1,2,3,4,5};
    
    //My_Usart_SendDatas(USART1,datas,5);
    
    //printf("hello world\r\n");
    	while(1)
	{
          uint32_t currentTime = GetTick();//获取单片机当前时间
        
          uint32_t millSeconds = currentTime % 1000;//毫秒
          currentTime = currentTime / 1000;
        
          uint32_t seconds = currentTime % 60;//秒
          currentTime = currentTime / 60;
        
          uint32_t minutes = currentTime % 60;//分
           currentTime = currentTime / 60;
        
          uint32_t hours = currentTime;
        
          printf("%02u:%02u:%02u.%03u",hours,minutes,seconds,millSeconds);
        
          Delay(100);
	}
}

int fputc(int ch,FILE* f){//重写fputc函数
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//一直等,等到数据寄存器为空
    
    USART_SendData(USART1,(uint8_t)ch);//格式化字符串 重定向 到串口
    
    return ch;
    
}

void My_Usart_SendDatas(USART_TypeDef* USARTx,uint8_t * data,uint16_t size){
    
    for(uint32_t i = 0; i < size; i++){
        while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET);//一直等,等到数据寄存器为空
        
        USART_SendData(USARTx,data[i]);
    }
    
    while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET);//等待移位寄存器发完
    USART_ClearFlag(USARTx,USART_FLAG_TC);
}

//初始化USART1,并且波特率是115200,8位数据位,1位停止位,没有校验位
void My_Usart_Init(){
    //配置GPIO
    //这是使用重映射
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟
    GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);//重映射USART
    
    GPIO_InitTypeDef gpio_initstruct = {0};
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    //TX
    gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_6;
    gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
    
    GPIO_Init(GPIOB,&gpio_initstruct);
    
    //RX
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_7;
    
    GPIO_Init(GPIOB,&gpio_initstruct);
    
    //配置的是USART1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟
    
    
    USART_InitTypeDef usart_initstruct = {0};
    
    usart_initstruct.USART_BaudRate = 115200;
    usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    usart_initstruct.USART_Parity = USART_Parity_No;
    usart_initstruct.USART_StopBits = USART_StopBits_1;
    usart_initstruct.USART_WordLength = USART_WordLength_8b;
    
    USART_Init(USART1,&usart_initstruct);//初始化USART1
    
    USART_Cmd(USART1,ENABLE);//使能USART1
    //这是使用复用功能
    //配置GPIO
    //GPIO_InitTypeDef gpio_initstruct = {0};
    
//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//    //TX
//    gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
//    gpio_initstruct.GPIO_Pin = GPIO_Pin_9;
//    gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
//    
//    GPIO_Init(GPIOA,&gpio_initstruct);
//    
//    //RX
//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//    gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
//    gpio_initstruct.GPIO_Pin = GPIO_Pin_10;
//    
//    GPIO_Init(GPIOA,&gpio_initstruct);

}

4. 接收数据

4.1 标志位

• RxNE:接收数据寄存器非空。如图:

4.2 错误的标志位

4.2.1 PE:奇偶校验错,如图:

4.2.2 FE:帧格式错误,如图:

4.2.3 NE:噪声错,这个对一个bit数据进行多次采样的,如果每次采样都不一样,就说明有噪声错。如图:

4.2.4 ORE:过载错,覆盖移位寄存器的值,来不及取走数据。如图:

5. 实践

• 用串口控制led,示例代码:

cpp 复制代码
#include "stm32f10x.h"
#include "stdio.h"
#include "delay.h"


void My_Usart_Init();
void My_USART_Receive();
void My_LED_Init();


int main(void)
{

    
    My_Usart_Init();
    My_LED_Init();
    
   while(1)
   {
        while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);//一直等接收数据寄存器有数据
        
        uint16_t byteRecv = USART_ReceiveData(USART1);//接收数据
        
        if(byteRecv == '0'){//灭
            GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
        }else if(byteRecv == '1'){//亮
            GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
        }
       USART_ClearFlag(USART1,USART_FLAG_RXNE);   
    }
}


void My_LED_Init(){
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
    
    GPIO_InitTypeDef gpio_initstruct = {0};
    
    gpio_initstruct.GPIO_Mode = GPIO_Mode_Out_OD;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_13;
    gpio_initstruct.GPIO_Speed = GPIO_Speed_2MHz;
    
    GPIO_Init(GPIOC,&gpio_initstruct);
    
    GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//灭
}


//初始化USART1,并且波特率是115200,8位数据位,1位停止位,没有校验位
void My_Usart_Init(){
    //配置GPIO
    //这是使用重映射
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟
    GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);//重映射USART
    
    GPIO_InitTypeDef gpio_initstruct = {0};
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    //TX
    gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_6;
    gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
    
    GPIO_Init(GPIOB,&gpio_initstruct);
    
    //RX
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
    gpio_initstruct.GPIO_Pin = GPIO_Pin_7;
    
    GPIO_Init(GPIOB,&gpio_initstruct);
    
    //配置的是USART1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟
    
    
    USART_InitTypeDef usart_initstruct = {0};
    
    usart_initstruct.USART_BaudRate = 115200;
    usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    usart_initstruct.USART_Parity = USART_Parity_No;
    usart_initstruct.USART_StopBits = USART_StopBits_1;
    usart_initstruct.USART_WordLength = USART_WordLength_8b;
    
    USART_Init(USART1,&usart_initstruct);//初始化USART1
    
    USART_Cmd(USART1,ENABLE);//使能USART1
    //这是使用复用功能
    //配置GPIO
    //GPIO_InitTypeDef gpio_initstruct = {0};
    
//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//    //TX
//    gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
//    gpio_initstruct.GPIO_Pin = GPIO_Pin_9;
//    gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
//    
//    GPIO_Init(GPIOA,&gpio_initstruct);
//    
//    //RX
//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//    gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
//    gpio_initstruct.GPIO_Pin = GPIO_Pin_10;
//    
//    GPIO_Init(GPIOA,&gpio_initstruct);

}
相关推荐
qqssss121dfd8 小时前
STM32H750XBH6的ETH模块移植LWIP
网络·stm32·嵌入式硬件
想放学的刺客10 小时前
单片机嵌入式试题(第27期)设计可移植、可配置的外设驱动框架的关键要点
c语言·stm32·单片机·嵌入式硬件·物联网
天昊吖10 小时前
stc8H启用DMA发送后 卡住【踩坑日志】
单片机
李永奉10 小时前
杰理芯片SDK开发-ENC双麦降噪配置/调试教程
人工智能·单片机·嵌入式硬件·物联网·语音识别
BackCatK Chen11 小时前
第 1 篇:软件视角扫盲|TMC2240 软件核心特性 + 学习路径(附工具清单)
c语言·stm32·单片机·学习·电机驱动·保姆级教程·tmc2240
兆龙电子单片机设计11 小时前
【STM32项目开源】STM32单片机多功能电子秤
stm32·单片机·开源·毕业设计·智能家居
切糕师学AI11 小时前
ARM 架构中的复位(Reset)与复位流程
arm开发·单片机·嵌入式·复位
wotaifuzao11 小时前
STM32多协议网关-FreeRTOS事件驱动架构实战
stm32·嵌入式硬件·can·freertos·uart·modbus·spi
llilian_1611 小时前
信号发生器 多通道多功能脉冲信号发生器应用解决方案 多功能脉冲发生器
功能测试·单片机·嵌入式硬件·测试工具
yuanmenghao12 小时前
Classic AUTOSAR深入浅出系列 - 【第十六篇】MCAL:为什么 MCU 换了,上层几乎不用动
单片机·嵌入式硬件·autosar