STM32应用(七)JY901九轴姿态模块(串口DMA空闲中断和I2C通信)

0.相关基础知识介绍

IMU模块中6轴、9轴包含哪些模块?

欧拉角、旋转矩阵、四元数合辑
这个确实复杂,如果以后有机会接触到飞控,再好好看看。

0.1六轴、九轴传感器有什么区别?

加速度传感器(即加速计)、角速度传感器(即陀螺仪)、磁感应传感器(即电子罗盘)。这三类传感器测量的数据在空间坐标系中都可以被分解为X,Y,Z三个方向轴的力,因此也常常被称为3轴加速度计、3轴陀螺仪、3轴磁力计。

加速度传感器(即加速计)检测横向加速
角速度传感器(即陀螺仪)检测角度旋转和平衡
磁感应传感器(即电子罗盘)获取磁场数据
气压传感器获取海拔高度的数据

6轴 : 三轴(XYZ)加速度计 + 三轴(XYZ)陀螺仪(也叫角速度传感器)
9轴 : 6轴 + 三轴(XYZ)磁场传感器

1.JY901模块简介

以下内容来自JY901使用说明书V4.4,仅摘录了需要注意的内容。

1.1 产品概述

模块内部自带电压稳定电路, 工作电压 3.3v~5v, 引脚电平兼容 3.3V/5V 的嵌入式系统,连接方便。
模块内部集成了姿态解算器, 配合动态卡尔曼滤波算法, 能够在动态环境下准确输出模块的当前姿态, 姿态测量精度静态 0.05 度, 动态 0.1 度, 稳定性极高。
支持串口和 IIC 两种数字接口。 方便用户选择最佳的连接方式。 串口速率
2400bps~921600bps 可调, IIC 接口支持全速 400K 速率。(本博客采用串口通信)

1.2 性能参数

1、 电压: 3.3V~5V
2、 电流: <25mA
3、 测量维度: 加速度: 3 维角速度: 3 维; 磁场: 3 维;角度: 3 维, 气压:1 维(JY-901B);GPS: 3 维(接 GPS 模块)
4、 量程: 加速度:±2/4/8/16 g(可选)角速度:±250/500/1000/2000 °/s(可选) ;角度 X,Z 轴±180°, Y 轴±90°。
5、 数据输出内容: 时间、 加速度、 角速度、 角度、 磁场、 端口状态、 气压(JY-901B) 、高度(JY-901B) 、 经纬度(需连接 GPS) 、 地速(需连接 GPS) 。
6、 数据接口: 串口(TTL 电平, 波特率支持 2400、 4800、 9600(默认) 、 19200、 38400、57600、 115200、 230400、 460800、 921600) , I2C(最大支持高速 IIC 速率 400K)
7.扩展口功能: 模拟输入(0~VCC) 、 数字输入、 数字输出、 PWM 输出(周期 1us-65535us,分辨率 1us)

1.3 实物图和接线

在这里插入图片描述
串口通信接线如下:

JY901单片机
VCC3.3V 或 5V
RXTX
TXRX
GNDGND

在这里插入图片描述
IIC通信接线如下:

JY901单片机
VCC3.3V 或 5V
SCLI2C 时钟线
SDAI2C 数据线
GNDGND

在这里插入图片描述
注意: 为了能在 IIC总线上面挂接多个模块, 模块的 IIC 总线是开漏输出的, MCU 在连接模块时需要将 IIC 总线通过一个 4.7K 的电阻上拉到 VCC。
注意: VCC 为 3.3V, 要另外接电源供电。 直接用模块上面的电源供电, 可能会产生压降, 使模块实际电压没有3.3~5V。

2.软件和使用说明书

上位机软件
提取码:nu3h
JY901使用说明书
提取码:apmd

3.串口通信JY901

3.1 cubemx工程配置

利用DMA空闲中断通信。
1.开启串口1并开启对应的中断
在这里插入图片描述
在这里插入图片描述
2.添加DMA通道
在这里插入图片描述

3.2代码编写

参考博客:
STM32 Cubemax(十) ——JY901陀螺仪数据的读取与简单数据处理

3.2.1 DMA空闲中断的开启

1.在usatr.c文件中添加DMA空闲中断

