STM32L0xx_HAL_Driver庫的使用——UART

單片機型號:STM32L051C8T6

開發環境MDK5.12

庫版本:STM32L0xx_HAL_Driver V1.1.0

主機環境:Windows XP

之前一直使用的STM32F030C8T6單片機來做開發,因需求更改更換了一個新型號STM32L051C8T6,主要是用到了其低功耗特性,本以爲直接把代碼拷貝一下就可以使用了,結果是太天真了,STM32F030C8T6使用的庫是STM32F0_StdPeriph_Lib而STM32L051C8T6使用的庫是STM32L0xx_HAL_Driver兩者的差別還是很大的,而且官方也推薦使用後者,沒辦法,重新學習一下吧。。。參考其例程磕磕絆絆的勉強可以寫一個工程了,這裏寫一下有關UART的調試。

參考的程序是STM32L053R8-Nucleo例程中的UART_TwoBoards_ComIT工程,採用中斷方式來進行兩個單片機之間的通信。STM32L0xx_HAL_Driver庫的分層更加明顯,板極初始化代碼如下

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{  
  GPIO_InitTypeDef  GPIO_InitStruct;
  
  /*##-1- Enable peripherals and GPIO Clocks #################################*/
  /* Enable GPIO TX/RX clock */
  USARTx_TX_GPIO_CLK_ENABLE();
  USARTx_RX_GPIO_CLK_ENABLE();
  /* Enable USART1 clock */
  USARTx_CLK_ENABLE(); 
  
  /*##-2- Configure peripheral GPIO ##########################################*/  
  /* UART TX GPIO pin configuration  */
  GPIO_InitStruct.Pin       = USARTx_TX_PIN;
  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull      = GPIO_NOPULL;
  GPIO_InitStruct.Speed     = GPIO_SPEED_HIGH;
  GPIO_InitStruct.Alternate = USARTx_TX_AF;
  
  HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);
    
  /* UART RX GPIO pin configuration  */
  GPIO_InitStruct.Pin = USARTx_RX_PIN;
  GPIO_InitStruct.Alternate = USARTx_RX_AF;
    
  HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);
    
  /*##-3- Configure the NVIC for UART ########################################*/
  /* NVIC for USART1 */
  HAL_NVIC_SetPriority(USARTx_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(USARTx_IRQn);
}

/**
  * @brief UART MSP De-Initialization 
  *        This function frees the hardware resources used in this example:
  *          - Disable the Peripheral's clock
  *          - Revert GPIO and NVIC configuration to their default state
  * @param huart: UART handle pointer
  * @retval None
  */
void HAL_UART_MspDeInit(UART_HandleTypeDef *huart)
{
  /*##-1- Reset peripherals ##################################################*/
  USARTx_FORCE_RESET();
  USARTx_RELEASE_RESET();

  /*##-2- Disable peripherals and GPIO Clocks #################################*/
  /* Configure UART Tx as alternate function  */
  HAL_GPIO_DeInit(USARTx_TX_GPIO_PORT, USARTx_TX_PIN);
  /* Configure UART Rx as alternate function  */
  HAL_GPIO_DeInit(USARTx_RX_GPIO_PORT, USARTx_RX_PIN);
  
  /*##-3- Disable the NVIC for UART ##########################################*/
  HAL_NVIC_DisableIRQ(USARTx_IRQn);
}

這兩個函數沒什麼可說的跟開發板對應修改即可,這樣使得串口初始化更加的簡潔只需要進行邏輯上的初始化即可

/**********************************************************************
函數:uart_init()
函數作用:串口初始化
參數:
	uint32_t BaudRate=========================串口波特率
返回值:無
上一版本:無
當前版本:1.0
作者:
最後修改時間:2015-04-02
說明: 
**********************************************************************/
void uart_init(uint32_t BaudRate)
{
    
    UartHandle.Instance        = USARTx;
    UartHandle.Init.BaudRate   = BaudRate;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits   = UART_STOPBITS_1;
    UartHandle.Init.Parity     = UART_PARITY_NONE;
    UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode       = UART_MODE_TX_RX;
    
    if(HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        Error_Handler();
    }
	__HAL_UART_ENABLE(&UartHandle);

	NVIC_SetPriority(USARTx_IRQn,0);
	NVIC_EnableIRQ(USARTx_IRQn);
	uart_rev.front = aRxBuffer;
	uart_rev.rear = aRxBuffer;	//兩個指針指向相同的地址空間
	if(HAL_UART_Receive_IT(&UartHandle,(uint8_t*)aRxBuffer,1) != HAL_OK)
	{
		Error_Handler();
	}
}

這裏爲串口的接收開闢了500個字節的緩衝區aRxBuffer使用首尾指針來進行數據的接收和存取,即單緩衝機制。

struct uart
{
	uint8_t *rear;			//在中斷函數中更改
	uint8_t *front;			//在主循環中更改
};

