首先讲下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,在初始化好参数以后,测试
使用HAL_UART_Transmit()与HAL_UART_Receive()来配合超时操做来发送与接收数据
以ECHO方式(即收到什么发什么)为例,这种方式进行操做
用轮询方式的代码是比较简短的ui
if(HAL_UART_Receive(&huart1, testReceiveData, 10, 1000) == HAL_OK) { HAL_UART_Transmit(&huart1, testReceiveData, 10, 1000); }
以这种方式就能够实现发送接收的数据,不过这种方式来处理的话,长度不定的时候,数据的丢失量会比较大code
减小等待超时,与调整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); }
若是将发送改成由寄存器直接操做的话it
if(HAL_UART_Receive(&huart1, testReceiveData, 1, 10) == HAL_OK) { huart1.Instance->DR = testReceiveData[0]; } else { __HAL_UART_CLEAR_OREFLAG(&huart1); }
这样测试过来数据就没有丢失。
说明仍是在发送API的时候,同时又接收到数据致使的数据丢失,或者说API发送使用时间相对于直接操做寄存器仍是要长不少io
使用HAL_UART_Transmit_IT()与HAL_UART_Receive_IT来发送接收,在发送或接收完以后,再进行函数回调HAL_UART_TxCpltCallback与HAL_UART_RxCpltCallback来进行处理这两个函数都是由用户从新定义的,来实现用户本身的操做test
在系统初始化后,直接调用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配置
使用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的进出操做,使数据在很短的时间从设备的代码提取出来,而不影响设备的接收与发送,能够防止数据丢失的发生。