/* USER CODE BEGIN 0 */
extern User_USART JY901_data;//数据变量声明
/* USER CODE END 0 */

  /* USER CODE BEGIN USART1_Init 2 */
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);  
	HAL_UART_Receive_DMA(&huart1,JY901_data.RxBuffer,RXBUFFER_LEN);  
  /* USER CODE END USART1_Init 2 */

2.在stm32f4xxx_it.c文件中编写中断服务程序

/* USER CODE BEGIN PV */
extern User_USART JY901_data;
/* USER CODE END PV */

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	uint32_t temp_flag = 0;
	uint32_t temp;
	temp_flag = __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
	if((temp_flag!=RESET))																
	{
			__HAL_UART_CLEAR_IDLEFLAG(&huart1);
			temp = huart1.Instance->SR;   										
			temp = huart1.Instance->DR; 										
			HAL_UART_DMAStop(&huart1);   									
			temp = hdma_usart1_rx.Instance->NDTR; 	
			//F1的板子	temp = hdma_usart3_rx.Instance->CNDTR; 				
			JY901_data.Rx_len = RXBUFFER_LEN-temp;  				
			JY901_Process();					//按照自己需求改写这个函数						
			JY901_data.Rx_flag = 1;   											
	}
	HAL_UART_Receive_DMA(&huart1,JY901_data.RxBuffer,RXBUFFER_LEN);

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

3.2.2 添加JY901.h

淘宝客服提供。

#ifndef __JY901_H
#define __JY901_H

#define SAVE 			0x00
#define CALSW 		0x01
#define RSW 			0x02
#define RRATE			0x03
#define BAUD 			0x04
#define AXOFFSET	0x05
#define AYOFFSET	0x06
#define AZOFFSET	0x07
#define GXOFFSET	0x08
#define GYOFFSET	0x09
#define GZOFFSET	0x0a
#define HXOFFSET	0x0b
#define HYOFFSET	0x0c
#define HZOFFSET	0x0d
#define D0MODE		0x0e
#define D1MODE		0x0f
#define D2MODE		0x10
#define D3MODE		0x11
#define D0PWMH		0x12
#define D1PWMH		0x13
#define D2PWMH		0x14
#define D3PWMH		0x15
#define D0PWMT		0x16
#define D1PWMT		0x17
#define D2PWMT		0x18
#define D3PWMT		0x19
#define IICADDR		0x1a
#define LEDOFF 		0x1b
#define GPSBAUD		0x1c

#define YYMM				0x30
#define DDHH				0x31
#define MMSS				0x32
#define MS					0x33
#define AX					0x34
#define AY					0x35
#define AZ					0x36
#define GX					0x37
#define GY					0x38
#define GZ					0x39
#define HX					0x3a
#define HY					0x3b
#define HZ					0x3c			
#define Roll				0x3d
#define Pitch				0x3e
#define Yaw					0x3f
#define TEMP				0x40
#define D0Status		0x41
#define D1Status		0x42
#define D2Status		0x43
#define D3Status		0x44
#define PressureL		0x45
#define PressureH		0x46
#define HeightL			0x47
#define HeightH			0x48
#define LonL				0x49
#define LonH				0x4a
#define LatL				0x4b
#define LatH				0x4c
#define GPSHeight   0x4d
#define GPSYAW      0x4e
#define GPSVL				0x4f
#define GPSVH				0x50
#define q0          0x51
#define q1          0x52
#define q2          0x53
#define q3          0x54
      
#define DIO_MODE_AIN 0
#define DIO_MODE_DIN 1
#define DIO_MODE_DOH 2
#define DIO_MODE_DOL 3
#define DIO_MODE_DOPWM 4
#define DIO_MODE_GPS 5		

struct STime
{
	unsigned char ucYear;
	unsigned char ucMonth;
	unsigned char ucDay;
	unsigned char ucHour;
	unsigned char ucMinute;
	unsigned char ucSecond;
	unsigned short usMiliSecond;
};
struct SAcc//加速度
{
	short a[3];
	short T;
};
struct SGyro//角速度
{
	short w[3];
	short T;
};
struct SAngle//角度
{
	short Angle[3];
	short T;
};
struct SMag//磁场输出
{
	short h[3];
	short T;
};

struct SDStatus//端口状态数据输出
{
	short sDStatus[4];
};

struct SPress//气压高度
{
	long lPressure;
	long lAltitude;
};

struct SLonLat//经纬度
{
	long lLon;
	long lLat;
};

