stm32usart串口通信(标准库)

通信

按照数据传输方式

  • 串行通信:数据位一个接一个地按顺序发送。

    • 异步串行:如RS-232、UART。
    • 同步串行:如SPI、I2C、CAN。
  • 并行通信:多个数据位同时在多个通道上传输,通常速度较快,但受限于距离和干扰。

    • 并行总线:如PCI、IDE。

 按照通信模式

  • 单工(Simplex):数据只能单向传输。例如,传统的广播电视。

  • 半双工(Half-Duplex):数据可以双向传输,但不能同时进行。例如,对讲机。

  • 全双工(Full-Duplex):数据可以同时双向传输。例如,电话通信。

ce5ea4f68d5d4cfead2e8346f7f91ed8.png

通信速率

在数字通信系统中,通信速率(传输速率)指数据在信道中传输的速度,它分为两种:传信率和传码率。

传信率:每秒钟传输的信息量,即每秒钟传输的二进制位数,单位为 bit/s(即比特每秒),因而又称为比特率。

传码率:每秒钟传输的码元个数,单位为 Baud(即波特每秒),因而又称为波特率。

波特率被传输的是码元,码元是信号被调制后的概念,每个码元都可以表示一定 bit 的数据信息量。举个例子,在 TTL 电平标准的通信中,用 0V 表示逻辑 0,5V 表示逻辑 1,这时候这个码元就可以表示两种状态。如果电平信号 0V、2V、4V 和 6V 分别表示二进制数 00、01、10、11,这时候每一个码元就可以表示四种状态。

比特率和波特率的关系可以用以下式子表示:

比特率 = 波特率 * log2M

其中 M 表示码元承载的信息量。我们也可以理解 M 为码元的进制数。采用二进制的时候,波特率和比特率数值上相等。

数据传输方式:同步、异步

同步:共用同一时钟信号,在总线上保持统一的时序和周期完成信息传输。

优点:可以实现高速率、大容量的数据传输,以及点对多点传输。

468173e47ba044aab43ba18425a04d28.png

异步通信不需要时钟信号,而是在数据信号中加入开始位和停止位等一些同步信号,以便

使接收端能够正确地将每一个字符接收下来,某些通信中还需要双方约定传输速率。452e07e45f8a408ba16e416a83796080.png

UART(通用异步收发传输器)是一个全双工通用异步串行收/发模块,主要用于打印程序调试信息、上位机和下位机的通信以及ISP程序下载等场合。

UART至少需要两根数据线用于通信双方进行数据双向同时传输,最简单的UART接口由TxD、RxD、GND共3根线组成。其中,TxD用于发送数据,RxD用于接收数据,GND为信号地线,通过交叉连接实现两个芯片间的串口通信。

492ba7c9143043538d73c31950322377.png

串口通讯

在电路加USB转串口芯片,实现USB通讯协议和UART串行通讯协议的转换.

串口通信的数据包由发送设备的 TXD 接口传输到接收设备的 RXD 接口。在串口通信的协议层中,规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成,通讯双方的 数据包格式要约定一致才能正常收发数据.

a9dfc6cdc25c41dea72409d79d3a4afa.png

1.波特率设置

异步通信设置相同波特率

2.数据帧格式

  • 起始位、结束位

一个数据帧是从起始位开始,直到停止位。数据帧中的起始位是由一个逻辑 0

的数据位表示,而数据帧的停止位可以是 0.5、1、1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。

  • 数据位

有效数据位通常会被约定为 5、6、7 或者 8 个位长。有效数据位是低位(LSB)在前,高位(MSB)在后。

  • 校验位

校验位可以认为是一个特殊的数据位。校验位一般用来判断接收的数据位有无错误,检验

方法有:奇检验、偶检验、0 检验、1 检验以及无检验。

奇校验是指有效数据为和校验位中“1”的个数为奇数,比如一个 8 位长的有效数据为:10101001,总共有 4 个“1”,为达到奇校验效果,校验位设置为“1”,最后传输的数据是 8 位的有效数据加上 1 位的校验位总共 9 位。

偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,比如数据帧:11001010,此时数据帧“1”的个数为 4 个,所以偶校验位为“0”。0 校验是指不管有效数据中的内容是什么,校验位总为“0”,1 校验是校验位总为“1”。无校验是指数据帧中不包含校验位

 

USART

USART发送和接收原理

发送过程

  1. 初始化USART:配置波特率、数据位、停止位等参数。
  2. 使能发送:允许USART进行数据发送操作。
  3. 写入数据:将要发送的数据写入数据寄存器(DR)。
  4. 等待发送完成:USART会通过状态寄存器(SR)中的标志位指示发送是否完成。

