4.8 STM32 RS485串口通讯及程序设计

更新时间:2023-05-24 19:44:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

学ARM从STM32开始

STM32开发板库函数教程--实战篇

官方网站:官方店铺:官方论坛:刘洋课堂:

4.8STM32RS485串口通讯实验

4.8.1概述

4.8.1.1RS485串口通讯

RS-485接口具有良好的抗噪声干扰性、长的传输距离和多站能力等。上述优点就使其成为首选的串行接口。因为工业RS485通讯接口组成的半双工网络,一般只需二根连线,所以工业RS485通讯接口均采用屏蔽双绞线传输。

4.8.1.2EIA-485标准简介

为扩展应用范围,EIA于1983年在EIA-422基础上制定了EIA-485标准,增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上。同时增加了发送器的驱动能力和冲突保护特性,扩展了总线共模范围,后命名为TIA/EIA-485-A标准。

由于EIA-485是从EIA-422基础上发展而来的,所以EIA-485许多电气规定与EIA-422相仿,如都采用平衡传输方式、都需要在传输线上接终接电阻、最大传输距离约为1219米、最大传输速率为10Mbps等。但是,EIA-485可以采用二线与四线方式,采用二线制时可实现真正的多点双向通信,而采用四线连接时,与EIA-422一样只能实现点对多点通信,但它比EIA-422有改进,无论四线还是二线连接方式总线上可接多达32个设备。EIA-232、EIA-422与EIA-485标准的优点4.8.2RS485通讯实验目的

通过下传编写好的串口通讯程序,验证串口通讯的正确性,掌握串口通讯软件的设计方法。4.8.3硬件设计

在这里使用的串口通讯芯片是常规芯片MXA485,RS485串口通讯电路是一个很成熟的电路,电路大家都熟悉了(参考原理图纸)。从图中可以看出芯片1#管脚是数据接收端,4#管脚是数据发送端;2#、3#管脚是发送/接收状态转换控制端。

图一RS485通讯电路

4.8.4软件设计

RS485通讯程序中我们用到中断、端口复用功能等函数。我们采用PA端口进行串口通讯;PA2端口接TX,PA3端口接RX,状态转换控制线连接PE5端口。在这里我们也是全部使用库函数编写程序。4.8.4.1STM32库函数文件

stm32f10x_gpio.cstm32f10x_rcc.c

Misc.c//中断控制字(优先级设置)库函数stm32f10x_exti.c//外部中断库处理函数stm32f10x_usart.c//串口通讯函数

都是串口通讯,引用的库函数和RS232都是一样的。其中

stm32f10x_gpio.h头文件包含了GPIO端口的定义。stm32f10x_rcc.h头文件包含了系统时钟配置函数以及相关的外设时钟使能函数,所以我们要把这两个头文件对应的stm32f10x_gpio.c和stm32f10x_rcc.c加到工程中;Misc.c库函数主要包含了中断优先级的设置,stm32f10x_exti.c库函数主要包含了外部中断设置参数,tm32f10x_usart.c库函数主要包含串行通讯设置,这些函数也要添加到函数库中。4.8.4.2自定义头文件

pbdata.h

pbdata.c

同时我们自己也创建了两个公共的文件,这两个文件主要存放我们自定义的公共函数和全局变量,以方便以后每个功能模块之间传递参数。4.8.4.3pbdata.h文件里的内容是

#ifndef_pbdata_H#define_pbdata_H#include#include#include#include#includevoidvoidvoidvoid

"stm32f10x.h""misc.h"

"stm32f10x_exti.h""stm32f10x_tim.h""stm32f10x_usart.h"

RCC_HSE_Configuration(void);//定义函数delay(u32nCount);delay_us(u32nus);delay_ms(u16nms);

#endif

语句#ifndef、#endif是为了防止pbdata.h文件被多个文件调用时出现错误提示。如果不加这两条语句,当两个文件同时调用pbdata文件时,会提示重复调用错误。

4.8.4.4pbdata.c文件里的内容是

#include"pbdata.h"//很重要,引用这个头文件

voidRCC_HSE_Configuration(void)//HSE作为PLL时钟,PLL作为SYSCLK{