struct SGPSV
{
	short sGPSHeight;
	short sGPSYaw;
	long lGPSVelocity;
};
struct SQ //四元数
{ short q[4];
};
 
#endif

3.2.3 重定义需要的结构体和JY901数据结构体

typedef struct
{
	float angle[3];
}Angle;

typedef struct
{
	float a[3];
}Acc;

typedef struct
{
	float w[3];
}SGyro;


typedef struct//四元数
{ float q[4];
}SQ;

typedef struct//磁场输出
{
	float h[3];
}SMag;

typedef struct//气压高度
{
	float lPressure;
	float lAltitude;
}SPress;

typedef struct//经纬度
{
	float lLon;
	float lLat;
}SLonLat;
typedef struct User_USART
{
		uint8_t Rx_flag;											
		uint8_t Rx_len;												
		uint8_t frame_head;					//帧头
		uint8_t RxBuffer[RXBUFFER_LEN];		//接收缓冲
		Angle angle;						//角度
		Acc acc;								//加速度
		SGyro w;								//角速度
		SMag h;									//磁场
		SPress lPressure;   	  //气压
		SPress lAltitude;     	//高度
		SLonLat lLon;						//经度
		SLonLat lLat;						//维度
		SQ q; 									//四元数
}User_USART;

3.2.4 接收结构体初始化

void User_USART_Init(User_USART *Data)
{
		for(uint16_t i=0; i < RXBUFFER_LEN; i++)	Data->RxBuffer[i] = 0;
		Data->frame_head = 0x55;
		Data->Rx_flag = 0;
		Data->Rx_len = 0;
}

3.2.5 数据解算

#define RXBUFFER_LEN 99		//接收9类数据,一共99位

User_USART JY901_data;
struct SAcc 		stcAcc;
struct SGyro 		stcGyro;
struct SAngle 	stcAngle;
struct SMag 	stcMag;
struct SPress 	stcPress;
struct SLonLat 	stcLonLat;
struct SQ stcQ;

void JY901_Process()
{
		if(JY901_data.Rx_len < RXBUFFER_LEN) return;   	//如果位数不对

		for(uint8_t i=0;i<9;i++)
		{
				if(JY901_data.RxBuffer[i*11]!= JY901_data.frame_head) return;	//如果帧头不对
				switch(JY901_data.RxBuffer[i*11+1])
				{
						case 0x51:	
							memcpy(&stcAcc,&JY901_data.RxBuffer[2 + i*11],8);
							for(uint8_t j = 0; j < 3; j++) 
						JY901_data.acc.a[j] = (float)stcAcc.a[j]/32768*16;
						break;
						
						case 0x52:	
							memcpy(&stcGyro,&JY901_data.RxBuffer[2 + i*11],8);
							for(uint8_t j = 0; j < 3; j++) 
						JY901_data.w.w[j] = (float)stcGyro.w[j]/32768*2000;
						break;
						
						case 0x53:			
							memcpy(&stcAngle,&JY901_data.RxBuffer[2 + i*11],8);
							for(uint8_t j = 0; j < 3; j++) 
						JY901_data.angle.angle[j] = (float)stcAngle.Angle[j]/32768*180;
						break;
						
						case 0x54:	//磁场解算
							memcpy(&stcMag,&JY901_data.RxBuffer[2 + i*11],8);
							for(uint8_t j = 0; j < 3; j++) 
						JY901_data.h.h[j] = (float)stcMag.h[j];
						break;
						
						case 0x55:	//D0-D3端口状态
						break;
						
						case 0x56:	//气压高度
							memcpy(&stcPress,&JY901_data.RxBuffer[2 + i*11],8);			
						JY901_data.lPressure.lPressure = (float)stcPress.lPressure;
						JY901_data.lPressure.lAltitude = (float)stcPress.lAltitude/100;
						break;
						
						case 0x57:	//经纬度
							memcpy(&stcLonLat.lLat,&JY901_data.RxBuffer[2 + i*11],8);
						JY901_data.lLon.lLat = (float)stcLonLat.lLat/10000000+(double)(stcLonLat.lLat % 10000000)/1e5;	
						JY901_data.lLon.lLat = (float)stcLonLat.lLon/10000000+(double)(stcLonLat.lLon % 10000000)/1e5;
						break;
						
						case 0x58:	//GPS
						break;
						
						case 0x59:	//四元数
							memcpy(&stcQ,&JY901_data.RxBuffer[2 + i*11],8);
							for(uint8_t j = 0; j < 4; j++) 
						JY901_data.q.q[j] = (float)stcQ.q[j]/32768;		
						break;	
				}
		}
}

