day16:USART串口通信(上位機和下位機互相發送數據)


各個串口的引腳說明:

如果是同步通信,則會使用到SCLK同步時鍾,下面是它的結構體,如果是異步通信,就用不上(實際中常用的是異步通信)

USART串口通信使用到固件庫函數:

下面是要做的實驗:

 ===================================== (1)編寫簡單的串口通信實驗:從開發板發送數據到電腦的串口調試助手

(1)拷貝工程模板,改工程名為:USART,在main.c同級目錄下新建目錄usart,在usart目錄下新建兩個文件:bsp_usart.c、bsp_usart.h

(2)bsp_usart.h

#ifndef __BSP_USART_H__
#define __BSP_USART_H__

#include "stm32f10x.h"

// ----------------------- 串口1-USART1
// 使用哪個串口(串口1..5)
#define  DEBUG_USARTx                   USART1					
// APB2串口的同步時鍾
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1	
// APB2系統時鍾(因為串口USART1是掛載到APB2總線上的,所以要打開APB2總線的時鍾)
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd	
// 串口通信的波特率
#define  DEBUG_USART_BAUDRATE           115200


// ----------------------- USART GPIO 引腳宏定義
// GPIO引腳
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)	
// APB2系統時鍾(因為串口USART1是掛載到APB2總線上的,所以要打開APB2總線的時鍾)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd	

// GPIO引腳,發送接PA9,接收接PA10   
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   		
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler

/* 串口調試配置函數:配置串口的相關參數,使能串口 */
void DEBUG_USART_Config(void);

#endif		/* __BSP_USART_H__ */

(3)bsp_usart.c

#include "./usart/bsp_usart.h"

/* 串口調試配置函數:配置串口的相關參數,使能串口 */
void DEBUG_USART_Config(void)
{
	/* 結構體變量聲明 */
	GPIO_InitTypeDef GPIO_InitStructure;		// GPIO
	USART_InitTypeDef USART_InitStructure;		// USART
	
	/* ------------ 第一步:初始化GPIO */
	// 打開串口GPIO的時鍾
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);	
	
	// 將USART Tx的GPIO配置為推挽復用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;		// 引腳
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				// 模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			// 速率
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);	// 初始化結構體
	
	// 將USART Rx的GPIO配置為浮空輸入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	/* ------------ 第二步:配置串口的初始化結構體 */
	// 打開串口外設的時鍾
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
	/* 配置串口的工作參數 */
	// 波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 針數據字長
	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(DEBUG_USARTx, &USART_InitStructure);
	
	/* ------------ 第三步:使能串口 */
	USART_Cmd(DEBUG_USARTx, ENABLE);
}

(4)main.c

/* 
USART串口通信實驗:先實現開發發送數據到電腦的串口通信實驗的最簡單操作
隨便發送一個簡單字符到電腦,使用串口調試助手接收並打印顯示出來
*/

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"

int main(void)
{
	/* USART串口通信初始化 */
	DEBUG_USART_Config();
	
	/* 嘗試從開發板發送一個字符到電腦上的串口調試助手,並顯示 */
	USART_SendData(DEBUG_USARTx, 'Q');
	
    while(1);
}

(5)實驗現象:

1.首先確保電腦已經安裝了USB轉串口的驅動CH340;

2.使用單片機的USB線將電腦USB口和USB轉串口接口連接,編譯代碼,並燒錄到單片機中;

3.打開秉火串口調試助手V1.0.exe,並設置好參數(和程序中參數一致);

4.按下單片機上的RESET鍵,在串口助手上就可以接收到數據

 

 ===================================== (2)發送一個字節的數據

(1)在bsp_usart.h中添加:

/* 發送一個字節 */
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t ch);

 (2)在bsp_usart.c中添加:

