寫在前面:
本文章旨在總結備份、方便以後查詢,由於是個人總結,如有不對,歡迎指正;另外,內容大部分來自網絡、書籍、和各類手冊,如若侵權請告知,馬上刪帖致歉。
目錄
一、串口通訊方式
這裏就不再累贅闡釋了,看以前的篇章 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>© 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的篇章,等待下一篇吧!)