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

发布于:2024-07-03 ⋅ 阅读:(12) ⋅ 点赞:(0)

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

通信接口区别

名称 引脚 双工 时钟 电平 设备
USART TX、RX 全双工 异步 单端 点对点
I2C SCL、SDA 半双工 同步 单端 多设备
SPI SCLK、MOSI、MISO、CS 全双工 同步 单端 多设备
CAN CAN_H、CAN_L 半双工 异步 差分 多设备
USB DP、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位。

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);
		}
	}
}