欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32笔记之 USART(串口)

程序员文章站 2022-03-06 08:08:26
...

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

 

目录

一、串口通讯方式

二、USART收发功能框图

三、USART模式配置

四、代码实现过程

五、printf()函数重定向

六、总工程代码


 

一、串口通讯方式

这里就不再累赘阐释了,看以前的篇章 UART/ USRAT、I2C、SPI通信方式扫盲

 

二、USART收发功能框图

STM32笔记之 USART(串口)

 

三、USART模式配置

1、各模式功能支持

STM32笔记之 USART(串口)

2、I/O复用配置

STM32笔记之 USART(串口)

 

四、代码实现过程

在这次例程中,我们配置成 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的篇章,等待下一篇吧!)

相关标签: STM32 stm32 uart