/* 發送一個字節 */
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t ch)
{
	/* 發送一個字節數據到USART */
	USART_SendData(pUSARTx, ch);
		
	/* 等待發送數據寄存器為空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

(3)main.c

/* 
USART串口通信實驗:先實現開發發送數據到電腦的串口通信實驗的最簡單操作
隨便發送一個簡單字符到電腦,使用串口調試助手接收並打印顯示出來
*/

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"

int main(void)
{
	/* USART串口通信初始化 */
	DEBUG_USART_Config();
	
	/* 嘗試從開發板發送一個字符到電腦上的串口調試助手,並顯示 */
	//USART_SendData(DEBUG_USARTx, 'Q');
	Usart_SendByte(DEBUG_USARTx, 0x42);
	
    while(1);
}

實驗現象:

(1)發送的數據是42,卻出現了FF(屬於亂碼)

(2)把波特率改為:19200,就不會有亂碼和其他奇怪的字符了,可能發送數據的速度太快了,又沒有進行校驗位校驗,而且是異步通信,沒有

系統時鍾進行同步,數據在傳輸過程中出現了重疊導致的錯誤。(具體原因還有待研究)

 

  ===================================== (3)發送字符串

 (1)在bsp_usart.h中添加:

/* 發送字符串 */
void Usart_SendString(USART_TypeDef* pUSARTx, char* str);

 (2)在bsp_usart.c中添加:

/* 發送字符串 */
void Usart_SendString(USART_TypeDef* pUSARTx, char* str)
{
	unsigned int k=0;
	do 
	{
		Usart_SendByte(pUSARTx, *(str + k));
		k++;
	} while(*(str + k)!='\0');
  
	/* 等待發送完成 */
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}

(3)修改main.c

/* 
USART串口通信實驗:先實現開發發送數據到電腦的串口通信實驗的最簡單操作
隨便發送一個簡單字符到電腦,使用串口調試助手接收並打印顯示出來
*/

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"

int main(void)
{
	/* USART串口通信初始化 */
	DEBUG_USART_Config();
	
	/* 嘗試從開發板發送一個字符到電腦上的串口調試助手,並顯示 */
	//USART_SendData(DEBUG_USARTx, 'Q');		// 發送一個字符
	//Usart_SendByte(DEBUG_USARTx, 0x42);		// 發送一個字節
	Usart_SendString(DEBUG_USARTx, "歡迎使用STM32F103RCT6!");		// 發送字符串
	
    while(1);
}

實驗現象:出現亂碼

用記事本打開本地的main.c文件,另存為ANSI格式(一定要保證本地和keil中改文件要是相同的格式,同是ANSI或同是UTF-8)。

重新編譯燒錄到開發板中,再試:

把波特率改成115200,再試:數據不整齊,可能會有亂碼

 

  ===================================== (4)使用C語言的printf函數輸出數據到上位機

main.c

/* 
USART串口通信實驗:先實現開發發送數據到電腦的串口通信實驗的最簡單操作
隨便發送一個簡單字符到電腦,使用串口調試助手接收並打印顯示出來
*/

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "stdio.h"

int main(void)
{
	/* USART串口通信初始化 */
	DEBUG_USART_Config();
	
	/* 嘗試從開發板發送一個字符到電腦上的串口調試助手,並顯示 */
	//USART_SendData(DEBUG_USARTx, 'Q');		// 發送一個字符
	//Usart_SendByte(DEBUG_USARTx, 0x42);		// 發送一個字節
	//Usart_SendString(DEBUG_USARTx, "歡迎使用STM32F103RCT6!");		// 發送字符串
	
	// 使用printf函數將數據輸出到上位機
	printf("使用printf函數將數據輸出到上位機");
	
    while(1);
}

實驗現象:將程序燒錄到單片機中,按Reset鍵並不會打印數據

原因:使用printf發送數據到上位機的過程是,printf -> fputchar -> USART_SendData函數 -> 電腦的上位機

所以下面將USART_SendData函數替換掉fputchar函數即可

(1)在bsp_usart.c中添加:

/* 重定向c庫函數printf到串口,重定向后可使用printf函數 */
int fputc(int ch, FILE *f)
{
	/* 發送一個字節數據到串口 */
	USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
	/* 等待發送完畢 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
	return (ch);
}

/* 重定向c庫函數scanf到串口,重寫向后可使用scanf、getchar等函數 */
int fgetc(FILE *f)
{
    /* 等待串口輸入數據 */
    while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

    return (int)USART_ReceiveData(DEBUG_USARTx);
}

 (2)在bsp_usart.h中添加庫文件:

#include "stdio.h"

 (3)修改main.c

/* 
USART串口通信實驗:先實現開發發送數據到電腦的串口通信實驗的最簡單操作
隨便發送一個簡單字符到電腦,使用串口調試助手接收並打印顯示出來
*/

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"

int main(void)
{
	/* USART串口通信初始化 */
	DEBUG_USART_Config();
	
	/* 嘗試從開發板發送一個字符到電腦上的串口調試助手,並顯示 */
	//USART_SendData(DEBUG_USARTx, 'Q');		// 發送一個字符
	//Usart_SendByte(DEBUG_USARTx, 0x42);		// 發送一個字節
	//Usart_SendString(DEBUG_USARTx, "歡迎使用STM32F103RCT6!");		// 發送字符串
	
	// 使用printf函數將數據輸出到上位機
	printf("使用printf函數將數據輸出到上位機\r\n");
	
    while(1);
}

(4)如果使用的是MDK,請在工程屬性的“Target“-》”Code Generation“中勾選”Use MicroLIB“

實驗現象:按下RESET鍵,串口助手上打印顯示數據

 

   ========================== (5)上位機發送數據到單片機,單片機將數據返回給上位機

 原理是:在bsp_usart.c串口配置函數中添加串口中斷配置函數,當單片機接收到上位機發過來的數據之后產生一個中斷,就會去bsp_usart.h中找

中斷函數的宏定義 USART1_IRQHandler,進而找到中斷服務函數的宏定義名函數名 DEBUG_USART_IRQHandler,找到中斷函數響應文件stm32f10x_it.c,

找到中斷服務函數 DEBUG_USART_IRQHandler,在這個函數里面接收上位機發送過來的數據,並將此數據返回到上位機,

上位機接收到數據之后打印出來。

(1)bsp_usart.c,在函數void DEBUG_USART_Config(void)中添加

	/* -------------------------------------------------------- */
	// 串口中斷優先級配置
	NVIC_Configuration();	
	
	// 使能串口接收中斷
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
	/* -------------------------------------------------------- */

完整文件:

#include "./usart/bsp_usart.h"

/* 串口中斷配置函數 */
static void NVIC_Configuration(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
  
	/* 嵌套向量中斷控制器組選擇 */
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
	/* 配置USART為中斷源 */
	NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
	/* 搶斷優先級*/
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	/* 子優先級 */
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	/* 使能中斷 */
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	/* 初始化配置NVIC */
	NVIC_Init(&NVIC_InitStructure);
}

/* 串口調試配置函數:配置串口的相關參數,使能串口 */
void DEBUG_USART_Config(void)
{
	/* 結構體變量聲明 */
	GPIO_InitTypeDef GPIO_InitStructure;		// GPIO
	USART_InitTypeDef USART_InitStructure;		// USART
	
	/* ------------ 第一步:初始化GPIO */
	// 打開串口GPIO的時鍾
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);	
	
	// 將USART Tx的GPIO配置為推挽復用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;		// 引腳
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				// 模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			// 速率
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);	// 初始化結構體
	
	// 將USART Rx的GPIO配置為浮空輸入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	/* ------------ 第二步:配置串口的初始化結構體 */
	// 打開串口外設的時鍾
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
	/* 配置串口的工作參數 */
	// 波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 針數據字長
	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(DEBUG_USARTx, &USART_InitStructure);
	
	/* -------------------------------------------------------- */
	// 串口中斷優先級配置
	NVIC_Configuration();	
	
	// 使能串口接收中斷
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
	/* -------------------------------------------------------- */
	
	/* ------------ 第三步:使能串口 */
	USART_Cmd(DEBUG_USARTx, ENABLE);
}

