【正点原子探索者STM32F407开发板例程连载+教学】第9章 串口通信实验-USART

2019-07-20 08:17发布

第九章 串口通信实验

[mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板 2.软件平台:MDK5.1 3.固件库版本:V1.4.0[/mw_shl_code]
前面三章介绍了STM32F4IO口操作。这一章我们将学习STM32F4的串口,教大家如何使用STM32F4的串口来发送和接收数据。本章将实现如下功能:STM32F4通过串口和上位机的对话,STM32F4在收到上位机发过来的字符串后,原原本本的返回给上位机。本章分为如下几个小节: 9.1 STM32F4串口简介 9.2 硬件设计 9.3 软件设计 9.4 下载验证  

9.1 STM32F4串口简介

       串口作为MCU的重要外部接口,同时也是软件开发重要的调试手段,其重要性不言而喻。现在基本上所有的MCU都会带有串口,STM32自然也不例外。 STM32F4的串口资源相当丰富的,功能也相当强劲。ALIENTEK探索者STM32F4开发板所使用的STM32F407ZGT6最多可提供6路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持LIN、支持调制解调器操作、智能卡协议和IrDA SIR ENDEC规范、具有DMA等。 5.3节对串口有过简单的介绍,大家看这个实验的时候记得翻过去看看。接下来我们将主要从库函数操作层面结合寄存器的描述,告诉你如何设置串口,以达到我们最基本的通信功能。本章,我们将实现利用串口1不停的打印信息到电脑上,同时接收从串口发过来的数据,把发送过来的数据直接送回给电脑。探索者STM32F4开发板板载了1USB串口和2RS232串口,我们本章介绍的是通过USB串口和电脑通信。4.4.章节端口复用功能已经讲解过,对于复用功能的IO,我们首先要使能GPIO时钟,然后使能相应的外设时钟,同时要把GPIO模式设置为复用。这些准备工作做完之后,剩下的当然是串口参数的初始化设置,包括波特率,停止位等等参数。在设置完成只能接下来就是使能串口,这很容易理解。同时,如果我们开启了串口的中断,当然要初始化NVIC设置中断优先级别,最后编写中断服务函数。 串口设置的一般步骤可以总结为如下几个步骤: 1)      串口时钟使能,GPIO时钟使能。 2)      设置引脚复用器映射:调用GPIO_PinAFConfig函数。 3)      GPIO初始化设置:要设置模式为复用功能。 4)      串口参数初始化:设置波特率,字长,奇偶校验等参数。 5)      开启中断并且初始化NVIC,使能中断(如果需要开启中断才需要这个步骤)。 6)      使能串口。 7)      编写中断处理函数:函数名格式为USARTxIRQHandler(x对应串口号) 下面,我们就简单介绍下这几个与串口基本配置直接相关的几个固件库函数。这些函数和定义主要分布在stm32f4xx_usart.hstm32f4xx_usart.c文件中。 1)      串口时钟和GPIO时钟使能。 串口是挂载在APB2下面的外设,所以使能函数为: RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟 GPIO时钟使能,就非常简单,因为我们使用的是串口1,串口1对应着芯片引脚PA9,PA10,所以这里我们只需要使能GPIOA时钟即可: RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟 2)      设置引脚复用器映射 引脚复用器映射配置方法在我们4.4小节讲解非常清晰,调用函数为: GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //PA9复用为USART1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//PA10复用为USART1 因为串口使用到PA9,PA10,所以我们要把PA9PA10都映射到串口1。所以这里我们要调用两次函数。        对于GPIO_PinAFConfig函数的第一个和第二个参数很好理解,就是设置对应的IO口,如果是PA9那么第一个参数是GPIOA,第二个参数就是GPIO_PinSource9。第二个参数,实际我们不需要去记忆,只需要根据我们4.7小节讲解的快速组织代码技巧里面,去相应的配置文件找到外设对应的AF配置宏定义标识符即可,串口1GPIO_AF_USART1 3)      GPIO端口模式设置:PA9PA10要设置为复用功能。 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9GPIOA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9PA10 4)      串口参数初始化:设置波特率,字长,奇偶校验等参数 串口初始化是调用函数USART_Init来实现的,具体设置方法如下: USART_InitStructure.USART_BaudRate = bound;//一般设置为9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口 5)      使能串口 使能串口调用函数USART_Cmd来实现,具体使能串口1方法如下: USART_Cmd(USART1, ENABLE);  //使能串口 6)      串口数据发送与接收。 STM32F4的发送与接收是通过数据寄存器USART_DR来实现的,这是一个双寄存器,包含了TDRRDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。 STM32库函数操作USART_DR寄存器发送数据的函数是: void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 通过该函数向串口寄存器USART_DR写入一个数据。 STM32库函数操作USART_DR寄存器读取串口接收到的数据的函数是: uint16_t USART_ReceiveData(USART_TypeDef* USARTx); 通过该函数可以读取串口接受到的数据。 7)      串口状态 串口的状态可以通过状态寄存器USART_SR读取。USART_SR的各位描述如图9.1.1所示:
