STM32串口通信之Hello(STM32_11)
一、开发板硬件介绍
1、MCU串口管脚
本程序使用STM32F103ZET6芯片,芯片有5个串口,其中有3个USART和两个UART,本程序使用异步方式,5个串口的管脚如下表所示。
STM32F103ZET6芯片的5个串口外设管脚
引脚 | APB2总线 | APB1总线 | |||
USART1 | USART2 | USART3 | UART4 | UART5 | |
TX | PA9 | PA2 | PB10 | PC10 | PC12 |
RX | PA10 | PA3 | PB11 | PC11 | PD2 |
SCLK | PA8 | PA4 | PB12 |
|
|
nCTS | PA11 | PA0 | PB13 |
|
|
nRTS | PA12 | PA1 | PB14 |
|
|
2、卡发板串口电路
① USART1的电路连接
开发板中将USART1的TX(USART1_TXD, PA9)和RX(USART1_RXD, PA10)连接至CH340G (USB转串口芯片)的TXD和RXD,在TXD引脚串联一个二极管是为了防止CH340G给单片机供电而使单片机不能正常断电,从而导致程序下载失败。DTR连RSET可以实现程序下载完成后自动复位系统。下载程序时通过RTS将BOOT0拉低。
② USART3的电路连接
需要使用直通线与PC端RS232串口连接
二、使用库函数的串口程序项目配置
1、新建项目文件夹"pUartHello";
2、在"pUartHello"文件夹中创建"CMSIS","Device","Lib","Startup"和"User"子文件夹,在"User"文件夹下新建"Uart"子文件夹;
3、向上述新建文件夹中复制必要文件。
① 向"CMSIS"文件夹中复制"core_cm3.c"和"core_cm3.h"文件;
② 向"Device"文件夹复制"stm32f10x.h"、"system_stm32f10x.c"、"system_stm32f10x.h"、"stm32f10x_conf.h"、" stm32f10x_it.c"和" stm32f10x_it.h "文件;
③ 向"Startup"文件夹下复制"startup_stm32f10x_hd.s"文件;
④ 向"Lib"文件夹下复制3.5版固件库函数的"inc"和"src"文件夹;
4、在Keil5中新建项目pUartHello,存放在"pUartHello"文件夹中;
5、点击按钮或者通过菜单"Project"->"Manage"->"Components,Environment,Books…"打开进行"Manage Project Items"对话框,对项目文件管理。在"Groups"中分别添加"CMSIS"、"Device"、"Startup"、"Lib"和"User"5个组,并为每一个组添加上述3中叙述的程序文件(.c和.s的文件)。
6、点击工具按钮,在对话框的"Output"选项卡中选中"Create HEX File",在"C/C++"选项卡中的"Include Paths"项目点击
按钮,打开"Folder Setup"对话框,添加如下包含路径:".\CMSIS",".\Lib\inc",".\Device",".\User\Uart";在"C/C++"选项卡中的"Preprocessor Symbols"中的"Define"中输入"USE_STDPERIPH_DRIVER",表示使用标准外设驱动库(在"stm32f10x.h"文件中有
#ifdef USE_STDPERIPH_DRIVER #include "stm32f10x_conf.h" #endif |
如果定义了该标示,则包含"stm32f10x_conf.h"头文件,在"stm32f10x_conf.h"头文件中可以选择需要包含的库函数头文件)。
7、新建"main.c"文件保存在".\User"文件夹下,内容为:
#include "stm32f10x.h" int main() { return 0; } |
并将"main.c"文件添加到“项目文件管理”的"User"组中,此时可以编译生成".Hex"文件。
三、使用库函数的串口程序设计
1、在"stm32f10x_conf.h"头文件中包含本程序需要的GPIO、RCC和USART三个头文件,将其它头文件包含注释掉:
#include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #include "stm32f10x_usart.h" |
2、新建"Uart.h"头文件,保存在".\User\Uart"文件夹下,内容为:
#ifndef __UART__H #define __UART__H #include "stm32f10x.h" void USART1_Config(void); #endif |
3、新建"Uart.c"程序文件,保存在".\User\Uart"文件夹下,内容为:
#include "uart.h" void USART1_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; //Enable GPIOA and USART1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //Config GPIOA_9 and GPIOA_10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); //Config USART1 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; 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); //Enable USART1 USART_Cmd(USART1, ENABLE); } |
3、修改"main.c"文件,内容为:
#include "uart.h" int main() { u8 i; u8 s[] = "Hello\r\n"; USART1_Config(); while(1) { for(i=0; i<sizeof(s)/sizeof(s[0]); i++) USART_SendData(USART1, s[i]); } } |
以上程序中,首先调用USART1_Config函数使能GPIOA和USART1的时钟、配置GPIOA_9和GPIOA_10管脚、配置UASRT1的工作模式为“波特率115200,8个数据位,一个停止位,无校验,无硬件流控制”、打开USART1,然后调用库函数USART_SendData实现数据发送,不断发送"Hello\r\n"。
PC端通过串口助手收到信息如下:
在上述串口通信程序中,从PC端的串口调试助手收到的信息并不是完整的"Hello"信息,而是比较杂乱的,原因是在STM32中程序运行速度是比较快的,交给串口USART1发送一个字符后,USART1还没有发送完成,程序又向它传了下一个字符,这时这个字符会丢弃掉,这样,接收方就收不到完整信息,所以,需要在通过USART_SendData函数发送一个数据后,再通过USART_GetFlagStatus函数检查数据发送是否完成。
函数USART_SendData的原形为:
FlagStatus USART_GetFlagStatus(USART_TypeDef*USARTx, u16 USART_FLAG);
其中USART_FLAG可以取的值为:
USART_FLAG | 描述 |
USART_FLAG_CTS | CTS标志位 |
USART_FLAG_LBD | LIN中断检测标志位 |
USART_FLAG_TXE | 发送数据寄存器空标志位 |
USART_FLAG_TC | 发送完成标志位 |
USART_FLAG_RXNE | 接收数据寄存器非空标志位 |
USART_FLAG_IDLE | 空闲总线标志位 |
USART_FLAG_ORE | 溢出错误标志位 |
USART_FLAG_NE | 噪声错误标志位 |
USART_FLAG_FE | 帧错误标志位 |
USART_FLAG_PE | 奇偶错误标志位 |
该函数的返回值可以为"SET"或"RESET"。
这里将修改"main.c"文件,改为:
#include "uart.h" int main() { u8 i; u8 s[] = "Hello\r\n"; USART1_Config(); while(1) { for(i=0; i<sizeof(s)/sizeof(s[0]); i++) { USART_SendData(USART1, s[i]); while( USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET ); } } } |
调整后的结果为:
四、使用USART3发送数据
1、在"uart.h"头文件中增加"USART3_Config"函数的声明:
void USART3_Config(void);
2、在"uart.c"程序文件中增加"USART3_Config"函数的定义:
void USART3_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; //Enable GPIOB and USART3 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //Config GPIOB_10 and GPIOB_11 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, &GPIO_InitStructure); //Config USART3 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; 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(USART3, &USART_InitStructure); //Enable USART3 USART_Cmd(USART3, ENABLE); } |
由于USART3在APB1上,所以使用RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);来使能USART3的时钟,USART3的数据发送和数据接收使用PB10和PB11,所以需要对GPIOB使能时钟,使用RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);实现。其他与USART1类似。
在"main.c"中使用如下:
#include "uart.h" int main() { u8 i; u8 s[] = "Hello\r\n"; USART3_Config(); while(1) { for(i=0; i<sizeof(s)/sizeof(s[0]); i++) { USART_SendData(USART3, s[i]); while( USART_GetFlagStatus(USART3, USART_FLAG_TC) != SET ); } } } |
注意,使用开发板的USART3首先要将P232跳线帽接到COM3,然后通过直连RS232串口线与电脑COM口相连。