/* 發送一個字節 */
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t ch)
{
	/* 發送一個字節數據到USART */
	USART_SendData(pUSARTx, ch);
		
	/* 等待發送數據寄存器為空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/* 發送字符串 */
void Usart_SendString(USART_TypeDef* pUSARTx, char* str)
{
	unsigned int k=0;
	do 
	{
		Usart_SendByte(pUSARTx, *(str + k));
		k++;
	} while(*(str + k)!='\0');
  
	/* 等待發送完成 */
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}

/* 重定向c庫函數printf到串口,重定向后可使用printf函數 */
int fputc(int ch, FILE *f)
{
	/* 發送一個字節數據到串口 */
	USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
	/* 等待發送完畢 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
	return (ch);
}

/* 重定向c庫函數scanf到串口,重寫向后可使用scanf、getchar等函數 */
int fgetc(FILE *f)
{
    /* 等待串口輸入數據 */
    while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

    return (int)USART_ReceiveData(DEBUG_USARTx);
}

(2)在stm32f10x_it.c

/* 串口中斷服務函數,DEBUG_USART_IRQHandler函數名在bsp_usart.h中宏定義 */
void DEBUG_USART_IRQHandler(void)
{
	// 聲明一個變量暫時存放接收過來的數據
	uint8_t ucTemp;
	
	// 判斷是否接收到數據
	if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)
	{		
		// 如果接收到數據,那么從DEBUG_USARTx串口(在bsp_usart.h中定義為USART1)獲取數據
		ucTemp = USART_ReceiveData(DEBUG_USARTx);
		
		// 最后將數據返回到上位機
		USART_SendData(DEBUG_USARTx, ucTemp);    
	}	 
}

(3)main.c不用修改

/* 
USART串口通信實驗:先實現開發發送數據到電腦的串口通信實驗的最簡單操作
隨便發送一個簡單字符到電腦,使用串口調試助手接收並打印顯示出來
*/

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"

int main(void)
{
	/* USART串口通信初始化 */
	DEBUG_USART_Config();
	
	/* 嘗試從開發板發送一個字符到電腦上的串口調試助手,並顯示 */
	//USART_SendData(DEBUG_USARTx, 'Q');		// 發送一個字符
	//Usart_SendByte(DEBUG_USARTx, 0x42);		// 發送一個字節
	//Usart_SendString(DEBUG_USARTx, "歡迎使用STM32F103RCT6!");		// 發送字符串
	
	// 使用printf函數將數據輸出到上位機
	//printf("使用printf函數將數據輸出到上位機\r\n");
	
    while(1);
}

實驗現象:

以上的stm32f10x_it.c函數每接收一個數據的時候就產生一次中斷,這樣對CPU的負荷比較大,在實際的項目當中不會這么操作,而是使用隊列的形式來發送數據,

即等上位機將所有數據都發送到開發板之后,開發板再將數據一次性發送到上位機(只產生一次中斷)

/* 串口中斷服務函數,DEBUG_USART_IRQHandler函數名在bsp_usart.h中宏定義 */
void DEBUG_USART_IRQHandler(void)
{
	// 聲明一個變量暫時存放接收過來的數據
	uint8_t ucTemp;
	
	// 判斷是否接收到數據
	if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)
	{		
		// 如果接收到數據,那么從DEBUG_USARTx串口(在bsp_usart.h中定義為USART1)獲取數據
		ucTemp = USART_ReceiveData(DEBUG_USARTx);
		
		// 最后將數據返回到上位機
		USART_SendData(DEBUG_USARTx, ucTemp);    
	}	 
}

以下是環形隊列發送數據:

(1)rx_data_queue.h

#ifndef __ESP_DATA_QUEUE_H_
#define __ESP_DATA_QUEUE_H_

#include "stm32f10x.h"

#include <string.h>
#include <stdio.h>

//緩沖隊列的個數需要為2的冪
#define QUEUE_NODE_NUM        (2)			//緩沖隊列的大小(有多少個緩沖區)
#define QUEUE_NODE_DATA_LEN   (2*1024 )		//單個接收緩沖區大小

// 隊列的主體數據類型接口
#define QUEUE_DATA_TYPE  				ESP_USART_FRAME
// 隊列的調試輸出接口
#define DATA_QUEUE_LOG  				QUEUE_DEBUG
#define DATA_QUEUE_LOG_ARRAY 	QUEUE_DEBUG_ARRAY


