【STM32】串口相关配置寄存器、库函数(UART一般步骤)

发布者:DazzlingSmile最新更新时间:2019-03-13 来源: eefocus关键字:STM32  串口  配置寄存器  库函数 手机看文章 扫描二维码
随时随地手机看文章

STM32F1xx官方资料:


《STM32中文参考手册V10》-第25章通用同步异步收发器(USART)


 


串口相关配置寄存器

状态寄存器(USART_SR)



状态寄存器适用于检测串口此时所处的状态。它能够检测到的状态有:发送寄存器空位、发送完成位、读数据寄存器非空位、检测到主线空闲位、过载错误为等等。


这边主要关注两个位:RXNE和TC(第5、6两位)。


RXNE(读数据寄存器非空):当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来了(即RDR移位寄存器中的数据被转移到USART_DR寄存器中)。这时候要做的就是尽快读取USART_DR,从而将该位清零,也可以向该位写0,直接清除。


TC(发送完成):当该位被置1的时候,表示USART_DR内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:读USART_SR,写USART_DR;直接向该位写0。


数据寄存器(USART_DR)



USART_DR实际是包含了两个寄存器,一个专门用于发送的TDR,一个专门用于接收的RDR。进行发送数据操作时,往USART_DR写入数据会自动存储在TDR内;当进行读取数据操作时,向USART_DR读取数据会自动提取RDR数据。


串行通信时一位一位传输的,所以TDR和RDR寄存器都是介于系统总线和移位寄存器间的;发送数据时把TDR内容转移到发送移位寄存器上,接收数据时则是把接收到的每一位顺序保存在接收移位寄存器内进而转移到RDR。


波特率寄存器 (USART_BRR)



波特率寄存器包括定义了两个部分:DIV_Mantissa(整数部分)和DIV_Fraction(小数部分)。


控制寄存器(USART_CRx)



控制寄存器主要是设置USART使能、检验控制使能、校验选择(奇校验偶校验)、PE中断使能、发送缓冲区空中断使能、发送完成中断使能、接收缓冲区非空使能、发送使能、接受使能、字长等等。


 


USART外设引脚复用

当使用USART的时候,GPIO需要引脚复用,下图介绍了USART的引脚设置:




波特率计算方法

学习波特率之前,首先了解一下通讯速率。通讯速率通常是以比特率来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)。容易和比特率混淆的概念是“波特率”,它表示每秒传输了多少码元。


码元是通讯信号调制的概念,时间间隔相同的符号来表示一个二进制数字,这样的信号就称为码元。如常见的通讯传输中:用0V表示数字0,5V表示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;若传输中,有0V、2V、4V和6V分别表示00、01、10、11,那么每个码元可以表示四种状态,两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表示比特率,其实二者是有区别的。 


异步通讯由于没有时钟信号,所以两个通讯设备需要规约好波特率,即每个码元的长度,以便对信号进行解码。常见的波特率为4800、9600、115200。


 


上面的公式中,fpclkx是给串口的时钟(PCLK1用于USART2、3、4、5,PCLK2用于USART1);USARTDIV是一个无符号定点数。我们只要得到USARTDIV的值,就可以计算出波特率。




串口操作相关库函数


1个初始化函数

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

作用:用于串口波特率,数据字长,奇偶校验,硬件流控以及收发使能等配置的初始化。


2个使能函数

void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

作用:前者使能串口,后者使能串口的相关中断。


2个数据收发函数

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

作用:前者发送数据到串口,后者从串口接收数据。


4个状态位函数

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);

ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);

void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

作用:前两者获取(或清除)状态标志位,后两者为获取(或清除)中断状态标志位。


 


串口操作的一般步骤

GPIO时钟使能,串口时钟使能。调用函数:RCC_APB2PeriphClockCmd()(可以参阅:【STM32】STM32端口复用和重映射(AFIO辅助功能时钟) 的部分内容);

串口复位(这一步不是必须的)。调用函数:USART_DeInit();

GPIO外设功能下的端口模式设置。调用函数:GPIO_Init();

串口参数初始化。调用函数:USART_Init();

开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)。调用函数:NVIC_Init();USART_ITConfig();

使能串口。调用函数:USART_Cmd();

编写中断处理函数。调用函数:USARTx_IRQHandler();

串口数据收发。调用函数:USART_SendData();USART_ReceiveData();

串口传输状态获取。调用函数:USART_GetFlagStatus();USART_ClearITPendingBit();

下面按照这个一般步骤来进行一个简单的串口程序:


void My_USART1_Init(void)

{

GPIO_InitTypeDef GPIO_InitStrue;

USART_InitTypeDef USART_InitStrue;

NVIC_InitTypeDef NVIC_InitStrue;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIO端口使能

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口端口使能


GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP;

GPIO_InitStrue.GPIO_Pin=GPIO_Pin_9;

GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;

GPIO_Init(GPIOA,&GPIO_InitStrue);


GPIO_InitStrue.GPIO_Mode=GPIO_Mode_IN_FLOATING;

GPIO_InitStrue.GPIO_Pin=GPIO_Pin_10;

GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;

GPIO_Init(GPIOA,&GPIO_InitStrue);


USART_InitStrue.USART_BaudRate=115200;

USART_InitStrue.USART_HardwareFlowControl=USART_HardwareFlowControl_None;

USART_InitStrue.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;

USART_InitStrue.USART_Parity=USART_Parity_No;

USART_InitStrue.USART_StopBits=USART_StopBits_1;

USART_InitStrue.USART_WordLength=USART_WordLength_8b;


USART_Init(USART1,&USART_InitStrue);//


USART_Cmd(USART1,ENABLE);//使能串口1


USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启接收中断


NVIC_InitStrue.NVIC_IRQChannel=USART1_IRQn;

NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE;

NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=1;

NVIC_InitStrue.NVIC_IRQChannelSubPriority=1;

NVIC_Init(&NVIC_InitStrue);


}

 

void USART1_IRQHandler(void)

{

u8 res;

if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)

 {

     res= USART_ReceiveData(USART1); 

     USART_SendData(USART1,res);   

  }

}

 

 int main(void)

 {

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

My_USART1_Init();

while(1);

 

 }

usrt_init函数


此处的GPIO端口模式设置,是在端口复用情况下的端口模式设置。至于在复用功能下,GPIO的模式怎么设置,可以查看手册《STM32中文参考手册》p110的内容;

USART_BaudRate波特率的设定是直接写值进去的,MDK5是没有预设的宏定义来选择的;

USART_Mode模式选择用USART_Mode_Tx|USART_Mode_Rx来表示发送使能和接收使能;

NVIC_IRQChannel中断通道,这是在stm32f10x.h开头部分以IRQn结尾的宏定义。

USART1_IRQHandlar函数


USART1_IRQHandlar函数是中断处理函数,不能随意定义的,需要遵循MDK的定义。这些函数的声明在启动文件startup_stm32f10x_hd.s文件中,可以在其中找到中断处理函数的名称。

中断处理函数中首先进行状态位判断,这是非常有必要的。因为可能我们在串口的中断设置中,设置了不止一处的中断,但是每个中断进入的中断处理函数都是同一个,这就要求我们要在中断处理函数中通过状态位的判断再进行接下来的操作。

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 

这里通过这个判断来确定是否接收中断。这个函数的返回值是ITStatus类型,它的定义是:


typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;

SET代表着1(接收到中断),RESET代表着0(未接收中断)。


除了中断处理函数的ITStatus需要判断之外,通常发送数据、接收数据之后也需要判断。比如,下面一段程序:

USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据

while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束

即运用while循环,在向串口发送数据之后,通过判断等待发送结束。


这段程序还有一个问题曾经困扰了我很久很久都没有领悟清楚:


在中断处理函数中,使用一下程序来接收数据:


u8 res;

     res= USART_ReceiveData(USART1); 

由于res是一个u8类型的数,若一次性向串口发送很多的数据,串口使用此程序进行接收的时候,一个u8很多装不下。而这个函数中使用的是if判断,如果一次装不下,if判断程序就走一遍,怎么把所有的数据全部接受呢?


解答:其实我们看一下RXNE标志位引发的中断,当RDR移位寄存器中的数据被转移到USART_DR寄存器中时,也就是有数据可以被接收到的时候,该位置1,引发中断,进入中断处理函数。但是我们在这个中断处理函数中,并没有和其他中断一样做清除中断位的操作,这就导致该位一直是1,不断地进入中断。那么什么时候停止呢?当数据接收完毕了,此时该位清零。


 


printf函数

printf函数支持的代码在SYSTEM文件夹下的usart.c文件中定义了,加入下面的代码就可以通过printf函数向串口发送需要的内容。这段代码不需要修改,只要引入到usart.h即可使用。


#if 1

#pragma import(__use_no_semihosting)             

//标准库需要的支持函数                 

struct __FILE 

int handle; 

 

}; 

 

FILE __stdout;       

//定义_sys_exit()以避免使用半主机模式    

_sys_exit(int x) 

x = x; 

//重定义fputc函数 

int fputc(int ch, FILE *f)

{      

while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   

    USART1->DR = (u8) ch;      

return ch;

}

#endif 

整个代码块比较奇怪,重定义了fputc函数之后,也并没有printf函数的显示声明,这样就可以向串口发送内容了。看了许久,也没有看明白是怎么一回事……

关键字:STM32  串口  配置寄存器  库函数 引用地址:【STM32】串口相关配置寄存器、库函数(UART一般步骤)

上一篇:【STM32】STM32固件库(标准外设库)
下一篇:【STM32】串口通信基本原理(超基础、详细版)

推荐阅读最新更新时间:2024-03-16 16:25

不可错过的单片机STM32的5个时钟源知识
  众所周知STM32有5个时钟源HSI、HSE、LSI、LSE、PLL,其实他只有四个,因为从上图中可以看到PLL都是由HSI或HSE提供的。   其中,高速时钟(HSE和HSI)提供给芯片主体的主时钟.低速时钟(LSE和LSI)只是提供给芯片中的RTC(实时时钟)及独立看门狗使用,图中可以看出高速时钟也可以提供给RTC。   内部时钟是在芯片内部RC振荡器产生的,起振较快,所以时钟在芯片刚上电的时候,默认使用内部高速时钟。而外部时钟信号是由外部的晶振输入的,在精度和稳定性上都有很大优势,所以上电之后我们再通过软件配置,转而采用外部时钟信号.   高速外部时钟(HSE):以外部晶振作时钟源,晶振频率可取范围为4~16MHz
[单片机]
STM32--UART异步通信学习
字符发送的过程描述:在UART的发送过程中先将数据输入到发送数据寄存器中(TDR)此时(TXE)被硬件置1,之后TDR寄存器将数据串行移入到发送移位寄存器中,将数据在TX端口发送,此时(TC)被硬件置1。 发送与接收是逆过程。 UART发送配置步骤: 1.通过USART_CR1寄存器上置位UE来激活USART。 2.编程USART_CR1的M位来定义字长。 3.在USART_CR2中编程停止位的位数。 4.如果采用多缓冲器通信,配置USART_CR3中的DMA使能位(DMAT)。按多缓冲器通信中的描述配置DMA寄存器。 5.利用USART_BRR寄存器选择要求的波特率。 6. 设
[单片机]
聊聊STM32芯片的DFU编程及相关话题
相当部分的 STM32芯片都带USB模块,有时我们会考虑利用STM32芯片的USB模块进行程序代码的下载或升级。USB协议中有专门针对设备固件升级的类协议,即可以通过DFU类协议进行产品固件的加载或更新。 关于STM32产品的DFU程序下载和升级,ST官方有相关的资料文档。可以去 www.stmcu.com.cn 或者去 www.st.com 搜索DFUse下载相关资料。 有个用户手册UM0412详细介绍了如何利用ST官方软件工具DfuSe进行相关编程操作。顺便提醒下,下载DfuSe安装包解压运行DfuSe_Demo_Vxx_Setup.exe之后,还不算安装完成,还得安装针对DfuSe的WINDOWS环境下的
[单片机]
聊聊<font color='red'>STM32</font>芯片的DFU编程及相关话题
STM32中断优先级详解
一、STM32中断优先级属性 STM32有两种优先级属性,分别为抢占式优先级和响应式优先级。其中,响应式优先级也称为副优先级。 具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套。 当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。 二、STM32中断优先级数目分组 STM32为了适应不同的优先级组合
[单片机]
嵌入式STM32学习笔记(3)——pwm波及呼吸灯
写pwm波函数可以调用stm32固件库函数直接生成,也可以通过中断来写pwm波;下面就介绍这两种方法,这里先说一下呼吸灯,其原理就是让LED灯由暗变亮再由亮变暗循环,类似呼吸的效果,亮-暗是一个大周期,而LED灯亮或暗是由其刷新的占空比决定,高电平时间占比长则亮,反之则暗; stm32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达4路的 PWM 输出,这样, STM32 最多可以同时产生 30 路 PWM 输出。关于映射及原理大家可查手册吧,这里不做具体叙述了;个人见解:很多知识用到再仔细
[单片机]
嵌入式<font color='red'>STM32</font>学习笔记(3)——pwm波及呼吸灯
联盛德 HLK-W801(一):串口下载复位和使用其他串口工具调试的问题分析和解决方法
开发环境 系统:win10 开发板:联盛德 HLK-W801开发板 程序下载方式:基于串口的Upgrade_Tools_V1.4.8下载工具 发现问题 usb连接电脑,用官方提供的Upgrade_Tools_V1.4.8下载工具,当打开点击打开串口的时候,芯片会复位。 当我想用MobaXterm等其他串口工具调试芯片的时候,发现根本无法使用,只要打开串口,w801就会停止工作。 问题出现的原因 通过查看电路图发现一个非常有意思的问题,如图: 没错,串口芯片ch340N的RTS引脚竟然和w801的reset引脚接在一起。 普及一下知识:我们平时使用串口一般只是用 TX、RX、GND这三根线,所以有很多人认为串口就三根线
[单片机]
联盛德 HLK-W801(一):<font color='red'>串口</font>下载复位和使用其他<font color='red'>串口</font>工具调试的问题分析和解决方法
STM32CubeMx启动串口调试功能Printf调试
## 概述 项目中往往需要调试信息,调试stm32的时候,需要标准库里面的printf函数。在keil MDK环境下重定向printf与keil C51不同,由于本人使用了STM32CubeMX生成工程模板,HAL_USART_Transmit函数即是模板里串口输出的函数。由于printf最终是调用fputc输出数据,fputc是一个弱引用(weak)函数,覆写即可重定向printf。 代码清单 /* USER CODE BEGIN Includes */ #include FreeRTOS.h #include task.h #include queue.h #include stdio.h /* USER CODE
[单片机]
STM32CubeMx启动<font color='red'>串口</font>调试功能Printf调试
STM32触摸按键原理和电路设计
01触摸按键原理 触摸使用RC充放电原理: RC电路是指由电阻R和电容C组成的电路,它是脉冲产生和整形电路中常用的电路。 充电过程: 电源通过电阻给电容充电,由于一开始电容两端的电压为0,所以电压的电压都在电阻上,这时电流大,充电速度快。随着电容两端电压的上升,电阻两端的电压下降,电流也随之减小,充电速度小。充电的速度与电阻和电容的大小有关。电阻R越大,充电越慢,电容C越大,充电越慢。衡量充电速度的常数t(tao)=RC。 放电过程: 电容C通过电阻R放电,由于电容刚开始放电时电压为E,放电电流I=E/R,该电流很大,所以放电速度很快。随着电容不断的放电,电容的电压也随着下降。电流也很快减小。电容的放电速度与RC有关,R的阻值
[单片机]
<font color='red'>STM32</font>触摸按键原理和电路设计
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved