1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#
本章将介绍使用串口进行数据的收发操作,具体实现APM32F407与上位机软件的数据通信,APM32F407将接受自上位机软件的数据原原本本地发送回给上位机软件。通过本章的学习,读者将学习到USART和GPIO引脚复用的使用。
本章分为如下几个小节:
13.1 硬件设计
13.2 程序设计
13.3 下载验证
13.1 硬件设计
13.1.1 例程功能
图13.1.3.1 USART1与USB转串口芯片跳线帽连接
因此在进行本章实验之前,需确保上图位置中正确安装了跳线帽
图13.1.3.2 串口通信跳线帽位置
板载的USB转串口芯片的USB接口通过板载的USB UART端口引出,其原理图如下图所示:
图13.1.3.3 USB转串口芯片相关原理图
从以上原理图可以看出,PA9引脚和PA10引脚分别作为发送和接收引脚分别与USB转串口芯片的接收和发送引脚进行连接,USB转串口芯片再通过一对USB差分信号连接至USB UART的接口,这样一来,APM32F407就可以通过USB与PC上位机软件进行串口通信了。
13.2 程序设计
13.2.1 Geehy标准库的GPIO驱动
在前面的章节中,介绍了GPIO引脚在输出模式和输入模式下的使用,但本章要使用GPIO引脚作为串口输出的收发引脚,因此不再是使用输出模式或输入模式,而是要使用GPIO的复用功能模式,例如将PA9引脚复用为USART1的数据发送引脚或将PA10引脚复用为USART1的数据接收引脚,具体的步骤如下:
①:配置GPIO的复用功能
②:配置GPIO为复用功能模式
在Geehy标准库中对应的驱动函数如下:
①:配置GPIO的复用功能
该函数用于配置GPIO的复用功能,其函数原型如下所示:
void GPIO_ConfigPinAF( GPIO_T* port,
GPIO_PIN_SOURCE_T gpioPinSource,
GPIO_AF_T gpioAf);
该函数的形参描述,如下表所示:
表13.2.1.1 函数GPIO_ConfigPinAF()形参描述
该函数的返回值描述,如下表所示:
表13.2.1.2 函数GPIO_ConfigPinAF()返回值描述
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_gpio.h"
void example_fun(void)
{
/* PA9引脚复用为USART1的数据发送引脚 */
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_9, GPIO_AF_USART1);
/* PA10引脚复用为USART1的数据接收引脚 */
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_10, GPIO_AF_USART1);
}
具体引脚能被复用为那些功能,被复用后又是被作为外设的什么功能引脚等,请读者查看《APM32405xG 407xExG数据手册》。
②:配置GPIO为复用功能模式
请见10.2.1小节中配置GPIO引脚的相关内容。
13.2.2 Geehy标准库的USART驱动
Geehy标准库的USART驱动提供了操作APM32F407片上USART的各种API函数,其中就包括配置USART、使能USART等函数,本章实验还是能了USART中断用于接收USART数据。配置并使用USART收发数据的步骤。配置USART的具体步骤如下所示:
①:配置USART
②:使能USART
③:使能USART接收缓冲区非空中断
④:使能USART中断,并配置其相关的中断优先级
在Geehy标准库中对应的驱动函数如下:
①:配置USART
该函数用于配置USART的各项参数,其函数的原型如下所示:
void USART_Config(USART_T* usart, USART_Config_T* usartConfig);
该函数的形参描述,如下表所示:
形参 描述
usart 指向USART外设结构体的指针
例如:USART1、UART4等(在apm32f4xx.h文件中有定义)
usartConfig 指向USART初始化结构体的指针
需自行定义,并根据USART的配置参数填充结构体中的成员变量
表13.2.2.1 函数USART_Config()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表13.2.2.2 函数USART_Config()返回值描述
该函数使用USART_Config_T类型的结构体变量传入USART外设的配置参数,该结构体的定义如下所示:
typedef enum
{
USART_WORD_LEN_8B, /* 8位数据长度 */
USART_WORD_LEN_9B /* 9位数据长度 */
} USART_WORD_LEN_T;
typedef enum
{
USART_STOP_BIT_1, /* 1比特停止位 */
USART_STOP_BIT_0_5, /* 0.5比特停止位 */
USART_STOP_BIT_2, /* 2比特停止位 */
USART_STOP_BIT_1_5 /* 1.5比特停止位 */
} USART_STOP_BIT_T;
typedef enum
{
USART_PARITY_NONE = (uint8_t)0x00, /* 无校验 */
USART_PARITY_EVEN = (uint8_t)0x01, /* 偶校验 */
USART_PARITY_ODD = (uint8_t)0x03 /* 奇校验 */
} USART_PARITY_T;
typedef enum
{
USART_MODE_RX = (uint8_t)0x01, /* 接收模式 */
USART_MODE_TX = (uint8_t)0x02, /* 发送模式 */
USART_MODE_TX_RX = (uint8_t)0x03 /* 收发模式 */
} USART_MODE_T;
typedef enum
{
USART_HARDWARE_FLOW_NONE, /* 无硬件流控 */
USART_HARDWARE_FLOW_RTS, /* RTS硬件流控 */
USART_HARDWARE_FLOW_CTS, /* CTS硬件流控 */
USART_HARDWARE_FLOW_RTS_CTS /* RTS和CTS硬件流控 */
} USART_HARDWARE_FLOW_T;
typedef struct
{
uint32_t baudRate; /* 通信波特率 */
USART_WORD_LEN_T wordLength; /* 数据宽度 */
USART_STOP_BIT_T stopBits; /* 停止位 */
USART_PARITY_T parity; /* 校验位 */
USART_MODE_T mode; /* 模式 */
USART_HARDWARE_FLOW_T hardwareFlow; /* 硬件流控 */
} USART_Config_T;
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_usart.h"
void example_fun(void)
{
USART_Config_T usart_init_struct;
/* 配置USART1 */
usart_init_struct.baudRate = 115200;
usart_init_struct.wordLength = USART_WORD_LEN_8B;
usart_init_struct.stopBits = USART_STOP_BIT_1;
usart_init_struct.parity = USART_PARITY_NONE;
usart_init_struct.mode = USART_MODE_TX_RX;
usart_init_struct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
USART_Config(USART1, &usart_init_struct);
}
②:使能USART
该函数用于使能USART外设,其函数原型如下所示:
void USART_Enable(USART_T* usart);
该函数的形参描述,如下表所示:
形参 描述
usart 指向USART外设结构体的指针
例如:USART1、UART4等(在apm32f4xx.h文件中有定义)
表13.2.2.3 函数USART_Enable()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表13.2.2.4 函数USART_Enable()返回值描述
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_usart.h"
void example_fun(void)
{
/* 使能USART1 */
USART_Enable(USART1);
}
③:使能USART指定的中断
该函数用于使能USART指定的中断,其函数原型如下所示:
void USART_EnableInterrupt(USART_T* usart, USART_INT_T interrupt);
该函数的形参描述,如下表所示:
表13.2.2.5 函数USART_EnableInterrupt()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表13.2.2.6 函数USART_EnableInterrupt()返回值描述
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_usart.h"
void example_fun(void)
{
/* 使能USART1的接收缓冲区非空中断 */
USART_EnableInterrupt(USART1, USART_INT_RXBNE);
/* 使能USART1的发送完成中断 */
USART_EnableInterrupt(USART1, USART_INT_TXC);
}
④:配置USART中断
请见第12.2.3小节中配置中断的相关内容。
使用USART发送数据的具体步骤如下所示:
①:等待USART发送缓冲区为空
②:往USART发送缓冲区写入数据
在Geehy标准库中对应的驱动函数如下:
①:获取USART状态标志
该函数用于获取USART的状态标志,其函数原型如下所示:
uint8_t USART_ReadStatusFlag(USART_T* usart, USART_FLAG_T flag);
该函数的形参描述,如下表所示:
表13.2.2.7 函数USART_ReadStatusFlag()形参描述
该函数的返回值描述,如下表所示:
表13.2.2.8 函数USART_ReadStatusFlag()返回值描述
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_usart.h"
void example_fun(void)
{
uint8_t status;
/* 读取USART1外设的发送缓冲区为空状态标志 */
status = USART_ReadStatusFlag(USART1, USART_FLAG_TXBE);
if (status == SET)
{
/* Do something. */
}
else
{
/* Do something. */
}
}
②:USART发送单个数据
该函数用于使用USART发送单个数据,其函数原型如下所示:
void USART_TxData(USART_T* usart, uint16_t data);
该函数的形参描述,如下表所示:
形参 描述
usart 指向USART外设结构体的指针
例如:USART1、UART4等(在apm32f4xx.h文件中有定义)
data 待发送的数据
表13.2.2.9 函数USART_TxData()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表13.2.2.10 函数USART_TxData()返回值描述
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_usart.h"
void example_fun(void)
{
uint8_t data;
/* 准备数据 */
data = 0xFF;
/* 通过USART1发送单个数据 */
USART_TxData(USART1, (uint16_t)data);
}
使用USART接收数据的具体步骤如下所示:
①:判断USART接收缓冲区非空
②:读取USART接收缓冲区中的数据
在Geehy标准库中对应的函数如下:
①:获取USART状态标志
请见第13.2.2小节中获取USART状态标志的相关内容。
②:获取USART接收到的单个数据
该函数用于获取USART获取到的单个数据,其函数原型如下所示:
uint16_t USART_RxData(USART_T* usart);
该函数的形参描述,如下表所示:
形参 描述
usart 指向USART外设结构体的指针
例如:USART1、UART4等(在apm32f4xx.h文件中有定义)
表13.2.2.11 函数USART_RxData()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
uint16_t类型数据 获取到的USART接收到的一个数据
表13.2.2.12 函数USART_RxData()返回值描述
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_usart.h"
void example_fun(void)
{
uint8_t data;
/* 接收USART1接收到的一个数据 */
data = USART_RxData(USART1);
/* Do something. */
}
13.2.3 串口通讯驱动
本实验的串口通信驱动主要是配置USART1并完成一些相关的初始化操作,并支持将printf函数重定向到USART1进行输出,同时向应用层提供了一个数据接收缓冲区和接收完成标志,通过这些,应用层就能够很方便地使用USART1进行数据传输了,本章实验中,串口通讯的驱动代码包括usart.c和usart.h两个文件。
串口通讯驱动中,对GPIO、USART的相关宏定义,如下所示:
#define USART_TX_GPIO_PORT GPIOA
#define USART_TX_GPIO_PIN GPIO_PIN_9
#define USART_TX_GPIO_PIN_SOURCE GPIO_PIN_SOURCE_9
#define USART_TX_GPIO_AF GPIO_AF_USART1
#define USART_TX_GPIO_CLK_ENABLE() \
do { \
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA); \
} while (0)
#define USART_RX_GPIO_PORT GPIOA
#define USART_RX_GPIO_PIN GPIO_PIN_10
#define USART_RX_GPIO_PIN_SOURCE GPIO_PIN_SOURCE_10
#define USART_RX_GPIO_AF GPIO_AF_USART1
#define USART_RX_GPIO_CLK_ENABLE() \
do { \
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA); \
} while (0)
#define USART_UX USART1
#define USART_UX_IRQn USART1_IRQn
#define USART_UX_IRQHandler USART1_IRQHandler
#define USART_UX_CLK_ENABLE() \
do { \
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_USART1); \
} while (0)
串口通讯驱动中,USART1的初始化函数,如下所示:
/**
* @brief 初始化串口
* @param baudrate: 通讯波特率
* @retval 无
*/
void usart_init(uint32_t baudrate)
{
GPIO_Config_T gpio_init_struct;
USART_Config_T usart_init_struct;
/* 使能时钟 */
USART_UX_CLK_ENABLE();
USART_TX_GPIO_CLK_ENABLE();
USART_RX_GPIO_CLK_ENABLE();
/* 初始化串口发送引脚 */
GPIO_ConfigPinAF( USART_TX_GPIO_PORT,
USART_TX_GPIO_PIN_SOURCE,
USART_TX_GPIO_AF);
gpio_init_struct.pin = USART_TX_GPIO_PIN;
gpio_init_struct.mode = GPIO_MODE_AF;
gpio_init_struct.speed = GPIO_SPEED_100MHz;
gpio_init_struct.otype = GPIO_OTYPE_PP;
gpio_init_struct.pupd = GPIO_PUPD_NOPULL;
GPIO_Config(USART_TX_GPIO_PORT, &gpio_init_struct);
/* 初始化串口接收引脚 */
GPIO_ConfigPinAF( USART_RX_GPIO_PORT,
USART_RX_GPIO_PIN_SOURCE,
USART_RX_GPIO_AF);
gpio_init_struct.pin = USART_RX_GPIO_PIN;
gpio_init_struct.mode = GPIO_MODE_AF;
gpio_init_struct.speed = GPIO_SPEED_100MHz;
gpio_init_struct.otype = GPIO_OTYPE_PP;
gpio_init_struct.pupd = GPIO_PUPD_NOPULL;
GPIO_Config(USART_RX_GPIO_PORT, &gpio_init_struct);
/* 初始化串口 */
usart_init_struct.baudRate = baudrate; /* 通讯波特率 */
usart_init_struct.wordLength = USART_WORD_LEN_8B; /* 数据位 */
usart_init_struct.stopBits = USART_STOP_BIT_1; /* 停止位 */
usart_init_struct.parity = USART_PARITY_NONE; /* 校验位 */
usart_init_struct.mode = USART_MODE_TX_RX; /* 收发模式 */
usart_init_struct.hardwareFlow = USART_HARDWARE_FLOW_NONE; /* 无硬件流控 */
USART_Config(USART_UX, &usart_init_struct);
USART_Enable(USART_UX);
/* 配置串口接收相关中断 */
USART_EnableInterrupt(USART_UX, USART_INT_RXBNE);
USART_ClearStatusFlag(USART_UX, USART_FLAG_RXBNE);
NVIC_EnableIRQRequest(USART_UX_IRQn, 3, 0);
}
在串口通信的初始化函数中,在使能USART1收发引脚的GPIO端口时钟和USART1外设时钟后,配置了USART1收发引脚的GPIO复用功能和配置其为复用功能模式,并在最后配置并使能了USART1,同时也使能并配置了USART1的接收缓冲区非空中断,这么一来,USART1的中断回调函数就会在USART1接收到数据的时候被调用。
串口通讯驱动中,USART1的中断回调函数,如下所示:
/**
* @brief 串口中断服务函数
* @param 无
* @retval 无
*/
void USART_UX_IRQHandler(void)
{
/* 判断接收缓冲区非空中断标志 */
if (USART_ReadIntFlag(USART_UX, USART_INT_RXBNE) == SET)
{
/* 接收一字节数据 */
g_rx_buffer[0] = USART_RxData(USART_UX);
if ((g_usart_rx_sta & 0x8000) == 0)
{
if (g_usart_rx_sta & 0x4000
{
if (g_rx_buffer[0] != 0x0A)
{
g_usart_rx_sta = 0;
}
else
{
g_usart_rx_sta |= 0x8000;
}
}
else
{
if (g_rx_buffer[0] == 0x0D)
{
g_usart_rx_sta |= 0x4000;
}
else
{
g_usart_rx_buf[g_usart_rx_sta & 0x3FFF] = g_rx_buffer[0];
g_usart_rx_sta++;
if (g_usart_rx_sta > (USART_REC_LEN - 1))
{
g_usart_rx_sta = 0;
}
}
}
}
/* 清除接收缓冲区非空中断标志 */
USART_ClearIntFlag(USART_UX, USART_INT_RXBNE);
}
}
在USART1的中断回调函数中主要用于读取USART1接收到的数据,并将其逐一存入接收的缓冲区,并在接收到“回车”和“换行”后标志数据接收完成。
13.2.4 实验应用代码
本章实验的应用代码,如下所示:
int main(void)
{
uint8_t len;
uint16_t times;
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3); /* 设置中断优先级分组为组3 */
sys_apm32_clock_init(336, 8, 2, 7); /* 配置系统时钟 */
delay_init(168); /* 初始化延时功能 */
usart_init(115200); /* 初始化串口 */
led_init(); /* 初始化LED */
while (1)
{
if (g_usart_rx_sta & 0x8000) /* 接收到了数据 */
{
len = g_usart_rx_sta & 0x3FFF; /* 获取有效数据长度 */
printf("\r\n您发送的消息为:\r\n");
g_usart_rx_buf[len] = '\0'; /* 末尾插入结束符 */
printf("%s", g_usart_rx_buf);
printf("\r\n\r\n"); /* 插入换行 */
g_usart_rx_sta = 0;
}
else
{
times++;
if (times % 5000 == 0)
{
printf("\r\n正点原子 APM32F407最小系统板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
if (times % 200 == 0)
{
printf("请输入数据,以回车键结束\r\n");
}
if (times % 30 == 0)
{
LED0_TOGGLE();
}
delay_ms(10);
}
}
}
本实验的实验代码很简单,在完成初始化后,就不断地通过串口通信驱动提供的数据接收完成标志判断数据是否接收完毕,若还未完成数据接收,则每间隔一段时间就使用printf函数通过USART1打印一段提示信息,若数据接收完毕,则将数据原原本本的使用printf函数通过USART1打印出去,实现数据的回显。
13.3 下载验证
在完成编译和烧录操作后,需要将开发板的USB UART接口与PC的USB接口通过具有数据传输功能的数据线进行连接,并保证13.1.3小节中指示跳线帽已正确安装。接着打开PC上的ATK-XCOM串口调试助手软件,选择好正确的COM端口和相关的配置后,就能看到串口调试助手上每间隔一段时间就打印一次“请输出数据,以回车键结束”,接下来就可以根据提示通过串口调试助手发送一段任意的数据(以回车换行结束),随后立马就能看到串口调试助手上显示发送出去的数据,这就是本实验实现的数据回显。