完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、理解通信
在计算机设备与设备之间或集成电路之间常常需要进行数据传输,所以需要通讯。 (一)串行通讯与并行通讯 按数据传送的方式,通讯可分为串行通讯与并行通讯,串行通讯是指设备之间通过少量数据信号线(一般是 8 根以下),地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。并行通讯一般是指使用 8、16、32 及 64 根或更多的数据线进行传输的通讯方式,并行通讯就像多个车道的公路,可以同时传输多个数据位的数据。 很明显,因为一次可传输多个数据位的数据 ,在数据传输速率相同的情况下,并行通 讯传输的数据量要大得多,而串行通讯则可以节省数据线的硬件成本(特别是远距离时)以 及 PCB 的布线面积。不过由于并行传输对同步要求较高,且随着通讯速率的提高,信号干扰的问题会显著影响通讯性能,现在随着技术的发展,越来越多的应用场合采用高速率的串行差分传输。 (二)按数据通信方向:全双工、半双工和单工 (三)按数据的同步方式:同步与异步 区分同步与异步,关键在于它有没有存在时钟信号。如果有时钟信号,则为同步。在同步通讯中,收发设备双方会使用一根信号线表示时钟信号,在时钟信号的驱动下双方进行协调,同步数据,通讯中通常双方会统一规定在时钟信号的上升沿或下降沿对数据线进行采样。 同步: 异步: 在异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用 的信号位,或者把主体数据进行打包,以数据帧的格式传输数据,某些通讯中还需要双方约定数据的传输速率,以便更好地同步。 在同步通讯中,数据信号所传输的内容绝大部分就是有效数据,而异步通讯中会包含有帧的各种标识符,所以同步通讯的效率更高,但是同步通讯双方的时钟允许误差较小, 而异步通讯双方的时钟允许误差较大。 (四)通信的速率 在确保稳定性的前提下,我们会加大通信的速率。 Bitrate——比特率:每秒钟传输的二进制位数,单位为比特每秒(bit/s)。 Baudrate——波特率:表示每秒钟传输的码元个数。 而码元是通讯信号调制的概念,通讯中常用 时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元。如常见的通讯传输中, 用 0V 表示数字 0,5V 表示数字 1,那么一个码元可以表示两种状态 0 和 1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;如果在通讯传输中,有 0V、 2V、4V 以及 6V 分别表示二进制数 00、01、10、11,那么每个码元可以表示四种状态, 即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率 的一半。 二、串口 (一)串口通信协议简介 物理层:规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。其实就是硬件部分。 协议层:协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。其实就是软件部分。 串口的标准有:
(二)RS232标准串口通讯结构图
为什么需要一个电平转换芯片,是因为他们之间的电平转换不一样: (三)RS-232信号线 在旧式的台式计算机中一般会有 RS-232 标准的 COM 口(也称 DB9 接口),在最初的应用中,RS-232 串口标准常用于计算机、路由与调制调解器(MODEN,俗称 “猫”)之间的通讯 ,在这种通讯系统中,设备被分为数据终端设备 DTE(计算机、路由)和 数据通讯设备 DCE(调制调解器)。 当两个设备进行连接时,需要使用“直通型”的串口线进行连接: 串口线中的 RTS、CTS、DSR、DTR 及 DCD 信号,使用逻辑 1 表示信号有效,逻辑 0 表示信号无效。例如,当计算机端控制 DTR 信号线表示为逻辑 1 时,它是为了告知远端的 调制调解器,本机已准备好接收数据,0 则表示还没准备就绪。 在目前的其它工业控制使用的串口通讯中,一般只使用 RXD、TXD 以及 GND 三条信 号线,直接传输数据信号。而 RTS、CTS、DSR、DTR 及 DCD 信号都被裁剪掉了。 (四)USB转串口通讯结构图
数据包的基本组成: 串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在 串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据。 (一)波特率 我们主要学习的是串口异步通讯,异步通讯中由于没有时钟信号,所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,如上图中用虚线分开的每一格就是代表一个码元。常见有4800、9600、115200等。 (二)通讯的起始和停止信号 串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一 个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、1、1.5 或 2 个逻辑 1 的数据位表示, 只要双方约定一致即可。 (三)有效数据 在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7 或 8 位长。 (四)数据校验 在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰 导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验 (odd)、偶校验(even)、0 校验(space)、1 校验(mark)以及无校验(noparity)。
我们将功能图分为四个部分,分别为1-引脚,2-数据寄存器,3-控制器,4-波特率。 (一)第一部分——引脚 TX:数据发送。 RX:数据接收。 SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚。 nRTS:请求以发送(Request To Send),n 表示低电平有效。如果使能 RTS 流控制,当 USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时, nRTS 将被设置为高电平。该引脚只适用于硬件流控制。 nCTS:清除以发送(Clear To Send),n 表示低电平有效。如果使能 CTS 流控制,发送 器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。 SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。 UART 只是异步传输功能,所以没有 SCLK、nCTS 和 nRTS 功能引脚。具体引脚的详细功能可去数据手册上了解。 (二)第二部分——数据寄存器(USART_DR) 9位有效,包含一个发送数据寄存器TDR和一个接收数据寄存器RDR。一个地址对应了两个物理内存。 USART_DR 实际是包含了两个寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。当进行发送操作时,往 USART_DR 写入数据会自动存储在 TDR 内;当进行读取操作时,向 USART_DR 读取数据会自动提取 RDR 数据。 TDR 和 RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的, 发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收 时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。 USART 支持 DMA 传输,可以实现高速数据传输。 (三)控制器
一个字符帧发送需要三个部分:起始位+数据帧+停止位。起始位是一个位周期的低电平,位周期就是每一位占用的时间;数据帧就是我们要发送的 8 位或 9 位数据,数据是从 最低位开始传输的;停止位是一定时间周期的高电平。 停止位时间长短是可以通过 USART 控制寄存器 2(USART_CR2)的 STOP[1:0]位控制, 可选 0.5 个、1 个、1.5 个和 2 个停止位。默认使用 1 个停止位。2 个停止位适用于正常 USART 模式、单线模式和调制解调器模式。0.5 个和 1.5 个停止位用于智能卡模式。 当选择 8 位字长,使用 1 个停止位时, 当发送使能位 TE 置 1 之后,发送器开始会先发送一个空闲帧(一个数据帧长度的高电平),接下来就可以往 USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待 USART 状态寄存器(USART_SR)的 TC 位为 1,表示数据传输完成,如果 USART_CR1 寄存器的 TCIE 位置 1,将产生中断。 在发送数据时,有几个重要的标志位如下:
(四)波特率——每秒钟要发送多少数据 为得到一个信号真实情况,需要用一个比这个信号频率高的采样信号去检测,称为过采样,这个采样信号的频率大小决定最后得到源信号准确度,一般频率越高得到的准确度越高,但为了得到越高频率采样信号越也困难,运算和功耗等等也会增加,所以一般选择合适就好。 接收器可配置为不同过采样技术,以实现从噪声中提取有效的数据。USART_CR1 寄 存器的 OVER8 位用来选择不同的采样采样方法,如果 OVER8 位设置为 1 采用 8 倍过采样, 即用 8 个采样信号采样一位数据;如果 OVER8 位设置为 0 采用 16 倍过采样,即用 16 个采样信号采样一位数据。 USART 的起始位检测需要用到特定序列。如果在 RX 线识别到该特定序列就认为是检测到了起始位。起始位检测对使用 16 倍或 8 倍过采样的序列都是一样的。该特定序列为: 1110X0X0X0000,其中 X 表示电平任意,1 或 0 皆可。 波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示, 单位为波特。比特率指单位时间内传输的比特数,单位 bit/s(bps)。 USART 的发送器和接收器使用相同的波特率。 其中,fPLCK为 USART 时钟,有90M与45M。 OVER8 为 USART_CR1 寄存器的 OVER8 位对应的值,USARTDIV 是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数。 其中 DIV_Mantissa[11:0]位定义 USARTDIV 的整数部分,DIV_Fraction[3:0]位定义 USARTDIV 的小数部分,DIV_Fraction[3]位只有在 OVER8 位为 0 时有效,否则必须清零。我们普遍使用OVER8为0的情况,比如我们的波特率115200: 这个寄存器只有四位存放小数点后的数,所以只有2的四次方等于16。最高精度为1/16。当波特率为115200bps以及OVER8=0,选择USART1,则fPLCK=90Mhz。由以上条件可以算得USARTDIV=48.825125,可以算的DIV_Fraction=0xD,DIV_Mantissa=0x30,即应 该设置 USART_BRR 的值为 0x30D。所以我们将0x30D传入寄存器USART_BRR中。由 USART_BRR 的值为 0x30D,可得 DIV_Fraction=13,DIV_Mantissa=48,所以 USARTDIV=48+16*0.13=48.8125,所以实际波特率为:115237;这个值跟我们的目标波特 率误差为 0.03%,这么小的误差在正常通信的允许范围内。 四、中断控制 由以上分析,我们可以得到如下的总结: [tr]中断事件事件标志使能控制位[/tr]
|
||
|
||
五、USART初始化结构体 typedef struct { uint32_t USART_BaudRate; //波特率 BRR uint16_t USART_WordLength; //字长 CR1_M uint16_t USART_StopBits; //停止位 CR2_STOP uint16_t USART_Parity; //校验控制 CR1_PCE、CR1_PS uint16_t USART_Mode; //模式选择CR1_TE、CR1_RE uint16_t USART_HardwareFlowControl; // 硬件流选择 CR3_CTSE、CR3_RTSE } USART_InitTypeDef;
typedef struct { uint16_t USART_Clock; // 同步时钟 CR2_CLKEN uint16_t USART_CPOL; // 极性 CR2_CPOL uint16_t USART_CPHA; // 相位 CR2_CPHA uint16_t USART_LastBit; //最后一个位的时钟脉冲 CR2_LBC } USART_ClockInitTypeDef;
(1)配置GPIO为具体的第二功能(复用功能需要指定) void GPIO_PinAFConfig (GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF) (2)中断配置函数 void USART_ITConfig (USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState) (3)串口使能函数 void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState) (4)数据发送函数 void USART_SendData (USART_TypeDef* USARTx, uint16_t Data) (5)数据接收函数 uint16_t USART_ReceiveData(USART_TypeDef* USARTx) (6)中断状态位获取函数 ITStatus USART_GetITStatus (USART_TypeDef* USARTx, uint16_t USART_IT) 七、实验 实验1:编写一个程序实现开发板与电脑通信,在开发板上电时通过USART发送一串字符串给电脑,然后开发板进入中断接受等待状态,如果电脑有发送数据过来,开发板就会产生中断,我们在中断服务函数接收数据,并且马上把数据返回发送给电脑。 1-初始化串口需要用到的GPIO,GPIO_InitTypeDef, GPIO_PinAFConfig(); 2-初始化串口,USART_InitTypeDef 3-中断配置 4-使能串口 5-编写发送和接收函数 6-编写中断服务函数 实现1到3步的程序如下: //配置嵌套向量中断控制器NVIC static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* 嵌套向量中断控制器组选择 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* 配置USART为中断源 */ NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ; /* 抢断优先级为1 */ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; /* 子优先级为1 */ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; /* 使能中断 */ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; /* 初始化配置NVIC */ NVIC_Init(&NVIC_InitStructure); } //配置串口 void Debug_USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_AHB1PeriphClockCmd(DEBUG_USART_RX_GPIO_CLK|DEBUG_USART_TX_GPIO_CLK,ENABLE); /* 使能 USART 时钟 */ RCC_APB2PeriphClockCmd(DEBUG_USART_CLK, ENABLE); /* GPIO初始化 */ GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* 配置Tx引脚为复用功能 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN ; GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure); /* 配置Rx引脚为复用功能 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN; GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure); /* 连接 PXx 到 USARTx_Tx*/ GPIO_PinAFConfig(DEBUG_USART_RX_GPIO_PORT,DEBUG_USART_RX_SOURCE,DEBUG_USART_RX_AF); /* 连接 PXx 到 USARTx__Rx*/ GPIO_PinAFConfig(DEBUG_USART_TX_GPIO_PORT,DEBUG_USART_TX_SOURCE,DEBUG_USART_TX_AF); /* 配置串DEBUG_USART 模式 */ /* 波特率设置:DEBUG_USART_BAUDRATE */ USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; /* 字长(数据位+校验位):8 */ USART_InitStructure.USART_WordLength = USART_WordLength_8b; /* 停止位:1个停止位 */ USART_InitStructure.USART_StopBits = USART_StopBits_1; /* 校验位选择:不使用校验 */ USART_InitStructure.USART_Parity = USART_Parity_No; /* 硬件流控制:不使用硬件流 */ USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; /* USART模式控制:同时使能接收和发送 */ USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; /* 完成USART初始化配置 */ USART_Init(DEBUG_USART, &USART_InitStructure); /* 嵌套向量中断控制器NVIC配置 */ NVIC_Configuration(); /* 使能串口接收中断 */ USART_ITConfig(DEBUG_USART, USART_IT_RXNE, ENABLE); /* 使能串口 */ USART_Cmd(DEBUG_USART, ENABLE); } 接下来是编写发送8位数据、16位数据以及字符串: //发送8位数据 void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch) { /* 发送一个字节数据到USART */ USART_SendData(pUSARTx,ch); /* 等待发送数据寄存器为空,当寄存器中值为空时,TXE=1,表示传输完成 */ while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); } //发送16位数据 void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch) { uint8_t temp_h, temp_l; /* 取出高八位 */ temp_h = (ch&0XFF00)>>8; /* 取出低八位 */ temp_l = ch&0XFF; /* 发送高八位 */ USART_SendData(pUSARTx,temp_h); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); /* 发送低八位 */ USART_SendData(pUSARTx,temp_l); while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); } //发送一个字符串 void Usart_SendString( USART_TypeDef * pUSARTx, char *str) { unsigned int k=0; do { Usart_SendByte( pUSARTx, *(str + k) ); k++; } while(*(str + k)!=' |