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 BAUDRATE_1		115200;			// 波特率設置	支持的波特率:115200,19200,9600,38400,57600,1200,2400,4800

#define EVAL_COM1		USART1

/************************************************
函數名稱 : UART1_Comfig
功    能 : UART1端口配置
參    數 : 無
返 回 值 : 無
*************************************************/
void UART1_Comfig(void)
{
    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 = BAUDRATE_1;              // 獲取波特率
    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 */
    #if 0
        Usart1.RxBuffer[Usart1.RxCounter++] = (USART_ReceiveData(EVAL_COM1) & 0x7F);  // 如果使能了奇偶校驗,則最高位 bit 8需要省掉
    #else
        Usart1.RxBuffer[Usart1.RxCounter++] = (USART_ReceiveData(EVAL_COM1) & 0xFF);  // 獲取數據
    #endif

        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);
//    }
//  }
}

在官方給的串口中斷處理函數中,讀取接收字符的代碼爲:RxBuffer[RxCount++] = (USART_ReceiveData(USART1) & 0x7F);這裏爲什麼與的不是 0xFF而是 0x7F,這是因爲在查看手冊後,瞭解到無論串口配置時選的數據寬度爲 8bit還是 9bit,其最高位一般保留爲奇偶檢驗的結果位,因此奇偶檢驗使能的情況下讀取實際數據的話應該省掉最高位

在這裏,用了超時判定來作爲是否接收完一幀,所以還要在時間定時器中進行判定,判定如下:

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"


#define BAUDRATE_1		115200;			// 波特率設置	支持的波特率:115200,19200,9600,38400,57600,1200,2400,4800

uint8_t g_TxCounter = 0; 				// 發送數據個數
uint8_t TxBuffer[TxBUFFER_SIZE] = {0};	// 發送暫存緩衝區

EVAL_COMx_TypeDef Usart1,Usart2;

/************************************************
函數名稱 : UART1_Comfig
功    能 : UART1端口配置
參    數 : 無
返 回 值 : 無
*************************************************/
void UART1_Comfig(void)
{
    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 = BAUDRATE_1;              // 獲取波特率
    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(void);
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 ---------------------------------------------------------*/

/************************************************
函數名稱 : main
功    能 : 主函數入口
參    數 : 無
返 回 值 : 無
*************************************************/
int main(void)
{
    /* Initial Configuration */
    SystemInit();
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SysTick_Init();
    UART1_Comfig();
	
    /* -------- 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的篇章,等待下一篇吧!)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章