学习STM32第五天

通讯

按数据传送方式,通讯可分为串行通讯与并行通讯;按数据通讯方向,通讯可分为全双工、半双工和单工通讯;按通讯的数据同步方式,通讯可分为同步和异步。衡量通讯性能的一个非常重要的参数就是通讯速率,以比特率表示,每秒传输的二进制数。波特率表示每秒传输多少个码元,用时间间隔相同的符号表示一个码元。二者区别如下

如常见的通讯传输中,用0V表示数字0,5V表示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特位, 此时波特率的大小与比特率一致;如果在通讯传输中,有0V、2V、4V以及6V分别表示二进制数00、01、10、11, 那么每个码元可以表示四种状态,即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。

串行通信一般是以帧格式传输数据,每帧都包含起始位(固定为低电平)、数据位、停止位(固定为高电平),还可能有校验位。

一、USART

USART(全双工通用同步/异步串行收发模块)是一个串行通信设备,可灵活地与外部设备进行全双工数据交换。一般在STM32硬件设计时都会预留一个USART通信接口连接电脑,以确保程序运行是否正确。C8T6有三个USART接口,其中USART1位于APB2总线,最大频率为72MHz;其余两个位于APB1总线,最大频率为36MHz。USART结构框图如下
USART结构框图
功能引脚

  • TX
    发送数据输出引脚
  • RX
    接收数据输入引脚
  • SW_RX
    数据接收引脚,只用于单线和智能卡模式,属于内部引脚
  • nRTS
    (Request To Send)发送请求,n表示低电平有效。若是低电平,表示USART准备好接收数据,只适用于硬件流控制。一般不用
  • nCTS
    (Clear To Send)清除发送,n表示低电平有效。若是高电平,在当前数据传输结束时阻断下一次的数据发送,只适用于硬件流控制。一般不用
  • SCLK
    时钟输出引脚,仅适用于同步模式。
    USART基本结构如下
    USART基本结构
    简单的双向串口通信有两根通信线(TX与RX),设备之间交叉连接。接线图如下
    USART接线图一个数据帧是否设置校验位可选造成8或9位。如果使用了奇校验,则9位数据中会出现奇数个1;如果使用了偶校验,则9位数据中会出现偶数个1。帧的停止位可自行配置为1、2、0.5、1.5个停止位。如图所示
    USART帧结构
  1. 发送器
    发送器根据是否设置校验位(即USART_CR1的M位)发送8位或9位的数据字,USART_CR1寄存器的发送使能位TE置1时,启动数据发送,发送移位寄存器的数据会在TX引脚输出,低位在前、高位在后,就意味着接收端先接收低位数据,后接收高位数据。使能位TE被激活后将发送一个空闲帧,然后就可以往USART_DR寄存器写入要发送的数据,在写入最后一个数据后,需要等待USART_SR的TC位为1,表示数据传输完成,如果USART_CR1寄存器的TCIE位置1,将产生中断。

  2. 接收器
    前提保证和发送器相同的波特率,并且要求每次采样的位置正好处于每一位的正中间,同时还要对噪声有一定的判断能力。根据USART_CR1的M位接收8或9位数据字,在辨认出一个特殊的采样序列(检测下降沿)后,就可认为侦测到一个起始位,若该序列不完整,那么接收端将退出起始位侦测并回到空闲状态等待下降沿。字符帧起始位的检测如图
    起始位检测
    由图可知,在检测到下降沿后,对信号线进行采样,为避免噪声对采样的干扰,在下降沿之后的第3、5、7位进行第一次采样和8、9、10位进行第两次采样,都要求两批采样中至少有2个0。数据的最低有效位首先被接收,最后接收最高有效位。根据NE状态判断数据有效性。

  3. 分数波特率
    波特率指数据信号对载波的调制速率,用单位时间内载波调制状态改变次数来表示。Tx / Rx 波特率 = = fCK/(16* USARTDIV ),fCK是外设的时钟即72MHz,USARTDIV是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数,其中DIV_Mantissa[11:0]位定义USARTDIV的整数部分,DIV_Fraction[3:0]位定义USARTDIV的小数部分。

  4. 数据模式

  • HEX模式:以原始数据的形式显示
  • 字符模式:以原始数据编码后的形式显示
  1. MicroLIB
    MicroLIB是Keil为嵌入式平台优化的一个精简库,经过深度优化,代码大小有明显优势。
    MicroLIB
    需要对printf进行重定向,将printf打印的东西输出到串口。在串口模块.c文件加上头文件#include "stdio.h"

二、实验案例

  1. 单字节串口发送
    使用USART1的TX引脚向电脑发送数据,编程步骤如下
  • 开启USART时钟和GPIO引脚时钟
  • 配置GPIO引脚
  • 配置USART
  • 使能USART
  • 功能函数编写
void Serial_Init(void)
{
	//开启USART1时钟和GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//配置PA9引脚
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//采用复用推完输出
	//一般RX配置是浮空输入或上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//配置USART1
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制,这里不选用
	USART_InitStructure.USART_Mode = USART_Mode_Tx;//作输出
	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);
	//开启USART1
	USART_Cmd(USART1, ENABLE);
}

//发送单个字节数据函数编写
void Serial_SendByte(uint8_t Byte)
{	//调用发送函数
	USART_SendData(USART1, Byte);//数据写入TDR,此处还等TDR的数据转移到移位寄存器
	//此处作等待标志位之用,即发送数据寄存器空标志位,标志位会自动置0
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) != SET);
}
  1. 单字节串口接收
    对于串口接收来说,可以使用查询和中断两种方法。
  • 使用查询
    查询不需要配置中断,在主函数中不断判断RXNE(读数据寄存器非空)标志位。当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位;若USART_CR1寄存器中的RXNEIE为1,则产生中断,对USART_DR的读操作可将该位清零,意思就是说读DR操作可自动清零标志位。此方法只适用于实现简单的功能
    代码如下
//使用查询
void Serial_Init(void)
{
	//PA9是USART1_TX,PA10是USART1_RX
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//配置PA9引脚
	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);
	//配置PA10引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//配置USART1
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = 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);
}

//数据接收函数
uint8_t RxData;
//不断判断RXNE标志位
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
		{
			RxData = USART_ReceiveData(USART1);
			OLED_ShowHexNum(2, 1, RxData, 5);
		}
  • 使用中断
    使用中断,可以使得程序更加灵活,代码如下
void Serial_Init(void)
{
	//PA9是USART1_TX,PA10是USART1_RX
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//配置PA9引脚
	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);
	//配置PA10引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//配置USART1
	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);
	//开启RXNE标志位到NVIC的输出
	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);
	
	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;
}
//读DR会自动清零
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);	//存储接收的数据
		Serial_RxFlag = 1;							//通过扫描标志位接收数据
		//如果读取了DR可以自动清除标志位
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}
//读数据函数如下
if(Serial_GetRxFlag() == 1)//始终是对标志位进行扫描判断
		{
			RxData = Serial_GetRxData();
			OLED_ShowHexNum(2, 1, RxData, 5);
		}

  • 26
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值