RCC_DeInit();/*将外设RCC寄存器重设为缺省值*/RCC_HSEConfig(RCC_HSE_ON);/*设置外部高速晶振(HSE)HSE晶振打开(ON)*/if(RCC_WaitForHSEStartUp()==SUCCESS){振稳定且就绪*/

/*等待HSE起振,

SUCCESS:HSE晶

RCC_HCLKConfig(RCC_SYSCLK_Div1);/*设置AHB时钟(HCLK)RCC_SYSCLK_Div1——AHB时钟=系统时*/

RCC_PCLK2Config(RCC_HCLK_Div1);/*设置高速AHB时钟(PCLK2)RCC_HCLK_Div1——APB2时钟=HCLK*/

RCC_PCLK1Config(RCC_HCLK_Div2);/*设置低速AHB时钟(PCLK1)RCC_HCLK_Div2——APB1时钟=HCLK/2*/

RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);/*设置PLL时钟源及倍频系数*/

RCC_PLLCmd(ENABLE);/*使能PLL*/

while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);/*检查指定的RCC标志位(PLL准备好标志)设置与否*/

RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);/*设置系统时钟(SYSCLK)*/while(RCC_GetSYSCLKSource()!=0x08);/*0x08:PLL作为系统时钟*/}

}

/************************************************************************名称:delay_us(u32nus)*功能:微秒延时函数*入口参数:u32nus*出口参数:无*说明:*调用方法:无

***********************************************************************/voiddelay_us(u32nus)

{

u32temp;

SysTick->LOAD=9*nus;

SysTick->VAL=0X00;//清空计数器

SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源do{

temp=SysTick->CTRL;//读取当前倒计数值

}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达SysTick->CTRL=0x00;//关闭计数器SysTick->VAL=0X00;//清空计数器}

/************************************************************************名称:delay_ms(u16nms)*功能:毫秒延时函数*入口参数:u16nms*出口参数:无*说明:*调用方法:无

***********************************************************************/voiddelay_ms(u16nms){

u32temp;

SysTick->LOAD=9000*nms;

SysTick->VAL=0X00;//清空计数器

SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源do{

temp=SysTick->CTRL;//读取当前倒计数值

}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达SysTick->CTRL=0x00;//关闭计数器SysTick->VAL=0X00;//清空计数器}

4.8.5STM32系统时钟配置SystemInit()

每个工程都必须在开始时配置并启动STM32系统时钟。4.8.6GPIO引脚时钟使能

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//设置串口2时钟使RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//功能复用IO时钟使能

无论编写任何程序都要打开相关的时钟设置,在这个实验中用到使能AHB外设时钟有3各部分,分别是使能端口时钟、使能串口2时钟、使能端口复用时钟。所有的外设时钟全部挂接在AHB后的两个预分频器上,初学者一般都感觉在设置外设时钟时不知道怎样填写使能或使能外设时钟函数中的“参数1”,下表我列出了一些“参数1”的固定书写格式,方便大家查询。同理当大家熟练使用C语言后,通过别的途径也可以查询到下表中“参数1”的固定书写格式。这些都存在于stm32f10x_rcc.c库函数中。

表1序号123456789101112131415161718192021222324

预分频器APB1、APB2控制的功能模块(部分)函数参数1

RCC_APB1Periph_TIM2RCC_APB1Periph_TIM3RCC_APB1Periph_TIM4RCC_APB1Periph_TIM5RCC_APB1Periph_TIM6RCC_APB1Periph_TIM7RCC_APB1Periph_TIM12RCC_APB1Periph_TIM13RCC_APB1Periph_TIM14RCC_APB1Periph_WWDGRCC_APB1Periph_SPI2RCC_APB1Periph_SPI3RCC_APB1Periph_USART2RCC_APB1Periph_USART3RCC_APB1Periph_UART4RCC_APB1Periph_UART5RCC_APB1Periph_I2C1RCC_APB1Periph_I2C2RCC_APB1Periph_USBRCC_APB1Periph_CAN1RCC_APB1Periph_CAN2RCC_APB1Periph_BKPRCC_APB1Periph_PWRRCC_APB1Periph_DAC

长度

((uint32_t)0x00000001)((uint32_t)0x00000002)((uint32_t)0x00000004)((uint32_t)0x00000008)((uint32_t)0x00000010)((uint32_t)0x00000020)((uint32_t)0x00000040)((uint32_t)0x00000080)((uint32_t)0x00000100)((uint32_t)0x00000800)((uint32_t)0x00004000)((uint32_t)0x00008000)((uint32_t)0x00020000)((uint32_t)0x00040000)((uint32_t)0x00080000)((uint32_t)0x00100000)((uint32_t)0x00200000)((uint32_t)0x00400000)((uint32_t)0x00800000)((uint32_t)0x02000000)((uint32_t)0x04000000)((uint32_t)0x08000000)((uint32_t)0x10000000)((uint32_t)0x20000000)

25

RCC_APB1Periph_CEC

((uint32_t)0x40000000)

4.8.7GPIO管脚电平控制函数

在主程序中采用while(1)循环语句,等待串口通讯中断的到来。

while(1)

4.8.8stm32f10x_it.c文件里的内容4.8.8.1中断处理程序

在中断处理子程序中仅串口2子函数非空,运行时仅它会有参数输出。中断处理子函数都包含在stm32f10x_it.h文件中,初学者如果不明白各种中断处理子函数的具体书写方式,可以进入这个头文件中查找,找到后复制到中断处理stm32f10x_it.c文件中使用。

#include#include#include#include#include

"stm32f10x_it.h""stm32f10x_exti.h""stm32f10x_rcc.h""misc.h""pbdata.h"

voidNMI_Handler(void)//空子程序{}

voidUSART1_IRQHandler(void)//串口1中断处理空子程序,没有被使用{}

voidUSART2_IRQHandler(void)//串口2中断处理函数{

u8temp=0;//中间临时变量temp付初值0

if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)//检查串口2接收中

断发生与否,接收中断到来,接收数据。

{

temp=USART_ReceiveData(USART2);//返回串口2最近接收到数据,把接

收到的数据放在临时变量temp中

GPIO_SetBits(GPIOE,GPIO_Pin_5);//把RS485发送控制线置高delay_ms(1);

USART_SendData(USART2,temp);//通过串口2发送单个数据

while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);//等待

RS485发送完毕,发送数据寄存器空标志位

delay_ms(2);

GPIO_ResetBits(GPIOE,GPIO_Pin_5);//把RS485发送控制线清零,转为接

收状态

}}