由於STM32L0xx_Hal_Driver庫的使用串口底層分爲了3種:查詢方式、中斷方式、DMA方式,都是使用HAL函數來實現,因此我們使用中斷方式接收不能自動開啓,必須使用函數HAL_UART_Receive_IT來打開接收中斷,這裏我們每接收一個字節就進入中斷。STM32L0xx_Hal_Driver庫的使用使得中斷函數也十分簡潔一句話搞定,採用回調函數機制來處理中斷

void USARTx_IRQHandler(void)
{
  	HAL_UART_IRQHandler(& UartHandle);
}
在HAL_UART_IRQHandler()中會自動調用串口接收中斷的回調函數

/**
  * @brief  Rx Transfer completed callback
  * @param  UartHandle: UART handle
  * @note   This example shows a simple way to report end of IT Rx transfer, and 
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
	uint8_t ret = HAL_OK;
	/* Set transmission flag: trasfer complete*/
	uart_rev.rear++;	//更新rear指針
	if(uart_rev.rear >= (aRxBuffer + BUFFSIZE))
		uart_rev.rear = aRxBuffer;
	do
	{
		ret = HAL_UART_Receive_IT(UartHandle,uart_rev.rear,1);
	}while(ret != HAL_OK);
}

每次把接收到的數據存入rear所指向的地址空間,存入數據只更新rear指針,同時開啓請求下一個數據的到來。在主函數中調用uart_read函數來取出數據

/**********************************************************************
函數:uart_read()
函數作用:從接收緩衝區中讀取數據
參數:
	uint8_t *fmt--------------------------------接收到的數據
	uint16_t time_out---------------------------超時時間
返回值:0:讀取到數據-1:沒有讀取到數據
上一版本:無
當前版本:1.0
作者:
最後修改時間:2015-04-08
說明: 
**********************************************************************/
int8_t uart_read(uint8_t *fmt, uint16_t time_out)
{
	while(time_out)
	{
		if(uart_rev.front != uart_rev.rear)
		{
			//如果隊首指針和隊尾指針不同表明緩衝區中有數據還未收取
			*fmt=*uart_rev.front;
		
			uart_rev.front++;

			if (uart_rev.front >= (aRxBuffer+BUFFSIZE))
				uart_rev.front = aRxBuffer;

			return 0;
		}
		time_out--;
	}
	return (int8_t)-1;
}

取數據只更新front指針,這裏有個不足的地方是如果一直不取數據或者取數據速度較慢而接收的數據很多會造成數據覆蓋即出現數據丟失的情況,不過一般很少會有這種情發生(對於我來說是在主循環中不停地進行讀取操作,所以沒事啦),整個文件如下

#include "UART.h"
#include "stm32l0xx_hal_def.h"
#include "utils.h"

UART_HandleTypeDef UartHandle;
uint8_t aRxBuffer[BUFFSIZE];
struct uart uart_rev;

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{  
  GPIO_InitTypeDef  GPIO_InitStruct;
  
  /*##-1- Enable peripherals and GPIO Clocks #################################*/
  /* Enable GPIO TX/RX clock */
  USARTx_TX_GPIO_CLK_ENABLE();
  USARTx_RX_GPIO_CLK_ENABLE();
  /* Enable USART1 clock */
  USARTx_CLK_ENABLE(); 
  
  /*##-2- Configure peripheral GPIO ##########################################*/  
  /* UART TX GPIO pin configuration  */
  GPIO_InitStruct.Pin       = USARTx_TX_PIN;
  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull      = GPIO_NOPULL;
  GPIO_InitStruct.Speed     = GPIO_SPEED_HIGH;
  GPIO_InitStruct.Alternate = USARTx_TX_AF;
  
  HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);
    
  /* UART RX GPIO pin configuration  */
  GPIO_InitStruct.Pin = USARTx_RX_PIN;
  GPIO_InitStruct.Alternate = USARTx_RX_AF;
    
  HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);
    
  /*##-3- Configure the NVIC for UART ########################################*/
  /* NVIC for USART1 */
  HAL_NVIC_SetPriority(USARTx_IRQn, 0, 1);
  HAL_NVIC_EnableIRQ(USARTx_IRQn);
}

/**
  * @brief UART MSP De-Initialization 
  *        This function frees the hardware resources used in this example:
  *          - Disable the Peripheral's clock
  *          - Revert GPIO and NVIC configuration to their default state
  * @param huart: UART handle pointer
  * @retval None
  */
void HAL_UART_MspDeInit(UART_HandleTypeDef *huart)
{
  /*##-1- Reset peripherals ##################################################*/
  USARTx_FORCE_RESET();
  USARTx_RELEASE_RESET();

  /*##-2- Disable peripherals and GPIO Clocks #################################*/
  /* Configure UART Tx as alternate function  */
  HAL_GPIO_DeInit(USARTx_TX_GPIO_PORT, USARTx_TX_PIN);
  /* Configure UART Rx as alternate function  */
  HAL_GPIO_DeInit(USARTx_RX_GPIO_PORT, USARTx_RX_PIN);
  
  /*##-3- Disable the NVIC for UART ##########################################*/
  HAL_NVIC_DisableIRQ(USARTx_IRQn);
}

/**********************************************************************
函數:uart_init()
函數作用:串口初始化
參數:
	uint32_t BaudRate=========================串口波特率
返回值:無
上一版本:無
當前版本:1.0
作者:
最後修改時間:2015-04-02
說明: 
**********************************************************************/
void uart_init(uint32_t BaudRate)
{
    
    UartHandle.Instance        = USARTx;
    UartHandle.Init.BaudRate   = BaudRate;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits   = UART_STOPBITS_1;
    UartHandle.Init.Parity     = UART_PARITY_NONE;
    UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode       = UART_MODE_TX_RX;
    
    if(HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        Error_Handler();
    }
	__HAL_UART_ENABLE(&UartHandle);

	NVIC_SetPriority(USARTx_IRQn,0);
	NVIC_EnableIRQ(USARTx_IRQn);
	uart_rev.front = aRxBuffer;
	uart_rev.rear = aRxBuffer;	//兩個指針指向相同的地址空間
	if(HAL_UART_Receive_IT(&UartHandle,(uint8_t*)aRxBuffer,1) != HAL_OK)
	{
		Error_Handler();
	}
}
void Error_Handler(void)
{
    while(1)
    {
        
    }
}

/**********************************************************************
函數:uart_read()
函數作用:從接收緩衝區中讀取數據
參數:
	uint8_t *fmt--------------------------------接收到的數據
	uint16_t time_out---------------------------超時時間
返回值:0:讀取到數據-1:沒有讀取到數據
上一版本:無
當前版本:1.0
作者:
最後修改時間:2015-04-08
說明: 
**********************************************************************/
int8_t uart_read(uint8_t *fmt, uint16_t time_out)
{
	while(time_out)
	{
		if(uart_rev.front != uart_rev.rear)
		{
			//如果隊首指針和隊尾指針不同表明緩衝區中有數據還未收取
			*fmt=*uart_rev.front;
		
			uart_rev.front++;

			if (uart_rev.front >= (aRxBuffer+BUFFSIZE))
				uart_rev.front = aRxBuffer;

			return 0;
		}
		time_out--;
	}
	return (int8_t)-1;
}

int8_t uart_send(uint8_t *fmt, uint8_t len)
{
	while(len)
	{
		printf("%c",*fmt);
		fmt++;
		len--;
	}

	return 0;
}

#ifdef UART_DEBUG
int fputc(int ch, FILE *f)
{
    USART1->TDR = ch;
    while(!(USART1->ISR & USART_ISR_TXE));
    return(ch);
}
#endif

/**
  * @brief  Tx Transfer completed callback
  * @param  UartHandle: UART handle. 
  * @note   This example shows a simple way to report end of IT Tx transfer, and 
  *         you can add your own implementation. 
  * @retval None
  */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef * huart)
{
	uint8_t ret = HAL_OK;
	UartReady = SET;
	#if 1
	uart_snd.front++;	//更新rear指針
	if(uart_snd.front >= (aTxBuffer + BUFFSIZE))
		uart_snd.front = aTxBuffer;
	if(uart_snd.front != uart_snd.rear)
	{
		//如果隊首指針和隊尾指針不同表明緩衝區中有數據還未發送
		do
		{
			ret = HAL_UART_Transmit_IT(&UartHandle,uart_snd.front,1);//請求發送下一個數據
		}while(ret != HAL_OK);
	}
	#endif
}
/**
  * @brief  Rx Transfer completed callback
  * @param  UartHandle: UART handle
  * @note   This example shows a simple way to report end of IT Rx transfer, and 
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
	uint8_t ret = HAL_OK;
	/* Set transmission flag: trasfer complete*/
	uart_rev.rear++;	//更新rear指針
	if(uart_rev.rear >= (aRxBuffer + BUFFSIZE))
		uart_rev.rear = aRxBuffer;
	do
	{
		ret = HAL_UART_Receive_IT(UartHandle,uart_rev.rear,1);
	}while(ret != HAL_OK);
}
/******************************************************************************/
/*                 STM32L0xx Peripherals Interrupt Handlers                   */
/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
/*  available peripheral interrupt handler's name please refer to the startup */
/*  file (startup_stm32l0xx.s).                                               */
/******************************************************************************/
/**
  * @brief  This function handles UART interrupt request.  
  * @param  None
  * @retval None
  * @Note   This function is redefined in "main.h" and related to DMA stream 
  *         used for USART data transmission     
  */
void USARTx_IRQHandler(void)
{
  	HAL_UART_IRQHandler(& UartHandle);
}

在實際測試中也沒有問題,當單緩衝機制可用時,就會考慮使用雙緩衝機制即爲串口發送也開闢一個緩衝區。下次再分解。。。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章