当前位置: 首页 > news >正文

【STM32HAL库学习】通信方式:USART、IIC、SPI

通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统

通信接口区别

名称引脚双工时钟电平设备
USARTTX、RX全双工异步单端点对点
I2CSCL、SDA半双工同步单端多设备
SPISCLK、MOSI、MISO、CS全双工同步单端多设备
CANCAN_H、CAN_L半双工异步差分多设备
USBDP、DM半双工异步差分点对点

以下是一些常见概念:

  • 双工:通信设备是否同时能进行双方通信,一般全双工的都有两根通讯线,如 USART 就有TX/RX这两根通讯线。 I2C 只有一根通讯线SDA(另外一根是时钟线),所以 I2C 半双工的通信

  • 时钟:分为同步异步
    同步:有单独的时钟信号线,保证通讯时用的是同一个时钟
    异步:没有单独的时钟信号线,只能双方规定制定的时钟频率。如串口发送方中可以指定波特率来实现间隔多少时间向TX发送一个数据(变化一次高低电平),那么接收方也要根据这个波特率来接收数据(间隔多长时间读取一次RX的电平)

  • 电平
    单端:电平参考需要一样,即如串口通讯中,发送方与接收方需要共地,保证它们的参考电压是一样的。
    差分:不需要共同的参考电平,是根据两根通讯线的电平差异来获取结果的。如两根通讯线的电平不同则表示结果0,相同则表示结果1。

  • 电平标准
    即通讯协议中,双方数据1和0的表达方式标准。即传输过程中人为规定电压与数据的对应关系,常用的有以下3种,抗干扰性RS485 > RS232 > TTL

    • TTL电平:+3.3V或+5V表示1,0V表示0
    • RS232电平:(-3 , -15V)表示1,(+3 , +15V)表示数据0
    • RS485标准:使用的不是绝对电压,而是两根信号线的相对电压(差分信号)作为标准。两根线电压差(+2, +6V)表示1,相差(-2, -6V)表示0。抗干扰信号非常强,距离可达到上千米
    • 当电平标准不一致时,需要加电平转换芯片
  • 波特率
    用于指定发送的频率和接收的频率,假如发送方1秒发送1位,那么接收方也必须1秒接收(读取一次RX的电平)1位,假如接收方频率更快,那么有可能相同一个数据被接收方多次接收。其单位是bps,即1秒发
    送的位数。1000bps就是1秒发送1000位数据,1位数据的发送耗时是1ms(二进制下,1位=1baud)

  • 数据模式

    • HEX模式/十六进制模式/二进制模式:以原始数据的形式显示
    • 文本模式/字符模式:以原始数据编码后的形式显示
    • 如果要显示汉字,就得制定汉字的字符集如GB2312、GBK,另外Unicode字符集:全球的语言,最常用的传输形式是UTF8

串口通信

1、参数

  • 波特率
  • 1位起始位(标志一个数据帧的开始,固定为低电平)
  • 1位停止位(标志一个数据帧的结束,固定为高电平)一般停止位为1位,也可设置为0.5、1、1.5、2,停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
  • 8/9位数据位(是否奇偶校验,如偶校验:第9位保证9位数据中1的个数为偶数),数据低位先行
    因此,在无奇偶校验的情况下一帧的数据长度为10位。

2、串口串口通信理解

它支持同步单向通信和半双工单线通信,也支持LIN(局部互连网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。它还允许多处理器通信。 使用多缓冲器配置的DMA方式,可以实现高速数据通信。
在这里插入图片描述

  • 发送数据时,Stm32会先判断TXE(Transmit Buffer Empty)信号,假如为1,表示发送数据寄存器
    TDR寄存器(只写寄存器)为空。此时STM32写入一帧数据到TDR上,并设置TXE为0,写入后系统会
    判断移位寄存器是否正在移位,假如没有在移位说明前一帧数据已经发送完成。数据寄存器TDR寄
    存器的数据会全部转移到发送移位寄存器中
    ,然后数据数由数据移位寄存器按照波特率传输出去(即
    按波特率设置TX引脚的电平)。与此同时,会设置TXE为1,发送数据寄存器TDR寄存器也会写入新的
    数据。也就是第一帧从TDR寄存器转移到移位寄存器后,马上设置TXE为1。这时候同时会发生两件
    事情:

    1. 移位寄存器负责一位位发送第一帧的数据
    2. stm32读取到TXE为1,会把第二帧数据写入到TDR上。

    根据TXE信号判断是否写TDR->写入根据发送移位寄存器的状态是否转移数据->发送移位寄存器发送数据+再次写TDR

  • 数据接收时,接收移位寄存器会一位位的从RX引脚读取数据(按照波特率的频率读取RX引脚的电平) ,假如读取完一帧后,数据会被转运到接收数据寄存器RDR中,转运完成后会设置RXNE标志位。stm32读取到这个标志位后就把RDR寄存器的数据读走。同时接收移位寄存器会再次读取下一帧数据。
    接收移位寄存器接收数据->数据转移到RDR中>转移完成则读取RDR,接收移位寄存器再次接收数据

  • 下图为数据手册里的参考图,主要注意3部分:数据读写部分、中断、时钟
    发送和接收的波特率由波特率寄存器BRR里的DIV决定,而波特率由内部时钟72MHZ分频得到的。所以DIV的值需要满足一下公式 波特率 = fPCLK2/1 / (16 * DIV),所以假如设置3600的波特率,那么DIV就需要设置成
    72000000/3600/16 = 468.75。这个是带小数的值,会被写两个寄存器。
    在这里插入图片描述

3、利用CubMX配置串口通信

配置只需要一步,关键掌握如何收发各种格式的数据。
在这里插入图片描述

发送数据
//1、发送一个字节
void hhSerialSendByte(uint8_t Byte){HAL_UART_Transmit(&huart1, &Byte, 1, HAL_MAX_DELAY);
}//2、发送一个数组
void hhSerialSendArray(uint8_t *Array,uint16_t Length){for(uint16_t i=0;i<Length;i++){hhSerialSendByte(Array[i]);}
}//3、发送一个字符串
void hhSerialSendString(char * mString){for(uint16_t i=0;mString[i]!='\0';i++){hhSerialSendByte(mString[i]);}
}//4、发送一个数字
uint32_t Serial_Pow(uint32_t X, uint32_t Y){uint32_t Result = 1;while (Y --){Result *= X;}return Result;
}
void hhSerial_SendNumber(uint32_t Number, uint8_t Length){uint8_t i;for (i = 0; i < Length; i ++){hhSerialSendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');}
}
//5、发送浮点数 前设置
#include <stdarg.h>
#include "stdio.h"
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);hhSerialSendString(String);
}
接收数据

1、查询法接收
在前面配置好USART的波特率等信息后,循环不断查询是否有数据传输过来

uint8_t ByteRecv;
int main(void){HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init();OLED_Init();OLED_Clear();while (1){HAL_UART_Receive(&huart1, &ByteRecv, 1, HAL_MAX_DELAY);hhSerialSendByte(ByteRecv);}
}

2、中断法接收

//1.单字节发送
void hhSerialSendByte(uint8_t Byte){HAL_UART_Transmit(&huart1, &Byte, 1, HAL_MAX_DELAY);
}
uint8_t Serial_RxFlag;
uint8_t Serial_GetRxFlag(void){if (Serial_RxFlag == 1){Serial_RxFlag = 0;return 1;}return 0;
}
uint8_t ByteRecv;
//接收中断函数
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){if (huart == &huart1){Serial_RxFlag=1;//已接收标志位,说明已经接收完一次HAL_UART_Receive_IT(&huart1, &ByteRecv, 1);//接收了一次后需要再次打开接收中断为下次中断接收做准备}
}int main(void){HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART1_UART_Init();OLED_Init();OLED_Clear();HAL_UART_Receive_IT(&huart1, &ByteRecv, 1);//启动中断接收一个字节OLED_ShowString(1, 1, "RxData:");while (1){if (Serial_GetRxFlag() == 1){hhSerialSendByte(ByteRecv);//将接收到的数据重新发送返回给电脑串口OLED_ShowHexNum(1, 8, ByteRecv, 2);}}
}

相关文章:

  • 如何快速使用JNI
  • Docker在windows上使用vscode远程连接容器
  • 23种设计模式【创建型模式】详细介绍之【单例模式】
  • LabVIEW项目外协时选择公司与个人兼职的比较
  • 工业路由器与家用路由器的区别
  • 4、音视频封装格式---FLV
  • 深度学习之半监督学习:一文梳理目标检测中的半监督学习策略
  • IT专业入门,高考假期预习指南
  • 浅谈前置处理器之用户参数
  • 一文学会LVS:概念、架构、原理、搭建过程、常用命令及实战案例
  • 基于Java微信小程序小说阅读系统设计和实现(源码+LW+调试文档+讲解等)
  • 使用 Vue 实现包含单选框的弹窗功能(附Demo)
  • 苹果公司的Wifi定位服务(WPS)存在被滥用的风险
  • GPIO和PIN
  • 用户想知道数据库每天增长几个G,到底都是啥?
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • golang 发送GET和POST示例
  • happypack两次报错的问题
  • MD5加密原理解析及OC版原理实现
  • Mithril.js 入门介绍
  • NSTimer学习笔记
  • Python 基础起步 (十) 什么叫函数?
  • react 代码优化(一) ——事件处理
  • SpiderData 2019年2月13日 DApp数据排行榜
  • 给新手的新浪微博 SDK 集成教程【一】
  • 山寨一个 Promise
  • 设计模式(12)迭代器模式(讲解+应用)
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 想写好前端,先练好内功
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​queue --- 一个同步的队列类​
  • # 透过事物看本质的能力怎么培养?
  • #laravel 通过手动安装依赖PHPExcel#
  • #前后端分离# 头条发布系统
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (十)c52学习之旅-定时器实验
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .md即markdown文件的基本常用编写语法
  • .Net OpenCVSharp生成灰度图和二值图
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?
  • []T 还是 []*T, 这是一个问题
  • [2019.3.20]BZOJ4573 [Zjoi2016]大森林
  • [BZOJ1060][ZJOI2007]时态同步 树形dp
  • [C++从入门到精通] 14.虚函数、纯虚函数和虚析构(virtual)
  • [C++基础]-初识模板
  • [CareerCup] 17.8 Contiguous Sequence with Largest Sum 连续子序列之和最大
  • [EFI]Lenovo ThinkPad X280电脑 Hackintosh 黑苹果引导文件
  • [go 反射] 进阶
  • [JavaWeb]——过滤器filter与拦截器Interceptor的使用、执行过程、区别
  • [json]定义、读写
  • [Linux] Boot分区满了的处理方法 The volume boot has only 0 bytes disk space remaining
  • [Linux] 一文理解HTTPS协议:什么是HTTPS协议、HTTPS协议如何加密数据、什么是CA证书(数字证书)...