STM32笔记之 USART(串口)
写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
目录
一、串口通讯方式
这里就不再累赘阐释了,看以前的篇章 UART/ USRAT、I2C、SPI通信方式扫盲
二、USART收发功能框图
三、USART模式配置
1、各模式功能支持
2、I/O复用配置
四、代码实现过程
在这次例程中,我们配置成 UART(异步串口),这是我们比较常用的,而 USART(同步串口)只是多了一条时钟线,所以,在用 UART时,我们需要引入一个可以判断是否接受完成的节点,可以是时间超时判定,结束符判定等等
在配置之前我们先定义一个接收的结构体
#define RxBUFFER_SIZE 0xFF
typedef struct
{
uint8_t RxBuffer[RxBUFFER_SIZE]; // 接收暂存缓冲区
__IO uint8_t RxCounter; // 接收数据个数
uint8_t Receiving_Time; // 接收时间
uint8_t Frame_flag; // 一帧完成标志
}EVAL_COMx_TypeDef;
由于发送用的缓冲区我们可以共用(毕竟发送的时候只能单发),所以就只是定义一个数组就好了
#define TxBUFFER_SIZE 100
uint8_t g_TxCounter = 0; // 发送数据个数
uint8_t TxBuffer[TxBUFFER_SIZE] = {0}; // 发送暂存缓冲区
好了,现在就开始配置我们的 UART吧
1、UART1功能配置:
#define EVAL_COM1 USART1
/************************************************
函数名称 : UART1_Comfig
功 能 : UART1端口配置
参 数 : Baudrate ---- 波特率
返 回 值 : 无
*************************************************/
void UART1_Comfig( uint32_t Port )
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* config GPIOA clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* config USART1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* USART1 GPIO config */
/* Configure USART1 Tx (PA.09) as alternate function push-pull */
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);
/* Configure USART1 Rx (PA.10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable the USART1 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* USART1 mode config */
USART_InitStructure.USART_BaudRate = Port; // 获取波特率
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(EVAL_COM1, &USART_InitStructure);
USART_ITConfig(EVAL_COM1, USART_IT_RXNE, ENABLE); // 使能串口接收中断
USART_Cmd(EVAL_COM1, ENABLE); // 使能串口
}
2、中断接收处理:
/************************************************************************/
/* STM32F10x USART Interrupt Handlers */
/************************************************************************/
/**
* @brief This function handles USART1 global interrupt request.
* @param None
* @retval None
*/
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(EVAL_COM1, USART_IT_RXNE) != RESET) // 判断接收
{
/* Read one byte from the receive data register */
Usart1.RxBuffer[Usart1.RxCounter++] = (USART_ReceiveData(EVAL_COM1) & 0x7F); // 获取数据
if(Usart1.RxCounter >= RxBUFFER_SIZE) // 判断是否超出接收最大长度
{
// /* Disable the EVAL_COM1 Receive interrupt */
// USART_ITConfig(EVAL_COM1, USART_IT_RXNE, DISABLE);
Usart1.Frame_flag = 0; // 接收完成标志清零
Usart1.RxCounter = 0; // 计数清零
Usart1.Receiving_Time = 0; // 接收超时时间清零
}
Usart1.Receiving_Time = 2; // 设置超时判定时间
}
/* 因为我们不去利用中断进行发送,所以下面的操作屏蔽掉 */
// if(USART_GetITStatus(EVAL_COM1, USART_IT_TXE) != RESET)
// {
// /* Write one byte to the transmit data register */
// USART_SendData(EVAL_COM1, TxBuffer[TxCounter++]);
// if(TxCounter == RxBUFFER_SIZE)
// {
// /* Disable the EVAL_COM1 Transmit interrupt */
// USART_ITConfig(EVAL_COM1, USART_IT_TXE, DISABLE);
// }
// }
}
在这里,用了超时判定来作为是否接收完一帧,所以还要在时间定时器中进行判定,判定如下:
if(Usart1.Receiving_Time)
{
Usart1.Receiving_Time--;
if(!Usart1.Receiving_Time)
Usart1.Frame_flag = 1;
}
至于什么时候才算一帧数据完成,这就取决于你设定的超时时间系数
3、发送输出处理:
/************************************************
函数名称 : USART_SendByte
功 能 : 串口字符发送
参 数 : c ---- 发送的数据
返 回 值 : 无
*************************************************/
void USART_SendByte( USART_TypeDef* USARTx, uint8_t c )
{
USART_SendData(USARTx, c);
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
/************************************************
函数名称 : USART_SendString
功 能 : 串口字符串发送
参 数 : USARTx ---- 串口
pData ---- 字符串
Length ---- 长度
返 回 值 : 无
*************************************************/
void USART_SendString( USART_TypeDef* USARTx, const uint8_t *pData, uint16_t Length )
{
while(Length--)
{
USART_SendByte(USARTx, *pData);
pData++;
}
}
/************************************************
函数名称 : USART_Printf
功 能 : 串口打印输出
参 数 : USARTx ---- 串口
String ---- 字符串
返 回 值 : 无
*************************************************/
void USART_Printf( USART_TypeDef* USARTx, char *String )
{
do
{
USART_SendByte(USARTx, *String);
String++;
}while((*String) != '\0');
}
五、printf()函数重定向
在官方提供的例程中,我们可以在 main.c文件中可以看到以下这个例程函数:
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART */
USART_SendData(EVAL_COM1, (uint8_t) ch);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(EVAL_COM1, USART_FLAG_TC) == RESET)
{}
return ch;
}
看注释我们知道这是官方给出的将 C库的 printf函数重定向到 USART的模版,那么我们修改一下,变成以下这样:
#define DEBUG_UART USART1
/************************************************
函数名称 : fputc
功 能 : 重定向 c库函数 printf到 DEBUG_UART
参 数 : ch
返 回 值 : 无
*************************************************/
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到 DEBUG_UART */
USART_SendData(DEBUG_UART, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_UART, USART_FLAG_TXE) == RESET);
return (ch);
}
/************************************************
函数名称 : fgetc
功 能 : 重定向 c库函数 scanf到 DEBUG_UART
参 数 : f ---- 文件
返 回 值 : 无
*************************************************/
int fgetc(FILE *f)
{
/* 等待 DEBUG_UART输入数据 */
while (USART_GetFlagStatus(DEBUG_UART, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_UART);
}
六、总工程代码
bsp_uart.c 源文件
#include "bsp_uart.h"
uint8_t g_TxCounter = 0; // 发送数据个数
uint8_t TxBuffer[TxBUFFER_SIZE] = {0}; // 发送暂存缓冲区
EVAL_COMx_TypeDef Usart1,Usart2;
/************************************************
函数名称 : UART1_Comfig
功 能 : UART1端口配置
参 数 : Baudrate ---- 波特率
返 回 值 : 无
*************************************************/
void UART1_Comfig( uint32_t Port )
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* config GPIOA clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* config USART1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/* USART1 GPIO config */
/* Configure USART1 Tx (PA.09) as alternate function push-pull */
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);
/* Configure USART1 Rx (PA.10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable the USART1 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* USART1 mode config */
USART_InitStructure.USART_BaudRate = Port; // 获取波特率
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(EVAL_COM1, &USART_InitStructure);
USART_ITConfig(EVAL_COM1, USART_IT_RXNE, ENABLE); // 使能串口接收中断
USART_Cmd(EVAL_COM1, ENABLE); // 使能串口
}
/************************************************
函数名称 : USART_SendByte
功 能 : 串口字符发送
参 数 : c ---- 发送的数据
返 回 值 : 无
*************************************************/
void USART_SendByte( USART_TypeDef* USARTx, uint8_t c )
{
USART_SendData(USARTx, c);
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
/************************************************
函数名称 : USART_SendString
功 能 : 串口字符串发送
参 数 : USARTx ---- 串口
pData ---- 字符串
Length ---- 长度
返 回 值 : 无
*************************************************/
void USART_SendString( USART_TypeDef* USARTx, const uint8_t *pData, uint16_t Length )
{
while(Length--)
{
USART_SendByte(USARTx, *pData);
pData++;
}
}
/************************************************
函数名称 : USART_Printf
功 能 : 串口打印输出
参 数 : USARTx ---- 串口
String ---- 字符串
返 回 值 : 无
*************************************************/
void USART_Printf( USART_TypeDef* USARTx, char *String )
{
do
{
USART_SendByte(USARTx, *String);
String++;
}while((*String) != '\0');
}
/************************************************
函数名称 : fputc
功 能 : 重定向 c库函数 printf到 DEBUG_UART
参 数 : ch
返 回 值 : 无
*************************************************/
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到 DEBUG_UART */
USART_SendData(DEBUG_UART, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_UART, USART_FLAG_TXE) == RESET);
return (ch);
}
/************************************************
函数名称 : fgetc
功 能 : 重定向 c库函数 scanf到 DEBUG_UART
参 数 : f ---- 文件
返 回 值 : 无
*************************************************/
int fgetc(FILE *f)
{
/* 等待 DEBUG_UART输入数据 */
while (USART_GetFlagStatus(DEBUG_UART, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_UART);
}
/************************************************************************/
/* STM32F10x USART Interrupt Handlers */
/************************************************************************/
/**
* @brief This function handles USART1 global interrupt request.
* @param None
* @retval None
*/
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(EVAL_COM1, USART_IT_RXNE) != RESET) // 判断接收
{
/* Read one byte from the receive data register */
Usart1.RxBuffer[Usart1.RxCounter++] = (USART_ReceiveData(EVAL_COM1) & 0x7F); // 获取数据
if(Usart1.RxCounter >= RxBUFFER_SIZE) // 判断是否超出接收最大长度
{
// /* Disable the EVAL_COM1 Receive interrupt */
// USART_ITConfig(EVAL_COM1, USART_IT_RXNE, DISABLE);
Usart1.Frame_flag = 0; // 接收完成标志清零
Usart1.RxCounter = 0; // 计数清零
Usart1.Receiving_Time = 0; // 接收超时时间清零
}
Usart1.Receiving_Time = 2; // 设置超时判定时间
}
/* 因为我们不去利用中断进行发送,所以下面的操作屏蔽掉 */
// if(USART_GetITStatus(EVAL_COM1, USART_IT_TXE) != RESET)
// {
// /* Write one byte to the transmit data register */
// USART_SendData(EVAL_COM1, TxBuffer[TxCounter++]);
// if(TxCounter == RxBUFFER_SIZE)
// {
// /* Disable the EVAL_COM1 Transmit interrupt */
// USART_ITConfig(EVAL_COM1, USART_IT_TXE, DISABLE);
// }
// }
}
/*---------------------------- END OF FILE ----------------------------*/
bsp_uart.h 头文件
#ifndef __BSP_UART_H
#define __BSP_UART_H
#include <stdio.h>
#include "stm32f10x.h"
#define DEBUG_UART USART1
#define EVAL_COM1 USART1
#define EVAL_COM2 USART2
#define TxBUFFER_SIZE 100
#define RxBUFFER_SIZE 0xFF
typedef struct
{
uint8_t RxBuffer[RxBUFFER_SIZE]; // 接收暂存缓冲区
__IO uint8_t RxCounter; // 接收数据个数
uint8_t Receiving_Time; // 接收时间
uint8_t Frame_flag; // 一帧完成标志
}EVAL_COMx_TypeDef;
extern EVAL_COMx_TypeDef Usart1,Usart2;
extern uint8_t g_TxCounter;
extern uint8_t TxBuffer[TxBUFFER_SIZE];
void UART1_Comfig( uint32_t Baudrate );
void USART_SendByte( USART_TypeDef* USARTx, uint8_t c );
void USART_SendString( USART_TypeDef* USARTx, const uint8_t *pData, uint16_t Length );
void USART_Printf( USART_TypeDef* USARTx, char *String );
#endif /* __BSP_UART_H */
/*---------------------------- END OF FILE ----------------------------*/
main.c 文件
/**
******************************************************************************
* @file Project/STM32F10x_StdPeriph_Template/main.c
* @author MCD Application Team
* @version V3.5.0
* @date 08-April-2011
* @brief Main program body
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>© COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdio.h>
#include "bsp.h"
#include "bsp_uart.h"
/* Private functions ---------------------------------------------------------*/
const uint32_t Baudrate_1 = 115200; // 波特率设置 支持的波特率:115200,19200,9600,38400,57600,1200,2400,4800
const uint32_t Baudrate_2 = 115200; // 波特率设置 支持的波特率:115200,19200,9600,38400,57600,1200,2400,4800
/************************************************
函数名称 : main
功 能 : 主函数入口
参 数 : 无
返 回 值 : 无
*************************************************/
int main(void)
{
/* Initial Configuration */
SystemInit();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SysTick_Init();
UART1_Comfig(Baudrate_1);
/* -------- End -------- */
/* Infinite loop */
while (1)
{
if(Usart1.Frame_flag)
{
Usart1.Frame_flag = 0;
USART_SendString(USART1, Usart1.RxBuffer, Usart1.RxCounter);
printf("\n>>>>> 接收成功\n");
Usart1.RxCounter = 0;
}
}
}
#if 0
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART */
USART_SendData(EVAL_COM1, (uint8_t) ch);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(EVAL_COM1, USART_FLAG_TC) == RESET)
{}
return ch;
}
#endif
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
/**
* @}
*/
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
在这里,没有把 UART的串口中断放到 stm32f10x_it.c文件中,是个人觉得统一放在自定义的 uart文件中易于管理,包括后面的 Time、ADC等,而那些 I/O中断就放回 stm32f10x_it.c文件中(主要是觉得不可能一些 LED、Key什么的,又创一个 bsp文件给她吧,这样就太太累赘了);还有就是那个超时判定的那部分在定时器中代码并没有贴出来(主要是还没发 Time的篇章,等待下一篇吧!)
下一篇: STM32笔记之 ADC(模数转换)