在中断处理程序中,各个中断位的判断很重要,有些不好理解,下文中对通讯中断标志位的应用做了详细的说明,相信初学者会比较容易的掌握。4.8.8.2函数USART_GetITStatus

表2

USART_GetITStatus说明

USART_GetITStatus

ITStatusUSART_ClearFlag(USART_TypeDef*USARTx,u16USART_IT)检查指定的USART中断发生与否

USARTx:x可以是1,2或者3,来选择USART外设USART_IT:待检查USART中断源

参阅Section:USART_IT查阅更多该参数允许取值范围无

USART_IT的新状态无无

函数名函数原型功能描述输入参数1输入参数2输出参数返回值先决条件被调用函数

USART_IT

附表2给出了所有可能被函数USART_GetITStatus检查的中断标志位列表附表2USART_IT值USART_ITUSART_IT_PEUSART_IT_TXEUSART_IT_TCUSART_IT_RXNEUSART_IT_IDLEUSART_IT_LBDUSART_IT_CTSUSART_IT_OREUSART_IT_NEUSART_IT_FE

描述

奇偶错误中断发送中断发送完成中断接收中断空闲总线中断LIN中断探测中断CTS中断溢出错误中断噪音错误中断帧错误中断

在这个程序中我们先要要检查接收中断(USART_IT_RXNE)发生与否,如果有中断产生,先接收数据;接受完数据后把控制线PE置高,转为发送状态,

再把接收到的数据发送回去,发送完毕,把控制线PE清零,转为接收状态。4.8.9main.c文件里的内容是