// 數據主體
typedef struct 
{
	char  *head; 	// 緩沖區頭指針	
	uint16_t len; 	// 接收到的數據長度

}ESP_USART_FRAME;


// 隊列結構
typedef struct {
	int         size;  		/* 緩沖區大小 */
	int         read; 		/* 讀指針 */
	int         write;   	/* 寫指針 */
	int read_using;			/* 正在讀取的緩沖區指針 */
	int write_using;		/* 正在寫入的緩沖區指針 */
	QUEUE_DATA_TYPE    *elems[QUEUE_NODE_NUM];  /* 緩沖區地址 */
} QueueBuffer;

extern QueueBuffer rx_queue;

/* 信息輸出 */
#define QUEUE_DEBUG_ON         1
#define QUEUE_DEBUG_ARRAY_ON		1

#define QUEUE_INFO(fmt,arg...)           printf("<<-QUEUE-INFO->> "fmt"\n",##arg)
#define QUEUE_ERROR(fmt,arg...)          printf("<<-QUEUE-ERROR->> "fmt"\n",##arg)
#define QUEUE_DEBUG(fmt,arg...)          do{\
                                          if(QUEUE_DEBUG_ON)\
                                          printf("<<-QUEUE-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)

#define QUEUE_DEBUG_ARRAY(array, num)    do{\
	 int32_t i;\
	 uint8_t* a = array;\
	 if(QUEUE_DEBUG_ARRAY_ON)\
	 {\
			printf("\n<<-QUEUE-DEBUG-ARRAY->>\n");\
			for (i = 0; i < (num); i++)\
			{\
					printf("%02x   ", (a)[i]);\
					if ((i + 1 ) %10 == 0)\
					{\
							printf("\n");\
					}\
			}\
			printf("\n");\
	}\
 }while(0)	

//輸出隊列的狀態信息
#define cbPrint(cb)		    DATA_QUEUE_LOG("size=0x%x, read=%d, write=%d\n", cb.size, cb.read, cb.write);\
	  DATA_QUEUE_LOG("size=0x%x, read_using=%d, write_using=%d\n", cb.size, cb.read_using, cb.write_using);


QUEUE_DATA_TYPE* cbWrite(QueueBuffer *cb);
QUEUE_DATA_TYPE* cbRead(QueueBuffer *cb);
void cbReadFinish(QueueBuffer *cb);
void cbWriteFinish(QueueBuffer *cb);
//void cbPrint(QueueBuffer *cb);
QUEUE_DATA_TYPE* cbWriteUsing(QueueBuffer *cb) ;
int cbIsFull(QueueBuffer *cb) ; 
int cbIsEmpty(QueueBuffer *cb) ;

void rx_queue_init(void);
void pull_data_from_queue(void);
void push_data_to_queue(char *src_dat,uint16_t src_len);

#endif

(2)rx_data_queue.c

/**
  ******************************************************************************
  * @file    rx_data_queue.c
  * @author  fire
  * @version V1.0
  * @date    2015-01-xx
  * @brief   環形緩沖區,適用於接收外部數據時用作緩沖
  ******************************************************************************
  * @attention
  *
  * 實驗平台:秉火 IOT STM32 開發板 
  * 論壇    :http://www.firebbs.cn
  * 淘寶    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */ 

#include "./usart/rx_data_queue.h"



//實例化節點數據類型
QUEUE_DATA_TYPE  node_data[QUEUE_NODE_NUM]; 
//實例化隊列類型
QueueBuffer rx_queue;

//隊列緩沖區的內存池
__align(4) char node_buff[QUEUE_NODE_NUM][QUEUE_NODE_DATA_LEN] ;



/*環形緩沖隊列*/

/**
  * @brief  初始化緩沖隊列
  * @param  cb:緩沖隊列結構體
  * @param  size: 緩沖隊列的元素個數
  * @note 	初始化時還需要給cb->elems指針賦值
  */
void cbInit(QueueBuffer *cb, int size) 
{
    cb->size  = size;	/* maximum number of elements           */
    cb->read = 0; 		/* index of oldest element              */
    cb->write   = 0; 	 	/* index at which to write new element  */
//    cb->elems = (uint8_t *)calloc(cb->size, sizeof(uint8_t));  //elems 要額外初始化
}
 
//(此函數改成了宏,在頭文件)
/**
  * @brief  輸出緩沖隊列當前的狀態信息
  * @param  cb:緩沖隊列結構體
  */
//void cbPrint(QueueBuffer *cb) 
//{
//    DATA_QUEUE_LOG("size=0x%x, read=%d, write=%d\n", cb->size, cb->read, cb->write);
//	  DATA_QUEUE_LOG("size=0x%x, read_using=%d, write_using=%d\n", cb->size, cb->read_using, cb->write_using);
//}
 
/**
  * @brief  判斷緩沖隊列是(1)否(0)已滿
  * @param  cb:緩沖隊列結構體
  */
int cbIsFull(QueueBuffer *cb) 
{
    return cb->write == (cb->read ^ cb->size); /* This inverts the most significant bit of read before comparison */ 
}
 
/**
  * @brief  判斷緩沖隊列是(1)否(0)全空
  * @param  cb:緩沖隊列結構體
  */		
int cbIsEmpty(QueueBuffer *cb) 
{
    return cb->write == cb->read; 
}

/**
  * @brief  對緩沖隊列的指針加1
  * @param  cb:緩沖隊列結構體
  * @param  p:要加1的指針
  * @return  返回加1的結果
  */	
