STM32学习之使用库函数编写串口通信

2020-07-01来源: eefocus关键字:STM32  库函数  串口通信

注:使用的芯片是STM32F103ZET6


一、串口初始化部分的程序

1.初始化时钟

  因为要使用串口1这个外设,因此在使用之前就需要开启串口1的时钟,并且之后还需要设置IO输入输出的模式,在这里我们也需要开启GPIO的时钟。程序如下:


//初始化串口时钟

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

 //初始化GPIO时钟

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);


  可是为什么要初始化时钟呢在《STM32中文参考手册_V10》的RCC中有这样一句话“当外设时钟没有启用时,软件不能读出外设寄存器的数值,返回的数值始终是0x0。”,也就是只有开启了对应外设的时钟,才能操作外设。

  该部分解释参考如下文档:

https://blog.csdn.net/dp29sym41zygndvf/article/details/82321133


2. 初始化GPIO

  初始化GPIO就是设置使用端口的引脚、模式以及速度。因为使用的是串口1,所以在STM32F103ZET6芯片上USART_TX、USART_RX分别对应的引脚是PIOA.9和GPIOA.10。引脚复用的功能不同,对应引脚模式设置就不同,详细的可以参考《STM32中文参考手册_V10》的8.1.11,下表就是使用参考手册里的表21 USART。


这里是引用《STM32中文参考手册》里的表21

截取《STM32中文参考手册》里的图

  一般都设置串口的配置为全双工模式,因此设置GPIOA.9的模式为推挽复用输出,设置GPIOA.10的模式为浮空输入。引脚的速度没有太大的限制。该端程序如下:


GPIO_InitTypeDef GPIO_InitStruct;


//初始化GPIO

 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;  //GPIOA.9是USART1的TX

 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //设置其模式为推挽复用输出

 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz; //速度为10MHz

 GPIO_Init(GPIOA,&GPIO_InitStruct);

 

 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;  //GPIOA.10是USART1的RX

 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置其模式为浮空输入

 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;  //速度为10MHz

 GPIO_Init(GPIOA,&GPIO_InitStruct);


3. 初始化串口

  初始化串口就是设置串口的停止位、奇偶校验位、字长、波特率、串口的模式以及硬件流控制。

   停止位:可以设置停止位为0.5个、1个、1.5个和2个停止位,当通常都设置1个停止位。

  字长:可以设置8个数据位或9个数据位。

  奇偶校验位:当设置有8个数据位时,就没有奇偶校验位;当设置有9个数据位时,第9位为奇偶校验位,以保证传送数据的准确性。如果设置为奇校验,如果前8个数据位中1的个数为奇数,则该位为0,如果为偶数,该位为1。若为偶校验,如果1的个数为偶数该位为0,为奇数该位为1。


  波特率:就是数据传输的速率,接收方的波特率要与发送方的波特率相同。

  串口的模式:设置发送使能和接受使能

  硬件流控制:这个只有在半双工时使用,一般不开启这个功能。

  该段程序如下:


USART_InitTypeDef USART_InitStruct;


//初始化串口

 USART_InitStruct.USART_BaudRate = 115200; //设置波特率 

 USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //不使用这个

 USART_InitStruct.USART_Mode = (USART_Mode_Rx | USART_Mode_Tx);  //设置模式为即接受又发送

 USART_InitStruct.USART_Parity = USART_Parity_No;  //无奇偶校验位,也就是无第九位

 USART_InitStruct.USART_StopBits = USART_StopBits_1;  //1位停止位

 USART_InitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位

 USART_Init(USART1,&USART_InitStruct);


4.串口使能

  之前都只是设置串口的一些配置,并没有使串口开始工作,因此我们要使能串口,需要调用USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)这个函数,其最终是对控制寄存器1(USART_CR1)的位13的操作,如果该位为1,USART模块使能,若为0,禁止USART分频器和输出。


  该段程序如下:


//使能串口

 USART_Cmd(USART1,ENABLE);


  通常接受数据需要使用串口中断,而串口发送可以不使用串口中断。如果不需要开启串口中断,到这一步已经完成串口的初始化部分。如果需要用到串口中断,则再需要下面两个步骤。