接收过程

  1. 配置接收:设置USART以接收数据。
  2. 读取数据:从数据寄存器(DR)读取接收到的数据。
  3. 检查接收状态:通过状态寄存器(SR)的标志位确定新数据是否可用。

USART寄存器概述

USART的主要寄存器包括:

  • BRR(波特率寄存器)
  • CR1(控制寄存器1)
  • CR2(控制寄存器2)
  • CR3(控制寄存器3)
  • SR(状态寄存器)
  • DR(数据寄存器)

波特率寄存器(BRR)

  • 功能:配置USART的波特率。
  • 计算公式BRR = F_CLK / (16 * BaudRate)
USART1->BRR = SystemCoreClock / 115200; // 设置波特率为115200

 

控制寄存器1(CR1)

  • 功能:配置USART的基本功能。

  • 重要位解析:

    • UE(USART使能位):设置为1使能USART模块。
    • TE(发送使能位):设置为1以启用发送功能。
    • RE(接收使能位):设置为1以启用接收功能。
    • RXNEIE(接收中断使能位):当接收到数据时使能接收中断。
    • TXEIE(发送中断使能位):当发送缓冲区为空时使能发送中断。

 控制寄存器2(CR2)

  • 功能:配置停止位数等。

  • 重要位解析:

    • STOP[13:12]:配置停止位的数量,常用的选项有0(1个停止位)、1(0.5个停止位)、2(2个停止位)和3(1.5个停止位)。

控制寄存器3(CR3)

  • 功能:配置一些高级功能,如流控等。

  • 重要位解析:

    • CTSE(CTS使能位):使能CTS流控制。
    • RTSE(RTS使能位):使能RTS流控制。

状态寄存器(SR)

  • 功能:反映USART的当前状态。

  • 重要位解析:

    • TXE(发送数据寄存器空标志):如果为1,则可以写入数据到DR,表示发送缓冲区为空。
    • RXNE(接收数据寄存器非空标志):如果为1,则可从DR读取数据,表示接收到数据。
    • TC(传输完成标志):如果为1,则所有数据已经成功发送。

4. 数据寄存器(DR)

  • 功能:用于发送和接收数据。

  • 当要发送数据时,将数据写入此寄存器;接收到数据时,从此寄存器读取数据。

 

USART的3种方式

1.轮询方式

CPU不断地查询I/O设备是否准备就绪,如果准备就绪就发送,否则提示超时错误;会占用CPU的大量时间,效率低。

发送数据

在使用轮询方式发送数据时,主要依赖于TXE标志位来判断数据寄存器是否为空。当数据寄存器为空时,可以向其写入数据进行发送。

  • 检查USART_SR寄存器中的TXE标志位。
  • TXE为1,则写入数据到DR寄存器。

接收数据

在接收数据时,我们通过检查RXNE标志位来判断是否有新数据可读。当接收寄存器有数据时,该标志位被置位

  • 检查USART_SR寄存器中的RXNE标志位。
  • RXNE为1,则读取DR寄存器中的数据。
#include "usart.h"


void USART_Init_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);  
	USART_DeInit(USART1);      //重置指定的USART外设到其初始状态                                                 
    
    //USART1_TX   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;         //选择要使用的I/O引脚,此处选择PA9引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的输出速度为50MHz
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	  //设置引脚输出模式为复用推挽输出模式
    GPIO_Init(GPIOA, &GPIO_InitStructure); 
   
    //USART1_RX	  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;            //选择要使用的I/O引脚,此处选择PA10引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置引脚输入模式为浮空输入模式
    GPIO_Init(GPIOA, &GPIO_InitStructure); 

    //USART1 配置
	USART_InitStructure.USART_BaudRate = 115200;                                   //设置波特率为115200
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;                    //数据位占8位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;                         //设置1位停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;                            //设置无校验
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//设置不采用硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                //设置为收/发模式
	USART_Init(USART1, &USART_InitStructure);                                      //初始化串口1
    USART_Cmd(USART1, ENABLE);                                                     //使能串口1
}


//重定向printf函数
int fputc(int ch, FILE *f)
{
		USART_SendData(USART1, (uint8_t) ch);
		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

//重定向scanf函数
int fgetc(FILE *f)
{
		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(USART1);
}

2.中断方式

中断发送

在USART中断发送过程中,我们通常利用TXE标志位来触发发送中断。当数据寄存器(DR)为空时,该标志位会被置位,表示可以发送下一个字节的数据。

  • 启用USART发送中断(TXEIE)。
  • 当用户通过程序写入数据到USART的数据寄存器时,若数据寄存器空,TXE标志位被置位。
  • 触发USART发送中断,进入中断服务例程(ISR)。
  • 在ISR中,可以继续发送下一个字节并清除TXE标志位。

中断接收

在USART中断接收过程中,我们利用RXNE标志位来触发接收中断。当接收数据寄存器有新数据时,该标志位被置位,表示可以读取数据。

  • 启用USART接收中断(RXNEIE)。
  • 当USART接收到新的数据时,RXNE标志位被置位。
  • 触发USART接收中断,进入中断服务例程(ISR)。
  • 在ISR中,读取数据寄存器的内容并清除RXNE标志位。

 usart.c

#include "usart.h"


void NVIC_Usart_Config(void)
{
   NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);

}


