初始化
首先讲下UART的初始化
1.声明UART的初始化结构体,并赋值
2.MX生成的代码会调用HAL_UART_MspInit();来初始化UART,当然这个代码也是自动生成,不过用户可以在这个函数里面添加自己想要添加的操作,时面包括了NVIC_Configuration,DMA_Configuration等,也可以添加一些置位操作如__HAL_UART_ENABLE,__HAL_UART_ENABLE_IT等等
3.在HAL_UART_MspDeInit()中添加一些与HAL_UART_MspInit相反的操作来完成UART的重置操作
对于以上的初始化操作,都可以由stm32cubemx自动生成,无需去具体配置寄存器。
而用户使用HAL库来驱动UART,在初始化好参数之后,
官方提供了三种方式
一、轮询模式(Polling mode IO operation)
使用HAL_UART_Transmit()与HAL_UART_Receive()来配合超时操作来发送与接收数据
以ECHO方式(即收到什么发什么)为例,这种方式进行操作
用轮询方式的代码是比较简短的
if(HAL_UART_Receive(&huart1, testReceiveData, 10, 1000) == HAL_OK)
{
HAL_UART_Transmit(&huart1, testReceiveData, 10, 1000);
}
以这种方式就可以实现发送接收的数据,不过这种方式来处理的话,长度不定的时候,数据的丢失量会比较大
减少等待超时,与调整BUFFER的长度都还是会有不同程度的数据丢失
如果将BUFFER的长度调整为1,数据丢失量会减少,不过这个时候会出现UART工作一段时间之后就发生异常,因为UART发生ORE错误置位,需要将这个错误置位清除掉才可以再正常接收
代码如下
if(HAL_UART_Receive(&huart1, testReceiveData, 1, 10) == HAL_OK)
{
HAL_UART_Transmit(&huart1, testReceiveData, 1, 10);
}
else
{
__HAL_UART_CLEAR_OREFLAG(&huart1);
}
如果将发送改为由寄存器直接操作的话
if(HAL_UART_Receive(&huart1, testReceiveData, 1, 10) == HAL_OK)
{
huart1.Instance->DR = testReceiveData[0];
}
else
{
__HAL_UART_CLEAR_OREFLAG(&huart1);
}
这样测试过来数据就没有丢失。
说明还是在发送API的时候,同时又接收到数据导致的数据丢失,或者说API发送使用时间相对于直接操作寄存器还是要长很多
二、中断模式(Interrupt mode IO operation)
使用HAL_UART_Transmit_IT()与HAL_UART_Receive_IT来发送接收,在发送或接收完之后,再进行函数回调HAL_UART_TxCpltCallback与HAL_UART_RxCpltCallback来进行处理这两个函数都是由用户重新定义的,来实现用户自己的操作
在系统初始化后,直接调用HAL_UART_Receive_IT(&huart1, testReceiveData, 1);即可这个长度可由用户自己定义
当达到接收长度之后,就可以进行cplt完成函数的重构及回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)
{
if(uartHandle->Instance == USART1)
{
uartHandle->Instance->DR = testReceiveData[0];
//HAL_UART_Transmit_IT(uartHandle, testReceiveData, 1);
HAL_UART_Receive_IT(uartHandle, testReceiveData, 1);
}
}
使用寄存器直接操作的方式是可以做到数据不丢失,而使用发送函数还是会出现不同程序的数据丢失
数据接收完之后,若要重新开始接收必须重新开启HAL_UART_Receive_IT
三、DMA模式(DMA mode IO operation)
使用HAL_UART_Transmit_DMA()与HAL_UART_Receive_DMA()来发送接收,在发送或接收完之后,也使用HAL_UART_TxCpltCallback与HAL_UART_RxCpltCallback来完成实际操作,同时接收到一半的时候,也可以调用相应的HAL_UART_TxHalfCpltCallback与HAL_UART_RxHalfCpltCallback,如果需要用到这个操作的情况下可以添加自己的操作,当然来还用到一关于DMA的API函数,如HAL_UART_DMAPause,HAL_UART_DMAResume, HAL_UART_DMAStop等
在初始化UART的同时需要初始化相应的DMA,并将DMA与UART进行关联,不过这部分代码都可以自动生成
开始时调用HAL_UART_Receive_DMA(&huart1, uartDeviceRxBuf, UART_BUF_LEN);
在接收到的相应长度的数据之后DMA会产生一个完成的中断,其回调函数与中断模式相同,虽然两者发生中断地方不一致,但是操作是同一个
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)
{
if(uartHandle->Instance == USART1)
{
HAL_UART_Transmit(uartHandle, uartDeviceRxBuf, 100, 1000);
HAL_UART_Receive_DMA(uartHandle, uartDeviceRxBuf, 100);
}
}
同样在接收完成后,要重新开启接收,不然之后的数据就接收不到了
其他
除了上述官方的方式,当然还有一些别的方式,直接操作寄存器肯定也是可以的,而用HAL库时面也有一定宏定义可以直接来操作寄存器
__HAL_UART_CLEAR_FLAG(uartHandle, UART_FLAG_RXNE);
__HAL_UART_ENABLE_IT(uartHandle, UART_IT_RXNE);
可以使用这些定义来直接操作寄存器,初化接收中断
在中断中也直接操作寄存器来完成接收
/* USER CODE BEGIN USART1_IRQn 0 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET)
{
uint8_t tmp;
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
tmp = huart1.Instance->DR;
huart1.Instance->DR = tmp;
}
/* USER CODE END USART1_IRQn 0 */
//HAL_UART_IRQHandler(&huart1);
中断中如此操作来完成ECHO操作
对于官方提供的操作方式,无论是哪种方式基本上是不能同时使用Transmit与Receive操作,而且官方提供的这些API,很好用,但是用到实际的应用中,还需要用户写一部分代码来完成整个操作,主要就是一个BUFFER的进出操作,使数据在很短的时间从设备的代码提取出来,而不影响设备的接收与发送,可以防止数据丢失的发生。