第九章 串口通信实验
[mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板
2.软件平台:MDK5.1
3.固件库版本:V1.4.0[/mw_shl_code]
前面三章介绍了
STM32F4的
IO口操作。这一章我们将学习
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开发板板载了
1个
USB串口和
2个
RS232串口,我们本章介绍的是通过
USB串口和电脑通信。
在
4.4.章节端口复用功能已经讲解过,对于复用功能的
IO,我们首先要使能
GPIO时钟,然后使能相应的外设时钟,同时要把
GPIO模式设置为复用。这些准备工作做完之后,剩下的当然是串口参数的初始化设置,包括波特率,停止位等等参数。在设置完成只能接下来就是使能串口,这很容易理解。同时,如果我们开启了串口的中断,当然要初始化
NVIC设置中断优先级别,最后编写中断服务函数。
串口设置的一般步骤可以总结为如下几个步骤:
1) 串口时钟使能,
GPIO时钟使能。
2) 设置引脚复用器映射:调用
GPIO_PinAFConfig函数。
3) GPIO初始化设置:要设置模式为复用功能。
4) 串口参数初始化:设置波特率,字长,奇偶校验等参数。
5) 开启中断并且初始化
NVIC,使能中断(如果需要开启中断才需要这个步骤)。
6) 使能串口。
7) 编写中断处理函数:函数名格式为
USARTxIRQHandler(x对应串口号
)。
下面,我们就简单介绍下这几个与串口基本配置直接相关的几个固件库函数。这些函数和定义主要分布在
stm32f4xx_usart.h和
stm32f4xx_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,所以我们要把
PA9和
PA10都映射到串口
1。所以这里我们要调用两次函数。
对于
GPIO_PinAFConfig函数的第一个和第二个参数很好理解,就是设置对应的
IO口,如果是
PA9那么第一个参数是
GPIOA,第二个参数就是
GPIO_PinSource9。第二个参数,实际我们不需要去记忆,只需要根据我们
4.7小节讲解的快速组织代码技巧里面,去相应的配置文件找到外设对应的
AF配置宏定义标识符即可,串口
1为
GPIO_AF_USART1。
3) GPIO端口模式设置:PA9和PA10要设置为复用功能。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 |
GPIO_Pin_10; //GPIOA9与
GPIOA10
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); //初始化
PA9,
PA10
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来实现的,这是一个双寄存器,包含了
TDR和
RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。
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寄存器各位描述
这里我们关注一下两个位,第
5、
6位
RXNE和
TC。
RXNE(读数据寄存器非空),当该位被置
1的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取
USART_DR,通过读
USART_DR可以将该位清零,也可以向该位写
0,直接清除。
TC(发送完成),当该位被置位的时候,表示
USART_DR内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:
1)读
USART_SR,写
USART_DR。
2)直接向该位写
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(USART1,
USART_IT_TC,
ENABLE);
这里还要特别提醒,因为我们实验开启了串口中断,所以我们在系统初始化的时候需要先设置系统的中断优先级分组,我们是在我们
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之前还没有介绍过,本实验用到的串口
1与
USB串口并没有在
PCB上连接在一起,需要通过跳线帽来连接一下。这里我们把
P6的
RXD和
TXD用跳线帽与
PA9和
PA10连接起来。如图
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;
//GPIOA和
USART1时钟使能①
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; //GPIOA9与
GPIOA10
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); //初始化
PA9,
PA10
//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_RX为
1(默认设置就是
1的)。该函数才会配置中断使能,以及开启串口
1的
NVIC中断。这里我们把串口
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
一周热门 更多>