STM32F407ZET6+CubeMX学习笔记5——串口收发

【STM32F4+CubeMX零基础快速入门】串口收发全攻略_哔哩哔哩_bilibili

什么是串口通信

串口通信是一种通信协议

串口通信需要至少两根线形成电势差区分高低电平以表达逻辑1/0

双方GND相接,另一根传输信号,发送方为TX,接收方为RX

同理加一根反向线即可实现双向传输

电脑和单片机进行通信时由于电脑没有接线端,所以需要使用到usb转ttl模块

信号传输

在闲置状态下,发送端保持高电平

当需要发送数据的时候,发送端拉低为低电平作为起始信号以让接收端做好准备,称之为起始位

接着开始发送数据,发送端以一定频率发送高低电平,接收端以一定频率进行采样

发送完数据后进入校验阶段,留下一位作为校验位验证传输是否正确

最后发送端将电平拉高表示发送完成

参数选择

数据位:可以选5-8位

校验方式:分为奇校验,偶校验,无校验

奇校验表示数据位和校验位中1的个数总数为奇数,偶校验同理,接收端以此判断数据传输是否出错

停止位:1-2位,停止位越长传输越稳定,但是速度越慢

传输速度(波特率bit/s):典型值:9600及其各种分频/倍频。波特率越高传输速度越快,但越不稳定更容易出错。

(发送端和接收端配置必须配置一样才能传输数据)

CUBEMX配置

UART只支持异步串口

USART支持同步/异步串口

模式设置为异步串口

从上至下即以此为波特率,数据位,校验方式,停止位

最后检查GPIO口是否正确

代码部分

        阻塞式

串口发送

阻塞式发送:当发送的数据发送完了函数才会结束

HAL_UART_Transmit(&huart1,(uint8_t*)"Hello World!",12,0xFFFF);

第一个参数:操作的端口

第二个参数:发送数据的首地址。这里加入强制类型转换(uint8_t*)以消除警告

第三个参数:发送首地址及以后的多少位

第四个参数:超过(某16进制数)ms后若未完成传输视为超时强制停止

将电脑接上usb转ttl模块,正确接线(注意电脑的TX,RX和单片机的RX,TX相互依次对应)

printf发送(本质上还是阻塞式发送):

使用时需要调用标准库函数<stdio.h>

然后在用户定义函数区域重写fputc函数配合printf

    int fputc(int c,FILE *stream)
            {
                uint8_t ch1[]={c}; //定义字符数组存放单个字符
                HAL_UART_Transmit(&huart1,ch1,1,0xFFFF); //发送出去
                return c; //fputc要求如果没有发送错误返回值会是发送的字符
            }

然后printf就可以作为发送整个字符串的函数

数据接收

定义一个数组作为接收容器

            uint8_t buf[5];
            HAL_UART_Receive(&huart1,buf,3,0xFFFF);

第一个参数:操作端口

第二个参数:接收首地址

第三个参数:接收长度

第四个参数:超时时间

配合上述发送函数即可检验是否接收成功

同理重写fgetc也可用scanf来实现数据接收

由于scanf靠回车检测输入,因此发送时候需要勾选上发送新行

        中断式

阻塞式浪费cpu,大部分时间都浪费在了等待硬件传输的过程中。

中断处开启串口中断

HAL_UART_Transmit_IT(&huart1,(uint8_t*)"Hello World!",12);

代码参数意义与前面相同,只是少了一个超时时间

每当调用该函数的时候就会开启一个中断,每发送完一个字节就进入一次中断。在中断中,hal库就会自动触发下一个字节的发送,直到发完所有数据,所以这个函数不需要等待所有数据发送完成才退出。他只是触发了中断发送的流程。

此外所有数据发送完成后还会触发一个回调函数

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{

}

中断接收同理,需要先准备一个存储空间,然后函数就会开启一个中断,每接收到一个字节就会进入一次中断,在中断中,hal库就会把这个字节保存在接收区内。接收完之后同样会触发一个回调函数。

注意:此时准备的存储空间应该为一个全局变量,否则可能在接收到数据的时候作为局部变量已经被销毁了。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

}

应用实例

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
        {
            HAL_UART_Transmit_IT(&huart1,buf,5);//接收完后发送出来确认接收正确
            HAL_UART_Receive_IT(&huart1,buf,5);//随后开启下一个接收中断准备下一次接收
        }

HAL_UART_Receive_IT(&huart1,buf,5);//while外开启首次接收中断

DMA方式串口接收

DMA用于帮助CPU在内存和内存,内存和外设之间搬运数据,CPU只需要告诉DMA搬运哪些数据即可,不用亲自操作字节。

DMA可以把内存中的一部分数据依次通过串口发出,也可将串口收到的数据自动地保存到一片内存空间内。CPU只需要为DMA指定好内存空间即可。

DMA配置

在串口处的DMA设置上添加接收和发送的DMA即可

代码部分

使用DMA进行发送和接收的函数

HAL_UART_Transmit_DMA(&huart1,(uint8_t*)"Hello World!",12);

HAL_UART_Receive_DMA(&huart1,buf,5);

形式上区别只有在把IT换成DMA,实际上区别为中断式每个字节都会触发一次中断而DMA不需要。

同理DMA也会触发中断回调函数,所以应用案例与中断处相似

区别与问题:DMA进行接收的时候,假设设置接收为5个字节,当实际发送过来的字符不满5个字节的时候不会触发中断回调函数。

为了解决不定长接收的问题引入空闲中断,当传输线进入空闲状态的时候就会进入这个中断

先删除原来的中断回调函数的重写,另外开启串口中断

  __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);

其中的UART_IT_IDLE即是开启空闲中断

由于HAL库没有提供空闲中断的中断回调函数,需要手动编写

打开it.c文件找到串口1的中断服务函数,当串口1产生任何中断的时候都会进入该函数。

首先判断是否为空闲中断:查看中断标志位

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern uint8_t buf[5];  //注意在it.c里面要将buf作为外部变量引入
/* USER CODE END PV */

if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET)
{

        __HAL_UART_CLEAR_IDLEFLAG(&huart1);

        HAL_UART_DMAStop(&huart1);

        uint8_t len = 5 - __HAL_DMA_GET_COUNTER(huart1.hdmarx); //huart1.hdmarx为串口1的接收DMA句柄

        HAL_UART_Transmit_DMA(&huart1,buf,len);
        HAL_UART_Receive_DMA(&huart1,buf,5);
}

__HAL_UART_GET_FLAG获取串口的中断标志

第一个参数:串口

第二个参数:获取的状态,此处为空闲中断状态

当空闲中断状态不为0即说明触发了空闲中断。

进入判断后首先清除标志位

然后停止DMA的接收,代表此时已经接收完了

然后利用DMA内部的计数器判断接收位数。当使用DMA接收函数的时候内部就会设置一个计数器,大小为预设的接收字节大小,DMA每接收到一个字节就会-1,所以其计数就是剩余的字节。故用预设的接收大小,获取当前接收函数的计数器计数值相减,就可以得到实际获取到的字节数

将实际获取到的字节数发送出去(对不定长接收作出反应)

最后重新开启下一次的接收

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值