如果有51单片机对串口UART学习的基础,学这个应该会很容易;
本篇博文是基于STM32f103ZET6芯片和3.5.0库开发方式的博文;
如有不足指出,还望多多指教;
串口框图
工程建立后,需要在工程中添加stm32f10x_usart.h和stm32f10x_usart.c文件;
串口设置的一般步骤
①串口时钟使能,GPIO时钟使能
②串口复位
③GPIO端口模式设置
④串口参数初始化
⑤开启中断并初始化NVIC(需要开启中断时开启)
⑥使能串口
⑦编写中断处理函数
串口设置的相关函数以及解释
1.串口使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USARTx,ENABLE);
2.串口复位
解释一下复位:当外设出现异常的时候通过复位设置实现该外设的复位,然后重新设置这个外设达到让其重新工作的目的。一般在系统刚开始的时候,都会执行复位该外设的操作。下面是复位所需的函数;
void USART_DeInit(USART_TypeDef* USARTx); //复位串口
3.串口参数初始化
void USART_Init(USART_TypeDef* USARTx , USART_InitTypeDef* USART_InitStruct);
解释一下这个函数的参数:
第一个入口参数是指定初始化串口标号,比如选择USART1或USART2等;
第二个入口参数是USART_InitTypeDef类型的结构体指针,这个结构体指针的成员变量用来配置串口的波特率,字长,停止位,奇偶校验位,硬件数据流控制,收或发模式等;
关于串口初始化的配置操作看如下代码:
USART_InitStructure.USART_BaudRate = bound; //设置波特率:9600,115200……
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置发送模式,即字长度
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStructure.USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl
= USART_HardwareFlowControl_None; //无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //收发模式
USART_Init(USARTx,&USART_InitStructure); //初始化串口
4.数据发送或接收
(截图来自STM32使用手册)
STM32的对帧的收和发通过双寄存器USART_DR(原名串口数据寄存器,包含USART_TDR和USART_RDR)来实现数据的暂存。向该寄存器写入数据的时候串口会自动发送,当接收的时候数据会存储在该寄存器中;
发送:
void USART_SendData(USART_TypeDef * USARTx , uint16_t Data);
接收:
void USART_ReceiveData(USART_TypeDef * USARTx);
5.串口状态
通过串口状态寄存器UASRT_SR来获取当前串口寄存器状态。
只说两个位:
RXNE(读数据寄存器非空):1=数据接收完成,现在可以读出来了;
RXNE清零方式:
1)向该位写0,则USART_DR会被清零
2)读取USART_DR的值,该位会被清零;
TC(发送完成) : 1= 写入到USART_DR数据发送完成;(开在此处设置中断)
TC清零方式:
1)向该位写0,则USART_DR会被清零
2)读USART_SR寄存器,或向USART_DR写数据;
获取USART_SR的位状态
FlagStatus USART_GetFlagStatus(USART_TypeDaf * USARTx , uint16_t USART_FLAG); //第一个入口参数决定串口,第二入口参数表示我们要访问的是串口的那个状态;
判断是否接收完成:
USART_GetFlagStatus(USARTx , USART_FLAG_RXNE);
判断是否发送完成:
USART_GetFlagStatus(USARTx , USART_FLAG_TC);
6.串口使能
USART_Cmd(USARTx , ENABLE); //使能串口
7.开启串口响应中断。(当需要中断的时候)
void USART_ITConfig(USART_TypeDef * USARTx , uint16_t USART_IT , FunctionalState NewState); //第二个入口参数是表示使能串口的类型,也就是使能那种中断;
例如:在接收一个数据完成后要产生中断,那么中断方式是:
USART_ITConfig(USARTx , USART_IT_RXNE , ENABLE); //开启中断,接收到数据中断
再例如,当发送结束要进行中断时:
USART_ITConfig(USARTx , USART_IT_TC , ENABLE);
8.获取相应中断状态
(读到这里要转换一下角度:这里是从中断讲到串口,想像一下一个场景,当一个中断发生的时候,我们不一定就知道是那个中断,这时候标准库提供了能获取具体是那一个中断引起的中断)
ITStatus USART_GetITStatus(USART_TypeDef* USARTx , uint16_t USART_IT);
例如:
ITStatus USART_GetITStatus(USART1 , USART_IT_TC);
//若ITStatus = SET,则说明串口发送完成中断发生;否则没发生;
对一些知识的补充理解
奇偶校验:
起因:主机和从机在进行数据的发送和接收时候,有可能因为各种原因导致发送的数据(位)发生错误,这时候需要一个纠错机制,即奇偶校验;
特点:奇偶校验能检查出信息传输过程中的部分误码(1位误码可以检查出来,但是2位和2位以上的误码就不能检查出来了),同时他不能纠错,发现错误后,只能从新发送,但是由于实现简单,从而广泛使用;有些检错方法具有自动纠错功能。比如循环冗余码(CRC)检错等;
纠错实现的过程:
奇校验:所有传送的数位(包括校验位)1的个数为奇数;
1000 0000 0
1100 0000 1
偶校验:所有传送的数位(包括校验位)1的个数为偶数;
1000 0000 1
1100 0000 0
硬件流控制
起因:
数据在两个串口之间传输时,常常会出现丢失数据的现象,或者两台计算机的处理速度不同,如台式机与单片机之间的通讯,接收端数据缓冲区已满,则此时继续发送来的数据就会丢失。现在在网络上通过modem进行数据传输,这个问题就尤为突出。流控制能解决这个问题,当接收端数据处理不过来时,就发出“不再接收”的信号,发送端就停止发送,直到收到“可以继续发送”的信号再发送数据。因此流控制可以控制数据传输的进程,防止数据的丢失。 pc机中常用的两种流控制是硬件流控制(包括rts/cts、dtr/cts等)和软件流控制xon/xoff(继续/停止);
今天只介绍硬件数据流控制,因为暂时只用到这个;
硬件流控制常用的两种方式:rts/cts流控制和dtr/dsr(数据终端就绪/数据设置就绪)流控制
实现硬件流控制的必要条件:必须将相应的电缆线连上,用rts/cts(请求发送/清除发送)流控制时,应将通讯两端的rts、cts线对应相连,数据终端设备(如计算机)使用rts来起始调制解调器或其它数据通讯设备的数据流,而数据通讯设备(如调制解调器)则用cts来起动和暂停来自计算机的数据流。
工作原理以及编程原理(结合图片):
编程时根据接收端缓冲区大小设置一个高位标志(可为缓冲区大小的75%)和一个低位标志(可为缓冲区大小的25%),当缓冲区内数据量达到高位时,在接收端将cts线置低电平(送逻辑0),当发送端的程序检测到cts为低后,就停止发送数据,直到接收端缓冲区的数据量低于低位而将cts置高电平。rts则用来标明接收设备有没有准备好接收数据。
什么是同步,什么是异步?
1.异步通信方式的特点:
异步通信是按字符传输的。每传输一个字符就用起始位来进来收、发双方的同步。不会因收发双方的时钟频率的小的偏差导致错误。
这种传输方式利用每一帧的起、止信号来建立发送与接收之间的同步。特点是:每帧内部各位均采用固定的时间间隔,而帧与帧之间的间隔时随即的。接收机完全靠每一帧的起始位和停止位来识别字符时正在进行传输还是传输结束。
2.同步通信方式的特点:
进行数据传输时,发送和接收双方要保持完全的同步,因此,要求接收和发送设备必须使用同一时钟。
优点是可以实现高速度、大容量的数据传送;缺点是要求发生时钟和接收时钟保持严格同步,同时硬件复杂。
(SPI总线是一个典型代表)
3.相似处:
可以这样说,不管是异步通信还是同步通信都需要进行同步,只是异步通信通过传送字符内的起始位来进行同步,而同步通信采用共用外部时钟来进行同步。所以,可以说前者是自同步,后者是外同步。
上面的1.2.3.来自前辈的解释:
http://blog.csdn.net/changyourmind/article/details/52471465
基于USART1(PA9—Tx1,PA10—Rx1)串口配置代码如下:
void uart1_init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
#ifdef EN_USART_Rx
NVIC_InitTypeDef NVIC_InitStructure;
#endif
//1.串口时钟使能,GPIO时钟使能;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA , ENABLE);
//2.串口复位
USART_DeInit(USART1); //复位串口1
//3.GPIO端口模式设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //USART1_TX PA9
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出速度为50MHz
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //USART1_RX PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
//这里是输入,所以没有输出速度
GPIO_Init(GPIOA,&GPIO_InitStructure);
//4.串口参数初始化
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | 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);
#ifdef EN_USART1_RX
//5.初始化NVIC
NVIC_initStructure.NVIC_IRQChannel = USART1_IRQn; //打开USART1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级初始化
//6.开启中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启中断(RXNE接收中断)
#endif
//使能串口
USART_Cmd(USART1,ENABLE);
}