由于自己忘性比较大,所以打算花点时间把近期学的一些知识点给整理顺便巩固一下,话不多说直接开始。
一、什么时UART?
uart就是通用异步收发传输设备的简称,从名字里我们可以捕捉到几个关键词:异步、收发。那么也就是说UART没有时钟线或者时钟校准这一说因为它是异步的,而且它不仅可以收信息也可以发信息。因为这一简单实用的特点,在一些数据传输时UART被大量使用。
二、用cubemx配置UART
首先还是选择芯片型号啥的,这里我就不截图了。
然后点开connectivity,所有和通讯相关的大部分都在这儿,
点开后我们会发现不仅有uart还有usart,这两个其实没啥区别,区别就在于uart是异步传输,而usart不仅支持异步传输还支持同步传输。结合我的开发板所以我选择了pa10和pa9两个引脚作为串口设备,也理所应当地选择了usart1。
点开Mode界面我们会发现一堆模式:
从上到下分别是:异步模式
同步模式
单线模式
多处理器通讯
红外线传感器
LIN(一种运用于汽车电子设备通讯协议)
智能卡
有计时功能的智能卡
我们在这里使用第一个异步模式,软件自动为我们选择了两个引脚pa10和pa9,其中pa10作为RX(收信息),pa9作为TX(发信息)。
设置完后再去设置一下系统时钟,因为待会我们会使用串口进行中断所以我们还要使能一下NVIC(中断),最后生成一下代码。
三、HAL库函数讲解
在HAL库中关于UART设备的函数有很多,我会挑选常用且一会使用到的函数进行讲解。
我们打开stm32f1xx_hal_uart.c文件,会大体看到st公司把这么多关于uart的函数分为了4个部分:
1、Initialization and Configuration functions(初始化配置功能)
2、IO operation functions(IO操作功能)
3、Peripheral Control functions(外围操作功能)
4、Peripheral State and Errors functions(外围状态和操作功能)
我们接下来会把重点放在1和2中,3和4都是关于外设的如单线模式、LIN和多处理器之间的通讯。
1是用作初始化功能的函数,在使用标准库时这些是我们自己要进行配置的,但是有了cubemx的帮助这部分我们几乎不用管。
2才是我们真正用来串口通讯的函数,在进行通讯前我们要选择以堵塞方式还是非堵塞方式进行通讯。关于阻塞和非阻塞我也并不能说是很懂,个人感觉阻塞就是传递信息这个过程没有结束那么接下来的任务都不会进行直到这个过程结束,非阻塞就是不管你信息有没有传递完毕都会返回一个值。
阻塞和非阻塞之间的区别给我最大的感受就是,非阻塞有中断功能而阻塞没有。。。。。。而且st官方也对阻塞和非阻塞进行了解释,但是这个解释感觉并没有把阻塞与非阻塞之间的区别给清晰的写出来,也有可能是我语文理解不大好。但是st官方把大篇幅都花在了非阻塞上,而且关于非阻塞的api比阻塞的api明显多了很多(阻塞的只有2个。。。),再根据我的实践得出能用非阻塞就用非阻塞,阻塞在处理信息时有很大的误差,而非阻塞由于有中断功能所以处理信息会高效的很多而且错误率也低,所以我在使用时遵循以下这个规则:由单片机发出的信息使用阻塞式,向单片机发送的信息使用非阻塞式。
在解决完阻塞与非阻塞的问题后让我们来看一下常用的api有哪些:
(+) HAL_UART_Transmit();//阻塞模式发送 (+) HAL_UART_Receive();//阻塞模式接收 (+) HAL_UART_Transmit_IT();//非阻塞中断模式下发送 (+) HAL_UART_Receive_IT();//非阻塞中断模式下接收 (+) HAL_UART_IRQHandler();//中断回调函数句柄 (+) HAL_UART_RxCpltCallback();//中断回调函数
以上这些api可以帮你完成大部分的数据收发功能,接下来我会编写一个实例,实例可以做到接收数据并进行判断,判断成功会发送数据。
uint8_t message_receive[50]; void User_transmite(){ uint8_t message[]="hello,this is SAT8"; HAL_UART_Transmit(&huart1,message,sizeof(message),50); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){ if(huart == &huart1){ uint16_t num1=0,num2=0; uint8_t message1[]="hello,you are my wife,right?"; uint8_t message2[]="who are you..."; for(int i=0;i<=sizeof(message1);i++){ if(message_receive[i]== message1[i]){ num1++; } else { break; } } for(int i=0;i<=14;i++){ if(message_receive[i]==message2[i]){ num2++; } else{ break; } } if(num1==sizeof(message1)-1){ uint8_t shy_word[]="yes...i am your wife"; HAL_UART_Transmit(&huart1,shy_word,sizeof(shy_word),50); } if(num2==sizeof(message2)-1){ uint8_t angry_word[]="you forget your wife?fxxk!"; HAL_UART_Transmit(&huart1,angry_word,sizeof(angry_word),50); } } } void User_receive(){ HAL_UART_Receive_IT(&huart1,message_receive,30); HAL_Delay(100); }
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ HAL_UART_IRQHandler(&huart1); User_transmite(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { User_receive(); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
调试成功,有一点要说明一下,
void User_receive(){ HAL_UART_Receive_IT(&huart1,message_receive,30); HAL_Delay(100); }
在这个接收函数中我添加了一个延时,时间为100ms。具体为什么是因为如果不加,在两次发送讯息之间时间如果过短会导致数据接收不到,具体原因还不清楚,但是一加上去后在两次快速发送讯息时导致的数据接收问题就大大改善了,如果有人能够回答一下的话就太好了。