int cbIncr(QueueBuffer *cb, int p) 
{
    return (p + 1)&(2*cb->size-1); /* read and write pointers incrementation is done modulo 2*size */
}
 
/**
  * @brief  獲取可寫入的緩沖區指針
  * @param  cb:緩沖隊列結構體
  * @return  可進行寫入的緩沖區指針
  * @note  得到指針后可進入寫入操作,但寫指針不會立即加1,
           寫完數據時,應調用cbWriteFinish對寫指針加1
  */
QUEUE_DATA_TYPE* cbWrite(QueueBuffer *cb) 
{
    if (cbIsFull(cb)) /* full, overwrite moves read pointer */
    {
			return NULL;
		}		
		else
		{
			//當wriet和write_using相等時,表示上一個緩沖區已寫入完畢,需要對寫指針加1
			if(cb->write == cb->write_using)
			{
				cb->write_using = cbIncr(cb, cb->write); //未滿,則增加1
			}
		}
		
	return  cb->elems[cb->write_using&(cb->size-1)];
}



/**
  * @brief 數據寫入完畢,更新寫指針到緩沖結構體
  * @param  cb:緩沖隊列結構體
  */
void cbWriteFinish(QueueBuffer *cb)
{
    cb->write = cb->write_using;
}
 
/**
  * @brief  獲取可讀取的緩沖區指針
  * @param  cb:緩沖隊列結構體
  * @return  可進行讀取的緩沖區指針
  * @note  得到指針后可進入讀取操作,但讀指針不會立即加1,
					 讀取完數據時,應調用cbReadFinish對讀指針加1
  */
QUEUE_DATA_TYPE* cbRead(QueueBuffer *cb) 
{
		if(cbIsEmpty(cb))
			return NULL;
		
	//當read和read_using相等時,表示上一個緩沖區已讀取完畢(即已調用cbReadFinish),
	//需要對寫指針加1
	if(cb->read == cb->read_using)	
		cb->read_using = cbIncr(cb, cb->read);
	
	return cb->elems[cb->read_using&(cb->size-1)];
}


/**
  * @brief 數據讀取完畢,更新讀指針到緩沖結構體
  * @param  cb:緩沖隊列結構體
  */
void cbReadFinish(QueueBuffer *cb) 
{	
		//重置當前讀完的數據節點的長度
		cb->elems[cb->read_using&(cb->size-1)]->len = 0;
	
    cb->read = cb->read_using;
}



//隊列的指針指向的緩沖區全部銷毀
void camera_queue_free(void)
{
    uint32_t i = 0;

    for(i = 0; i < QUEUE_NODE_NUM; i ++)
    {
        if(node_data[i].head != NULL)
        {
					//若是動態申請的空間才要free
//            free(node_data[i].head);
            node_data[i].head = NULL;
        }
    }

    return;
}


/**
  * @brief  緩沖隊列初始化,分配內存,使用緩沖隊列時,
  * @param  無
  * @retval 無
  */
void rx_queue_init(void)
{
  uint32_t i = 0;

  memset(node_data, 0, sizeof(node_data));
		 
	/*初始化緩沖隊列*/
	cbInit(&rx_queue,QUEUE_NODE_NUM);

    for(i = 0; i < QUEUE_NODE_NUM; i ++)
    {
        node_data[i].head = node_buff[i];
        
        /*初始化隊列緩沖指針,指向實際的內存*/
        rx_queue.elems[i] = &node_data[i];
        
        DATA_QUEUE_LOG("node_data[i].head=0x%x,\r\nrx_queue.elems[i] =0x%x", (uint32_t)node_data[i].head,(uint32_t)rx_queue.elems[i]->head);

        memset(node_data[i].head, 0, QUEUE_NODE_DATA_LEN);
    }
		
	cbPrint(rx_queue);	
}



/**
  * @brief  往隊列中寫入數據的樣例
  */
void push_data_to_queue(char *src_dat,uint16_t src_len)
{
	QUEUE_DATA_TYPE *data_p;
	uint8_t i;
	
	for(i=0;i<src_len;i++)
	{
		/*獲取寫緩沖區指針,准備寫入新數據*/
		data_p = cbWrite(&rx_queue);
		
		if (data_p != NULL)	//若緩沖隊列未滿,開始傳輸
		{		
			//往緩沖區寫入數據,如使用串口接收、dma寫入等方式
			*(data_p->head + i) = src_dat[i];
				data_p->len++;
			printf("\r\ndata_p->len =%d",data_p->len);
		}else return;	
		
		cbPrint(rx_queue);	
	}	
	
	/*寫入緩沖區完畢*/
	cbWriteFinish(&rx_queue);
	
	cbPrint(rx_queue);	

}


/**
  * @brief  從隊列中取數據的樣例
  */
void pull_data_from_queue(void)
{
	QUEUE_DATA_TYPE *rx_data;	
		
	/*從緩沖區讀取數據,進行處理,*/
	rx_data = cbRead(&rx_queue); 

	if(rx_data != NULL)//緩沖隊列非空
	{		
		//加上字符串結束符,方便直接輸出字符串
		*(rx_data->head+rx_data->len) = '\0';
		
		QUEUE_DEBUG("接收到的數據:%s",rx_data->head);
		QUEUE_DEBUG_ARRAY((uint8_t*)rx_data->head,rx_data->len);

		//使用完數據必須調用cbReadFinish更新讀指針
		cbReadFinish(&rx_queue);
	}
}

 stm32f10x_it.c

