完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
写在前面
串口,说简单其实是个很简单的东西,但架不住涉及的东西比较多,STM32串口功能很强大,同步的,异步的,还有红外都集成了,还涉及很多概念,中断、DMA、DMA还有DMA中断。。。这里讲的东西说实在,还是很基础,而且受限于篇幅,这里写不说DMA,后面打算更一篇单独说DMA的 1 串口简介 串行通讯接口,简称串口。即数据在通信线上一次传输一位,按先后一定顺序传输。我们通常所说的单片机串口准确来说应该是串行异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART),使用TTL电平。常用两根数据线,即TXD和RXD,配合GND至少需要三根线即可进行通信。以下除特殊说明,串口均指代单片机UART串口 串口信息以数据线上的高低电平表示,高电平为1,低电平为0 图1 串口时序图 图1为数据线上典型时序图,数据线空闲时为高电平,以一位低电平最为起始(除特殊说明,串口大部分都是一位起始位),而后以先传输低位,再传输高位的方式发送数据位,最后以高电平为数据停止位(常用1位停止位,也有2位和1.5位等)。数据位长度此处为8位,但不一定是8位,也会有7位、9位等,有时也会包含有奇偶校验位(校验位实际上就是数据的最后一位,但通常不算在数据位里)。上图所示即我们常用的8位数据位,无校验,一位停止位的串口格式 最为异步通信接口,串口不具有时钟线,所以需要在通信双方设置数据位长度,即电平持续时间,其倒数为电平转换的频率,这一参数即为波特率(但注意,波特的概念并不是位的概念,此处按下不表)。例9600波特率即为电平改变频率为9600Hz,电平持续时间1/9600秒,则一秒最多传输9600/(1+8+1)=960个字节(不严谨,但差不多) 因串口发送数据线与接收数据线分离,其为全双工接口,串口最少使用三根线即可完成通讯,但除这三根线外还可以有硬件流控RTS、CTS等,我没用到过,具体我也不是很清楚 区分UART、TTL、RS-232、RS-422、RS-485 UART即使用TTL电平的串口,TTL是电平形式,通常低于0.8V为低电平,高于2.4V为高电平,不是协议格式,但我们有时也常说TTL串口,以区分RS-232串口 RS-232串口数据格式与上文介绍的UART串口基本一致,但是电平不一样,通常以-3V至-15V为逻辑1,3V-15V为逻辑0,与UART不可混接,有烧板子的风险 RS-422常用4线制和2线制,4线制发送和接收均有两根线,两根线上为差分信号,增加传输距离,全双工。2线制的两根线上为差分信号,同时只能做为发或者收,半双工 RS-485为422的改进版本,最少可用两根线通信,因为使用差分信号,不使用地线也可正常通信,但不建议,半双工 2 设置STM32串口 STM32有多种类型串口UART、USART、LPUART等,这些串口功能强大,我们现在仅讨论UART功能。在这里,我们选用USART的异步收发功能即UART功能 新建工程、时钟配置等操作请参考博客STM32CubeMX5.1.0使用教程,以STM32L431为例(二):新建工程、时钟、gpio 如下图所示,点击左侧1处,在2处选择Asynchronous(异步),即出现3处,在3处我们可以设置数据位、波特率等参数,这里我们使用默认的,也是最常用的8位数据位,无校验,1位停止位,115200波特率。在2中选择完后,4处也会自动设置引脚功能。除PA9、PA10外,USART还可以设置为PB6和PB7,若有需要,仅需在响应引脚上左键并选择功能即可,系统会自动切换。 图2 Cube串口设置 设置好后,我们点击GENERATE CODE按钮,生成keil工程并打开 3 串口发送与printf函数 3.1 串口发送函数 Cube库的串口发送函数有三个,函数原型分别如下所示: HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size, uint32_t Timeout) HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size) HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size) 三个函数的返回值均为硬件状态,通过对硬件状态的判断,可以得知硬件是否已准备好发送下一帧数据。第一个参数为串口结构体,用于指向指定串口号,第二个参数为需要发送数据的首地址,第三个参数为发送字节数。第一个原型还具有第四个参数,因其未使用中断或DMA,需要指定超时时间,单位为毫秒,经过超时时间发送未完成则终止发送。 当前,我们还未使用中断和DMA,则使用第一个函数原型进行串口发送测试。 /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_UART_Transmit(&huart1, "hellorn", 7, 1); HAL_Delay(1000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ 如上所示,在生成的main.c指定位置编写代码(包含有自动生成的代码,便于读者定位,以下所有代码均如此),这样在Cube重新生成工程后,自己编写的代码也不会被覆盖。编译,下载,打开串口助手即可收到约一秒一次的hellorn(rn为换行)数据。 3.2 printf函数 使用printf函数能够更加方便的通过串口发送数据。 首先,如图3所示,在项目选项中勾选Use Micro LIB选项 图3 勾选Use Micro LIB选项 printf函数会调用fputs函数,fputs函数为弱定义函数,即使用__weak修饰符修饰的函数,可通过自己编写函数定义覆盖原定义,我们在此处重写fputs函数。在usart.c中添加如下代码,同样,注意添加位置,避免重新生成工程后被覆盖 /* USER CODE BEGIN 0 */ #include #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF); return ch; } /* USER CODE END 0 */ __GNUC__宏定义标志是否实用的是GCC编译器,因函数原型与类型定义在stdio.h中声明,所以需包含该文件,这样,我们就能使用printf函数向串口发送数据了 添加完后,在main.c中添加如下编写如下两段代码 /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" /* USER CODE END Includes */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { printf("hello worldrn"); HAL_Delay(1000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ 编译,下载即可在串口助手收到熟悉的hello world 通常,我们接收串口数据会使用中断方式,所以这里不展开讲如何接受数据。下面在串口中断中将解如何使用中断方式接收串口数据。 4 串口中断 图4 串口中断设置 如图4所示设置串口中断,选中NVIC选项,而后勾选串口中断使能,3处可以更改串口中断的抢占优先级和子优先级。设置好后,再次点击GENERATE CODE按钮,重新生成keil工程并打开。此前自己编写的代码如果都是放在了指定位置上,那么重新生成代码并不会覆盖我们自己编写的代码 4.1 串口中断发送 将重定向的puts函数作如下更改,主函数不变 PUTCHAR_PROTOTYPE { HAL_UART_Transmit_IT(&huart1 , (uint8_t *)&ch, 1); return ch; } 此处使用使用中断方式发送数据,编译,下载,观察串口助手输出,发现本应输出hello world结果,但仅输出了单个字符h,这是因为printf函数不断调用puts函数,第一个字符h正在发送中,HAL_UART_Transmit_IT便又一次被调用,该函数便将该字符丢弃并返回HAL_BUSY串口状态。通过这一返回值,我们对函数进行如下修改 PUTCHAR_PROTOTYPE { while(HAL_UART_Transmit_IT(&huart1 , (uint8_t *)&ch, 1)==HAL_BUSY); return ch; } 此处while循环判断若返回值为正忙,则不断重新调用,保障数据均能正常发送。此时编译,下载即可观察到正确结果 4.2串口中断接收 串口接收函数同样有三个,函数原型分别如下所示 HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size,uint32_t Timeout) HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size) HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size) 三个函数分别为不使用中断和DMA接收,使用中断方式接收,使用DMA方式接收。我们这里只讲中断方式,函数参数与发送一致,就不说了 当调用HAL_UART_Receive_IT后,函数便会使能串口接收中断,并等待接收Size个字节的数据,使用这种方式,函数并不会一直占用CPU时间,我们还可以继续进行其他操作。 那我们如何知道是否已经接收完成了呢?当Size个字节接收完后,中断将自动调用HAL_UART_RxCpltCallback函数,该函数也为若定义函数,我们可重写该函数。注意,当Size字节接收完后,串口接收中断会被自动关闭,需要再次调用HAL_UART_Receive_IT函数进行接收。 在main.c文件特定位置添加如下几段代码 /* USER CODE BEGIN PV */ uint8_t RxBuffer[20]; uint8_t isRxDone = 0; /* USER CODE END PV */ 1 2 3 4 /* USER CODE BEGIN 2 */ HAL_UART_Receive_IT(&huart1, RxBuffer, 5); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if (isRxDone == 1) { HAL_UART_Transmit_IT(&huart1, RxBuffer, 5); printf("hello world"); isRxDone = 0; HAL_UART_Receive_IT(&huart1, RxBuffer, 5); } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ /* USER CODE BEGIN 4 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) //判断是否为串口1 { isRxDone = 1; } } /* USER CODE END 4 */ 添加完后,编译,下载,通过串口助手发送5字节数据,便会收到同样的5字节数据。 写在后面 串口的中断接收方式可能在实际项目开发中存在一定的局限性,不够灵活,当然也可以选择使用__HAL_UART_ENABLE_IT函数使能中断,并在stm32l4xx_it.c文件中的USART1_IRQHandler函数中编写自己的中断逻辑,这样具有更好地灵活性与定制功能。 最近确实比较忙,有的项目要结题,有的项目催进度,有的项目改需求。。。看到有人评论催更,看到有人点赞,还有许多人看这篇教程,感谢大家,就赶紧更了一篇。后面我也会尽快更新的。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1561 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1501 浏览 1 评论
933 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
665 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1555 浏览 2 评论
1848浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
610浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
504浏览 3评论
508浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
489浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-10 17:34 , Processed in 0.753461 second(s), Total 78, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号