3.3现象观测

打开上位机软件,勾选需要测试的数据。

在这里插入图片描述
可以看到JY901读取到的数据,作为参考量。
在这里插入图片描述
在debug模式,读取到的数据。

在这里插入图片描述

4.I2C通信JY901

JY901的I2C通信的排针没有焊,以后抽空更。
I2C通信也可参见该博客。
STM32 CubeMx(六)I2C同步串行通信与EEPROM 24C02的读写

  • 30
    点赞
  • 191
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
串口DMA空闲中断是一种用于处理STM32系列微控制器中串口DMA传输完成后的中断。当DMA传输完成后,会触发串口空闲中断,我们可以在该中断函数中进行相关处理,比如处理接收到的数据或发送下一帧数据。 下面是一个基本的实现流程: 1. 配置串口DMA的相关寄存器。 2. 初始化中断服务函数,设置空闲中断优先级。 3. 在空闲中断函数中进行相关处理。 具体步骤如下: 1. 配置串口DMA相关寄存器。首先,你需要配置串口的相关寄存器,如波特率、数据位、停止位等。然后,配置DMA通道,设置传输方向、传输大小、传输模式等。最后,使能串口DMA。 2. 初始化中断服务函数。你需要编写一个中断服务函数,并设置其优先级。你可以使用HAL库提供的函数来初始化中断服务函数。 3. 在空闲中断函数中进行相关处理。当DMA传输完成后,会触发串口空闲中断。在该中断函数中,你可以读取接收缓冲区中的数据或发送下一帧数据。 以下是一个简单的示例代码,用于配置USART1和DMA1通道5进行串口接收: ```c #include "stm32f4xx.h" // 定义接收缓冲区大小 #define BUFFER_SIZE 100 // 定义接收缓冲区 uint8_t rx_buffer[BUFFER_SIZE]; // 初始化空闲中断回调函数 void HAL_UART_IdleCallback(UART_HandleTypeDef *huart) { // 判断是否是串口1触发的空闲中断 if (huart->Instance == USART1) { // 获取接收数据长度 uint32_t length = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx); // 处理接收到的数据 // TODO: 在这里添加你的数据处理代码 // 重新启动DMA传输 HAL_UART_Receive_DMA(huart, rx_buffer, BUFFER_SIZE); } } int main(void) { // 初始化HAL库 HAL_Init(); // 配置串口1 __HAL_RCC_USART1_CLK_ENABLE(); USART1->BRR = 16000000 / 115200; // 波特率为115200 USART1->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; // 使能串口和发送/接收功能 // 配置DMA1通道5 __HAL_RCC_DMA1_CLK_ENABLE(); DMA1_Stream5->PAR = (uint32_t)(&(USART1->DR)); // 外设地址为串口1的数据寄存器地址 DMA1_Stream5->M0AR = (uint32_t)rx_buffer; // 存储器地址为接收缓冲区地址 DMA1_Stream5->NDTR = BUFFER_SIZE; // 数据长度为接收缓冲区大小 DMA1_Stream5->CR |= DMA_SxCR_CHSEL_4 | DMA_SxCR_CHSEL_0; // 选择通道4 DMA1_Stream5->CR |= DMA_SxCR_PL_0; // 设置DMA传输优先级为低 DMA1_Stream5->CR |= DMA_SxCR_MINC | DMA_SxCR_CIRC; // 使能存储器递增模式和循环模式 DMA1_Stream5->CR |= DMA_SxCR_TCIE; // 使能传输完成中断 // 配置空闲中断 NVIC_SetPriority(USART1_IRQn, 0); // 设置中断优先级为最高 NVIC_EnableIRQ(USART1_IRQn); // 使能中断 // 启动DMA传输 HAL_UART_Receive_DMA(&huart1, rx_buffer, BUFFER_SIZE); while (1) { // 主循环代码 // TODO: 在这里添加你的主循环代码 } } // 空闲中断处理函数 void USART1_IRQHandler(void) { HAL_UART_IRQHandler(&huart1); } // DMA传输完成中断处理函数 void DMA1_Stream5_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart1_rx); } ``` 请根据你的具体需求进行适当的修改和优化。希望以上信息对你有所帮助!如有任何疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值