9.1.1 USART_SR寄存器各位描述 这里我们关注一下两个位,第56RXNETC RXNE(读数据寄存器非空),当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取USART_DR,通过读USART_DR可以将该位清零,也可以向该位写0,直接清除。 TC(发送完成),当该位被置位的时候,表示USART_DR内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:1)读USART_SR,写USART_DR2)直接向该位写0     状态寄存器的其他位我们这里就不做过多讲解,大家需要可以查看中文参考手册。 在我们固件库函数里面,读取串口状态的函数是: FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG) 这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态,比如上面讲解的RXNE(读数据寄存器非空)以及TC(发送完成)。例如我们要判断读寄存器是否非空(RXNE),操作库函数的方法是: USART_GetFlagStatus(USART1, USART_FLAG_RXNE); 我们要判断发送是否完成(TC),操作库函数的方法是: USART_GetFlagStatus(USART1, USART_FLAG_TC); 这些标识号在MDK里面是通过宏定义定义的: #define USART_IT_PE                          ((uint16_t)0x0028) #define USART_IT_TC                          ((uint16_t)0x0626) #define USART_IT_RXNE                        ((uint16_t)0x0525) ……//(省略部分代码) #define USART_IT_NE                          ((uint16_t)0x0260) #define USART_IT_FE                          ((uint16_t)0x0160) 8)      开启中断并且初始化NVIC,使能相应中断        这一步如果我们要开启串口中断才需要配置NVIC中断优先级分组。通过调用函数NVIC_Init来设置。  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;            //响应优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //IRQ通道使能 NVIC_Init(&NVIC_InitStructure);      //根据指定的参数初始化VIC寄存器、 同时,我们还需要使能相应中断,使能串口中断的函数是: void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState) 这个函数的第二个入口参数是标示使能串口的类型,也就是使能哪种中断,因为串口的中断类型有很多种。比如在接收到数据的时候(RXNE读数据寄存器非空),我们要产生中断,那么我们开启中断的方法是: USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断 我们在发送数据结束的时候(TC,发送完成)要产生中断,那么方法是: USART_ITConfig(USART1USART_IT_TCENABLE); 这里还要特别提醒,因为我们实验开启了串口中断,所以我们在系统初始化的时候需要先设置系统的中断优先级分组,我们是在我们main函数开头设置的,代码如下: NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2 我们设置分组为2,也就是2位抢占优先级,2位响应优先级。 9)      获取相应中断状态 当我们使能了某个中断的时候,当该中断发生了,就会设置状态寄存器中的某个标志位。经常我们在中断处理函数中,要判断该中断是哪种中断,使用的函数是: ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT) 比如我们使能了串口发送完成中断,那么当中断发生了, 我们便可以在中断处理函数中调用这个函数来判断到底是否是串口发送完成中断,方法是: USART_GetITStatus(USART1, USART_IT_TC) 返回值是SET,说明是串口发送完成中断发生。 10)    中断服务函数        串口1中断服务函数为: void USART1_IRQHandler(void) ; 当发生中断的时候,程序就会执行中断服务函数。然后我们在中断服务函数中编写我们相应的逻辑代码即可。 通过以上一些寄存器的操作外加一下IO口的配置,我们就可以达到串口最基本的配置了,关于串口更详细的介绍,请参考《STM32F4XX中文参考手册》第676页至720页,通用同步异步收发器这一章节。

9.2 硬件设计

本实验需要用到的硬件资源有: 1)  指示灯DS0 2)  串口1 串口1之前还没有介绍过,本实验用到的串口1USB串口并没有在PCB上连接在一起,需要通过跳线帽来连接一下。这里我们把P6RXDTXD用跳线帽与PA9PA10连接起来。如图9.2.1所示:  9.2.1 硬件连接图示意图 连接上这里之后,我们在硬件上就设置完成了,可以开始软件设计了。

9.3 软件设计