大家都知道串行通讯是按位进行发送和接收字节的通讯方式。RS485通讯也是这样的,在这个试验中主程序while(1)循环等待,等待串口中断的产生后转入通讯处理程序。下面是主程序的内容。

#include"pbdata.h"voidvoidvoidvoid

RCC_Configuration(void);GPIO_Configuration(void);NVIC_Configuration(void);USART_Configuration(void);

intmain(void){

RCC_Configuration();//系统时钟初始化GPIO_Configuration();//端口初始化USART_Configuration();//串口初始化NVIC_Configuration();GPIO_ResetBits(GPIOE,GPIO_Pin_5);while(1);}

voidRCC_Configuration(void){

SystemInit();//72m

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//端口PA时钟使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//设置串口2时钟

使能

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//功能复用IO时钟

使能,通过APB2预分频器打开功能复用IO时钟(所有的)

}

voidGPIO_Configuration(void){

GPIO_InitTypeDefGPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;//RS_485发送/接收控制线

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_Init(GPIOE,&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;//TXGPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//推挽输出模式GPIO_Init(GPIOA,&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;//RX

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入模式GPIO_Init(GPIOA,&GPIO_InitStructure);}

voidNVIC_Configuration(void){

NVIC_InitTypeDefNVIC_InitStructure;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//设置优先级分组NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;//USART2全局中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//先占优先级

设置

NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//从优先级设置NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能NVIC_Init(&NVIC_InitStructure);}

voidUSART_Configuration(void){

USART_InitTypeDefUSART_InitStructure;

USART_ART_BaudRate=9600;

USART_ART_WordLength=USART_WordLength_8b;USART_ART_StopBits=USART_StopBits_1;USART_ART_Parity=USART_Parity_No;

USART_ART_HardwareFlowControl=USART_HardwareFlowContr

ol_None;

USART_ART_Mode=USART_Mode_Rx|USART_Mode_Tx;

USART_Init(USART2,&USART_InitStructure);

USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);//使能串口2中断USART_Cmd(USART2,ENABLE);//使能串口2外设

}

USART_ClearFlag(USART2,USART_FLAG_TC);清楚串口2待处理标志位

在main(void)程序体中代码比较多,USART_Init函数理解起来有些困难,下面对函数USART_Init做一下比较详细的说明。1、函数USART_Init

表1

USART_Init说明

USART_Init

VoidUSART_Init(USART_TypeDef*USARTx,USART_InitTypeDef*USART_InitStruct)根据USART_InitStruct中制定的参数初始化外设USARTx寄存器USARTx:x可以是1,2或者3,来选择USART外设

USART_InitStruct:指向结构USART_TypeDef的指针,包含了外设USART的配置信息。参阅Section:USART_TypeDef查阅更多该参数允许取值范围无无无无

函数名函数原型功能描述输入参数1输入参数2输出参数返回值先决条件被调用函数

4.8.10程序下载

请根据下图所指向的7个重点区域配置。其中(1)号区域根据自己机器的实际情况选择,我的机器虚拟出来的串口号是COM3。(2)号区域请自己选择程序所在的文件夹。(7)号区域当程序下载完后,进度条会到达最右边,并且提示一切正常。(4、5、6)号区域一定要按照上图显示的设置。当都设置好以后就可以直接点击(3)号区域的开始编程按钮下传程序了。

本节实验的源代码在光盘中:(LY-STM32光盘资料\1.课程\1,基础篇\基础篇16.STM32RS485串口通讯实验\程序)4.8.11实验效果图

RS485通讯程序写入实验板后,使用公司开发的多功能监视系统,在串口调试界面中的发送区输入随机数字,点击发送按钮(参考红色框图1部分),如果在接收区看到有数据返回,就说明程序设计成功,RS485通讯正常(参考红色框图2部分)。串口通讯涉及到函数很多,设置也比较多,细节决定成败,大家需要花费多一些的时间去理解和编程实验。只要学习者认真看书和看视频,再加上勤奋,一定会很快掌握。

具体实验效果参考下图。

(众想)哈尔滨卓恩科技开发有限公司

STM32开发板用户使用手册

版本号:V2.2

第 14页共 14页

官方网站:

本文来源:https://www.bwwdw.com/article/uhv4.html

Top