5.初始化NVIC

  首先就是了解一下NVIC是什么,NVIC,就是Nested vectored interrupt controller,即嵌套向量中断控制器。


  在STM32F103ZET6芯片中有16个内核中断,60个可屏蔽中断,16个可编程中断优先级,怎么多中断怎么管理呢,这时候NVIC嵌套向量中断管理器就派上用场了。首先先对中断进行分组,如下表所示:


AIRCR[10:8] IP bit[7:4]分配情况 分配结果

0 111 0:4 0位抢占优先级,4位响应优先级

1 110 1:3 1位抢占优先级,3位响应优先级

2 101 2:2 2位抢占优先级,2位响应优先级

3 100 3:1 3位抢占优先级,1位响应优先级

4 011 4:0 4位抢占优先级,0位响应优先级

注:该表是参考正点原子第24讲 NVIC中断优先级管理的PPT


  这里所说的中断分组并不是说把各个中断分到了这几个组,而是说每一个组对应的抢占优先级和响应优先级的位数不同,根据自己需要多少位抢占优先级和几位响应优先级来设置,总共有4个位来控制。


  抢占优先级的讲解:抢占优先级高的中断可以打断抢占优先级低的中断,这就有点像51的中断优先级,0的时候抢占优先级最高。


  响应优先级的讲解:响应优先级高的不能打断响应优先级低的,只有在抢占优先级相同时起作用,当抢占优先级相同时,响应优先级高的,当和响应优先级低的中断相同时发生时,响应优先级高的中断先触发。


  上面说到总共有4个位来设置这两个优先级,因此24就为16,就有16级可编程中断优先级。

  举个例子:

  我要设置串口1的中断,其抢占优先级为2,响应优先级也为2。

  1.先设置中断分组。

  因为抢占优先级为2,则不能用组1,因为组1可设置的抢占优先级为0位,不可以设置,所以应该从组1到4选择,因为响应优先级为2,则不能用组4,组4的响应优先级没有分配位数,因此可以从组2到3选择,我选择组2。


  2.设置优先级。

  因为组2,抢占优先级分配2个位,响应优先级分配2个位,因此抢占优先级可以设置22个也就是4个优先级等级,响应优先级也是4个等级,即0到3。要设置抢占优先级为2,响应优先级也为2,把2写入NVIC_IP寄存器的位6和位7,设置抢占优先级;把2写入NVIC_IP寄存器的位4和位5,设置响应优先级优先级。


  在我们使用库函数编写程序时,void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);是进行中断分组,NVIC_Init(&NVIC_InitStruct);是对NVIC初始化,也就是设置抢占优先级,响应优先级,IRQ的串口通道打开和IRQ通道使能这四个参数。

  抢占优先级和响应优先级是几设置即就行。

  为什么要打开IRQ对应的串口1通道,因为IRQ是interrupt

request,即中断请求通道,也就是选择什么来触发中断,因为使用的是串口1,因此需要开启IRQ的串口1通道,选择使用串口1触发。使能串口1的IRQ通道就不用说了。

  该段程序如下:


NVIC_InitTypeDef NVIC_InitStruct;


//初始化NVIC(设置抢占优先级和响应优先级)

 NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;  //选择IRQ为USART1

 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能IRQ通道

 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; //设置抢占优先级

 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; //设置响应优先级

 NVIC_Init(&NVIC_InitStruct);  


  中断分组有关的库函数放在了主函数刚开始,没在在这部分程序中。


6.开启串口1中断

  在之前的步骤中把有关于串口的配置都配置好了,而这一步是设置串口1是通过什么触发中断的,比如是发送完成后触发中断还是接受到数据触发中断。在库函数中void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);是开启串口1中断的,这个函数第二个形参就是设置串口是通过哪种方式触发中断的,一般情况下我们使用的是USART_IT_RXNE,就是设置当接收到数据触发中断。

  该段程序如下:


USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //设置接受到数据触发中断

  之前设置串口1的IRQ通道,只是设置了是通过串口1来触发中断,也就是中断源,但具体是由串口1的哪种方式来控制触发中断,就需要这一步设置。


  到此,串口初始化已经全部配置完成。


二、中断服务函数部分

  由于这个程序只是编写一个简单的串口收发,所以在中断服务函数部分就编写了收到什么数据就发送什么数据,程序如下:


void USART1_IRQHandler(){  //串口1中断服务函数

 u8 Recv;

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

  Recv = USART_ReceiveData(USART1);

  USART_SendData(USART1,Recv);

 }

}


  进入串口1的中断服务函数后,先通过调用USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)这个函数来判断是不是因为接收到数据而触发中断的,或许该奇怪了,之前明明设置的就是接收到数据后触发中断,为什么进中断服务函数还需要再判断,这是因为虽然选择中断的触发方式,但是可能是别的情况触发,下面引用《STM32中文参考手册_V10》25.4中断请求的话来解释一下:


  USART的各种中断事件被连接到同一个

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

上一篇:怎么使用stm32写IAP的bootloader和APP
下一篇:STM32实现串口下载

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

推荐阅读

关于STM32CubeMx printf重定向,及报错。"FILE" is undefined
PFP *//* USER CODE BEGIN 0 */PUTCHAR_PROTOTYPE{    HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);    return ch;}/* USER CODE END 0 */ 这样写会报错"FILE" is undefined  添加头文件 stdio.h即可
发表于 2020-06-06
【STM32】keil MDK下重定向printf到串口(基于STM32CubeMX)
概述在keil MDK环境下重定向printf与keil C51不同,由于本人使用了STM32CubeMX生成工程模板,HAL_USART_Transmit函数即是模板里串口输出的函数。由于printf最终是调用fputc输出数据,fputc是一个弱引用(weak)函数,覆写即可重定向printf。代码清单extern USART_HandleTypeDef husart1;int fputc(int ch, FILE *f) {    HAL_USART_Transmit(&husart1, (uint8_t *)&ch, 1, 0xFFFF);    return ch
发表于 2020-06-06
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"
发表于 2020-06-06
STM32CubeMx启动串口调试功能Printf调试
STM32F1xx HAL库中文版——USART篇
38.1 UART Firmware driver registers structures //串口固件驱动寄存器结构38.1.1 UART_InitTypeDefUART_InitTypeDef被定义在stm32f1xx_hal_uart.h头文件中数据字段:• uint32_t BaudRate 波特率• uint32_t WordLength 字长• uint32_t StopBits 停止位• uint32_t Parity 奇偶校验位• uint32_t Mode 模式• uint32_t HwFlowCtl 硬件流控制• uint32_t OverSampling 过采样字段的文档:• uint32
发表于 2020-06-06
Stm32-输入捕获
输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。STM32 的输入捕获,简单地说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)中。1. 相关寄存器介绍1) 捕获/比较模式寄存器 (TIMx_CCMRx) 当在输入捕获模式下使用的时候,对应上图的第二行描述,从图中可以看出,TIMx_CCMR1 明显是针对 2 个通道的配置,低八位[7:0]用于捕获/比较通道 1 的控制,而高八位[15:8]则用
发表于 2020-06-06
Stm32-输入捕获
STM32库函数和寄存器的区别
库函数版和寄存器版的系统时钟设置的区别:**1.**库函数的目的是让用户应用的,而寄存器更加原始库函数的系统时钟是默认设置的,且放在启动文件里。而寄存器版的系统时钟是Stm32_Clock_Init(336,8,2,7);.**2.**库函数的快捷的,但不是每个芯片都有的;寄存器是复杂的,但是每个芯片厂商都有提供系统的寄存器设置信息。分别打开库函数和寄存器版的I/O口设置:库函数:RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);gotoh后先通过assert_param();函数检查格式是否正确同时只要是ENABLE,RCC->AHB1ENR
发表于 2020-06-06
STM32库函数和寄存器的区别
何立民专栏 单片机及嵌入式宝典

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

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