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

在实际测试中也没有问题,当单缓冲机制可用时,就会考虑使用双缓冲机制即为串口发送也开辟一个缓冲区。下次再分解。。。

你可能感兴趣的:(单片机,STM32)