串口屏加速度陀螺仪传感器的工作原理:
串口屏介绍
c
STVC070WT-01 是本公司生产的串口屏,其开发简单,易于使用,您可访问本公司官网查看所有显示差异。
需要特别注意的是,我们的串口屏支持串口通信。部分型号支持 TTL/RS232/RS485,但部分仅支持 RS232。若您的 MCU 串口为 TTL 电平,需添加 MAX232 进行电平转换。如需确认具体型号支持 TTL 或同时支持 TTL 和 RS232,请查阅公司官网。
“工业型”和“高级型”屏幕通常仅支持RS232或RS485,而“民用型”屏幕可支持TTL/RS232/RS485。
如果您选择“高级型”或“工业型”,但您的SCM仅支持TTL,则需要进行以下转换:
其他相关信息可查看或下载于官方网站:
STONE串口屏开发的三步流程
- 使用STONETOOL软件设计显示逻辑和按钮逻辑,并将设计文件下载至显示模块。
- MCU通过串口与STONE显示模块通信。
- 使用步骤2获取的数据,MCU执行其他操作。
项目介绍
今天我要展示的是重力、陀螺仪、欧拉角的演示,
功能如下:
- 三个文本框显示加速度值
- 三个文本框显示陀螺仪值
- 三个文本框显示欧拉角值
- 一个文本框显示当前刷新时间
- 两个按钮调整刷新时间
首先,我们需要使用Photoshop设计两个UI界面,设计结果如下:
第一张图片为主界面,第二张图片为按钮效果。
然后打开“TOOL2019”并在TOOL中设计效果:
使用了两个主要组件:
数值显示单元
增量按钮
设计完成后,可在模拟界面中看到模拟运行效果:
MPU-6050
特性
数字输出6轴或9轴旋转矩阵、四元数、欧拉角形式融合计算数据。3轴角速度传感器(陀螺仪)具有131 LSBs/°/ SEC灵敏度,全网格感应范围为±250、±500、±1000和±2000°/SEC。可通过程序控制,程序控制范围为±2g、±4g、±8g和±16g。消除加速度计与陀螺仪轴之间的灵敏度,减少设置和传感器漂移的影响。DMP(数字运动处理)引擎可减轻复杂融合算法、传感器同步、姿态感知等任务的处理负载。运动处理数据库内置支持Android、Linux和Windows系统中的运行时间偏差校正及磁传感器校正算法。带数字输出和数字输入的温度传感器,支持同步引脚,可实现视频电子阴影相位稳定技术,并支持GPS可编程中断控制,支持手势识别、摇动、图片缩放、滚动、快速下降中断、高G中断、零运动检测、触摸检测和摇动检测。VDD供电电压为2.5V ±5%、3.0V ±5%或3.3V ±5%。VDDIO工作电流为1.8V ±5%:5mA;陀螺仪待机电流:5μA; 加速度计工作电流:350uA,加速度计省电模式电流:20uA@10Hz。I2C在快速模式下最高可达400kHz,或SPI串行主机接口最高可达20MHz,内置频率发生器在整个温度范围内频率变化仅±1%。最小且最薄的封装(4x4x0.9mm QFN)专为便携式产品设计,已通过RoHS和环境标准测试。
关于引脚
SCL和SDA连接至MCU的IIC接口,通过该接口MCU控制MPU6050。
还提供一个IIC接口AXCL, XDA可用于连接外部从设备(如磁传感器)以构成九轴传感器。VLOGIC为IO端口电压,最低引脚可达1.8V。通常可直接使用VDD。AD0为IIC接口的地址控制引脚(连接至MCU),用于控制IIC地址的最低位。如果 GND 连接,则 MPU6050 的 IIC 地址为 0X68;如果 VDD 连接,则为 0X69。注意:此处的地址不包含数据传输的最低位(最低位用于读写)。
以下是我使用的 mpu-6050 模块:
STM32微控制器
STM32F103RCT6 MCU具有强大的功能。以下是MCU的基本参数:
- 系列: STM32F10X
- 内核:ARM – CORTEX32
- 速度:72 MHz
- 通信接口:CAN、I2C、IrDA、LIN、SPI、UART/USART、USB
- 外设:DMA、电机控制PWM、PDR、POR、PVD、PWM、温度传感器、WDT
- 程序存储容量:256KB
- 程序存储类型:FLASH
- RAM容量:48K
- 电压 – 电源(Vcc/Vdd):2 V ~ 3.6 V
- 振荡器:内部
- 工作温度:-40°C ~ 85°C
- 封装/外壳:64-lqfp
在本项目中,我将使用STM32F103RCT6的UART、GPIO、看门狗和定时器。
以下是该项目的代码开发记录。
STM32 使用 Keil MDK 软件进行开发,相信大家对此软件已经比较熟悉,因此不再赘述安装方法。
STM32 可通过 j-link 或 st-link 等仿真工具进行在线仿真。
下图是我使用的 STM32 开发板:
添加串口驱动程序
STM32F103RCT6 具有多个串口。在本项目中,我使用了串口通道 PA9/PA10,串口波特率设置为 115200。
相关的串口初始化代码如下:
u16 USART_RX_STA=0;
void uart_init(u32 bound){
//GPIO
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //ʹÄÜUSART1£¬GPIOAʱÖÓ
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);//INIT GPIOA.9
//USART1_RX GPIOA.10 INIT
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//FLOTING
GPIO_Init(GPIOA, &GPIO_InitStructure);//INIT GPIOA.10
//Usart1 NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//USART
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
u8 USART_RX_END=0;
void USART1_IRQHandler(void) //Uart1 handler
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
if(USART_RX_END==0)
{
Res =USART_ReceiveData(USART1);
USART_RX_BUF[USART_RX_STA]=Res ;
USART_RX_STA++;
if(USART_RX_STA>8)
{
USART_RX_END=1;
}
}
}
}
看门狗
为了防止程序运行时系统崩溃,我添加了看门狗。实际上,所有使用微控制器的项目通常都会使用看门狗。
STM32内置了两个看门狗,提供了更高的安全性、时间精度和灵活性。可使用两个看门狗设备(独立看门狗和窗口看门狗)来检测并解决由软件错误引起的故障。当计数器达到预设超时值时,将触发中断(仅窗口看门狗)或系统复位。
独立看门狗(IWDG)
由专用低速时钟(LSI)驱动,即使主时钟故障仍能正常工作。
适用于需要看门狗在主程序外完全独立工作且对时间精度要求较低的应用场景。
窗口看门狗(WWDG)
时钟驱动源自APB1时钟频率分频。通过可配置的时间窗口检测异常应用操作。适用于需要看门狗在精确时间窗口内工作的程序。
由于本DEMO程序,我将不添加看门狗功能。
#include "mpu6050.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
u8 MPU_Init(void)
{
u8 res;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
MPU_AD0_CTRL=0;
MPU_IIC_Init();
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80);
delay_ms(100);
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00);
MPU_Set_Gyro_Fsr(3);
MPU_Set_Accel_Fsr(0);
MPU_Set_Rate(50);
MPU_Write_Byte(MPU_INT_EN_REG,0X00);
MPU_Write_Byte(MPU_USER_CTRL_REG,0X00);
MPU_Write_Byte(MPU_FIFO_EN_REG,0X00);
MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80);
res=MPU_Read_Byte(MPU_DEVICE_ID_REG);
if(res==MPU_ADDR)
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01);
MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00);
MPU_Set_Rate(50);
}else return 1;
return 0;
}
u8 MPU_Set_Gyro_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_GYRO_CFG_REG,fsr<<3);
}
u8 MPU_Set_Accel_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_ACCEL_CFG_REG,fsr<<3);
}
u8 MPU_Set_LPF(u16 lpf)
{
u8 data=0;
if(lpf>=188)data=1;
else if(lpf>=98)data=2;
else if(lpf>=42)data=3;
else if(lpf>=20)data=4;
else if(lpf>=10)data=5;
else data=6;
return MPU_Write_Byte(MPU_CFG_REG,data);
}
u8 MPU_Set_Rate(u16 rate)
{
u8 data;
if(rate>1000)rate=1000;
if(rate<4)rate=4;
data=1000/rate-1;
data=MPU_Write_Byte(MPU_SAMPLE_RATE_REG,data);
return MPU_Set_LPF(rate/2);
}
short MPU_Get_Temperature(void)
{
u8 buf[2];
short raw;
float temp;
MPU_Read_Len(MPU_ADDR,MPU_TEMP_OUTH_REG,2,buf);
raw=((u16)buf[0]<<8)|buf[1];
temp=36.53+((double)raw)/340;
return temp*100;;
}
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);
if(res==0)
{
*gx=((u16)buf[0]<<8)|buf[1];
*gy=((u16)buf[2]<<8)|buf[3];
*gz=((u16)buf[4]<<8)|buf[5];
}
return res;;
}
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,MPU_ACCEL_XOUTH_REG,6,buf);
if(res==0)
{
*ax=((u16)buf[0]<<8)|buf[1];
*ay=((u16)buf[2]<<8)|buf[3];
*az=((u16)buf[4]<<8)|buf[5];
}
return res;;
}
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
u8 i;
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|0);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg);
MPU_IIC_Wait_Ack();
for(i=0;i<len;i++)< span="" style="box-sizing: border-box; -webkit-tap-highlight-color: transparent;"></len;i++)<>
{
MPU_IIC_Send_Byte(buf[i]);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
}
MPU_IIC_Stop();
return 0;
}
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|0);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg);
MPU_IIC_Wait_Ack();
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|1);
MPU_IIC_Wait_Ack();
while(len)
{
if(len==1)*buf=MPU_IIC_Read_Byte(0);
else *buf=MPU_IIC_Read_Byte(1);
len--;
buf++;
}
MPU_IIC_Stop();
return 0;
}
u8 MPU_Write_Byte(u8 reg,u8 data)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg);
MPU_IIC_Wait_Ack();
MPU_IIC_Send_Byte(data);
if(MPU_IIC_Wait_Ack())
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Stop();
return 0;
}
u8 MPU_Read_Byte(u8 reg)
{
u8 res;
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);
MPU_IIC_Wait_Ack();
MPU_IIC_Send_Byte(reg);
MPU_IIC_Wait_Ack();
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|1);
MPU_IIC_Wait_Ack();
res=MPU_IIC_Read_Byte(0);
MPU_IIC_Stop();
return res;
}
MPU-6050 驱动程序
此代码使用IIC通信模式读取MPU6050的数据,IIC通信采用软件模拟IIC实现。相关代码较多,此处不再赘述。
以下是 MPU6050 的驱动程序:
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"
#include "mpu6050.h"
#include "usmart.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
extern u8 USART_RX_END;
#define aacx_h 0x00
#define aacx_l 0x01
#define aacy_h 0x00
#define aacy_l 0x05
#define aacz_h 0x00
#define aacz_l 0x09
#define gyrox_h 0x00
#define gyrox_l 0x0D
#define gyroy_h 0x00
#define gyroy_l 0x11
#define gyroz_h 0x00
#define gyroz_l 0x15
#define pitch_h 0x00
#define pitch_l 0x19
#define roll_h 0x00
#define roll_l 0x1D
#define yaw_h 0x00
#define yaw_l 0x21
#define refresh_addr 0x25
u8 aacx_send[8]= {0xA5, 0x5A, 0x05, 0x82, aacx_h, aacx_l, 0x00,0x00};
u8 aacy_send[8]= {0xA5, 0x5A, 0x05, 0x82, aacy_h, aacy_l, 0x00,0x00};
u8 aacz_send[8]= {0xA5, 0x5A, 0x05, 0x82, aacz_h, aacz_l, 0x00,0x00};
u8 gyrox_send[8]= {0xA5, 0x5A, 0x05, 0x82, gyrox_h, gyrox_l, 0x00,0x00};
u8 gyroy_send[8]= {0xA5, 0x5A, 0x05, 0x82, gyroy_h, gyroy_l, 0x00,0x00};
u8 gyroz_send[8]= {0xA5, 0x5A, 0x05, 0x82, gyroz_h, gyroz_l, 0x00,0x00};
u8 pitch_send[8]= {0xA5, 0x5A, 0x05, 0x82, pitch_h, pitch_l, 0x00,0x00};
u8 roll_send[8]= {0xA5, 0x5A, 0x05, 0x82, roll_h, roll_l, 0x00,0x00};
u8 yaw_send[8]= {0xA5, 0x5A, 0x05, 0x82, yaw_h, yaw_l, 0x00,0x00};
void UART1_SendAccGyr(short send_data,u8 arr[])
{
u8 i=0;
arr[6]=(send_data>>8)&0XFF;
arr[7]=send_data&0XFF;
while(i<8)
{
USART_SendData(USART1,arr[i]);
while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);
i++;
}
}
int main(void)
{
u16 t=0,t_refresh=100;
float pitch,roll,yaw;
short aacx,aacy,aacz;
short gyrox,gyroy,gyroz;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200); //INIT UART
delay_init();
MPU_Init(); //INIT MPU6050
while(mpu_dmp_init())
{
//printf("MPU6050 Error\r\n");
delay_ms(200);
}
//printf("MPU6050 OK\r\n");
while(1)
{
if(USART_RX_END)
{
switch (USART_RX_BUF[5])
{
case refresh_addr:
t_refresh = (short) (USART_RX_BUF[8] << 8) | USART_RX_BUF[9];
break;
default:
break;
}
}
if(mpu_dmp_get_data(&pitch,&roll,&yaw)==0)
{
//printf("IS_DATA!\r\n");
MPU_Get_Accelerometer(&aacx,&aacy,&aacz); //GET Acc DATA
MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //GET Gyr DATA
if(t>=t_refresh)
{
t=0;
//printf("ROLL:%d PITCH:%d YAW:%d \r\n",(int)(roll),(int)(pitch),(int)(yaw));
UART1_SendAccGyr(aacx,aacx_send);
UART1_SendAccGyr(aacy,aacy_send);
UART1_SendAccGyr(aacz,aacz_send);
UART1_SendAccGyr(gyrox,gyrox_send);
UART1_SendAccGyr(gyroy,gyroy_send);
UART1_SendAccGyr(gyroz,gyroz_send);
}
}
else
{
// printf("NO_DATA!\r\n");
}
t++;
delay_ms(1);
}
}
Main.C 文件主要分为以下几个部分:
- 包含头文件
- 宏定义串口屏的地址
- 定义一个通过串口发送加速度和陀螺仪数据的函数
- 定义变量值
- 初始化 STM32 的外设
- 初始化 MPU – 6050
While(1) 的代码逻辑:
- 判断是否接收到串口屏的数据,并设置刷新时间
- 判断是否获取到 MPU-6050 的数据。如果已获取,通过串口发送至串口屏
- 延迟1毫秒以设置刷新时间
本程序具备获取欧拉角的功能,只需将其添加至串口显示器即可显示。
本程序中显示的数据为从MPU6050采集的原始数据。
STM32、MPU6050与串口屏的连接模式如下: