STM32F10x 学习笔记之USART实现串口通讯

2019-08-26来源: eefocus关键字:STM32F10x  USART  串口通讯

STM32F10x 系列单片机中都包含了USART 模块,所谓USART,就是通用同步异步收发器。通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。它支持同步单向通信和半双工单线通信,也支持LIN(局部互连网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。


从前面的介绍可知USART模块功能非常的强大。这里我只简单讲讲如何用USART模块来实现标准EIA-232 串口通讯。


用过单片机的人肯定都接触过串口,设置串口无非就是设置波特率、数据位、停止位、奇偶校验位。发送接收也就三种基本方式,轮询、中断和DMA。STM32F10x 的USART 模块也不过如此。所以我重点讲讲我在调试代码时犯得各种错误,那些很容易得到的代码就不详细的讲解了。


首先说说我的硬件环境。还是那块神舟4号开发板,用的是串口2,对应的是USART2。默认情况下USART2是连接到IO端口A的,但是我这里需要将USART的管腿重定向到IO端口D上。具体的管腿的关系参见下表。这个表是从STM32参考手册上拷下来的。

初始化USART的代码很简单。USART2 连接到APB1 总线上了,先要打开USART2的时钟,然后设置波特率一类的参数。


  1. USART_InitTypeDef USART_InitStructure;  

  2.   

  3. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);  

  4. USART_InitStructure.USART_BaudRate = 9600;  

  5. USART_InitStructure.USART_WordLength = USART_WordLength_8b;  

  6. USART_InitStructure.USART_StopBits = USART_StopBits_1;  

  7. USART_InitStructure.USART_Parity = USART_Parity_No;  

  8. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  

  9. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;  

  10. USART_Init(USART2, &USART_InitStructure );   


这样设置了还不能使用。因为我们将USART2 重定向了。重定向操作需要写复用重映射和调试I/O配置寄存器(AFIO_MAPR)。GPIO_PinRemapConfig() 可以完成这项任务。


  1. GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);  

光这样操作还不够。STM32参考手册上有这么一段话:

对寄存器AFIO_EVCR,AFIO_MAPR和AFIO_EXTICRX进行读写操作前,应当首先打开AFIO的时钟。参考第6.3.7节APB2外设时钟使能寄存器(RCC_APB2ENR)。

所以需要先打开AFIO的时钟。因此,USART2的重定向需要两步操作:


  1. RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  

  2. GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);  

我原以为这样就能工作了,可是结果还是什么都没有输出。没办法只能继续研究。在读GPIO的相关章节时看到下图让我恍然大悟。


USART2的输入输出都是借用PD口管腿,PD 口的时钟却还没给。用到的几个IO 端口也没有设置相应的输入输出状态。在读到8.1.9 复用功能配置这一小节时发现了如下的表格。


按照上面给出的配置,写好程序:


  1. GPIO_InitTypeDef GPIO_InitStructure;  

  2. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD , ENABLE);  

  3. /* Configure USART Tx as alternate function push-pull */  

  4. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  

  5. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  

  6. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

  7. GPIO_Init(GPIOD, &GPIO_InitStructure);  

  8.       

  9. /* Configure USART Rx as input floating */  

  10. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  

  11. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  

  12. GPIO_Init(GPIOD, &GPIO_InitStructure);  


再次测试,一切正常。

发送一个字符的函数可以这么写:


  1. void UART_PutChar(USART_TypeDef* USARTx, uint8_t Data)  

  2. {  

  3.     while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET ) {};  

  4.     USART_SendData (USARTx, Data);  

  5. }  

这个函数可以手工优化一下,里面的两个函数调用都可以去掉,甚至于这个函数可以用汇编来实现或者写成inline 函数。不过这里只是个示例代码,没有考虑这些。


发送字符串的函数如下:


  1. void UART_PutStr (USART_TypeDef* USARTx, uint8_t *str)  

  2. {  

  3.     while (0 != *str)  

  4.     {  

  5.         UART_PutChar(USARTx, *str);  

  6.         str++;  

  7.     }  

  8. }  


上面串口初始化的代码可以放到一个函数中:


  1. void USART2_init(void)  

  2. {  

  3.     GPIO_InitTypeDef GPIO_InitStructure;  

  4.     USART_InitTypeDef USART_InitStructure;  

  5.   

  6.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);  

  7.       

  8.     /* Configure USART Tx as alternate function push-pull */  

  9.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  

  10.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  

  11.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

  12.     GPIO_Init(GPIOD, &GPIO_InitStructure);  

  13.       

  14.     /* Configure USART Rx as input floating */  

  15.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  

  16.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  

  17.     GPIO_Init(GPIOD, &GPIO_InitStructure);  

  18.       

  19.     GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);  

  20.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);  

  21.   

  22.     USART_InitStructure.USART_BaudRate = 9600;  

  23.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;  

  24.     USART_InitStructure.USART_StopBits = USART_StopBits_1;  

  25.     USART_InitStructure.USART_Parity = USART_Parity_No;  

  26.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  

  27.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;  

  28.     USART_Init(USART2, &USART_InitStructure );   

  29.   

  30.     USART_Cmd(USART2, ENABLE);  

  31. }  


今天先写这么多。接收字符的函数与发送字符的函数差不多,但是这种轮询方式效率很低,不建议使用。下次写一篇介绍如何用中断方式发送接收串口数据,中断方式的效率会高很多。如果有时间再写一篇DMA方式发送接收数据的文章。


这次讲讲利用串口收发中断来进行串口通讯。STM32 上为每个串口分配了一个中断。也就是说无论是发送完成还是收到数据或是数据溢出都产生同一个中断。程序需在中断处理函数中读取状态寄存器(USART_SR)来判断当前的是什么中断。下面的中断映像图给出了这些中断源是如何汇合成最终的中断信号的。图中也给出了如何控制每一个单独的中断源是否起作用。


另外,Cortex-M3 内核中还有个NVIC,可以控制这里的中断信号是否触发中断处理函数的执行,还有这些外部中断的级别。关于NVIC 可以参考《ARM CortexM3 权威指南》,里面讲解的非常详细。

简单的说,为了开启中断,我们需要如下的代码:


  1. NVIC_InitTypeDef NVIC_InitStructure;  

  2. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;  

  3. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  

  4. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  

  5. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  

  6. NVIC_Init(&NVIC_InitStructure);  

  7.   

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

  9. USART_ITConfig(USART1, USART_IT_TXE, ENABLE); // 开启发送中断  

这里多说一句,串口的发送中断有两个,分别是:

  1. l发送数据寄存器空中断(TXE)

  2. l发送完成中断(TC)


一般来说我们会使用发送数据寄存器空中断,用这个中断发送的效率会高一些。

中断处理函数的框架如下,如果检测到错误就清除错误,收到数了就处理。发完当前数据了就发下一个。


  1. void USART1_IRQHandler(void)  

  2. {  

  3.     unsigned int data;  

  4.   

  5.     if(USART1->SR & 0x0F)   

  6.     {  

  7. // See if we

[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] ..[15]

关键字:STM32F10x  USART  串口通讯

编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic472456.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:stm32F4 串口DMA+环形缓冲区的实现
下一篇:STM32——串口通信升级版(队列方式)

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM32F10x_ADC三通道DMA连续转换(3通道、软件单次触发)

)本文讲述的知识点相对较多,若初次学习STM32的ADC转换功能,可以参考我另外一篇相对简单一点的文章:STM32F10x_ADC1单通道单次采集关于本文的更多详情请往下看。Ⅱ、实例工程下载笔者针对于初学者提供的例程都是去掉了许多不必要的功能,精简了官方的代码,对初学者一看就明白,以简单明了的工程供大家学习。笔者提供的实例工程都是在板子上经过多次测试并没有问题才上传至360云盘,欢迎下载测试、参照学习。提供下载的软件工程是基于Keil(MDK-ARM) V5版本、STM32F103ZE芯片,但F1其他型号也适用(适用F1其他型号: 关注微信,回复“修改型号”)。STM32F10x_ADC三通道DMA连续转换(3通道、软件单次触发)实例
发表于 2019-09-18
STM32F10x_ADC三通道DMA连续转换(3通道、软件单次触发)

STM32F10x JTAG端口重映射

STM32F10x系列的MCU复位后,PA13/14/15和PB3/4默认配置为JTAG功能。为了充分利用MCU I / O口的资源,会把这些端口设置为普通I/O口。【相关代码】: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);     //使能PB端口时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);        //开启AFIO时钟    // 改变指定管脚的映射
发表于 2019-09-03
STM32F10x JTAG端口重映射

STM32F10x TIM1 CH3/CH4 的重映射PWM输出

TIMx在ARR上的预装载寄存器  TIM_CtrlPWMOutputs(TIM1,ENABLE);        //MOE 主输出使能,高级定时器必须开启这个  TIM_Cmd(TIM1, ENABLE);  //使能TIM1  }    .H文件#ifndef __TIMER_H#define __TIMER_H#include "stm32f10x.h"  void TIM1_PWM_Init(u16 arr,u16 psc); #endif2.应用版源码   
发表于 2019-08-21

Stm32F4x采用外部触发法测矩形波频率和占空比

的值,进行时间计算,得出1秒内高电平的时间,进而计算出占空比。之后同样进行线性补偿。二、 方案具体实施主函数编写。在timer.h进行函数声明。TIM3初始化,开启中断。TIM3的中断子函数编写。TIM2初始化函数编写,将I/O设置为复用功能。TIM2模式选择函数编写,进行模式切换时注意将外设TIM2重设为省缺值,否则定时器会自动重装之前的ARR和PSC值,将无法进行模式切换。三、 Matlab线性拟合曲线1、 频率误差曲线拟合收集测量值。第一行为函数信号发生器显示频率,第二行为实际测量值,第三行为误差值。将数据导入Matlab进行数值分析,拟合曲线。以实际测量值为X,频率误差值为Y,采用多项式拟合,得到X-Y关系式。占空比误差
发表于 2019-08-20
Stm32F4x采用外部触发法测矩形波频率和占空比

基于stm32f10x的超声波模块HC-SR04的测距示例

一.所需材料:1任何一种型号的stm32f10x的微控制器2.HC-SR04模块3.安装串口驱动与串口助手(这里用的火哥的串口调试助手)4.ST-link或者串口等下载方式都可以二。超声波原理网上一大堆,这里我就大体说一下:单片机先给TRIG一个大于10us的高电平,然后模块ECHO引脚会发出一个高电平,检测高电平的时间乘声速便可算出距离。这里ECHO发出也接收,所以检测的时间,假设按秒算,然后乘170便是以m为单位的距离。三.源代码分析1.接口定义://由于只是用的定时器的基本计时功能,所以IO口随便找两个便可以#define HCSR04_PORT           
发表于 2019-08-20
基于stm32f10x的超声波模块HC-SR04的测距示例

STM8L USART串口使用

STM8L上有多个串口,最多可达5个,分别为USART1~USART5,但依据型号不同,搭载数量并不相同。 以STM8L052R8为例,其只具有USART1~USART3。 因为STM8系列功能众多,很多Pin都是复用的,因此使用前必须检查STML的参考手册。 通过手册可知,以USART1为例,RX/TX可以使用以下的管脚,默认是PC2/PC3, 如果要变更,需要修改SYSCFG remap control register 1 (SYSCFG_RMPCR1)的5:4位进行切换。Bits 5:4 USART1TR_REMAP[1:0]: USART1_TX and USART
发表于 2019-09-17

小广播

何立民专栏

单片机及嵌入式宝典

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

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved