串口通信
UART、I2C、SPI、USB的异同点 #通信协议#
名称 | 引脚 | 双工 | 时钟 | 电平 | 设备 | 通信距离 | 传输速率 |
---|---|---|---|---|---|---|---|
USART | TX、RX | 全双工 | 异步 | 单端 | 点对点 | 远(最多1200m) | 慢(波特率设置) |
I2C | SCL、SDA | 半双工 | 同步 | 单端 | 多设备(一主多从,寻址) | 近 | 慢 |
SPI | SCLK、MOSI、MISO、CS | 全双工 | 同步 | 单端 | 多设备(一主多从,片选 选从机) | 远 | 快 |
CAN | CAN_H、CAN_L | 半双工 | 异步 | 差分 | 多设备(多主机) | ||
USB | DP、DM | 半双工 | 异步 | 差分 | 点对点 | 近 | 快 |
-
单端电平:指电平高低根据对GND的电位差确定,通信双方必须共地才可通信
-
差分电平:无需共地,抗干扰能力强
-
异步通信没有时钟线,需要双方约定一个采样频率,并添加帧头帧尾等进行采样位置的对齐
-
UART:通用异步串行口,速率不快,可全双工,结构上一般由波特率产生器、UART发送器、UART接收器组成,硬件上两线,一收一发;
-
IIC:双向、两线、串行、多主控接口标准。速率不快,半双工,同步接口,具有总线仲裁机制,非常适合器件间近距离经常性数据通信,可实现设备组网;
-
SPI:高速同步串行口,高速,可全双工,收发独立,同步接口,可实现多个SPI设备互联,硬件3~4线;
-
USB通用串行总线,高速,半双工,由主机、hub、设备组成。设备可以与下级hub相连构成星型结构。
-
串口对比图(老)
-
-
同步通讯和异步通讯的区别
异步通信
-
优点:节省一根时钟线
-
缺点:对时钟要求严格,对硬件电路的依赖比较严重
同步通信
-
优点:对时间要求不严格,对硬件电路不怎么依赖,可以使用软件模拟时序
-
缺点:多一根时钟线
UART
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器。比UART(通用异步收发传输器(Universal Asynchronous Receiver/Transmitter))多了个同步通信功能
-
USART的同步时钟信号只能输出,不能输入。所以其同步模式更多是未来兼容其他协议而设计的,不支持两个USART之间进行同步通信。我们一般只使用异步通信
-
时钟可以模拟SPI,或者根据周期测量波特率
-
-
硬件自动处理收发寄存器中的值,发送/接收时自动添加/删除起始、停止位。
-
USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
-
通信双方的波特率需一致,代表码元传输速率,即通信速度。波特率越高,传输速度越快,距离越短
-
自带波特率发生器,最高达4.5Mbits/s
-
可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
-
可选校验位(无校验/奇校验/偶校验)
-
支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
当电平标准不一致时,需要加电平转换芯片
-
电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
-
TTL电平:+3.3V或+5V表示1,0V表示0
-
RS232电平:-3 ~ -15V表示1,+3 ~ +15V表示0
-
RS485电平:两线压差+2 ~ +6V表示1,-2 ~ -6V表示0(差分信号)
-
STM32F103C8T6 USART资源: USART1、 USART2、 USART3
通信协议
空闲电平为高电平,起始位拉低电平,停止位置高电平
-
波特率:串口通信的速率
-
起始位:标志一个数据帧的开始,固定为低电平
-
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
-
校验位:用于数据验证,根据数据位计算得来 奇偶校验码
-
停止位:用于数据帧间隔,固定为高电平
一般选择 8位字长(无校验) or 9位字长(有校验)。这样每帧的有效载荷都是1字节
USART框图
-
流控:控制传输速度,避免发送方发送过快,接收方来不及接收
-
TX与CTS是一对,RX与RTS是一对
-
来不及接收时,接收方RTS置高电平,发送方的CTS收到后就会暂停发送
-
-
唤醒单元:用来实现多设备通讯,有寻址功能
USART基本结构
串口工作的三种方式
-
查询:不断地循环查询标志,看看当前有没有数据要它传送或接收,有的话进行相应的操作
-
中断:如果发现有一个中断来,则意味着有数据需要接收(接收中断)或数据已经发送完成(发送中断),最常用
-
DMA:设置好DMA工作方式(接收和发送缓冲位置),由DMA来自动接收或发送数据,可以最小化占用CPU时间
数据包封装
HEX数据包:传输原始数据
-
优点:数据解析简单,比较适合模块发送原始数据,如陀螺仪、温湿度传感器。
-
缺点:灵活性不足,载荷容易与包头包尾重复
-
格式
-
接收过程(使用状态机思想)
文本数据包:
-
优点:数据直观易理解,适合输入指令与人机交互的场合。如蓝牙模块的AT指令。
-
缺点:解析效率低
-
格式
-
接收过程
常用函数
void USART_DeInit(USART_TypeDef* USARTx);
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct); // 时钟输出设置
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct); // 时钟输出设置
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState); // 中断
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState); // DMA
// 发送 及 接收数据(读写寄存器)
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
// 读写标志位
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
基于HAL库
代码实例
串口发送
#include "stm32f10x.h" // Device header
#include "stdio.h"
#include "stdarg.h" // 封装sprintf
// 使用USART1,PA9为TX,PA10为RX
void Serial_Init(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// GPIO_InitStructure.GPIO_Mode = ;
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置串口发送
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600; // 直接写波特率数值,Init函数自动算好对应的分频系数
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
void Serial_SentByte(uint8_t Byte) {
USART_SendData(USART1, Byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 看手册得知不需手动清零,下一次发送时,该标志位会自动清零
}
void Serial_SendArray(uint8_t *Array, uint16_t Length) {
for (uint16_t i = 0; i < Length; i++) {
Serial_SentByte(Array[i]);
}
}
void Serial_SendString(char* String) {
for (uint16_t i = 0; i < String[i] != '\0'; i++) {
Serial_SentByte(String[i]);
}
}
// 返回X的Y次方
uint32_t Serial_Pow(uint32_t X, uint32_t Y) {
uint32_t Result = 1;
while (Y--) {
Result *= X;
}
return Result;
}
void Serial_SendNumber(uint32_t Number, uint8_t Length) {
for (uint16_t i = 0; i < Length; i++) {
Serial_SentByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
}
}
// fputc 是 printf 的底层函数,次数重写fputc,实现printf重定向输出到串口的功能
int fputc(int ch, FILE *f) {
Serial_SentByte(ch);
return ch;
}
// 对sprintf函数的封装
void Serial_Printf(char *format, ...) {
char String[100];
va_list arg;
va_start(arg, format);
vsprintf(String, format, arg);
va_end(arg);
Serial_SendString(String);
}
在main.c中
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
int main() {
OLED_Init();
Serial_Init();
// uint8_t Arr[] = {97,98, 99, 100};
while(1) {
// Serial_SendArray(Arr, 4);
// Serial_SendString("Hello~\r\n");
// Serial_SendNumber(12345, 6);
// 重定向printf到串口1
// printf("Num = %d \r\n", 666);
// char String[100];
// sprintf(String, "Num = %d \r\n", 777);
// Serial_SendString(String);
Serial_Printf("Num = %d \r\n", 888);
Serial_Printf("你好世界\r\n");
Delay_ms(1000);
}
}
注意:要想显示汉字不乱码,两边编码格式必须一样(如都是UTF-8),且项目设置C/C++中加上选项--no-multibyte-chars
串口发送接收
#include "stm32f10x.h" // Device header
#include "stdio.h"
#include "stdarg.h" // 封装sprintf
uint8_t Serial_RxData, Serial_RxFlag;
// 使用USART1,PA9为TX,PA10为RX
void Serial_Init(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入(浮空也可)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置串口发送
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600; // 直接写波特率数值,Init函数自动算好对应的分频系数
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 发送 + 接收
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStructure);
// 开启中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 接收数据触发中断
// 配置NVIC (misc.h)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART1, ENABLE);
}
void Serial_SentByte(uint8_t Byte) {
USART_SendData(USART1, Byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 看手册得知不需手动清零,下一次发送时,该标志位会自动清零
}
void Serial_SendArray(uint8_t *Array, uint16_t Length) {
for (uint16_t i = 0; i < Length; i++) {
Serial_SentByte(Array[i]);
}
}
void Serial_SendString(char* String) {
for (uint16_t i = 0; i < String[i] != '\0'; i++) {
Serial_SentByte(String[i]);
}
}
// 返回X的Y次方
uint32_t Serial_Pow(uint32_t X, uint32_t Y) {
uint32_t Result = 1;
while (Y--) {
Result *= X;
}
return Result;
}
void Serial_SendNumber(uint32_t Number, uint8_t Length) {
for (uint16_t i = 0; i < Length; i++) {
Serial_SentByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
}
}
// fputc 是 printf 的底层函数,次数重写fputc,实现printf重定向输出到串口的功能
int fputc(int ch, FILE *f) {
Serial_SentByte(ch);
return ch;
}
// 对sprintf函数的封装
void Serial_Printf(char *format, ...) {
char String[100];
va_list arg;
va_start(arg, format);
vsprintf(String, format, arg);
va_end(arg);
Serial_SendString(String);
}
uint8_t Serial_GetRxFlag(void) {
if (Serial_RxFlag == 1) {
Serial_RxFlag = 0;
return 1;
}
return 0;
}
uint8_t Serial_GetRxData(void) {
return Serial_RxData;
}
void USART1_IRQHandler(void) {
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {
Serial_RxData = USART_ReceiveData(USART1);
Serial_RxFlag = 1;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
uint8_t RxData;
int main() {
OLED_Init();
Serial_Init();
// uint8_t Arr[] = {97,98, 99, 100};
while(1) {
// 查询方式 接收
// if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) {
// RxData = USART_ReceiveData(USART1);
// OLED_ShowHexNum(1, 1, RxData, 2);
// }
if (Serial_GetRxFlag() == 1) {
RxData = Serial_GetRxData();
Serial_SentByte(RxData);
OLED_ShowHexNum(1, 1, RxData, 2);
}
}
}
串口收发HEX数据包(定长)
uint8_t Serial_TxPacket[4]; //FF 01 02 03 04 FE
uint8_t Serial_RxPacket[4]; // Tx和Rx缓冲区均声明为全局变量
uint8_t Serial_RxFlag;
// 自动加上包头包尾发送
void Serial_SendPacket(void) {
Serial_SendByte(0xFF);
Serial_SendArray(Serial_TxPacket, 4);
Serial_SendByte(0xFE);
}
uint8_t Serial_GetRxFlag(void) {
if (Serial_RxFlag == 1) {
Serial_RxFlag = 0;
return 1;
}
return 0;
}
void USART1_IRQHandler(void) {
static uint8_t RxState = 0; // 状态码S
static uint8_t pRxPacket = 0; // 接收的长度
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {
uint8_t RxData = USART_ReceiveData(USART1);
if (RxState == 0) {
pRxPacket = 0; // 重置接收的长度
if (RxData == 0xFF) RxState = 1;
} else if (RxState == 1) {
Serial_RxPacket[pRxPacket] = RxData;
pRxPacket++;
if (pRxPacket >= 4) RxState = 2;
} else if (RxState == 2) {
if (RxData == 0xFE) RxState = 0;
Serial_RxFlag = 1;
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
串口收发文本数据包(不定长)
效果:解析不定长文本数据包,若收到“@LED_ON\r\n”则点亮LED,并返回“LED_ON_OK\r\n”给串口
// 作为全局变量,在Serial.h中需声明为extren
char Serial_RxPacket[100]; // 接收数据包
uint8_t Serial_RxFlag;
// 状态机接收数据包
void USART1_IRQHandler(void) {
static uint8_t RxState = 0; // 状态码S
static uint8_t pRxPacket = 0; // 接收的长度
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {
uint8_t RxData = USART_ReceiveData(USART1);
if (RxState == 0) {
if (RxData == '@' && Serial_RxFlag == 0) { // Serial_RxFlag为0时才开始接收,避免旧数据包被覆盖
RxState = 1;
pRxPacket = 0; // 重置接收的长度
}
} else if (RxState == 1) {
if (RxData == '\r') {
RxState = 2;
} else {
Serial_RxPacket[pRxPacket] = RxData;
pRxPacket++;
}
} else if (RxState == 2) {
if (RxData == '\n') {
RxState = 0;
Serial_RxPacket[pRxPacket] = '\0'; // 添加字符串结束位
Serial_RxFlag = 1; // 消息解析完成后才置1
}
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
main.c中
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
#include "string.h"
int main(void)
{
OLED_Init();
LED_Init();
Serial_Init();
OLED_ShowString(1, 1, "TxPacket");
OLED_ShowString(3, 1, "RxPacket");
while (1)
{
if (Serial_RxFlag == 1)
{
OLED_ShowString(4, 1, " ");
OLED_ShowString(4, 1, Serial_RxPacket);
if (strcmp(Serial_RxPacket, "LED_ON") == 0)
{
LED1_ON();
Serial_SendString("LED_ON_OK\r\n");
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, "LED_ON_OK");
}
else if (strcmp(Serial_RxPacket, "LED_OFF") == 0)
{
LED1_OFF();
Serial_SendString("LED_OFF_OK\r\n");
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, "LED_OFF_OK");
}
else
{
Serial_SendString("ERROR_COMMAND\r\n");
OLED_ShowString(2, 1, " ");
OLED_ShowString(2, 1, "ERROR_COMMAND");
}
Serial_RxFlag = 0;
}
}
}
I2C IIC
IIC(Inter-Intergrated Circuit)芯片与芯片之间的通讯
同步 半双工通信协议(两线通信,大端),带数据应答,支持挂载多设备(一主多从、多主多从)
-
SCL 时钟线:只由主设备发送,从设备接收
-
SDA 数据线:设备内部有两个引脚(发送引脚/接受引脚),它们都连接到外部的SDA线上
两种工作方式(按字节发送)
-
主发从收(主机控制SCL和SDA)
-
流程:主start -> 主发地址 -> 从ACK -> [ 主发数据 -> 从ACK (循环)] -> 主stop
-
-
主收从发(主控SCL,从控SDA)
-
流程:主start -> 从发地址 -> 主ACK -> [ 从发数据 -> 主ACK (循环)] -> 接受至最后一个字节,主NACK -> 主stop
-
从设备在数据未准备好时,拉低SCL
-
I2C总线上的器件有唯一的地址,D7~D1为从机地址(由固定部分 + 可编程部分组成,其中可编程部分决定了从机的数量),D0 为数据传输方向(0表主机发,1表主机收)
I2C总线协议
-
空闲状态:SCL 与 SDA 均为高电平;
-
启停信号:当SCL为高时,SDA由高电平变低电平(启动) or SDA低变高(停止)
总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态。
-
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
-
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
-
应答信号:发送器每发一个字节,发送器总是需等待一个应答信号,在时钟信号第9个脉冲时,检查ACK信号,为1代表非应答NACK,为0代表应答ACK。
-
从机发完数据后,若主机不应答,从机会觉得“可能主机不想要数据了”,就停止发送
-
-
数据有效性:靠时钟来保证,仅当SCL为低时,SDA才可被改变;当SCL为高时,SDA保持不变(每位数据的传输由SCL上升沿触发)
-
通过总线仲裁应对同时到来的请求。
-
-
数据帧格式:SDA线上每个字节必须是8位长,字节数没有限制。8位数据中,先传输最高有效位(大端MSB)传输
-
从机地址为7位。一般从机地址前4位固定,后几位可以通过芯片外接端口置0/1
-
I2C时序
读写后,地址指针会自增
-
指定地址写
-
对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
此处从机地址为0xD0,第一个字节最后一位位0,代表写;在地址0x19写入数据0xAA
-
-
当前地址读
-
对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
-
若主机读完给从机应答了,从机会再次读取数据(从下一个地址指针的位置);
-
主机在连续读写时,需要在读取的最后一个字节给非应答,让从机知道主机不想读了
-
-
指定地址读(复合数据帧:起始+重复起始+停止)
-
对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
-
Sr(Start Repeat)代表重复起始条件,相当于另起一个时序
-
硬件电路
-
所有I2C设备的SCL连在一起,SDA连在一起
-
设备的SCL和SDA均要配置成开漏输出模式
-
为了避免电路短路(两段分别推挽输出高低电平,信号撞在一起),I2C禁止所有设备输出强上拉的高电平
-
总线会有线与的特性,只要有一个或多个设备输出了低电平,总线就处于低电平;只有所有设备都输出高电平时总线才为高。这个特征可以执行多主机模式下的时钟同步和总线总裁
-
-
SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右
传输速率:在标准模式下可以达到100kb/s,快速模式下可以达到400kb/s。
24C02 存储芯片
MPU6050 六轴姿态传感器
MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景
-
3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度
-
3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度
MPU6050参数
-
16位ADC采集传感器的模拟信号,量化范围:-32768~32767
-
加速度计满量程选择:±2、±4、±8、±16(g)
-
量程选择越大,精度越低
-
-
陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)
-
可配置的数字低通滤波器
-
可配置的时钟源
-
可配置的采样分频
-
I2C从机地址:1101000(AD0=0)or 1101001(AD0=1)
硬件电路
-
XCL、XDA:是MPU6050作为主机,连接其他从机外设(如磁力计、气压计等)而设计的,可直接读取这些外设数据,并在MPU6050内部的DMP单元,进行数据融合和姿态解算
-
实际上,因为I2C可以挂载多设备的特性,气压计等外设也可以一起连接到SCL、SDA引脚上(此时无法使用MPU6050的解算功能)
-
-
左上角部分是稳压器,VCC_5V可以输入3.3V ~ 5V电压,输出稳定的3.3V,给芯片端供电
SPI
SPI(Serial Peripheral Interface)串行外设接口,用于芯片与芯片之间的通讯
一种同步的串行全双工通信协议,一般用于芯片间的通讯,一主多从,四线通信。传输时高位在前(大端)
-
SCLK:系统时钟(同步通信协议,则需要有时钟)
-
设置选项:(有四种工作方式)
-
时钟极性(CPOL):设置空闲时的电平
-
时钟相位(CPHA):设置有效沿触发方向是第一次还是第二次有效沿触发
-
-
-
MOSI:主发从收
-
MISO:主收从法
-
NSS(CS):使能
SS一般是主机的使能,CS是从机的使能
工作原理:按位循环
读写过程
CAN总线
Controller Area Network 控制器 域 网络,
CAN通讯 需要专门的CAN收发芯片,其负责 逻辑信号 与 物理信号(差分电平)的转换。
#TODO#(191条消息) CAN通信知识梳理及在Stm32上的应用(HAL库)_冬瓜~的博客-CSDN博客
差分信号 使用两根线表示。使用差分信号(双绞线)可以避免信号干扰(受干扰时压差不变)
-
单片机发送低电平时,CAN两根线分别输出3.5V和1.5V,差值为2V,表示逻辑0
-
当信号差为0时,两根线都是2.5V,压差0V,表示逻辑1
数据帧格式
-
起始位:都为0
-
识别码:相当于 设备地址,信号同时到来时,识别码小的优先处理
-
控制码:控制数据长度
-
IDE位:区分标准格式和扩展格式
-
DLC:Data Link Control 数据长度代码
-
4位二进制码表示,转换为十进制n表示数据码有n个字节
-
-
-
CRC:循环冗余校验
-
CRC界定符:一定为1,用于将帧区分为两个部分
-
后两位:ACK确认码
-
应答码:与发送方相反
-
ACK界定位:一定为1
-
-
-
结束位:7位1