void USART_Init_Config(void){

     GPIO_InitTypeDef GPIO_InitStructure;   //定义一个GPIO_InitTypeDef类型的结构体变量,用于配置GPIo引脚
	USART_InitTypeDef USART_InitStructure; //定义一个USART_InitTypeDer类型的结构体变量,用于配置串口
   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //开始USART1和GPIOA的时钟
	USART_DeInit(USART1);                                                       //复位USART1
    
    //USART1_TX  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;         
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的输出速度为50MHz
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	  //设置引脚输出模式为复用推挽输出模式
    GPIO_Init(GPIOA, &GPIO_InitStructure); 
   
    //USART1_RX	  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;           
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置引脚输入模式为浮空输入模式
    GPIO_Init(GPIOA, &GPIO_InitStructure); 

    //USART1 配置
	USART_InitStructure.USART_BaudRate = 115200;                                   //设置波特率为115200
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;                    //数据位占8位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;                         //设置1位停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;                            //设置无校验
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//设置不采用硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;;               //设置为收/发模式
    
   
    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //打开串口1的接收中断
    USART_ITConfig(USART1,USART_IT_TC,ENABLE);   //开始串口1的发送中断
	USART_Init(USART1, &USART_InitStructure);    //初始化串口1
    USART_Cmd(USART1, ENABLE);                   //使能串口1
    
    NVIC_Usart_Config();
}

//重定向printf函数
int fputc(int ch, FILE *f)
{
		USART_SendData(USART1, (uint8_t) ch);
		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

//重定向scanf函数
int fgetc(FILE *f)
{
		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(USART1);
}


//中断处理函数
void USART1_IRQHandler(void)
{
    uint8_t Rx_Data;

    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        Rx_Data = (uint8_t)USART_ReceiveData(USART1);
        
        // 发送固定字符串 "hello zhi_bei_you"
       Usart_SendStr(USART1, (uint8_t *)"hello zhi_bei_you\n");

        // 判断输入的 Rx_Data 并相应处理
        if (Rx_Data == '0') // 注意这里比较的是字符'0'
        {
            GPIO_SetBits(GPIOB, GPIO_Pin_5); // 熄灭LED灯
            printf("熄灭LED灯\n");
        }
        else if (Rx_Data == '1') // 注意这里比较的是字符'1'
        {
            GPIO_ResetBits(GPIOB, GPIO_Pin_5); // 点亮LED灯
            printf("点亮LED灯\n");
        }
    }    
}

/* 发送字符串 */
void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str)
{
    while (*str) // 直到遇到字符串结束符 '\0'
    {
        while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); // 等待发送缓冲区空
        USART_SendData(pUSARTx, *str++); // 发送当前字符,并移动到下一个
    }
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET); // 等待发送完成
}

 main

#include "stm32f10x.h"
#include "systick.h"
#include "LED.h"
#include "usart.h"

int main()
{
		LED_ConfigInit();
		USART_Init_Config();
//		SystemClock_Config();

		while(1)
		{
			
//			printf('123\n');
//			
		}
//	//
//	LED_ConfigInit();
//	GPIO_ResetBits(GPIOB,GPIO_Pin_5);
//	uint8_t a[10]={100,2,3,4,5,6,7,8,9,10};
//	while(1){
//		//GPIO_SetBits(GPIOB,GPIO_Pin_5);
//		//SysTick_Delay_Ms(1000);
//		Usart_SendArray(DEBUG_USARTx, a,10);
//		//GPIO_ResetBits(GPIOB,GPIO_Pin_5);
//		//SysTick_Delay_Ms(1000);
//	}
//	
}

9ed67b9c5e7a400686fb685512601fa4.png

运行结果如图,但是hello_zhi_you被打印了两次,我不知道怎么回事,如果有知道的大佬愿意解惑,非常感谢!

3、DMA方式

直接存储器传送,使用 DMA(Direct Memory Access)配置 USART 进行数据接收或发送可以显著提高系统的性能,尤其是在需要处理大量数据的情况下。DMA 允许外设与内存之间的数据传输,而不占用 CPU 的时间,从而实现更高效的数据处理。

 

###持续更QwQ,如果有错,欢迎指出,送上作为一个浅陋学习者真诚的感谢!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值