文章目录
-
- 概要
- 代码
概要
陀螺仪 ,我们在控制类项目中最常用到的模块,至于具体的陀螺仪的原理和陀螺仪能干什么,我们就不在文章中细述,大家可以参考别的文章,所以要阅读此文的前提是你知道陀螺仪是干什么的和你需要陀螺仪来干什么
常见的陀螺仪有mpu6050(如下图)
虽然6050模块在价格和开源资料方面有很大的优势,但其精度(经常零漂)却很难帮助我们完成一些需要精准控制的项目,而且其DMP库移植的麻烦和各种抽象问题会让使用者头疼
所以介于此我们今天介绍一个全新的模块JY61,其精度在6050的基础上有了很大的改善,使零漂误差减少,且维特智能开发的上位机能更好的帮助我们直观的读取和感受角度的变换,还有就是其通过串口收发消息的方式更简单明了。
维特智能开发的上位机
代码
连线
|------|---|------|---|
| JY61 | | 单片机 | |
| VCC | | 3.3v | |
| RX | | TX | |
| TX | | RX | |
| GND | | GND | |
先要使能两个串口 串口一和串口二, 串口二用来接受JY62发送的串口信息,串口一用来将串口二接受的消息发送给电脑的串口调试助手帮助观测数据
使能串口一二,波特率选择115200(原因是JY62的默认波特率使115200),其他配置默认
如下就是全部代码,我在每行代码都进行了注释,在此我只简述代码流程
先开启了串口二的中断回调函数,JY62是不断在给我们的单片机通过串口发送消息的,只要我们的单片机通过串口二接收到JY62发送的消息就会进入串口二的中断回调函数,进入函数后我们会对串口而接受的信息进行处理(为什么这么处理是因为维特智能给出了数据的处理方式,我把图片给到最后供大家看),处理完串口二从JY62接受到的数据之后,我们进入while(1)中,将数据通过串口一发送到我们的电脑
#define RXBUFFERSIZE 255 //最大的接受字节数
#define TXBUFFERSIZE 255 //最大发送字节数
char RxBuffer[RXBUFFERSIZE]; //接受数据
char TxBuffer[TXBUFFERSIZE]; //发送数据
uint8_t aRxBuffer; //接受中断缓冲
uint8_t Uart1_Rx_Cnt = 0; //接受缓冲计数
uint8_t RxSucceeflag = 0; //接受成功标志
float aX = 0; // x轴上的加速度
float aY = 0; // y轴上的加速度
float aZ = 0; // z轴上的加速度
float wX = 0; // x轴上的角速度
float wY = 0; // y轴上的角速度
float wZ = 0; // z轴上的角速度
float RollX = 0; // 滚转角
float PitchY = 0; // 仰俯角
float YawZ = 0; // 偏航角
uint8_t hexData[] = {0xFF, 0xAA, 0x52}; //数组来存储发送的z轴角度归零的数据
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart2, (uint8_t *)RxBuffer, 44); //开启串口中断接受函数,将接受到的数据存到RxBuffer中
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
if(1 == RxSucceeflag) //判断标志位是否触发
{
HAL_UART_Transmit(&huart2, hexData, sizeof(hexData), HAL_MAX_DELAY); //发送指令给JY62使其z轴角度归零
RxSucceeflag = 0; //将标志位置为0
memset(TxBuffer,0x00,sizeof(TxBuffer)); //将接受数组清零 ,保证数据存入时数组中无其他数据
sprintf(TxBuffer,"W_X%.2f,W_Y%.2f,W_Z%.2f,R%.2f,P%.2f,Y_A%.2f\r\n", wX,wY,wZ,RollX,PitchY,YawZ);//将数据通通过sprintf函数存储到TxBuffer中去
HAL_UART_Transmit(&huart1, TxBuffer, strlen(TxBuffer),0xFFFF); //将数据通过串口一发送出去
while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测USART发送结束
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
int checkSum(char RxBuffer[])
{
int i; // 循环变量
int sum = 0;
for(i=0;i<10;i++)
{
sum = sum+RxBuffer[i];
}
if(RxBuffer[10] == (char)(sum))
{
return 1; // 返回1表示数据正确
}
else
{
return -1; // 返回-1表示数据错误
}
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) // 错误回调函数,串口接受数据错误时会进入此函数来对寄存器进行清除
{
uint32_t isrflags = READ_REG(huart->Instance->SR);//
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_PE))!=RESET)
{
READ_REG(huart->Instance->DR);//
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_PE);//
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_FE))!=RESET)
{
READ_REG(huart->Instance->DR);//
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_FE);
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_NE))!=RESET)
{
READ_REG(huart->Instance->DR);//
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_NE);
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE))!=RESET)
{
READ_REG(huart->Instance->CR1);//
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE);
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //串口中断回调函数,JY62每发送一次数据就会进入一次该函数
{
char tempBuffer[100] ; // 中间转存数组
char i = 0; //循环变量 //
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
if(huart == &huart2) 判断是否是串口二触发的中断
{
if(0X51 == RxBuffer[1]) //判断数据帧投是否正确
{
memset(tempBuffer,0x00,sizeof(tempBuffer)); //清楚中间转存数组
for(i=0;i<11;i++) //循环遍历将接受到的数据存储到转存数组,方便后面传入检验函数
{
tempBuffer[i] = RxBuffer[i]; //将接受到的数据存入中间转存数组,方便检查函数的检查
}
if(1 == checkSum(tempBuffer)) //检验函数对数据的检验和进行校验,若正确返回1
{
aX = (float)((float)((RxBuffer[3]<<8)|RxBuffer[2])/32768.0*16*9.8);//处理数据,依据官方所给文档
aY = (float)((float)((RxBuffer[5]<<8)|RxBuffer[4])/32768.0*16*9.8);
aZ = (float)((float)((RxBuffer[7]<<8)|RxBuffer[6])/32768.0*16*9.8);
}
}
if(0X52 == RxBuffer[12]) //判断数据帧投是否正确
{
memset(tempBuffer,0x00,sizeof(tempBuffer)); //清楚中间转存数组
for(i=11;i<22;i++) //循环遍历将接受到的数据存储到转存数组,方便后面传入检验函数
{
tempBuffer[i-11] = RxBuffer[i];//将接受到的数据存入中间转存数组,方便检查函数的检查
}
if(1 == checkSum(tempBuffer)) //检验函数对数据的检验和进行校验,若正确返回1
{
wX = (float)(((RxBuffer[14]<<8)|RxBuffer[13])/32768.0*2000);//处理数据,依据官方所给文档
wY = (float)(((RxBuffer[16]<<8)|RxBuffer[15])/32768.0*2000);
wZ = (float)(((RxBuffer[18]<<8)|RxBuffer[17])/32768.0*2000);
}
}
if(0X53 == RxBuffer[23])//判断数据帧投是否正确
{
memset(tempBuffer,0x00,sizeof(tempBuffer)); //清楚中间转存数组
for(i=22;i<33;i++)//循环遍历将接受到的数据存储到转存数组,方便后面传入检验函数
{
tempBuffer[i-22] = RxBuffer[i];//将接受到的数据存入中间转存数组,方便检查函数的检查
}
if(1 == checkSum(tempBuffer))//检验函数对数据的检验和进行校验,若正确返回1
{
RollX = (float)(((RxBuffer[25]<<8)|RxBuffer[24])/32768.0*180);//处理数据,依据官方所给文档
PitchY = (float)(((RxBuffer[27]<<8)|RxBuffer[26])/32768.0*180);
YawZ = (float)(((RxBuffer[29]<<8)|RxBuffer[28])/32768.0*180);
}
}
RxSucceeflag = 1; //置标志位为1,使能够进入主函数中
memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空接受数组,使JY62下次发送数据时 存储数据的数组中没有其他数据
HAL_UART_Receive_IT(&huart2, (uint8_t *)RxBuffer, 44); //开启接受中断,保证下次数据接受时能进入串口回调函数
}
}
数据为何要做如下处理的原因(如下图)
wX = (float)(((RxBuffer[14]<<8)|RxBuffer[13])/32768.0*2000);
wY= (float)(((RxBuffer[16]<<8)|RxBuffer[15])/32768.0*2000);
wZ = (float)(((RxBuffer[18]<<8)|RxBuffer[17])/32768.0*2000);