// 串口中斷服務函數
// 把接收到的數據寫入緩沖區,在main函數中輪詢緩沖區輸出數據
void DEBUG_USART_IRQHandler(void)
{	
	uint8_t ucCh;
	QUEUE_DATA_TYPE *data_p; 
	
	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
	{	
		ucCh  = USART_ReceiveData( DEBUG_USARTx );
		
		/*獲取寫緩沖區指針,准備寫入新數據*/
		data_p = cbWrite(&rx_queue); 
		
		if (data_p != NULL)	//若緩沖隊列未滿,開始傳輸
		{		

			//往緩沖區寫入數據,如使用串口接收、dma寫入等方式
			*(data_p->head + data_p->len) = ucCh;
				
			if( ++data_p->len >= QUEUE_NODE_DATA_LEN)
			{
				cbWriteFinish(&rx_queue);
			}
		}else return;	
	}
	
	if ( USART_GetITStatus( DEBUG_USARTx, USART_IT_IDLE ) == SET )//數據幀接收完畢
	{
			/*寫入緩沖區完畢*/
			cbWriteFinish(&rx_queue);
		ucCh = USART_ReceiveData( DEBUG_USARTx );  //由軟件序列清除中斷標志位(先讀USART_SR,然后讀USART_DR)

	}
}

 

在bsp_uart.c中的void DEBUG_USART_Config(void)函數中添加:

	/* -------------------------------------------------------- */
	// 串口中斷優先級配置
	NVIC_Configuration();  
	 
	 // 使能串口接收中斷【環形隊列接收數據配置】
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
	USART_ITConfig(DEBUG_USARTx, USART_IT_IDLE, ENABLE); //使能串口總線空閑中斷 
	/* -------------------------------------------------------- */	

 

在main.c文件中調用環形隊列發送數據函數:

/**
  ******************************************************************************
  * @file    main.c
  * @author  fire
  * @version V1.0
  * @date    2013-xx-xx
  * @brief   使用環形緩沖區的方式接收串口數據
  ******************************************************************************
  * @attention
  *
  * 實驗平台:秉火 F103-MINI STM32 開發板 
  * 論壇    :http://www.firebbs.cn
  * 淘寶    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */ 
 
 
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "./usart/rx_data_queue.h"

/**
  * @brief  主函數
  * @param  無
  * @retval 無
  */
int main(void)
{	
  /*初始化USART 配置模式為 115200 8-N-1,中斷接收*/
  USART_Config();
		
	rx_queue_init();
	
	/* 發送一個字符串 */
	Usart_SendString( DEBUG_USARTx,"這是一個串口中斷接收回顯實驗\n");
	printf("歡迎使用秉火STM32開發板\n\n\n\n");

  while(1)
	{	
		//獲取數據並輸出
		//實際應用中可參考pull data的方式獲取數據進行處理
		pull_data_from_queue();
	}	
}
/*********************************************END OF FILE**********************/

實驗現象:使用環形隊列方式發送數據

 

   ========================== (6)如何使用USART2、USART3、USART4、USART5進行通信?

 根據引腳接線圖,可知,串口USART2接在APB1總線上,TX對應的引腳是PA2,TX對應的引腳是PA3

(1)將板子上的串口區域的TXD引腳和PA10引腳之間的跳帽拔掉,RXD引腳和PA9引腳之間的跳帽拔掉,使用兩根杜邦線將

TXD引腳和A2引腳連接,RXD引腳和A3引腳連接;

(2)修改bsp_usart.h

#ifndef __BSP_USART_H__
#define __BSP_USART_H__

#include "stm32f10x.h"
#include "stdio.h"

// ----------------------- 串口2-USART2
// 使用哪個串口(串口1..5),改為串口2
#define  DEBUG_USARTx                   USART2					
// APB1串口的同步時鍾,改為總線APB1
#define  DEBUG_USART_CLK                RCC_APB1Periph_USART2	
// APB1系統時鍾(因為串口USART2是掛載到APB1總線上的,所以要打開APB1總線的時鍾)
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd	
// 串口通信的波特率						
#define  DEBUG_USART_BAUDRATE           19200


// ----------------------- USART GPIO 引腳宏定義
// GPIO引腳
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)	
// APB1系統時鍾(因為串口USART2是掛載到APB1總線上的,所以要打開APB1總線的時鍾)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB1PeriphClockCmd	

// GPIO引腳,改成:發送接PA2,接收接PA3   
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   		
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_2
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_3

// 中斷源改為串口2,在stm32f10x.h中
#define  DEBUG_USART_IRQ                USART2_IRQn	
// USART1_IRQHandler在startup_stm32f10x_hd.s的中斷向量表中定義了,改成串口2的中斷服務函數
#define  DEBUG_USART_IRQHandler         USART2_IRQHandler	

/* 串口調試配置函數:配置串口的相關參數,使能串口 */
void DEBUG_USART_Config(void);

/* 發送一個字節 */
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t ch);

/* 發送字符串 */
void Usart_SendString(USART_TypeDef* pUSARTx, char* str);

#endif		/* __BSP_USART_H__ */

其他文件都不要修改

 

============================================================================

一、上位機給單片機發送數據,單片機有兩個方法可以接收數據:
(1)使用中斷。
    單片機在檢測到有數據過來的時候,產生一個中斷,然后在中斷服務函數中將數據接收下來。
