STM32笔记之 USART(串口)

写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。

目录

一、串口通讯方式

二、USART收发功能框图

三、USART模式配置

四、代码实现过程

五、printf()函数重定向

六、总工程代码


一、串口通讯方式

这里就不再累赘阐释了,看以前的篇章 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>&copy; 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的篇章,等待下一篇吧!)

发布了40 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42992084/article/details/104098531