本章的代码设计,比前两章简单很多,因为我们的串口初始化代码和接收代码就是用我们之前介绍的SYSTEM文件夹下的串口部分的内容。这里我们对代码部分稍作讲解。 打开串口实验工程,然后在SYSTEM组下双击usart.c,我们就可以看到该文件里面的代码,先介绍uart_init函数,该函数代码如下: void uart_init(u32 bound) {    GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;   //GPIOAUSART1时钟使能① RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟        //USART_DeInit(USART1);  //复位串口1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //PA9复用为USART1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //PA10复用为USART1        //USART1_TX   PA.9 PA.10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9GPIOA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9PA10   //USART 初始化设置 ④ USART_InitStructure.USART_BaudRate = bound;//一般设置为9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;      //收发模式 USART_Init(USART1, &USART_InitStructure); //初始化串口   #if EN_USART1_RX      //NVIC设置,使能中断 ⑤ USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断 //Usart1 NVIC 配置  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;          //响应优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                     //IRQ通道使能 NVIC_Init(&NVIC_InitStructure);     //根据指定的参数初始化VIC寄存器、 #endif }   USART_Cmd(USART1, ENABLE);  //使能串口    从该代码可以看出,其初始化串口的过程,和我们前面介绍的一致。我们用标号①~⑥标示了顺序: ①     串口时钟使能,GPIO时钟使能 ②     设置引脚复用器映射 ③     GPIO端口初始化设置 ④     串口参数初始化 ⑤     初始化NVIC并且开启中断 ⑥     使能串口 这里需要注意一点,因为我们使用到了串口的中断接收,必须在usart.h里面设置 EN_USART1_RX1(默认设置就是1的)。该函数才会配置中断使能,以及开启串口1NVIC中断。这里我们把串口1中断放在组2,优先级设置为组2里面的最低。 串口1的中断服务函数USART1_IRQHandler,在5.3.3已经有详细介绍了,这里我们就不再介绍了。 介绍完了这两个函数,我们回到main.c,对于main.c前面引入的头文件为了篇幅考虑,我们后面的实验不再列出,详情请参考我们实验代码即可。主函数代码如下: int main(void) {        u8 t,len;  u16 times=0;            NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2        delay_init(168);            //延时初始化        uart_init(115200);  //串口初始化波特率为115200        LED_Init();                        //初始化与LED连接的硬件接口         LED0=0;                                 //先点亮红灯        while(1)        {               if(USART_RX_STA&0x8000)               {                                                         len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度                      printf(" 您发送的消息为: ");                      for(t=0;t<len;t++)                      {                             USART1->DR=USART_RX_BUF[t];                             while((USART1->SR&0X40)==0);//等待发送结束                      }                      printf(" ");//插入换行                      USART_RX_STA=0;               }else               {                      times++;                      if(times%5000==0)                      {   printf(" ALIENTEK 探索者STM32F407开发板 串口实验 ");                             printf("正点原子@ALIENTEK ");                      }                      if(times%200==0)printf("请输入数据,以回车键结束 ");                       if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.                      delay_ms(10);                 }        } } 这段代码比较简单,开头部分我们先调用NVIC_PriorityGroupConfig函数设置系统的中断优先级分组。然后调用uart_init函数,设置波特率为115200。接下来我们重点看下以下两句: USART_SendData(USART1, USART_RX_BUF[t]);         //向串口1发送数据 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET); 第一句,其实就是发送一个字节到串口。第二句呢,就是我们在我们发送一个数据到串口之后,要检测这个数据是否已经被发送完成了。USART_FLAG_TC是宏定义的数据发送完成标识符。 其他的代码比较简单,我们执行编译之后看看有没有错误,没有错误就可以开始仿真与调试了。整个工程的编译结果如图9.3.1所示:  9.3.1 编译结果 可以看到,编译没有任何错误和警告,下面我们可以开始下载验证了。

9.4 下载验证

我们把程序下载到探索者STM32F4开发板,可以看到板子上的DS0开始闪烁,说明程序已经在跑了。串口调试助手,串口调试助手,我们用XCOM V2.0,该软件在光盘有提供,且无需安装,直接可以运行,但是需要你的电脑安装有.NET Framework 4.0(WIN7直接自带了)或以上版本的环境才可以,该软件的详细介绍请看:http://www.openedv.com/posts/list/22994.htm 这个帖子。 接着我们打开XCOM V2.0,设置串口为开发板的USB转串口(CH340虚拟串口,得根据你自己的电脑选择,我的电脑是COM3,另外,请注意:波特率是115200),可以看到如图9.4.1所示信息:                           9.4.1 串口调试助手收到的信息 从图9.4.1可以看出,STM32F4的串口数据发送是没问题的了。但是,因为我们在程序上面设置了必须输入回车,串口才认可接收到的数据,所以必须在发送数据后再发送一个回车符,这里XCOM提供的发送方法是通过勾选发送新行实现,如图9.4.1,只要勾选了这个选项,每次发送数据后,XCOM都会自动多发一个回车(0X0D+0X0A)。设置好了发送新行,我们再在发送区输入你想要发送的文字,然后单击发送,可以得到如图9.4.2所示结果:  9.4.2 发送数据后收到的数据 可以看到,我们发送的消息被发送回来了(图中圈圈内)。大家可以试试,如果不发送回车(取消发送新行),在输入内容之后,直接按发送是什么结果。

 实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm  正点原子探索者STM32F407开发板购买地址http://item.taobao.com/item.htm?id=41855882779
  
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。