(2)使用庫函數重定向。
    單片機使用getchar()函數將數據接收下來。需要編寫fgetc(FILE *f)重定向函數。

	/* 重定向c庫函數scanf到串口,重寫向后可使用scanf、getchar等函數 */
	int fgetc(FILE *f)
	{
		/* 等待串口輸入數據 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
	}

 二、單片機給上位機發送數據,單片機有兩個方法可以發送數據:
(1)使用printf()函數。需要編寫fputc(int ch, FILE *f)重定向函數。其實函數底層還是使用USART_SendData函數

	/* 重定向c庫函數printf到串口,重定向后可使用printf函數 */
	int fputc(int ch, FILE *f)
	{
		/* 發送一個字節數據到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待發送完畢 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
	}

 (2)直接使用固件庫函數USART_SendData(DEBUG_USARTx, (uint8_t) ch)將數據發送到上位機。

============================================================================

 

   ========================== (7)使用getchar函數將數據發送到上位機

(1)為了不相互干涉,先把中斷函數屏蔽掉:

(2)在bsp_usart.c中添加 fgetc函數,用於單片機使用 getchar() 函數接收數據:

/* 重定向c庫函數scanf到串口,重寫向后可使用scanf、getchar等函數 */
int fgetc(FILE *f)
{
	/* 等待串口輸入數據 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

	return (int)USART_ReceiveData(DEBUG_USARTx);
}

(3)main.c

/* 
USART串口通信實驗:先實現開發發送數據到電腦的串口通信實驗的最簡單操作
隨便發送一個簡單字符到電腦,使用串口調試助手接收並打印顯示出來
*/

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"

int main(void)
{
	// 接收到的數據存放到局部變量中
	uint8_t temp;
	
	/* USART串口通信初始化 */
	DEBUG_USART_Config();
	
	/* 嘗試從開發板發送一個字符到電腦上的串口調試助手,並顯示 */
	//USART_SendData(DEBUG_USARTx, 'Q');		// 發送一個字符
	//Usart_SendByte(DEBUG_USARTx, 0x42);		// 發送一個字節
	//Usart_SendString(DEBUG_USARTx, "歡迎使用STM32F103RCT6!");		// 發送字符串
	
	// 使用printf函數將數據輸出到上位機
	//printf("使用printf函數將數據輸出到上位機\r\n");
	
    while(1)
	{
		// 使用getchar函數接收上位機數據
		temp = getchar();
		
		// 使用printf函數將數據返回到上位機
		printf("接收到的字符為:%c\n", temp);
	}
}

(4)實驗現象:

將程序燒錄到單片機,打開串口調試助手,發送一個字符 1 到單片機,單片機將字符 1 返回到串口調試助手

 

   ========================== (8)控制單片機上的LED燈的亮滅

(1)發送字符 1,D4亮D5滅

(2)發送字符 2,D4滅D5亮

(3)發送非 1/2字符,D4、D5均滅

-----------------------------------------------------------------------------------

 (1)在main.c的同級目錄下新建目錄led,在led目錄下新建兩個文件bsp_led.c和bsp_led.h,並將這兩個文件添加到工程中的USER目錄下

bsp_led.h:

/* 和LED功能模塊相關的程序 */
 
#ifndef __BSP_LED_H__
#define __BSP_LED_H__
 
#include "stm32f10x.h"
 
/*宏定義*/
#define GPIO_CLK_D4         RCC_APB2Periph_GPIOC        // 時鍾
#define GPIO_PORT_D4        GPIOC                       // C端口
#define GPIO_PIN_D4         GPIO_Pin_2                  // PC2引腳
 
#define GPIO_CLK_D5         RCC_APB2Periph_GPIOC        // 時鍾
#define GPIO_PORT_D5        GPIOC                       // C端口
#define GPIO_PIN_D5         GPIO_Pin_3                  // PC2引腳
 
/*參數宏定義*/
/*
digitalTOGGLE(p,i)是參數宏定義,p表示LED的端口號,ODR是數據輸出寄存器,
查stm32f10x的官方中文手冊的第8.2章的ODR寄存器,要點亮LED,根據原理圖,要輸出低電平0,
C語言中,^表示異或,即a^b表示a和b不同時輸出為1,相同時輸出為0,比如0^1=1,1^1=0,0^0=0,
這里為什么操作ODR,p是什么?查看stm32f10x.h文件,搜索GPIO_TypeDef就會明白,
i是LED的引腳對應的位電平,經過digitalTOGGLE(p,i) {p->ODR ^= i;}之后,
第一次p為0,i一直為1,第一次異或結果輸出1,第二次輸出0,第三次輸出1,這樣間斷輸出010101,燈不斷亮滅
*/
// LED燈的狀態翻轉
//#define digitalTOGGLE(p,i)	{p->ODR ^= i;}	
// 輸出高電平(讓LED端口置1,BSRR寄存器用於位置1)
#define digitalHi(p,i)			{p->BSRR = i;}
// 輸出低電平(讓LED端口置0,BRR寄存器用於位清除)
#define digitalLo(p,i)			{p->BRR = i;}

// LED狀態翻轉
//#define LED1_TOGGLE			digitalTOGGLE(GPIO_PORT_D4,GPIO_PIN_D4)
//#define LED2_TOGGLE         	digitalTOGGLE(GPIO_PORT_D5,GPIO_PIN_D5)
// D4這個LED亮
#define D4_LED_ON				digitalLo(GPIO_PORT_D4,GPIO_PIN_D4)
// D4這個LED滅
#define D4_LED_OFF				digitalHi(GPIO_PORT_D4,GPIO_PIN_D4)
// D5這個LED亮
#define D5_LED_ON				digitalLo(GPIO_PORT_D5,GPIO_PIN_D5)
// D5這個LED滅
#define D5_LED_OFF				digitalHi(GPIO_PORT_D5,GPIO_PIN_D5)

 
/*配置GPIO*/
void LED_GPIO_Config(void);
 
#endif  /*__BSP_LED_H__*/

bsp_led.c:

 

/* 和LED功能模塊相關的程序頭文件 */

/*絕對路徑,也可在Options for target中設置頭文件*/
#include "./led/bsp_led.h"  
 
/*GPIO初始化*/
void LED_GPIO_Config(void)
{
    /*外設結構體*/
    GPIO_InitTypeDef GPIO_InitStruct_D4, GPIO_InitStruct_D5;   
     
    /*第一步:打開外設的時鍾,看stm32f10x_rcc.c這個文件的RCC_APB2PeriphClockCmd函數介紹*/
    RCC_APB2PeriphClockCmd(GPIO_CLK_D4, ENABLE);
     
    /*第二步:配置外設的初始化結構體*/
    GPIO_InitStruct_D4.GPIO_Pin = GPIO_PIN_D4;          // PC2的那盞LED燈(D4)的引腳
    GPIO_InitStruct_D4.GPIO_Mode = GPIO_Mode_Out_PP;    // 推挽輸出模式
    GPIO_InitStruct_D4.GPIO_Speed = GPIO_Speed_10MHz;   // 引腳速率
     
    GPIO_InitStruct_D5.GPIO_Pin = GPIO_PIN_D5;          // PC3的那盞LED燈(D5)的引腳
    GPIO_InitStruct_D5.GPIO_Mode = GPIO_Mode_Out_PP;    // 推挽輸出模式
    GPIO_InitStruct_D5.GPIO_Speed = GPIO_Speed_10MHz;   // 引腳速率
     
    /*第三步:調用外設初始化函數,把配置好的結構體成員寫到寄存器里面*/
    GPIO_Init(GPIO_PORT_D4, &GPIO_InitStruct_D4);
    GPIO_Init(GPIO_PORT_D5, &GPIO_InitStruct_D5);
	
	/* 默認情況下D4和D5是不亮的 */
	D4_LED_OFF;
	D5_LED_OFF;
}

(2)main.c

/* 
USART串口通信實驗:先實現開發發送數據到電腦的串口通信實驗的最簡單操作
隨便發送一個簡單字符到電腦,使用串口調試助手接收並打印顯示出來
*/

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./led/bsp_led.h" 

int main(void)
{
	// 接收到的數據存放到局部變量中
	uint8_t temp;
	
	/* LED初始化 */	
	LED_GPIO_Config();
	
	/* USART串口通信初始化 */
	DEBUG_USART_Config();
	
	/* 嘗試從開發板發送一個字符到電腦上的串口調試助手,並顯示 */
	//USART_SendData(DEBUG_USARTx, 'Q');		// 發送一個字符
	//Usart_SendByte(DEBUG_USARTx, 0x42);		// 發送一個字節
	//Usart_SendString(DEBUG_USARTx, "歡迎使用STM32F103RCT6!");		// 發送字符串
	
	// 使用printf函數將數據輸出到上位機
	//printf("使用printf函數將數據輸出到上位機\r\n");
	
    while(1)
	{		
		// 使用getchar函數接收上位機數據
		temp = getchar();
		
		// 使用printf函數將數據返回到上位機
		printf("接收到的字符為:%c\n", temp);
		
		/* ============ 控制LED的亮滅 ============= */
		switch(temp)
		{
			case '1':
				D4_LED_ON;
				D5_LED_OFF;
				break;
			case '2':
				D4_LED_OFF;
				D5_LED_ON;
				break;
			default:
				D4_LED_OFF;
				D5_LED_OFF;
				break;
		}
	}
}

實驗現象:

將程序燒錄到單片機中,打開串口調試助手,默認情況下,D4和D5兩盞燈都是滅的,輸入1,D4亮D5滅,輸入2,D4滅D5亮,輸入其他字符,D4和D5都滅

 

 

 

 

 

 

 

 

 

 

 

 

 

 

一、上位機給單片機發送數據,單片機有兩個方法可以接收數據:
(1)使用中斷。
    單片機在檢測到有數據過來的時候,產生一個中斷,然后在中斷服務函數中將數據接收下來。
(2)使用庫函數重定向。
    單片機使用getchar()函數將數據接收下來。需要編寫fgetc(FILE *f)重定向函數。
    /* 重定向c庫函數scanf到串口,重寫向后可使用scanf、getchar等函數 */
    int fgetc(FILE *f)
    {
        /* 等待串口輸入數據 */
        while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

        return (int)USART_ReceiveData(DEBUG_USARTx);
    }

二、單片機給上位機發送數據,單片機有兩個方法可以發送數據:
(1)使用printf()函數。需要編寫fputc(int ch, FILE *f)重定向函數。其實函數底層還是使用USART_SendData函數
    /* 重定向c庫函數printf到串口,重定向后可使用printf函數 */
    int fputc(int ch, FILE *f)
    {
        /* 發送一個字節數據到串口 */
        USART_SendData(DEBUG_USARTx, (uint8_t) ch);
        
        /* 等待發送完畢 */
        while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);        
    
        return (ch);
    }

(2)直接使用固件庫函數USART_SendData(DEBUG_USARTx, (uint8_t) ch)將數據發送到上位機。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM